@oh-my-pi/pi-coding-agent 17.0.0 → 17.0.2
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/CHANGELOG.md +128 -0
- package/dist/cli.js +4006 -3966
- package/dist/types/advisor/advise-tool.d.ts +5 -2
- package/dist/types/advisor/config.d.ts +14 -6
- package/dist/types/advisor/runtime.d.ts +20 -11
- package/dist/types/autolearn/controller.d.ts +1 -0
- package/dist/types/cli-commands.d.ts +14 -1
- package/dist/types/config/model-discovery.d.ts +17 -0
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/model-resolver.d.ts +6 -2
- package/dist/types/config/models-config-schema.d.ts +10 -0
- package/dist/types/config/models-config.d.ts +6 -0
- package/dist/types/config/settings-schema.d.ts +32 -7
- package/dist/types/config/settings.d.ts +50 -0
- package/dist/types/cursor.d.ts +11 -0
- package/dist/types/discovery/helpers.d.ts +5 -2
- package/dist/types/discovery/substitute-plugin-root.d.ts +20 -7
- package/dist/types/exec/bash-executor.d.ts +2 -0
- package/dist/types/extensibility/extensions/managed-timers.d.ts +15 -0
- package/dist/types/extensibility/extensions/runner.d.ts +7 -0
- package/dist/types/extensibility/extensions/types.d.ts +18 -0
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +40 -0
- package/dist/types/extensibility/shared-events.d.ts +6 -0
- package/dist/types/extensibility/utils.d.ts +2 -2
- package/dist/types/internal-urls/agent-protocol.d.ts +1 -0
- package/dist/types/lib/xai-http.d.ts +11 -0
- package/dist/types/mcp/transports/stdio.d.ts +13 -1
- package/dist/types/modes/acp/acp-agent.d.ts +3 -0
- package/dist/types/modes/acp/acp-mode.d.ts +6 -2
- package/dist/types/modes/components/advisor-config.d.ts +8 -1
- package/dist/types/modes/components/assistant-message.d.ts +2 -0
- package/dist/types/modes/components/custom-editor-plugin-ctor.test.d.ts +1 -0
- package/dist/types/modes/components/custom-editor.d.ts +23 -1
- package/dist/types/modes/components/hook-editor.d.ts +16 -2
- package/dist/types/modes/components/login-dialog.test.d.ts +1 -0
- package/dist/types/modes/components/model-hub.d.ts +5 -2
- package/dist/types/modes/components/session-selector.d.ts +4 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -1
- package/dist/types/modes/components/tool-execution.test.d.ts +1 -0
- package/dist/types/modes/controllers/command-controller.d.ts +8 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +3 -1
- package/dist/types/modes/interactive-mode.d.ts +6 -0
- package/dist/types/modes/noninteractive-dispose.test.d.ts +1 -0
- package/dist/types/modes/print-mode.d.ts +1 -1
- package/dist/types/modes/types.d.ts +10 -0
- package/dist/types/modes/utils/transcript-render-helpers.d.ts +2 -2
- package/dist/types/modes/warp-events.d.ts +24 -0
- package/dist/types/modes/warp-events.test.d.ts +1 -0
- package/dist/types/plan-mode/model-transition.d.ts +47 -0
- package/dist/types/plan-mode/model-transition.test.d.ts +1 -0
- package/dist/types/registry/agent-lifecycle.d.ts +26 -1
- package/dist/types/sdk.d.ts +13 -2
- package/dist/types/session/agent-session.d.ts +43 -11
- package/dist/types/session/session-history-format.d.ts +10 -0
- package/dist/types/session/session-manager.d.ts +14 -0
- package/dist/types/session/streaming-output.d.ts +2 -0
- package/dist/types/slash-commands/helpers/active-oauth-account.d.ts +9 -0
- package/dist/types/slash-commands/types.d.ts +4 -4
- package/dist/types/system-prompt.d.ts +1 -1
- package/dist/types/task/label.d.ts +1 -1
- package/dist/types/telemetry-export.d.ts +34 -9
- package/dist/types/tools/approval.d.ts +8 -0
- package/dist/types/tools/bash.d.ts +2 -0
- package/dist/types/tools/browser/registry.d.ts +7 -3
- package/dist/types/tools/browser/tab-supervisor.d.ts +2 -0
- package/dist/types/tools/essential-tools.d.ts +29 -0
- package/dist/types/tools/image-gen.d.ts +2 -1
- package/dist/types/tools/index.d.ts +4 -1
- package/dist/types/tools/manage-skill.d.ts +2 -0
- package/dist/types/tools/xdev.d.ts +11 -2
- package/dist/types/utils/title-generator.d.ts +2 -1
- package/dist/types/web/search/index.d.ts +2 -0
- package/dist/types/web/search/providers/base.d.ts +3 -0
- package/dist/types/web/search/providers/kimi.d.ts +4 -1
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +21 -16
- package/src/advisor/__tests__/advisor.test.ts +1358 -86
- package/src/advisor/__tests__/config.test.ts +58 -2
- package/src/advisor/advise-tool.ts +7 -3
- package/src/advisor/config.ts +76 -24
- package/src/advisor/runtime.ts +445 -92
- package/src/autolearn/controller.ts +23 -28
- package/src/cli/bench-cli.ts +4 -1
- package/src/cli/grep-cli.ts +2 -1
- package/src/cli-commands.ts +57 -23
- package/src/cli.ts +5 -1
- package/src/config/model-discovery.ts +81 -21
- package/src/config/model-registry.ts +34 -6
- package/src/config/model-resolver.ts +57 -12
- package/src/config/models-config-schema.ts +1 -0
- package/src/config/settings-schema.ts +43 -6
- package/src/config/settings.ts +405 -25
- package/src/cursor.ts +20 -3
- package/src/debug/report-bundle.ts +40 -4
- package/src/discovery/codex.ts +14 -4
- package/src/discovery/helpers.ts +28 -5
- package/src/discovery/substitute-plugin-root.ts +23 -7
- package/src/exec/bash-executor.ts +14 -5
- package/src/export/html/template.js +2 -0
- package/src/extensibility/custom-tools/loader.ts +3 -3
- package/src/extensibility/custom-tools/wrapper.ts +2 -1
- package/src/extensibility/extensions/loader.ts +3 -3
- package/src/extensibility/extensions/managed-timers.ts +83 -0
- package/src/extensibility/extensions/runner.ts +26 -0
- package/src/extensibility/extensions/types.ts +18 -0
- package/src/extensibility/extensions/wrapper.ts +2 -1
- package/src/extensibility/hooks/loader.ts +3 -3
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +96 -1
- package/src/extensibility/plugins/legacy-pi-compat.ts +225 -22
- package/src/extensibility/plugins/manager.ts +2 -2
- package/src/extensibility/shared-events.ts +6 -0
- package/src/extensibility/utils.ts +91 -25
- package/src/internal-urls/__tests__/agent-protocol-nested.test.ts +73 -0
- package/src/internal-urls/agent-protocol.ts +73 -39
- package/src/irc/bus.ts +22 -3
- package/src/launch/broker.ts +3 -2
- package/src/launch/client.ts +2 -2
- package/src/launch/presence.ts +2 -2
- package/src/lib/xai-http.ts +28 -2
- package/src/lsp/client.ts +1 -1
- package/src/main.ts +11 -8
- package/src/mcp/manager.ts +9 -3
- package/src/mcp/transports/stdio.ts +103 -23
- package/src/modes/acp/acp-agent.ts +28 -9
- package/src/modes/acp/acp-event-mapper.ts +8 -0
- package/src/modes/acp/acp-mode.ts +18 -4
- package/src/modes/components/advisor-config.ts +65 -3
- package/src/modes/components/ask-dialog.ts +1 -1
- package/src/modes/components/assistant-message.ts +35 -14
- package/src/modes/components/chat-transcript-builder.ts +2 -0
- package/src/modes/components/custom-editor-plugin-ctor.test.ts +36 -0
- package/src/modes/components/custom-editor.ts +62 -1
- package/src/modes/components/hook-editor.ts +35 -5
- package/src/modes/components/login-dialog.test.ts +56 -0
- package/src/modes/components/login-dialog.ts +7 -3
- package/src/modes/components/model-hub.ts +138 -42
- package/src/modes/components/plan-review-overlay.ts +51 -10
- package/src/modes/components/session-selector.ts +10 -0
- package/src/modes/components/status-line/component.test.ts +1 -0
- package/src/modes/components/status-line/component.ts +2 -4
- package/src/modes/components/status-line/segments.ts +21 -6
- package/src/modes/components/tool-execution.test.ts +101 -0
- package/src/modes/components/tool-execution.ts +117 -9
- package/src/modes/components/transcript-container.ts +30 -2
- package/src/modes/controllers/command-controller-shared.ts +1 -1
- package/src/modes/controllers/command-controller.ts +168 -48
- package/src/modes/controllers/event-controller.ts +6 -0
- package/src/modes/controllers/extension-ui-controller.ts +4 -22
- package/src/modes/controllers/input-controller.ts +12 -12
- package/src/modes/controllers/selector-controller.ts +208 -46
- package/src/modes/interactive-mode.ts +205 -65
- package/src/modes/noninteractive-dispose.test.ts +60 -0
- package/src/modes/print-mode.ts +10 -5
- package/src/modes/rpc/host-tools.ts +2 -1
- package/src/modes/rpc/rpc-mode.ts +31 -7
- package/src/modes/theme/theme.ts +2 -2
- package/src/modes/types.ts +10 -0
- package/src/modes/utils/interactive-context-helpers.ts +3 -1
- package/src/modes/utils/transcript-render-helpers.ts +3 -2
- package/src/modes/warp-events.test.ts +794 -0
- package/src/modes/warp-events.ts +232 -0
- package/src/plan-mode/model-transition.test.ts +60 -0
- package/src/plan-mode/model-transition.ts +51 -0
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/registry/agent-lifecycle.ts +133 -18
- package/src/sdk.ts +227 -44
- package/src/session/agent-session.ts +1364 -382
- package/src/session/session-history-format.ts +20 -5
- package/src/session/session-listing.ts +6 -5
- package/src/session/session-loader.ts +9 -0
- package/src/session/session-manager.ts +48 -0
- package/src/session/session-persistence.ts +11 -0
- package/src/session/streaming-output.ts +3 -1
- package/src/slash-commands/builtin-registry.ts +9 -0
- package/src/slash-commands/helpers/active-oauth-account.ts +16 -0
- package/src/slash-commands/types.ts +4 -4
- package/src/system-prompt.ts +2 -2
- package/src/task/executor.ts +1 -1
- package/src/task/label.ts +2 -0
- package/src/telemetry-export.ts +453 -97
- package/src/tools/approval.ts +11 -0
- package/src/tools/bash.ts +71 -38
- package/src/tools/browser/registry.ts +9 -2
- package/src/tools/browser/tab-supervisor.ts +65 -10
- package/src/tools/browser.ts +4 -3
- package/src/tools/essential-tools.ts +45 -0
- package/src/tools/eval.ts +3 -0
- package/src/tools/gh.ts +169 -2
- package/src/tools/image-gen.ts +624 -513
- package/src/tools/index.ts +10 -6
- package/src/tools/manage-skill.ts +5 -3
- package/src/tools/output-meta.ts +7 -0
- package/src/tools/path-utils.ts +8 -5
- package/src/tools/read.ts +48 -3
- package/src/tools/write.ts +22 -4
- package/src/tools/xdev.ts +14 -3
- package/src/utils/title-generator.ts +15 -4
- package/src/web/search/index.ts +21 -6
- package/src/web/search/providers/base.ts +3 -0
- package/src/web/search/providers/kimi.ts +18 -12
- package/src/web/search/providers/xai.ts +40 -9
- package/src/web/search/types.ts +6 -1
|
@@ -43,7 +43,7 @@ import type { AuthStorage, OAuthAccountIdentity } from "../../session/auth-stora
|
|
|
43
43
|
import type { CompactMode } from "../../session/compact-modes";
|
|
44
44
|
import type { NewSessionOptions } from "../../session/session-entries";
|
|
45
45
|
import { formatShakeSummary, type ShakeMode, type ShakeResult } from "../../session/shake-types";
|
|
46
|
-
import { limitMatchesActiveAccount } from "../../slash-commands/helpers/active-oauth-account";
|
|
46
|
+
import { formatActiveAccountLabel, limitMatchesActiveAccount } from "../../slash-commands/helpers/active-oauth-account";
|
|
47
47
|
import { outputMeta } from "../../tools/output-meta";
|
|
48
48
|
import { resolveToCwd, stripOuterDoubleQuotes } from "../../tools/path-utils";
|
|
49
49
|
import { replaceTabs, truncateToWidth } from "../../tools/render-utils";
|
|
@@ -64,7 +64,7 @@ function showMarkdownPanel(ctx: InteractiveModeContext, title: string, markdown:
|
|
|
64
64
|
block.addChild(new Spacer(1));
|
|
65
65
|
block.addChild(new Markdown(markdown.trim(), 1, 1, getMarkdownTheme()));
|
|
66
66
|
block.addChild(new DynamicBorder());
|
|
67
|
-
ctx.
|
|
67
|
+
ctx.presentCommandOutput(block);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
export class CommandController {
|
|
@@ -349,46 +349,119 @@ export class CommandController {
|
|
|
349
349
|
this.ctx.present([new Spacer(1), new Text(info, 1, 0)]);
|
|
350
350
|
}
|
|
351
351
|
|
|
352
|
+
static readonly #advisorStatusGlyph: Record<string, string> = {
|
|
353
|
+
running: "●",
|
|
354
|
+
paused: "○",
|
|
355
|
+
no_model: "○",
|
|
356
|
+
quota_exhausted: "✕",
|
|
357
|
+
error: "✕",
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
static readonly #advisorStatusLabel: Record<string, string> = {
|
|
361
|
+
running: "running",
|
|
362
|
+
paused: "off",
|
|
363
|
+
no_model: "no model",
|
|
364
|
+
quota_exhausted: "quota exhausted",
|
|
365
|
+
error: "error",
|
|
366
|
+
};
|
|
367
|
+
|
|
352
368
|
async handleAdvisorStatusCommand(): Promise<void> {
|
|
353
369
|
const stats = this.ctx.session.getAdvisorStats();
|
|
354
|
-
if (!stats.
|
|
355
|
-
this.ctx.present([
|
|
356
|
-
new Spacer(1),
|
|
357
|
-
new Text(
|
|
358
|
-
stats.configured
|
|
359
|
-
? "Advisor setting is enabled, but no model is assigned to the 'advisor' role."
|
|
360
|
-
: "Advisor is disabled.",
|
|
361
|
-
1,
|
|
362
|
-
0,
|
|
363
|
-
),
|
|
364
|
-
]);
|
|
370
|
+
if (!stats.configured) {
|
|
371
|
+
this.ctx.present([new Spacer(1), new Text("Advisor is disabled.", 1, 0)]);
|
|
365
372
|
return;
|
|
366
373
|
}
|
|
367
|
-
|
|
374
|
+
// Fetch live quota data (cached 5 min by the auth-gateway) so we can show
|
|
375
|
+
// real usage windows/reset timers per advisor provider. Non-fatal when absent.
|
|
376
|
+
const usageProvider = this.ctx.session as { fetchUsageReports?: () => Promise<UsageReport[] | null> };
|
|
377
|
+
let usageReports: UsageReport[] | null = null;
|
|
378
|
+
if (usageProvider.fetchUsageReports) {
|
|
379
|
+
try {
|
|
380
|
+
usageReports = await usageProvider.fetchUsageReports();
|
|
381
|
+
} catch {
|
|
382
|
+
// Network/auth failure is non-fatal — just skip the quota line.
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// Resolve the active OAuth identity for each advisor's provider so quota
|
|
386
|
+
// filtering matches the credential actually in use (not sibling accounts).
|
|
387
|
+
const resolveActiveAdvisorAccount = (provider: string, sessionId?: string): OAuthAccountIdentity | undefined =>
|
|
388
|
+
this.ctx.session.modelRegistry.authStorage.getOAuthAccountIdentity(
|
|
389
|
+
provider,
|
|
390
|
+
sessionId ?? this.ctx.session.sessionId,
|
|
391
|
+
);
|
|
392
|
+
const nowMs = Date.now();
|
|
393
|
+
// Roster view: show every configured advisor with its status, even when
|
|
394
|
+
// none are live (all paused/no-model). The old code returned a generic
|
|
395
|
+
// message that hid the per-advisor state the user needs to act on.
|
|
396
|
+
if (stats.advisors.length > 1 || (stats.configured && !stats.active)) {
|
|
368
397
|
let info = `${theme.bold("Advisor Status")} (${stats.advisors.length} advisors)\n`;
|
|
369
398
|
for (const a of stats.advisors) {
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
info +=
|
|
379
|
-
if (a.
|
|
380
|
-
|
|
399
|
+
const glyph = CommandController.#advisorStatusGlyph[a.status] ?? "?";
|
|
400
|
+
const label = CommandController.#advisorStatusLabel[a.status] ?? a.status;
|
|
401
|
+
const color =
|
|
402
|
+
a.status === "running"
|
|
403
|
+
? "success"
|
|
404
|
+
: a.status === "quota_exhausted" || a.status === "error"
|
|
405
|
+
? "error"
|
|
406
|
+
: "dim";
|
|
407
|
+
info += `\n${theme.fg(color, glyph)} ${theme.bold(a.name)} ${theme.fg("dim", `[${label}]`)}\n`;
|
|
408
|
+
if (a.model) {
|
|
409
|
+
info += `${theme.fg("dim", "Model:")} ${a.model.provider}/${a.model.id}\n`;
|
|
410
|
+
}
|
|
411
|
+
if (a.model && usageReports) {
|
|
412
|
+
const quota = formatCompactQuota(
|
|
413
|
+
a.model.provider,
|
|
414
|
+
usageReports,
|
|
415
|
+
nowMs,
|
|
416
|
+
resolveActiveAdvisorAccount(a.model.provider, a.sessionId),
|
|
417
|
+
);
|
|
418
|
+
if (quota) info += `${theme.fg("dim", quota)}\n`;
|
|
419
|
+
}
|
|
420
|
+
if (a.status === "running" || a.status === "quota_exhausted") {
|
|
421
|
+
const ctx =
|
|
422
|
+
a.contextWindow > 0
|
|
423
|
+
? `${a.contextTokens.toLocaleString()} / ${a.contextWindow.toLocaleString()} (${Math.round((a.contextTokens / a.contextWindow) * 100)}%)`
|
|
424
|
+
: `${a.contextTokens.toLocaleString()}`;
|
|
425
|
+
info += `${theme.fg("dim", "Context:")} ${ctx}\n`;
|
|
426
|
+
info += `${theme.fg("dim", "Messages:")} ${a.messages.total.toLocaleString()}\n`;
|
|
427
|
+
info += `${theme.fg("dim", "Spend:")} ${a.tokens.input.toLocaleString()} in / ${a.tokens.output.toLocaleString()} out`;
|
|
428
|
+
if (a.cost > 0) info += `, $${a.cost.toFixed(4)}`;
|
|
429
|
+
info += "\n";
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
if (stats.active) {
|
|
433
|
+
info += `\n${theme.bold("Totals")}\n`;
|
|
434
|
+
info += `${theme.fg("dim", "Tokens:")} ${stats.tokens.total.toLocaleString()}\n`;
|
|
435
|
+
if (stats.cost > 0) info += `${theme.fg("dim", "Cost:")} $${stats.cost.toFixed(4)}\n`;
|
|
381
436
|
}
|
|
382
|
-
info += `\n${theme.bold("Totals")}\n`;
|
|
383
|
-
info += `${theme.fg("dim", "Tokens:")} ${stats.tokens.total.toLocaleString()}\n`;
|
|
384
|
-
if (stats.cost > 0) info += `${theme.fg("dim", "Cost:")} $${stats.cost.toFixed(4)}\n`;
|
|
385
437
|
this.ctx.present([new Spacer(1), new Text(info, 1, 0)]);
|
|
386
438
|
return;
|
|
387
439
|
}
|
|
388
|
-
|
|
440
|
+
// Single active advisor — detailed view.
|
|
441
|
+
const model = stats.model;
|
|
389
442
|
let info = `${theme.bold("Advisor Status")}\n\n`;
|
|
390
|
-
|
|
391
|
-
|
|
443
|
+
if (stats.advisors.length === 1) {
|
|
444
|
+
const a = stats.advisors[0];
|
|
445
|
+
const glyph = CommandController.#advisorStatusGlyph[a.status] ?? "?";
|
|
446
|
+
const label = CommandController.#advisorStatusLabel[a.status] ?? a.status;
|
|
447
|
+
info += `${theme.fg(a.status === "running" ? "success" : "error", glyph)} ${a.name} ${theme.fg("dim", `[${label}]`)}\n\n`;
|
|
448
|
+
}
|
|
449
|
+
if (model) {
|
|
450
|
+
info += `${theme.bold("Provider")}\n`;
|
|
451
|
+
info += `${theme.fg("dim", "Model:")} ${model.provider}/${model.id}\n`;
|
|
452
|
+
}
|
|
453
|
+
if (model && usageReports) {
|
|
454
|
+
const quota = formatCompactQuota(
|
|
455
|
+
model.provider,
|
|
456
|
+
usageReports,
|
|
457
|
+
nowMs,
|
|
458
|
+
resolveActiveAdvisorAccount(model.provider, stats.advisors[0]?.sessionId),
|
|
459
|
+
);
|
|
460
|
+
if (quota) {
|
|
461
|
+
info += `\n${theme.bold("Quota")}\n`;
|
|
462
|
+
info += `${theme.fg("dim", quota)}\n`;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
392
465
|
info += `\n${theme.bold("Messages")}\n`;
|
|
393
466
|
info += `${theme.fg("dim", "User:")} ${stats.messages.user.toLocaleString()}\n`;
|
|
394
467
|
info += `${theme.fg("dim", "Assistant:")} ${stats.messages.assistant.toLocaleString()}\n`;
|
|
@@ -406,14 +479,7 @@ export class CommandController {
|
|
|
406
479
|
if (stats.tokens.cacheRead > 0) {
|
|
407
480
|
info += `${theme.fg("dim", "Cache Read:")} ${stats.tokens.cacheRead.toLocaleString()}\n`;
|
|
408
481
|
}
|
|
409
|
-
if (stats.
|
|
410
|
-
info += `${theme.fg("dim", "Cache Write:")} ${stats.tokens.cacheWrite.toLocaleString()}\n`;
|
|
411
|
-
}
|
|
412
|
-
info += `${theme.fg("dim", "Total:")} ${stats.tokens.total.toLocaleString()}\n`;
|
|
413
|
-
if (stats.cost > 0) {
|
|
414
|
-
info += `\n${theme.bold("Cost")}\n`;
|
|
415
|
-
info += `${theme.fg("dim", "Total:")} $${stats.cost.toFixed(4)}\n`;
|
|
416
|
-
}
|
|
482
|
+
if (stats.cost > 0) info += `${theme.fg("dim", "Cost:")} $${stats.cost.toFixed(4)}\n`;
|
|
417
483
|
this.ctx.present([new Spacer(1), new Text(info, 1, 0)]);
|
|
418
484
|
}
|
|
419
485
|
|
|
@@ -993,6 +1059,12 @@ export class CommandController {
|
|
|
993
1059
|
return;
|
|
994
1060
|
}
|
|
995
1061
|
}
|
|
1062
|
+
try {
|
|
1063
|
+
await this.ctx.settings.flush();
|
|
1064
|
+
} catch (err) {
|
|
1065
|
+
this.ctx.showError(`Failed to save pending settings: ${err instanceof Error ? err.message : String(err)}`);
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
996
1068
|
|
|
997
1069
|
try {
|
|
998
1070
|
await this.ctx.sessionManager.moveTo(resolvedPath);
|
|
@@ -1000,7 +1072,6 @@ export class CommandController {
|
|
|
1000
1072
|
this.ctx.showError(`Move failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1001
1073
|
return;
|
|
1002
1074
|
}
|
|
1003
|
-
|
|
1004
1075
|
await this.ctx.applyCwdChange(resolvedPath);
|
|
1005
1076
|
|
|
1006
1077
|
this.ctx.updateEditorBorderColor();
|
|
@@ -1303,7 +1374,7 @@ export class CommandController {
|
|
|
1303
1374
|
}
|
|
1304
1375
|
|
|
1305
1376
|
const BAR_WIDTH_MAX = 24;
|
|
1306
|
-
const
|
|
1377
|
+
const COLUMN_WIDTH_MIN = 4;
|
|
1307
1378
|
|
|
1308
1379
|
function renderJobLine(job: AsyncJobSnapshotItem, now: number): string {
|
|
1309
1380
|
const duration = formatDuration(Math.max(0, now - job.startTime));
|
|
@@ -1537,6 +1608,57 @@ function resolveResetRange(limits: UsageLimit[], nowMs: number): string | null {
|
|
|
1537
1608
|
}
|
|
1538
1609
|
return `resets in ${formatDuration(minReset)}`;
|
|
1539
1610
|
}
|
|
1611
|
+
/**
|
|
1612
|
+
* Compact one-line quota summary for a single advisor's provider.
|
|
1613
|
+
* Returns `null` when the provider has no usage data.
|
|
1614
|
+
* When `activeAccount` is provided, only limits matching that credential
|
|
1615
|
+
* are shown (mirrors `renderUsageReports`'s account-stickiness filtering).
|
|
1616
|
+
* Example output: `Quota: 7d window · 67% used · resets in 3.2d`
|
|
1617
|
+
*/
|
|
1618
|
+
export function formatCompactQuota(
|
|
1619
|
+
provider: string,
|
|
1620
|
+
reports: UsageReport[],
|
|
1621
|
+
nowMs: number,
|
|
1622
|
+
activeAccount?: OAuthAccountIdentity,
|
|
1623
|
+
): string | null {
|
|
1624
|
+
const providerReports = reports.filter(r => r.provider === provider);
|
|
1625
|
+
if (providerReports.length === 0) return null;
|
|
1626
|
+
// Group limits by window id so we show BOTH the 5-hour and 7-day windows
|
|
1627
|
+
// (or any other distinct windows the provider exposes). Within each window,
|
|
1628
|
+
// pick the highest used fraction across accounts — that's the most pressing.
|
|
1629
|
+
const byWindow = new Map<string, { limit: UsageLimit; fraction: number }>();
|
|
1630
|
+
for (const report of providerReports) {
|
|
1631
|
+
for (const limit of report.limits) {
|
|
1632
|
+
// Skip limits that belong to a different credential than the one
|
|
1633
|
+
// the advisor is actually using, so we don't alarm the user with
|
|
1634
|
+
// an exhausted account that isn't theirs.
|
|
1635
|
+
if (activeAccount && !limitMatchesActiveAccount(report, limit, activeAccount)) continue;
|
|
1636
|
+
const fraction = resolveUsedFraction(limit);
|
|
1637
|
+
if (fraction === undefined) continue;
|
|
1638
|
+
const key = limit.window?.id ?? limit.scope.windowId ?? "—";
|
|
1639
|
+
const existing = byWindow.get(key);
|
|
1640
|
+
if (!existing || fraction > existing.fraction) byWindow.set(key, { limit, fraction });
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
if (byWindow.size === 0) return null;
|
|
1644
|
+
// Sort windows by urgency (highest fraction first) so the most pressing
|
|
1645
|
+
// quota is always the first thing the user sees.
|
|
1646
|
+
const entries = [...byWindow.values()].sort((a, b) => b.fraction - a.fraction);
|
|
1647
|
+
const lines: string[] = [];
|
|
1648
|
+
for (const { limit, fraction } of entries) {
|
|
1649
|
+
const pct = Math.round(fraction * 100);
|
|
1650
|
+
const windowLabel = limit.window?.label ?? limit.scope.windowId ?? "—";
|
|
1651
|
+
// Include the limit label (account/tier) when it carries identity beyond
|
|
1652
|
+
// the window name, so the user can tell which credential's quota is shown.
|
|
1653
|
+
const identity = limit.label.trim();
|
|
1654
|
+
const header = identity && identity !== windowLabel ? `${windowLabel} (${identity})` : windowLabel;
|
|
1655
|
+
const parts = [`${header}: ${pct}% used`];
|
|
1656
|
+
const reset = resolveResetRange([limit], nowMs);
|
|
1657
|
+
if (reset) parts.push(reset);
|
|
1658
|
+
lines.push(parts.join(" · "));
|
|
1659
|
+
}
|
|
1660
|
+
return `Quota: ${lines.join(" │ ")}`;
|
|
1661
|
+
}
|
|
1540
1662
|
|
|
1541
1663
|
function resolveStatusIcon(status: UsageLimit["status"], uiTheme: typeof theme): string {
|
|
1542
1664
|
if (status === "exhausted") return uiTheme.fg("error", uiTheme.status.error);
|
|
@@ -1571,7 +1693,7 @@ function renderUsageBar(limit: UsageLimit, uiTheme: typeof theme, barWidth: numb
|
|
|
1571
1693
|
}
|
|
1572
1694
|
|
|
1573
1695
|
/**
|
|
1574
|
-
* Pick a per-column width so
|
|
1696
|
+
* Pick a per-account column width so the columns and trailing amount fit in `available`.
|
|
1575
1697
|
* Falls back to the minimum when the terminal is too narrow rather than wrapping.
|
|
1576
1698
|
*/
|
|
1577
1699
|
function resolveColumnWidth(count: number, available: number, trailing: number): number {
|
|
@@ -1580,10 +1702,7 @@ function resolveColumnWidth(count: number, available: number, trailing: number):
|
|
|
1580
1702
|
const gaps = count - 1;
|
|
1581
1703
|
const spaceForBars = available - indent - gaps - (trailing > 0 ? trailing + 1 : 0);
|
|
1582
1704
|
const ideal = Math.floor(spaceForBars / count);
|
|
1583
|
-
|
|
1584
|
-
const max = BAR_WIDTH_MAX;
|
|
1585
|
-
if (ideal < min) return min;
|
|
1586
|
-
if (ideal > max) return max;
|
|
1705
|
+
if (ideal < COLUMN_WIDTH_MIN) return COLUMN_WIDTH_MIN;
|
|
1587
1706
|
return ideal;
|
|
1588
1707
|
}
|
|
1589
1708
|
|
|
@@ -1642,7 +1761,7 @@ export function renderUsageReports(
|
|
|
1642
1761
|
}
|
|
1643
1762
|
|
|
1644
1763
|
lines.push(uiTheme.bold(uiTheme.fg("accent", providerName)));
|
|
1645
|
-
const activeAccountLabel = activeAccount
|
|
1764
|
+
const activeAccountLabel = formatActiveAccountLabel(activeAccount);
|
|
1646
1765
|
if (activeAccountLabel) {
|
|
1647
1766
|
lines.push(` ${uiTheme.fg("accent", "in use by this session:")} ${activeAccountLabel}`);
|
|
1648
1767
|
}
|
|
@@ -1719,6 +1838,7 @@ export function renderUsageReports(
|
|
|
1719
1838
|
const sectionCount = renderableGroups.reduce((max, g) => Math.max(max, g.sortedLimits.length), 0);
|
|
1720
1839
|
const sectionTrailing = renderableGroups.reduce((max, g) => Math.max(max, visibleWidth(g.amountText)), 0);
|
|
1721
1840
|
const sectionColumnWidth = resolveColumnWidth(sectionCount, availableWidth, sectionTrailing);
|
|
1841
|
+
const sectionBarWidth = Math.min(sectionColumnWidth, BAR_WIDTH_MAX);
|
|
1722
1842
|
|
|
1723
1843
|
for (const { group, sortedLimits, sortedReports, amountText } of renderableGroups) {
|
|
1724
1844
|
const status = resolveAggregateStatus(sortedLimits);
|
|
@@ -1736,7 +1856,7 @@ export function renderUsageReports(
|
|
|
1736
1856
|
);
|
|
1737
1857
|
lines.push(` ${accountLabels.join(" ")}`.trimEnd());
|
|
1738
1858
|
const bars = sortedLimits.map(limit =>
|
|
1739
|
-
padColumn(renderUsageBar(limit, uiTheme,
|
|
1859
|
+
padColumn(renderUsageBar(limit, uiTheme, sectionBarWidth), sectionColumnWidth),
|
|
1740
1860
|
);
|
|
1741
1861
|
lines.push(` ${bars.join(" ")} ${amountText}`.trimEnd());
|
|
1742
1862
|
const resetText = sortedLimits.length <= 1 ? resolveResetRange(sortedLimits, nowMs) : null;
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
assistantUsageIsBilled,
|
|
37
37
|
splitAssistantMessageToolTimeline,
|
|
38
38
|
} from "../utils/transcript-render-helpers";
|
|
39
|
+
import { isWarpCliAgentProtocolActive } from "../warp-events";
|
|
39
40
|
import { StreamingRevealController } from "./streaming-reveal";
|
|
40
41
|
import { streamingStringKeysForTool, ToolArgsRevealController } from "./tool-args-reveal";
|
|
41
42
|
|
|
@@ -1187,6 +1188,7 @@ export class EventController {
|
|
|
1187
1188
|
// final history — seal it instead of letting its spinner tick while idle.
|
|
1188
1189
|
this.#resolveDisplaceablePoll();
|
|
1189
1190
|
this.#resolveDisplaceableTodo();
|
|
1191
|
+
this.ctx.flushPendingCommandOutput();
|
|
1190
1192
|
this.#lastAssistantComponent = undefined;
|
|
1191
1193
|
this.ctx.ui.requestRender();
|
|
1192
1194
|
this.#scheduleIdleCompaction();
|
|
@@ -1563,6 +1565,10 @@ export class EventController {
|
|
|
1563
1565
|
const notify = settings.get("completion.notify");
|
|
1564
1566
|
if (notify === "off") return;
|
|
1565
1567
|
|
|
1568
|
+
// Warp structured OSC 777 already drives native completion UX when the
|
|
1569
|
+
// protocol is negotiated — avoid a second legacy desktop/OSC-9 toast.
|
|
1570
|
+
if (isWarpCliAgentProtocolActive()) return;
|
|
1571
|
+
|
|
1566
1572
|
// Skip when the turn was aborted (e.g. ask cancelled with Ctrl+C) or
|
|
1567
1573
|
// errored — those are not "Task complete" events. Mirrors the gate
|
|
1568
1574
|
// already used by #currentContextTokens, #handleMessageEnd, and the
|
|
@@ -21,7 +21,6 @@ import type {
|
|
|
21
21
|
TerminalInputHandler,
|
|
22
22
|
} from "../../extensibility/extensions";
|
|
23
23
|
import { getSessionSlashCommands } from "../../extensibility/extensions/get-commands-handler";
|
|
24
|
-
import { createExtensionModelQuery } from "../../extensibility/extensions/model-api";
|
|
25
24
|
import { AskDialogComponent, boundPromptTitle } from "../../modes/components/ask-dialog";
|
|
26
25
|
import { HookEditorComponent } from "../../modes/components/hook-editor";
|
|
27
26
|
import { HookInputComponent } from "../../modes/components/hook-input";
|
|
@@ -494,32 +493,15 @@ export class ExtensionUiController {
|
|
|
494
493
|
if (!uiContext) {
|
|
495
494
|
return;
|
|
496
495
|
}
|
|
497
|
-
|
|
496
|
+
const runner = this.ctx.session.extensionRunner;
|
|
497
|
+
for (const registeredTool of runner?.getAllRegisteredTools() ?? []) {
|
|
498
498
|
if (registeredTool.definition.onSession) {
|
|
499
499
|
try {
|
|
500
500
|
await registeredTool.definition.onSession(event, {
|
|
501
|
+
...runner!.createContext(),
|
|
501
502
|
ui: uiContext,
|
|
502
|
-
getContextUsage: () => this.ctx.session.getContextUsage(),
|
|
503
|
-
compact: instructionsOrOptions => this.#compactSession(instructionsOrOptions),
|
|
504
503
|
hasUI: true,
|
|
505
|
-
|
|
506
|
-
sessionManager: this.ctx.session.sessionManager,
|
|
507
|
-
modelRegistry: this.ctx.session.modelRegistry,
|
|
508
|
-
model: this.ctx.session.model,
|
|
509
|
-
models: createExtensionModelQuery(
|
|
510
|
-
this.ctx.session.modelRegistry,
|
|
511
|
-
this.ctx.session.settings,
|
|
512
|
-
() => this.ctx.session.model,
|
|
513
|
-
),
|
|
514
|
-
isIdle: () => !this.ctx.session.isStreaming,
|
|
515
|
-
hasPendingMessages: () => this.ctx.session.queuedMessageCount > 0,
|
|
516
|
-
abort: () => {
|
|
517
|
-
this.ctx.session.abort({ reason: USER_INTERRUPT_LABEL });
|
|
518
|
-
},
|
|
519
|
-
shutdown: () => {
|
|
520
|
-
// Signal shutdown request
|
|
521
|
-
},
|
|
522
|
-
getSystemPrompt: () => this.ctx.session.systemPrompt,
|
|
504
|
+
compact: instructionsOrOptions => this.#compactSession(instructionsOrOptions),
|
|
523
505
|
});
|
|
524
506
|
} catch (err) {
|
|
525
507
|
this.showToolError(registeredTool.definition.name, err instanceof Error ? err.message : String(err));
|
|
@@ -35,7 +35,6 @@ import { EnhancedPasteController } from "../../utils/enhanced-paste";
|
|
|
35
35
|
import { getEditorCommand, openInEditor } from "../../utils/external-editor";
|
|
36
36
|
import { ensureSupportedImageInput, ImageInputTooLargeError, loadImageInput } from "../../utils/image-loading";
|
|
37
37
|
import { resizeImage } from "../../utils/image-resize";
|
|
38
|
-
import { generateSessionTitle } from "../../utils/title-generator";
|
|
39
38
|
|
|
40
39
|
/**
|
|
41
40
|
* Slash commands that may carry secrets in their arguments should never be
|
|
@@ -392,7 +391,16 @@ export class InputController {
|
|
|
392
391
|
this.ctx.editor.onClear = () => this.handleCtrlC();
|
|
393
392
|
this.ctx.editor.setActionKeys("app.exit", this.ctx.keybindings.getKeys("app.exit"));
|
|
394
393
|
this.ctx.editor.setActionKeys("app.display.reset", this.ctx.keybindings.getKeys("app.display.reset"));
|
|
395
|
-
this.ctx.editor.onDisplayReset = () =>
|
|
394
|
+
this.ctx.editor.onDisplayReset = () => {
|
|
395
|
+
// Explicit user gesture (Ctrl+L): re-query the terminal background once
|
|
396
|
+
// so a mid-session light/dark switch is picked up even on terminals
|
|
397
|
+
// without an end-to-end Mode 2031 notification path (#5352). The
|
|
398
|
+
// appearance callback re-evaluates the auto theme; the repaint below
|
|
399
|
+
// then renders the resolved palette. Bounded to one OSC 11 probe per
|
|
400
|
+
// gesture — no timers, no periodic polling.
|
|
401
|
+
this.ctx.ui.terminal.refreshAppearance?.();
|
|
402
|
+
this.ctx.ui.resetDisplay();
|
|
403
|
+
};
|
|
396
404
|
this.ctx.editor.onExit = () => this.handleCtrlD();
|
|
397
405
|
this.ctx.editor.setActionKeys("app.suspend", this.ctx.keybindings.getKeys("app.suspend"));
|
|
398
406
|
this.ctx.editor.onSuspend = () => this.handleCtrlZ();
|
|
@@ -821,16 +829,8 @@ export class InputController {
|
|
|
821
829
|
// chance, so titling defers past "hi" instead of latching onto it.
|
|
822
830
|
if (!this.ctx.sessionManager.getSessionName() && !$env.PI_NO_TITLE && !isLowSignalTitleInput(text)) {
|
|
823
831
|
this.#showTinyTitleDownloadProgress(this.ctx.settings.get("providers.tinyModel"));
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
text,
|
|
827
|
-
registry,
|
|
828
|
-
this.ctx.settings,
|
|
829
|
-
this.ctx.session.sessionId,
|
|
830
|
-
this.ctx.session.model,
|
|
831
|
-
provider => this.ctx.session.agent.metadataForProvider(provider),
|
|
832
|
-
this.ctx.session.titleSystemPrompt,
|
|
833
|
-
)
|
|
832
|
+
this.ctx.session
|
|
833
|
+
.generateTitle(text)
|
|
834
834
|
.then(async title => {
|
|
835
835
|
// Re-check: a concurrent attempt for an earlier message may have
|
|
836
836
|
// already named the session. Don't clobber it. Terminal title and
|