pi-ui-extend 0.1.33 → 0.1.34
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/app/icons.js +1 -1
- package/dist/app/model/model-usage-status.d.ts +2 -1
- package/dist/app/model/model-usage-status.js +33 -25
- package/dist/app/rendering/status-line-renderer.d.ts +1 -0
- package/dist/app/rendering/status-line-renderer.js +15 -1
- package/dist/app/session/session-event-controller.js +32 -7
- package/external/pi-tools-suite/src/tool-descriptions.ts +34 -53
- package/package.json +1 -1
package/dist/app/icons.js
CHANGED
|
@@ -7,13 +7,14 @@ export type ModelUsageDescriptor = BaseModelUsageDescriptor & ({
|
|
|
7
7
|
} | {
|
|
8
8
|
readonly kind: "google-antigravity";
|
|
9
9
|
readonly quotaModelKey: string;
|
|
10
|
-
readonly account
|
|
10
|
+
readonly account?: AntigravityQuotaAccount;
|
|
11
11
|
readonly accounts?: readonly AntigravityQuotaAccount[];
|
|
12
12
|
});
|
|
13
13
|
export type ModelUsageLimitWindow = {
|
|
14
14
|
readonly remainingPercent: number;
|
|
15
15
|
readonly resetAt: number;
|
|
16
16
|
readonly windowSeconds: number;
|
|
17
|
+
readonly hasKnownWindowDuration?: boolean;
|
|
17
18
|
};
|
|
18
19
|
export type ModelUsageStatus = {
|
|
19
20
|
readonly modelKey: string;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
2
|
import { readFile } from "node:fs/promises";
|
|
4
3
|
import { homedir } from "node:os";
|
|
5
4
|
import { join } from "node:path";
|
|
6
5
|
import { formatCompactProgressBar } from "../../context-progress-bar.js";
|
|
6
|
+
import { APP_ICONS } from "../icons.js";
|
|
7
7
|
const OPENAI_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
8
8
|
const ZAI_QUOTA_URL = "https://api.z.ai/api/monitor/usage/quota/limit";
|
|
9
9
|
const ZHIPU_QUOTA_URL = "https://bigmodel.cn/api/monitor/usage/quota/limit";
|
|
@@ -35,16 +35,12 @@ export function modelUsageDescriptor(model) {
|
|
|
35
35
|
}
|
|
36
36
|
if (ANTIGRAVITY_QUOTA_PROVIDERS.has(provider)) {
|
|
37
37
|
const quotaModelKey = resolveAntigravityQuotaModelKey(model);
|
|
38
|
-
|
|
39
|
-
const account = readActiveAntigravityQuotaAccount(accounts);
|
|
40
|
-
if (!quotaModelKey || !account)
|
|
38
|
+
if (!quotaModelKey)
|
|
41
39
|
return undefined;
|
|
42
40
|
return {
|
|
43
41
|
kind: "google-antigravity",
|
|
44
|
-
modelKey: `${model.provider}/${model.id}
|
|
42
|
+
modelKey: `${model.provider}/${model.id}`,
|
|
45
43
|
quotaModelKey,
|
|
46
|
-
account,
|
|
47
|
-
accounts,
|
|
48
44
|
};
|
|
49
45
|
}
|
|
50
46
|
return undefined;
|
|
@@ -395,7 +391,7 @@ export function googleAntigravityUsageStatusFromResponse(data, descriptor, now =
|
|
|
395
391
|
modelKey: descriptor.modelKey,
|
|
396
392
|
provider: "google-antigravity",
|
|
397
393
|
updatedAt: now,
|
|
398
|
-
...(descriptor.account
|
|
394
|
+
...(descriptor.account?.email ? { accountEmail: descriptor.account.email } : {}),
|
|
399
395
|
...(weekly ? { weekly } : {}),
|
|
400
396
|
...(hourly ? { hourly } : {}),
|
|
401
397
|
};
|
|
@@ -413,7 +409,9 @@ function googleAntigravityWindowFromResponse(data, quotaModelKey, now) {
|
|
|
413
409
|
}
|
|
414
410
|
async function queryGoogleAntigravityModelUsage(descriptor) {
|
|
415
411
|
const now = Date.now();
|
|
416
|
-
const accounts =
|
|
412
|
+
const accounts = await readAllAntigravityQuotaAccounts();
|
|
413
|
+
if (accounts.length === 0)
|
|
414
|
+
return undefined;
|
|
417
415
|
const windows = (await Promise.all(accounts.map(async (account) => {
|
|
418
416
|
try {
|
|
419
417
|
const response = await fetchGoogleAntigravityQuotaForAccount(account, now);
|
|
@@ -451,7 +449,7 @@ const GOOGLE_ACCOUNT_QUOTA_WINDOWS = [
|
|
|
451
449
|
{ label: "G3 Pro", quotaModelKey: "gemini-3.1-pro-low" },
|
|
452
450
|
];
|
|
453
451
|
async function queryGoogleAntigravityAccountUsage(now) {
|
|
454
|
-
const accounts = readAllAntigravityQuotaAccounts();
|
|
452
|
+
const accounts = await readAllAntigravityQuotaAccounts();
|
|
455
453
|
const results = await Promise.all(accounts.map(async (account) => {
|
|
456
454
|
const accountLabel = account.email ?? maskCredential(account.refreshToken);
|
|
457
455
|
try {
|
|
@@ -474,12 +472,8 @@ async function queryGoogleAntigravityAccountUsage(now) {
|
|
|
474
472
|
}));
|
|
475
473
|
return results;
|
|
476
474
|
}
|
|
477
|
-
function
|
|
478
|
-
const credential =
|
|
479
|
-
return accounts[clampAccountIndex(credential?.activeIndex, accounts.length)];
|
|
480
|
-
}
|
|
481
|
-
function readAllAntigravityQuotaAccounts() {
|
|
482
|
-
const credential = readPiAuthSync().antigravity;
|
|
475
|
+
async function readAllAntigravityQuotaAccounts() {
|
|
476
|
+
const credential = (await readPiAuth()).antigravity;
|
|
483
477
|
if (!credential)
|
|
484
478
|
return [];
|
|
485
479
|
const credentialClient = getGoogleOAuthClientCredentials(credential);
|
|
@@ -504,14 +498,6 @@ function readAllAntigravityQuotaAccounts() {
|
|
|
504
498
|
}) : undefined;
|
|
505
499
|
return account ? [account] : [];
|
|
506
500
|
}
|
|
507
|
-
function readPiAuthSync() {
|
|
508
|
-
try {
|
|
509
|
-
return JSON.parse(readFileSync(getPiAuthPath(), "utf8"));
|
|
510
|
-
}
|
|
511
|
-
catch {
|
|
512
|
-
return {};
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
501
|
function getAccountRefreshToken(account) {
|
|
516
502
|
if (account.refreshToken)
|
|
517
503
|
return account.refreshToken;
|
|
@@ -844,6 +830,7 @@ function modelUsageWindow(window, now) {
|
|
|
844
830
|
remainingPercent: clampPercent(Math.round(100 - window.used_percent)),
|
|
845
831
|
resetAt: now + Math.max(0, Math.round(window.reset_after_seconds)) * 1000,
|
|
846
832
|
windowSeconds: Math.max(0, Math.round(window.limit_window_seconds)),
|
|
833
|
+
hasKnownWindowDuration: true,
|
|
847
834
|
};
|
|
848
835
|
}
|
|
849
836
|
function accountWindowFromRateLimit(window, now) {
|
|
@@ -930,5 +917,26 @@ function maskCredential(value) {
|
|
|
930
917
|
return `${visible.slice(0, 4)}****${visible.slice(-4)}`;
|
|
931
918
|
}
|
|
932
919
|
function formatUsageWindow(_prefix, window, now) {
|
|
933
|
-
|
|
920
|
+
const warning = modelUsageWindowWillExhaustBeforeReset(window, now) ? ` ${APP_ICONS.alert}` : "";
|
|
921
|
+
return `${window.remainingPercent}% ${formatCompactProgressBar(window.remainingPercent)}${warning} ${formatDurationShort(window.resetAt, now)}`;
|
|
922
|
+
}
|
|
923
|
+
function modelUsageWindowWillExhaustBeforeReset(window, now) {
|
|
924
|
+
if (!window.hasKnownWindowDuration)
|
|
925
|
+
return false;
|
|
926
|
+
if (window.windowSeconds <= DAY_SECONDS)
|
|
927
|
+
return false;
|
|
928
|
+
if (window.remainingPercent <= 0)
|
|
929
|
+
return false;
|
|
930
|
+
const timeUntilResetSeconds = Math.max(0, (window.resetAt - now) / 1000);
|
|
931
|
+
const elapsedSeconds = Math.max(0, window.windowSeconds - timeUntilResetSeconds);
|
|
932
|
+
if (elapsedSeconds <= 0)
|
|
933
|
+
return false;
|
|
934
|
+
const total = 100;
|
|
935
|
+
const used = total - window.remainingPercent;
|
|
936
|
+
if (used <= 0)
|
|
937
|
+
return false;
|
|
938
|
+
const remaining = total - used;
|
|
939
|
+
const averageRate = used / elapsedSeconds;
|
|
940
|
+
const projectedSecondsUntilExhaustion = remaining / averageRate;
|
|
941
|
+
return projectedSecondsUntilExhaustion < timeUntilResetSeconds;
|
|
934
942
|
}
|
|
@@ -71,6 +71,7 @@ export declare class StatusLineRenderer {
|
|
|
71
71
|
private pushVoiceWidgetSegment;
|
|
72
72
|
private pushWorkspaceSegments;
|
|
73
73
|
private pushModelUsageSegments;
|
|
74
|
+
private modelUsageHasWarning;
|
|
74
75
|
private modelUsageResetLength;
|
|
75
76
|
private pushSegment;
|
|
76
77
|
private pushContextBarSegments;
|
|
@@ -491,11 +491,25 @@ export class StatusLineRenderer {
|
|
|
491
491
|
fill: color,
|
|
492
492
|
track: this.host.theme.colors.statusDotBase,
|
|
493
493
|
}, MODEL_USAGE_PROGRESS_BAR_WIDTH));
|
|
494
|
-
const
|
|
494
|
+
const defaultResetStart = barStart + MODEL_USAGE_PROGRESS_BAR_WIDTH + 1;
|
|
495
|
+
const warningStart = this.modelUsageHasWarning(modelUsageLabel, defaultResetStart - labelStart)
|
|
496
|
+
? defaultResetStart
|
|
497
|
+
: undefined;
|
|
498
|
+
const resetStart = warningStart === undefined
|
|
499
|
+
? defaultResetStart
|
|
500
|
+
: warningStart + APP_ICONS.alert.length + 1;
|
|
501
|
+
if (warningStart !== undefined) {
|
|
502
|
+
this.pushSegment(segments, warningStart, APP_ICONS.alert.length, this.host.theme.colors.warning);
|
|
503
|
+
}
|
|
495
504
|
const resetLength = this.modelUsageResetLength(modelUsageLabel, resetStart - labelStart);
|
|
496
505
|
this.pushSegment(segments, resetStart, resetLength, this.host.theme.colors.muted);
|
|
497
506
|
}
|
|
498
507
|
}
|
|
508
|
+
modelUsageHasWarning(modelUsageLabel, localStart) {
|
|
509
|
+
if (localStart < 0 || localStart >= modelUsageLabel.length)
|
|
510
|
+
return false;
|
|
511
|
+
return modelUsageLabel.startsWith(APP_ICONS.alert, localStart);
|
|
512
|
+
}
|
|
499
513
|
modelUsageResetLength(modelUsageLabel, localStart) {
|
|
500
514
|
if (localStart < 0 || localStart >= modelUsageLabel.length)
|
|
501
515
|
return 0;
|
|
@@ -195,7 +195,7 @@ export class AppSessionEventController {
|
|
|
195
195
|
this.host.updateQueuedMessageStatus();
|
|
196
196
|
break;
|
|
197
197
|
case "message_update":
|
|
198
|
-
this.handleMessageUpdate(event
|
|
198
|
+
this.handleMessageUpdate(event);
|
|
199
199
|
break;
|
|
200
200
|
case "tool_execution_start":
|
|
201
201
|
this.finishCurrentThinkingEntry();
|
|
@@ -506,7 +506,8 @@ export class AppSessionEventController {
|
|
|
506
506
|
return;
|
|
507
507
|
this.host.recordWorkspaceMutationForUserEntry(prepared.userEntryId, mutation);
|
|
508
508
|
}
|
|
509
|
-
handleMessageUpdate(
|
|
509
|
+
handleMessageUpdate(event) {
|
|
510
|
+
const assistantEvent = event.assistantMessageEvent;
|
|
510
511
|
if (this.assistantMessageClosed && assistantEvent.type !== "done")
|
|
511
512
|
return;
|
|
512
513
|
this.assistantMessageClosed = false;
|
|
@@ -522,7 +523,13 @@ export class AppSessionEventController {
|
|
|
522
523
|
case "text_delta":
|
|
523
524
|
this.finishCurrentThinkingEntry();
|
|
524
525
|
this.host.setSessionActivity("running");
|
|
525
|
-
|
|
526
|
+
{
|
|
527
|
+
const snapshotText = assistantTextSnapshotForContentIndex(event.message, assistantEvent.partial, assistantEvent.contentIndex);
|
|
528
|
+
if (snapshotText === undefined)
|
|
529
|
+
this.appendAssistantText(assistantEvent.delta, assistantEvent.contentIndex);
|
|
530
|
+
else
|
|
531
|
+
this.reconcileAssistantTextBlock(snapshotText, assistantEvent.contentIndex, { keepOpen: true });
|
|
532
|
+
}
|
|
526
533
|
break;
|
|
527
534
|
case "text_end":
|
|
528
535
|
this.finishCurrentThinkingEntry();
|
|
@@ -616,7 +623,7 @@ export class AppSessionEventController {
|
|
|
616
623
|
entry.text += visibleText;
|
|
617
624
|
this.touchEntry(entry);
|
|
618
625
|
}
|
|
619
|
-
reconcileAssistantTextBlock(content, contentIndex) {
|
|
626
|
+
reconcileAssistantTextBlock(content, contentIndex, options = {}) {
|
|
620
627
|
this.flushAssistantTextBuffer(true);
|
|
621
628
|
const hasVisibleTextBeforeBlock = this.hasVisibleTextBeforeCurrentAssistantBlock();
|
|
622
629
|
// C.11: normalise CRLF in the final block content (see appendAssistantText).
|
|
@@ -667,9 +674,16 @@ export class AppSessionEventController {
|
|
|
667
674
|
this.currentAssistantEntryId = entry.id;
|
|
668
675
|
if (contentIndex !== undefined)
|
|
669
676
|
this.assistantTextBlocksByContentIndex.set(contentIndex, visibleText);
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
677
|
+
if (options.keepOpen) {
|
|
678
|
+
this.currentAssistantTextBlockEntryId = entry.id;
|
|
679
|
+
this.currentAssistantTextBlockStartLength = startLength;
|
|
680
|
+
this.currentAssistantTextBlockContentIndex = contentIndex;
|
|
681
|
+
}
|
|
682
|
+
else {
|
|
683
|
+
this.currentAssistantTextBlockEntryId = undefined;
|
|
684
|
+
this.currentAssistantTextBlockStartLength = undefined;
|
|
685
|
+
this.currentAssistantTextBlockContentIndex = undefined;
|
|
686
|
+
}
|
|
673
687
|
this.assistantTextBuffer = "";
|
|
674
688
|
}
|
|
675
689
|
ensureAssistantTextBlockStarted(entry) {
|
|
@@ -891,6 +905,17 @@ function partialToolCallAt(partial, contentIndex) {
|
|
|
891
905
|
const block = partial.content[contentIndex];
|
|
892
906
|
return isRecord(block) && block.type === "toolCall" ? block : undefined;
|
|
893
907
|
}
|
|
908
|
+
function assistantTextSnapshotForContentIndex(message, partial, contentIndex) {
|
|
909
|
+
if (contentIndex === undefined)
|
|
910
|
+
return undefined;
|
|
911
|
+
return assistantTextContentAt(message, contentIndex) ?? assistantTextContentAt(partial, contentIndex);
|
|
912
|
+
}
|
|
913
|
+
function assistantTextContentAt(value, contentIndex) {
|
|
914
|
+
if (!isRecord(value) || !Array.isArray(value.content))
|
|
915
|
+
return undefined;
|
|
916
|
+
const block = value.content[contentIndex];
|
|
917
|
+
return isRecord(block) && block.type === "text" && typeof block.text === "string" ? block.text : undefined;
|
|
918
|
+
}
|
|
894
919
|
function assistantStreamVisibleTextForCompleteBlock(text, hasVisibleTextBeforeBlock) {
|
|
895
920
|
let buffer = text;
|
|
896
921
|
let visibleText = "";
|
|
@@ -72,52 +72,38 @@ export function asyncSubagentToolDescriptions(options: ToolDescriptionSetOptions
|
|
|
72
72
|
label: "Subagents",
|
|
73
73
|
description: [
|
|
74
74
|
"Manage isolated async sub-agents for large, parallel, context-heavy work.",
|
|
75
|
-
"
|
|
76
|
-
"
|
|
75
|
+
"Presets from async-subagents config and /subagent-preset choose role model/thinking/args; AGENTS_PRESET or /subagent-preset session <name> overrides the current session; /subagent-preset init creates a sample config.",
|
|
76
|
+
"Omit subagentType so the router chooses a configured role unless the user or task requires a role, vision, or deterministic override.",
|
|
77
77
|
repoDiscovery
|
|
78
78
|
? "Use for broad independent tracks, review axes, or hypotheses even though repo_* tools are available."
|
|
79
79
|
: "Use first for broad codebase discovery split into tracks, review axes, or incident-triage hypotheses when repo_* tools are unavailable.",
|
|
80
|
-
"Use action=spawn/status/wait/result/stop/cleanup
|
|
81
|
-
"
|
|
82
|
-
"Spawned agents run in isolated background pi processes with extensions disabled to prevent recursive sub-agent spawning.",
|
|
83
|
-
"Each agent has a wall-clock watchdog timeout (default 30 minutes); spawn timeoutSeconds or task timeoutSeconds can shorten it for tests or bounded probes.",
|
|
84
|
-
"Concurrency is limited project-wide by maxConcurrent (default 5); excess agents queue automatically.",
|
|
85
|
-
"Failed agents can be auto-retried with exponential backoff when retry is configured per type or globally.",
|
|
86
|
-
"Preset fallbackModels can switch future sub-agent spawns in the current process/session to a fallback provider/model after quota/rate-limit failures; Antigravity account rotation is allowed to exhaust all accounts for the model first.",
|
|
80
|
+
"Use action=spawn/status/wait/result/stop/cleanup; .pi/subagents tracks runs so status/wait/stop can omit the latest runDir and result can resolve by agentId. Parent sessions receive completion/failure follow-ups, so spawn can return without polling.",
|
|
81
|
+
"Results are compact with artifact links. Agents run isolated pi processes with extensions disabled to prevent recursive spawning; spawn/task timeoutSeconds can shorten the default 30m watchdog, project concurrency queues excess agents, and retry backoff/fallback models/Antigravity account rotation are config-driven.",
|
|
87
82
|
].join(" "),
|
|
88
83
|
promptSnippet:
|
|
89
|
-
"Use subagents action='spawn'
|
|
90
|
-
"
|
|
84
|
+
"Use subagents action='spawn' for multiple independent agents, explicit delegate/parallelize/split work requests, or one large review/debug track that should stay out of the parent context. " +
|
|
85
|
+
"Usually omit subagentType so the router chooses; set it only for user-named roles, vision/image handling, deterministic tests, or another concrete override. Avoid trivial reads/edits and do not call status/wait immediately after spawn just for progress. " +
|
|
91
86
|
(repoDiscovery
|
|
92
|
-
? "For one semantic code-discovery question, use repo_search
|
|
93
|
-
: "For one focused code-discovery question, use direct read/grep tools
|
|
87
|
+
? "For one semantic code-discovery question, use repo_search; for independent tracks/hypotheses/review axes, delegate even when repo_* tools exist. Read result only after completion when findings are needed."
|
|
88
|
+
: "For one focused code-discovery question, use direct read/grep. Without repo_* tools, spawn several focused scan/quick agents first for broad multi-track discovery, incident triage, release readiness, risk strategy, or parallel reviews. Read result only after completion when findings are needed."),
|
|
94
89
|
promptGuidelines: [
|
|
95
90
|
"Use action='spawn' only for LARGE or PARALLEL tasks: independent investigations, repo-wide sweeps, deep debugging, or code review/audit that would bloat the parent context.",
|
|
96
91
|
"Treat explicit requests to delegate, use sub-agents, run parallel agents, split into independent tracks, investigate hypotheses, or run separate review axes as spawn triggers; spawn first unless the request is trivial or clearly single-file.",
|
|
97
92
|
repoDiscovery
|
|
98
|
-
? "
|
|
99
|
-
: "
|
|
93
|
+
? "For one discovery question, use repo_search; spawn only for several independent tracks/hypotheses/review axes, and do not let repo_* availability suppress delegation."
|
|
94
|
+
: "For one discovery question, use direct read/grep; without repo_* tools, spawn a small focused swarm before parent-context search when the task spans several files/modules/hypotheses/tracks.",
|
|
100
95
|
repoDiscovery
|
|
101
|
-
? "
|
|
102
|
-
: "
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
? "Do not use subagents for exact-string lookups, known-file edits, typo/text replacements, or obvious one-file changes; use the cheapest direct path instead."
|
|
108
|
-
: "Do not use subagents for exact-string lookups, known-file edits, typo/text replacements, or obvious one-file changes; use the cheapest direct path instead.",
|
|
109
|
-
"Spawn multiple focused agents in one action='spawn' call when they investigate independent questions.",
|
|
110
|
-
"For synthetic tests or intentionally bounded probes, pass timeoutSeconds slightly above the expected runtime so hung sub-agents are stopped automatically.",
|
|
111
|
-
"For subagents action='spawn', default to leaving subagentType unset and let the lightweight router choose from configured role descriptions. Do not choose a role just because a built-in example seems to fit; the router has the current user config and presets. Set subagentType explicitly only when the user named the role, image inspection requires vision, tests need deterministic routing, or there is another concrete technical reason to bypass the router.",
|
|
112
|
-
"Use subagentType='vision' with imagePaths and optional focus when a text-only/blind parent model needs a visual description of screenshots, UI state, diagrams, or other images.",
|
|
96
|
+
? "For incident triage, release readiness, or risk/test strategy with separate hypotheses or review tracks, prefer focused review agents over serial parent-context work."
|
|
97
|
+
: "For incident triage, release readiness, or risk/test strategy with separate hypotheses or review tracks and no repo_* tools, call action='spawn' as the first discovery step; direct read/grep can follow.",
|
|
98
|
+
"Do not use subagents for exact-string lookups, known-file edits, typo/text replacements, obvious one-file changes, or interactive user input; use the cheapest direct path.",
|
|
99
|
+
"Spawn multiple focused agents in one action='spawn' call for independent questions; for synthetic tests or bounded probes, pass timeoutSeconds slightly above expected runtime.",
|
|
100
|
+
"For spawn, leave subagentType unset so the router chooses from configured roles. Set it only for user-named roles, vision/image handling, deterministic tests, or another concrete override.",
|
|
101
|
+
"Use subagentType='vision' with imagePaths and optional focus when images/screenshots/diagrams need visual inspection.",
|
|
113
102
|
"If the user asks to start, run, launch, or test parallel sub-agents, call action='spawn' and then stop; completion/failure notifications will wake the parent so do not immediately call action='status' or action='wait' just to see whether agents finished.",
|
|
114
|
-
"Use
|
|
115
|
-
"
|
|
116
|
-
"Use action='wait' only when the user asks to wait/collect now, or your next parent step depends on completion.",
|
|
117
|
-
"Use action='result' only after status/wait confirms completion; raw output is not inlined, so inspect linked artifacts only when full details are truly necessary.",
|
|
103
|
+
"Use status for non-blocking progress/recovery, wait only when requested or needed, and result only after completion; registry mappings can recover latest runDir/agentId after reload.",
|
|
104
|
+
"Result output is compact with artifact links; inspect raw artifacts only when full details are necessary.",
|
|
118
105
|
"Use action='stop' when the user asks to stop, cancel, or kill running sub-agents.",
|
|
119
106
|
"Use action='cleanup' with delete=true after collecting all results to free disk space.",
|
|
120
|
-
"Do NOT use subagents for trivial tasks, single file reads, simple edits, or interactive user input.",
|
|
121
107
|
],
|
|
122
108
|
},
|
|
123
109
|
spawnAction: {
|
|
@@ -143,9 +129,8 @@ export function asyncSubagentToolDescriptions(options: ToolDescriptionSetOptions
|
|
|
143
129
|
label: "Subagent Result Action",
|
|
144
130
|
description: [
|
|
145
131
|
"Read output from one async sub-agent after it completes.",
|
|
146
|
-
"
|
|
147
|
-
"
|
|
148
|
-
"Compact output avoids polluting the parent context; full result.md and stderr.log paths are included for manual inspection.",
|
|
132
|
+
"Returns compact structured summary/findings/files/risks/next actions plus artifact paths, not raw result text or stderr.",
|
|
133
|
+
"Writes structured result.json alongside raw result.md; full result.md and stderr.log paths are included for manual inspection.",
|
|
149
134
|
].join(" "),
|
|
150
135
|
},
|
|
151
136
|
stopAction: {
|
|
@@ -209,11 +194,9 @@ export const REPO_DISCOVERY_TOOLS: RepoDiscoveryToolDescription[] = [
|
|
|
209
194
|
promptGuidelines: [
|
|
210
195
|
"Phrase target as the behavior to locate, not a whitespace-separated pile of near-synonyms; keep exact identifiers only as useful anchors.",
|
|
211
196
|
"For initial unknown-file behavior searches, prefer the default hybrid ranking; pass --mode semantic only when lexical/symbol terms are misleading or the query is purely conceptual.",
|
|
212
|
-
"
|
|
213
|
-
"
|
|
214
|
-
"
|
|
215
|
-
"For mutation-site, bug-location, or behavior-cause questions, the assignment/write/branch/call that directly causes the behavior is the answer. If a read range contains that exact evidence, stop searching and answer from it.",
|
|
216
|
-
"Avoid repeated near-duplicate searches after finding direct source evidence. Continue only for a named gap such as callers, persistence path, tests, or user-requested impact analysis, and make the follow-up query narrower.",
|
|
197
|
+
"Make one targeted search, read the best returned ranges (prefer read over --include-content for full evidence), then refine only for a named gap; do not launch duplicate broad searches.",
|
|
198
|
+
"Use a specific conceptual target and narrow with --path-prefix, --max-files, --dedupe-file, or --exclude-tests when useful.",
|
|
199
|
+
"For bug/cause questions, stop when a read range shows the causal assignment/write/branch/call; continue only for a named gap such as callers, persistence, tests, or requested impact, and narrow follow-ups.",
|
|
217
200
|
],
|
|
218
201
|
targetDescription: "Natural-language behavior query, e.g. auth session token validation.",
|
|
219
202
|
},
|
|
@@ -248,25 +231,23 @@ export const REPO_DISCOVERY_TOOL_NAMES = REPO_DISCOVERY_TOOLS.map((tool) => tool
|
|
|
248
231
|
export const TODO_TOOL_DESCRIPTION: ToolDescription = {
|
|
249
232
|
name: "todo",
|
|
250
233
|
label: "Todo",
|
|
251
|
-
description: "Track and
|
|
234
|
+
description: "Track and synchronize non-trivial multi-step work. Actions: create, update, batch_create, batch_update, list, get, delete, clear, export, import. Supports hierarchy, blockers, deferred/out-of-scope items, dependencies, and replace:true for replacing obsolete plans. Skip trivial or chat-only requests; resync when requirements or discovered facts change. For multi-step plans, include a final user-facing report todo and keep exactly one task in_progress until verified.",
|
|
252
235
|
promptSnippet: "Track/sync non-trivial multi-step work; include final report item and close it before sending the report; resync when requirements change; keep one task in_progress",
|
|
253
236
|
promptGuidelines: [
|
|
254
237
|
"Use `todo` for complex work with 3+ steps, explicit user task lists, or new non-trivial requirements. Skip single trivial tasks and purely conversational requests.",
|
|
255
|
-
"For
|
|
256
|
-
"
|
|
257
|
-
"
|
|
258
|
-
"
|
|
259
|
-
"Before any non-trivial read/edit/test/tool work on a planned task, mark exactly one task in_progress with activeForm (present-continuous label); do this immediately after creating a plan if no task is active. Mark it completed immediately after the required verification, never in batches.",
|
|
238
|
+
"For multi-step implementation/debugging plans, include a final user-facing report todo in the initial plan with acceptance criteria for changed files/behavior, verification results, and remaining manual actions; close it immediately before the final response, never via compression.",
|
|
239
|
+
"Resync before continuing when the user or new findings change scope, requirements, safety, feasibility, approach, dependencies, or order; update relevant tasks, add required tasks/blockers, and defer obsolete work.",
|
|
240
|
+
"Update todos when you start, finish, block, split, abandon, or materially change a step; do not leave todo maintenance for end-of-task cleanup.",
|
|
241
|
+
"Before non-trivial planned work, mark exactly one task in_progress with activeForm. Complete it only after required verification, never in batches.",
|
|
260
242
|
"If implementation is partial, tests fail, or a blocker remains, keep the task in_progress and add/update a blocker task instead of completing it.",
|
|
261
243
|
"Never use `clear`, `delete`, or batch deletion to hide unfinished, stale, or forgotten todos. Defer obsolete items or update them with the reason; only delete when the user explicitly asks or the item was created by mistake.",
|
|
262
244
|
"Before giving a final response for work that used todos, ensure every visible todo is completed, deferred, or intentionally still in_progress with a blocker/explanation. Do not leave any just-finished todo item in_progress after you stop.",
|
|
263
245
|
"Keep subjects short and imperative; put details in description only when needed. Use parentId for large plans; use blockedBy on create and addBlockedBy/removeBlockedBy on update for dependencies.",
|
|
264
246
|
"Use batch_create/batch_update for large explicit plans, but still keep exactly one visible task in_progress unless the user asks otherwise.",
|
|
265
247
|
"When starting a new plan that supersedes existing unfinished todos, use batch_create with replace:true instead of appending; only omit replace when intentionally extending the current plan.",
|
|
266
|
-
"list hides deleted tombstones unless includeDeleted:true;
|
|
267
|
-
"Use export/import for handoff or plan migration; import with replace:true only when the user explicitly wants to overwrite the current todo state.",
|
|
248
|
+
"list hides deleted tombstones unless includeDeleted:true; use status/blockedOnly filters only when needed. Use export/import for handoff or migration; import with replace:true only when explicitly overwriting todo state.",
|
|
268
249
|
"When every visible todo is completed, todo state clears automatically; do not call clear afterward just to remove completed tasks.",
|
|
269
|
-
"
|
|
250
|
+
"Persistence uses `/todos persist on|off|status` or `/todos-persist on|off|status`; when resuming, ask which ids are in scope and run `/todos scope <id...>` or `/todos-scope <id...>` so out-of-scope active tasks are deferred.",
|
|
270
251
|
],
|
|
271
252
|
};
|
|
272
253
|
|
|
@@ -348,12 +329,12 @@ export const CODEX_ALIAS_TOOL_DESCRIPTIONS = {
|
|
|
348
329
|
shellCommand: {
|
|
349
330
|
name: "shell",
|
|
350
331
|
label: "shell",
|
|
351
|
-
description: "Run shell commands for builds, tests, package managers, git, and project CLIs. Set workdir/cwd instead of cd; prefer read for simple file reads.",
|
|
332
|
+
description: "Run shell commands for builds, tests, package managers, git, and project CLIs. Prefer tail for long test output. Set workdir/cwd instead of cd; prefer read for simple file reads.",
|
|
352
333
|
},
|
|
353
334
|
applyPatch: {
|
|
354
335
|
name: "apply_patch",
|
|
355
336
|
label: "apply_patch",
|
|
356
|
-
description: `Apply file edits with a relative-path patch or
|
|
337
|
+
description: `Apply file edits with a relative-path patch or standard unified diff. Use for creating, updating, moving, or deleting files; keep each patch focused.
|
|
357
338
|
|
|
358
339
|
Begin-patch format:
|
|
359
340
|
*** Begin Patch
|
|
@@ -363,8 +344,8 @@ Begin-patch format:
|
|
|
363
344
|
+new text
|
|
364
345
|
*** End Patch
|
|
365
346
|
|
|
366
|
-
|
|
347
|
+
Sections: *** Add File (new lines start with +), *** Update File (optionally *** Move to: new/path), and *** Delete File. One Begin Patch may edit multiple tightly related files; keep unrelated changes separate. Update hunks may use optional @@ context, omit line numbers/the first @@, use *** End of File, and be wrapped in <<EOF ... EOF. Matching tolerates trailing-space, trim, and common Unicode punctuation differences.
|
|
367
348
|
|
|
368
|
-
Unified diff is also supported
|
|
349
|
+
Unified diff with ---/+++ headers is also supported. Paths must be workspace-relative, never absolute. Provide the complete patch in input.`,
|
|
369
350
|
},
|
|
370
351
|
} satisfies Record<string, ToolDescription>;
|