@oh-my-pi/pi-coding-agent 17.2.10 → 17.2.12
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 +53 -0
- package/dist/{CHANGELOG-rhwzpexa.md → CHANGELOG-k9ghy5sn.md} +53 -0
- package/dist/cli.js +10775 -10762
- package/dist/types/advisor/runtime.d.ts +1 -1
- package/dist/types/capability/mcp.d.ts +11 -0
- package/dist/types/capability/skill.d.ts +6 -0
- package/dist/types/cli/command-help.d.ts +3 -0
- package/dist/types/commands/share.d.ts +25 -0
- package/dist/types/commit/agentic/index.d.ts +3 -1
- package/dist/types/commit/execute.d.ts +32 -0
- package/dist/types/commit/index.d.ts +2 -1
- package/dist/types/commit/pipeline.d.ts +7 -1
- package/dist/types/config/custom-models.d.ts +31 -0
- package/dist/types/config/model-config-values.d.ts +19 -0
- package/dist/types/config/model-patch.d.ts +93 -0
- package/dist/types/config/model-provider-discovery.d.ts +51 -0
- package/dist/types/config/model-registry.d.ts +9 -52
- package/dist/types/config/settings-schema.d.ts +1 -34
- package/dist/types/config/settings.d.ts +5 -3
- package/dist/types/debug/raw-sse-buffer.d.ts +9 -0
- package/dist/types/discovery/agent-plugin-format.d.ts +136 -0
- package/dist/types/discovery/agent-plugins.d.ts +1 -0
- package/dist/types/discovery/contained-path.d.ts +33 -0
- package/dist/types/discovery/index.d.ts +1 -0
- package/dist/types/eval/executor-base.d.ts +8 -2
- package/dist/types/eval/kernel-session-registry.d.ts +60 -0
- package/dist/types/export/share.d.ts +1 -1
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/extensions/loader.d.ts +7 -0
- package/dist/types/extensibility/extensions/types.d.ts +11 -0
- package/dist/types/extensibility/shared-events.d.ts +3 -2
- package/dist/types/extensibility/skills.d.ts +5 -0
- package/dist/types/lsp/diagnostics.d.ts +96 -0
- package/dist/types/lsp/index.d.ts +7 -128
- package/dist/types/lsp/servers.d.ts +72 -0
- package/dist/types/lsp/tool.d.ts +42 -0
- package/dist/types/lsp/workspace-diagnostics.d.ts +12 -0
- package/dist/types/lsp/writethrough.d.ts +33 -0
- package/dist/types/mcp/transports/header-policy.d.ts +46 -0
- package/dist/types/mcp/types.d.ts +15 -0
- package/dist/types/modes/components/agent-hub-projection.d.ts +2 -7
- package/dist/types/modes/components/agent-hub-renderer.d.ts +0 -7
- package/dist/types/modes/components/agent-hub.d.ts +2 -0
- package/dist/types/modes/components/agent-transcript-viewer.d.ts +2 -0
- package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -2
- package/dist/types/modes/components/status-line/types.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +2 -0
- package/dist/types/modes/theme/color.d.ts +19 -0
- package/dist/types/modes/theme/loader.d.ts +19 -0
- package/dist/types/modes/theme/schema.d.ts +175 -0
- package/dist/types/modes/theme/symbols.d.ts +28 -0
- package/dist/types/modes/theme/theme-class.d.ts +244 -0
- package/dist/types/modes/theme/theme.d.ts +9 -280
- package/dist/types/modes/theme/tui-adapters.d.ts +13 -0
- package/dist/types/modes/utils/transcript-render-helpers.d.ts +3 -2
- package/dist/types/registry/agent-registry.d.ts +1 -3
- package/dist/types/secrets/index.d.ts +26 -2
- package/dist/types/secrets/message-transform.d.ts +40 -0
- package/dist/types/secrets/obfuscator.d.ts +0 -108
- package/dist/types/secrets/placeholder-scan.d.ts +95 -0
- package/dist/types/secrets/placeholder.d.ts +94 -0
- package/dist/types/secrets/replacement.d.ts +82 -0
- package/dist/types/session/agent-session-events.d.ts +2 -2
- package/dist/types/session/agent-session-types.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +2 -2
- package/dist/types/session/messages.d.ts +1 -0
- package/dist/types/session/prewalk.d.ts +3 -1
- package/dist/types/session/session-handoff.d.ts +3 -3
- package/dist/types/session/session-manager.d.ts +32 -0
- package/dist/types/session/session-provider-boundary.d.ts +1 -1
- package/dist/types/session/session-tools.d.ts +8 -0
- package/dist/types/session/turn-recovery.d.ts +9 -4
- package/dist/types/slash-commands/builtin-collaboration.d.ts +2 -0
- package/dist/types/slash-commands/builtin-completions.d.ts +36 -0
- package/dist/types/slash-commands/builtin-control.d.ts +2 -0
- package/dist/types/slash-commands/builtin-lifecycle.d.ts +3 -0
- package/dist/types/slash-commands/builtin-marketplace.d.ts +11 -0
- package/dist/types/slash-commands/builtin-modes.d.ts +5 -0
- package/dist/types/slash-commands/builtin-registry.d.ts +1 -1
- package/dist/types/slash-commands/builtin-session.d.ts +2 -0
- package/dist/types/task/index.d.ts +2 -0
- package/dist/types/tools/gh-common.d.ts +69 -0
- package/dist/types/tools/gh-pr-checkout.d.ts +84 -0
- package/dist/types/tools/gh-pr-diff.d.ts +102 -0
- package/dist/types/tools/gh-run-watch.d.ts +78 -0
- package/dist/types/tools/gh-search.d.ts +61 -0
- package/dist/types/tools/gh-types.d.ts +340 -0
- package/dist/types/tools/gh-view.d.ts +88 -0
- package/dist/types/tools/gh.d.ts +4 -201
- package/dist/types/tools/read-archive.d.ts +16 -0
- package/dist/types/tools/read-format.d.ts +104 -0
- package/dist/types/tools/read-path-resolution.d.ts +19 -0
- package/dist/types/tools/read-pdf-images.d.ts +12 -0
- package/dist/types/tools/read-renderer.d.ts +24 -0
- package/dist/types/tools/read-selector.d.ts +27 -0
- package/dist/types/tools/read-sqlite.d.ts +16 -0
- package/dist/types/tools/read-summary.d.ts +19 -0
- package/dist/types/tools/read.d.ts +1 -23
- package/dist/types/tools/shell-tokenize.d.ts +18 -1
- package/dist/types/utils/changelog.d.ts +0 -5
- package/package.json +13 -13
- package/scripts/legacy-pi-virtual-module.ts +4 -1
- package/src/advisor/runtime.ts +12 -7
- package/src/capability/mcp.ts +11 -0
- package/src/capability/skill.ts +6 -0
- package/src/cli/command-help.ts +4 -0
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli-commands.ts +5 -0
- package/src/commands/commit.ts +16 -3
- package/src/commands/share.ts +71 -0
- package/src/commit/agentic/index.ts +39 -21
- package/src/commit/execute.ts +56 -0
- package/src/commit/index.ts +2 -1
- package/src/commit/pipeline.ts +20 -7
- package/src/config/custom-models.ts +188 -0
- package/src/config/model-config-values.ts +128 -0
- package/src/config/model-discovery.ts +1 -1
- package/src/config/model-patch.ts +252 -0
- package/src/config/model-provider-discovery.ts +132 -0
- package/src/config/model-registry.ts +84 -704
- package/src/config/settings-schema.ts +2 -33
- package/src/config/settings.ts +53 -3
- package/src/debug/raw-sse-buffer.ts +20 -0
- package/src/discovery/agent-plugin-format.ts +551 -0
- package/src/discovery/agent-plugins.ts +341 -0
- package/src/discovery/claude-plugins.ts +21 -5
- package/src/discovery/contained-path.ts +75 -0
- package/src/discovery/helpers.ts +23 -9
- package/src/discovery/index.ts +1 -0
- package/src/discovery/omp-plugins.ts +20 -7
- package/src/edit/hashline/diff.ts +5 -1
- package/src/eval/executor-base.ts +39 -6
- package/src/eval/jl/executor.ts +82 -371
- package/src/eval/kernel-session-registry.ts +398 -0
- package/src/eval/py/executor.ts +53 -261
- package/src/eval/rb/executor.ts +36 -279
- package/src/exec/non-interactive-env.ts +1 -0
- package/src/export/share.ts +2 -1
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/extensions/loader.ts +78 -14
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +12 -0
- package/src/extensibility/plugins/marketplace/manager.ts +2 -1
- package/src/extensibility/shared-events.ts +3 -2
- package/src/extensibility/skills.ts +9 -0
- package/src/internal-urls/skill-protocol.ts +14 -0
- package/src/internal-urls/vault-protocol.ts +2 -2
- package/src/launch/client.ts +11 -1
- package/src/launch/protocol.ts +7 -2
- package/src/lsp/diagnostics.ts +516 -0
- package/src/lsp/index.ts +20 -2819
- package/src/lsp/servers.ts +296 -0
- package/src/lsp/tool.ts +1352 -0
- package/src/lsp/workspace-diagnostics.ts +170 -0
- package/src/lsp/writethrough.ts +561 -0
- package/src/main.ts +3 -2
- package/src/mcp/config.ts +3 -0
- package/src/mcp/manager.ts +12 -9
- package/src/mcp/transports/header-policy.ts +95 -0
- package/src/mcp/transports/http.ts +35 -52
- package/src/mcp/transports/sse.ts +20 -27
- package/src/mcp/types.ts +15 -0
- package/src/modes/acp/acp-agent.ts +15 -4
- package/src/modes/components/agent-dashboard.ts +2 -0
- package/src/modes/components/agent-hub-projection.ts +2 -2
- package/src/modes/components/agent-hub-renderer.ts +3 -7
- package/src/modes/components/agent-hub.ts +5 -0
- package/src/modes/components/agent-transcript-viewer.ts +3 -0
- package/src/modes/components/chat-transcript-builder.ts +3 -0
- package/src/modes/components/custom-editor.ts +0 -9
- package/src/modes/components/status-line/component.ts +1 -0
- package/src/modes/components/status-line/segments.ts +6 -4
- package/src/modes/components/status-line/types.ts +2 -0
- package/src/modes/components/tool-execution.ts +13 -16
- package/src/modes/components/tree-selector.ts +62 -3
- package/src/modes/controllers/command-controller.ts +8 -2
- package/src/modes/controllers/event-controller.ts +15 -15
- package/src/modes/controllers/input-controller.ts +20 -2
- package/src/modes/controllers/selector-controller.ts +1 -0
- package/src/modes/theme/color.ts +133 -0
- package/src/modes/theme/loader.ts +178 -0
- package/src/modes/theme/schema.ts +263 -0
- package/src/modes/theme/symbols.ts +1000 -0
- package/src/modes/theme/theme-class.ts +611 -0
- package/src/modes/theme/theme.ts +27 -2453
- package/src/modes/theme/tui-adapters.ts +279 -0
- package/src/modes/utils/transcript-render-helpers.ts +4 -2
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/registry/agent-registry.ts +2 -2
- package/src/sdk.ts +6 -49
- package/src/secrets/index.ts +51 -6
- package/src/secrets/message-transform.ts +287 -0
- package/src/secrets/obfuscator.ts +39 -1303
- package/src/secrets/placeholder-scan.ts +506 -0
- package/src/secrets/placeholder.ts +309 -0
- package/src/secrets/replacement.ts +216 -0
- package/src/session/agent-session-events.ts +2 -2
- package/src/session/agent-session-types.ts +6 -0
- package/src/session/agent-session.ts +146 -21
- package/src/session/indexed-session-storage.ts +7 -2
- package/src/session/messages.ts +1 -0
- package/src/session/prewalk.ts +57 -17
- package/src/session/session-advisors.ts +32 -34
- package/src/session/session-context.ts +3 -5
- package/src/session/session-handoff.ts +39 -16
- package/src/session/session-manager.ts +67 -3
- package/src/session/session-provider-boundary.ts +3 -6
- package/src/session/session-tools.ts +37 -3
- package/src/session/todo-tracker.ts +11 -1
- package/src/session/turn-recovery.ts +115 -84
- package/src/slash-commands/builtin-collaboration.ts +504 -0
- package/src/slash-commands/builtin-completions.ts +291 -0
- package/src/slash-commands/builtin-control.ts +78 -0
- package/src/slash-commands/builtin-lifecycle.ts +506 -0
- package/src/slash-commands/builtin-marketplace.ts +567 -0
- package/src/slash-commands/builtin-modes.ts +518 -0
- package/src/slash-commands/builtin-registry.ts +21 -2996
- package/src/slash-commands/builtin-session.ts +592 -0
- package/src/task/executor.ts +17 -14
- package/src/task/index.ts +21 -6
- package/src/tools/bash-skill-urls.ts +15 -0
- package/src/tools/bash.ts +10 -11
- package/src/tools/debug.ts +1 -1
- package/src/tools/gh-common.ts +288 -0
- package/src/tools/gh-pr-checkout.ts +679 -0
- package/src/tools/gh-pr-diff.ts +473 -0
- package/src/tools/gh-run-watch.ts +1015 -0
- package/src/tools/gh-search.ts +500 -0
- package/src/tools/gh-types.ts +379 -0
- package/src/tools/gh-view.ts +612 -0
- package/src/tools/gh.ts +109 -3821
- package/src/tools/jtd-to-json-schema.ts +123 -21
- package/src/tools/read-archive.ts +209 -0
- package/src/tools/read-format.ts +593 -0
- package/src/tools/read-path-resolution.ts +36 -0
- package/src/tools/read-pdf-images.ts +250 -0
- package/src/tools/read-renderer.ts +289 -0
- package/src/tools/read-selector.ts +84 -0
- package/src/tools/read-sqlite.ts +215 -0
- package/src/tools/read-summary.ts +199 -0
- package/src/tools/read.ts +82 -1824
- package/src/tools/shell-tokenize.ts +99 -1
- package/src/utils/changelog.ts +1 -1
- package/src/utils/title-generator.ts +3 -1
- package/src/web/search/providers/exa.ts +1 -1
- package/src/web/search/providers/zai.ts +12 -3
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
import { getOAuthProviders } from "@oh-my-pi/pi-ai/oauth";
|
|
2
|
+
import { settings } from "../config/settings";
|
|
3
|
+
import type { AgentSession } from "../session/agent-session";
|
|
4
|
+
import type { SessionOAuthAccountList } from "../session/agent-session-types";
|
|
5
|
+
import {
|
|
6
|
+
getChangelogPath,
|
|
7
|
+
parseChangelog,
|
|
8
|
+
RECENT_CHANGELOG_ENTRY_LIMIT,
|
|
9
|
+
renderChangelogEntries,
|
|
10
|
+
} from "../utils/changelog";
|
|
11
|
+
import { formatTokenCount, refreshStatusLine } from "./builtin-modes";
|
|
12
|
+
import { buildContextReportText } from "./helpers/context-report";
|
|
13
|
+
import { formatDuration } from "./helpers/format";
|
|
14
|
+
import { handleMcpAcp } from "./helpers/mcp";
|
|
15
|
+
import { commandConsumed, errorMessage, parseSubcommand, usage } from "./helpers/parse";
|
|
16
|
+
import { describeRedeemOutcome, type ResetUsageAccount, toResetUsageAccounts } from "./helpers/reset-usage";
|
|
17
|
+
import { matchSessionPinAccounts, toSessionPinAccounts } from "./helpers/session-pin";
|
|
18
|
+
import { launchStatsDashboard, parseStatsDashboardArgs } from "./helpers/stats-dashboard";
|
|
19
|
+
import { handleTodoAcp } from "./helpers/todo";
|
|
20
|
+
import { buildUsageReportText } from "./helpers/usage-report";
|
|
21
|
+
import type { SlashCommandRuntime, SlashCommandSpec } from "./types";
|
|
22
|
+
|
|
23
|
+
async function handleUsageResetCommand(
|
|
24
|
+
arg: string,
|
|
25
|
+
session: AgentSession,
|
|
26
|
+
output: SlashCommandRuntime["output"],
|
|
27
|
+
): Promise<void> {
|
|
28
|
+
let accounts: ResetUsageAccount[];
|
|
29
|
+
try {
|
|
30
|
+
accounts = toResetUsageAccounts(await session.listResetCredits());
|
|
31
|
+
} catch (error) {
|
|
32
|
+
await output(`Could not load saved resets: ${errorMessage(error)}`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (accounts.length === 0) {
|
|
36
|
+
await output("No Codex accounts found. Use /login to add one.");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const targetArg = arg.trim();
|
|
40
|
+
if (!targetArg) {
|
|
41
|
+
const lines = ["Saved Codex rate-limit resets:"];
|
|
42
|
+
for (const account of accounts) {
|
|
43
|
+
const detail = account.error ? `unavailable (${account.error})` : `${account.availableCount} available`;
|
|
44
|
+
lines.push(`- ${account.label}: ${detail}${account.active ? " (active)" : ""}`);
|
|
45
|
+
}
|
|
46
|
+
lines.push("", "Spend one with `/usage reset <account email>` or `/usage reset active`.");
|
|
47
|
+
await output(lines.join("\n"));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const wanted = targetArg.toLowerCase();
|
|
51
|
+
const target =
|
|
52
|
+
wanted === "active"
|
|
53
|
+
? accounts.find(account => account.active)
|
|
54
|
+
: accounts.find(
|
|
55
|
+
account =>
|
|
56
|
+
account.label.toLowerCase() === wanted ||
|
|
57
|
+
account.target.email?.toLowerCase() === wanted ||
|
|
58
|
+
account.target.accountId?.toLowerCase() === wanted,
|
|
59
|
+
);
|
|
60
|
+
if (!target) {
|
|
61
|
+
await output(`No Codex account matches "${targetArg}".`);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (target.availableCount <= 0) {
|
|
65
|
+
await output(`${target.label}: no saved resets to spend.`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const outcome = await session.redeemResetCredit(target.target);
|
|
69
|
+
await output(describeRedeemOutcome(outcome, target.label));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function handleSessionPinCommand(
|
|
73
|
+
arg: string,
|
|
74
|
+
session: AgentSession,
|
|
75
|
+
output: SlashCommandRuntime["output"],
|
|
76
|
+
): Promise<void> {
|
|
77
|
+
if (session.isStreaming) {
|
|
78
|
+
await output("Cannot pin an account while the session is streaming.");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
let accountList: SessionOAuthAccountList | undefined;
|
|
82
|
+
try {
|
|
83
|
+
accountList = await session.listCurrentProviderOAuthAccounts();
|
|
84
|
+
} catch (error) {
|
|
85
|
+
await output(`Could not load provider accounts: ${errorMessage(error)}`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (!accountList) {
|
|
89
|
+
await output("Select a model before pinning a provider account.");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const provider = getOAuthProviders().find(candidate => candidate.id === accountList.provider);
|
|
93
|
+
const providerName = provider?.name ?? accountList.provider;
|
|
94
|
+
const accounts = toSessionPinAccounts(accountList.accounts);
|
|
95
|
+
if (accounts.length === 0) {
|
|
96
|
+
const source = session.modelRegistry.authStorage.describeCredentialSource(
|
|
97
|
+
accountList.provider,
|
|
98
|
+
session.sessionId,
|
|
99
|
+
);
|
|
100
|
+
await output(
|
|
101
|
+
source
|
|
102
|
+
? `No stored OAuth accounts for ${providerName}. Current auth comes from ${source}.`
|
|
103
|
+
: `No stored OAuth accounts for ${providerName}. Use /login to add one.`,
|
|
104
|
+
);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const selector = arg.trim();
|
|
109
|
+
if (!selector) {
|
|
110
|
+
const lines = [`OAuth accounts for ${providerName}:`];
|
|
111
|
+
for (const account of accounts) {
|
|
112
|
+
lines.push(`${account.position + 1}. ${account.label}${account.active ? " (active)" : ""}`);
|
|
113
|
+
}
|
|
114
|
+
lines.push("", "Pin one with `/session pin <number|email|account id>`.");
|
|
115
|
+
await output(lines.join("\n"));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const matches = matchSessionPinAccounts(accounts, selector);
|
|
120
|
+
if (matches.length === 0) {
|
|
121
|
+
await output(`No ${providerName} account matches "${selector}".`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (matches.length > 1) {
|
|
125
|
+
await output(
|
|
126
|
+
`"${selector}" matches multiple ${providerName} accounts: ${matches
|
|
127
|
+
.map(account => `${account.position + 1}. ${account.label}`)
|
|
128
|
+
.join(", ")}. Use the account number.`,
|
|
129
|
+
);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const account = matches[0];
|
|
133
|
+
if (!account || !session.pinCurrentProviderOAuthAccount(account.credentialId)) {
|
|
134
|
+
await output(`${account?.label ?? selector} is no longer available to pin.`);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
await output(`Pinned ${account.label} to this session for ${providerName}.`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export const BUILTIN_SESSION_SLASH_COMMANDS: ReadonlyArray<SlashCommandSpec> = [
|
|
141
|
+
{
|
|
142
|
+
name: "todo",
|
|
143
|
+
description: "View or modify the agent's todo list",
|
|
144
|
+
acpDescription: "Manage todos",
|
|
145
|
+
acpInputHint: "<subcommand>",
|
|
146
|
+
subcommands: [
|
|
147
|
+
{ name: "edit", description: "Open todos in $EDITOR (Markdown round-trip)" },
|
|
148
|
+
{ name: "copy", description: "Copy todos as Markdown to clipboard" },
|
|
149
|
+
{ name: "export", description: "Write todos as Markdown to a file (default: TODO.md)", usage: "[<path>]" },
|
|
150
|
+
{ name: "import", description: "Replace todos from a Markdown file (default: TODO.md)", usage: "[<path>]" },
|
|
151
|
+
{
|
|
152
|
+
name: "append",
|
|
153
|
+
description: "Append a task; phase fuzzy-matched or auto-created",
|
|
154
|
+
usage: "[<phase>] <task...>",
|
|
155
|
+
},
|
|
156
|
+
{ name: "start", description: "Mark task in_progress (fuzzy-matched)", usage: "<task>" },
|
|
157
|
+
{ name: "done", description: "Mark task/phase/all completed (fuzzy-matched)", usage: "[<task|phase>]" },
|
|
158
|
+
{ name: "drop", description: "Mark task/phase/all abandoned (fuzzy-matched)", usage: "[<task|phase>]" },
|
|
159
|
+
{ name: "rm", description: "Remove task/phase/all (fuzzy-matched)", usage: "[<task|phase>]" },
|
|
160
|
+
],
|
|
161
|
+
allowArgs: true,
|
|
162
|
+
getTuiAutocompleteDescription: runtime => {
|
|
163
|
+
const tasks = runtime.ctx.todoPhases.flatMap(phase => phase.tasks);
|
|
164
|
+
if (tasks.length === 0) return "Todos: none";
|
|
165
|
+
const pending = tasks.filter(task => task.status === "pending").length;
|
|
166
|
+
const inProgress = tasks.filter(task => task.status === "in_progress").length;
|
|
167
|
+
const completed = tasks.filter(task => task.status === "completed").length;
|
|
168
|
+
return `Todos: ${pending + inProgress} open (${inProgress} in progress, ${completed} done)`;
|
|
169
|
+
},
|
|
170
|
+
handle: handleTodoAcp,
|
|
171
|
+
handleTui: async (command, runtime) => {
|
|
172
|
+
await runtime.ctx.handleTodoCommand(command.args);
|
|
173
|
+
runtime.ctx.editor.setText("");
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
name: "session",
|
|
178
|
+
description: "Session management commands",
|
|
179
|
+
acpDescription: "Show or configure the current session",
|
|
180
|
+
acpInputHint: "[info|delete|pin [account]]",
|
|
181
|
+
subcommands: [
|
|
182
|
+
{ name: "info", description: "Show session info and stats" },
|
|
183
|
+
{ name: "delete", description: "Delete current session and return to selector" },
|
|
184
|
+
{
|
|
185
|
+
name: "pin",
|
|
186
|
+
description: "Pin the current provider to a stored OAuth account",
|
|
187
|
+
usage: "[account]",
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
allowArgs: true,
|
|
191
|
+
handle: async (command, runtime) => {
|
|
192
|
+
const { verb, rest } = parseSubcommand(command.args);
|
|
193
|
+
if (!verb || (verb === "info" && !rest)) {
|
|
194
|
+
await runtime.output(
|
|
195
|
+
[
|
|
196
|
+
`Session: ${runtime.session.sessionId}`,
|
|
197
|
+
`Title: ${runtime.session.sessionName}`,
|
|
198
|
+
`CWD: ${runtime.cwd}`,
|
|
199
|
+
].join("\n"),
|
|
200
|
+
);
|
|
201
|
+
return commandConsumed();
|
|
202
|
+
}
|
|
203
|
+
if (verb === "delete" && !rest) {
|
|
204
|
+
if (runtime.session.isStreaming) return usage("Cannot delete the session while streaming.", runtime);
|
|
205
|
+
const sessionFile = runtime.sessionManager.getSessionFile();
|
|
206
|
+
if (!sessionFile) return usage("No session file to delete (in-memory session).", runtime);
|
|
207
|
+
// Route through the active SessionManager so the persist writer is
|
|
208
|
+
// closed before the file is deleted. Constructing a fresh
|
|
209
|
+
// FileSessionStorage and calling deleteSessionWithArtifacts leaves
|
|
210
|
+
// the active writer attached to the now-deleted path, so the next
|
|
211
|
+
// prompt would silently resurrect or corrupt the "deleted" file.
|
|
212
|
+
try {
|
|
213
|
+
await runtime.sessionManager.dropSession(sessionFile);
|
|
214
|
+
} catch (err) {
|
|
215
|
+
return usage(`Failed to delete session: ${errorMessage(err)}`, runtime);
|
|
216
|
+
}
|
|
217
|
+
await runtime.output(
|
|
218
|
+
`Session deleted: ${sessionFile}. Use ACP \`session/load\` to switch to another session.`,
|
|
219
|
+
);
|
|
220
|
+
return commandConsumed();
|
|
221
|
+
}
|
|
222
|
+
if (verb === "pin") {
|
|
223
|
+
await handleSessionPinCommand(rest, runtime.session, runtime.output);
|
|
224
|
+
return commandConsumed();
|
|
225
|
+
}
|
|
226
|
+
return usage("Usage: /session [info|delete|pin [account]]", runtime);
|
|
227
|
+
},
|
|
228
|
+
handleTui: async (command, runtime) => {
|
|
229
|
+
const { verb, rest } = parseSubcommand(command.args);
|
|
230
|
+
if (verb === "delete" && !rest) {
|
|
231
|
+
runtime.ctx.editor.setText("");
|
|
232
|
+
await runtime.ctx.handleSessionDeleteCommand();
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (verb === "pin") {
|
|
236
|
+
if (rest) {
|
|
237
|
+
await handleSessionPinCommand(rest, runtime.ctx.session, text => runtime.ctx.showStatus(text));
|
|
238
|
+
refreshStatusLine(runtime.ctx);
|
|
239
|
+
} else {
|
|
240
|
+
await runtime.ctx.showSessionPinSelector();
|
|
241
|
+
}
|
|
242
|
+
runtime.ctx.editor.setText("");
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (!verb || (verb === "info" && !rest)) {
|
|
246
|
+
await runtime.ctx.handleSessionCommand();
|
|
247
|
+
} else {
|
|
248
|
+
runtime.ctx.showStatus("Usage: /session [info|delete|pin [account]]");
|
|
249
|
+
}
|
|
250
|
+
runtime.ctx.editor.setText("");
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
name: "jobs",
|
|
255
|
+
description: "Show async background jobs status",
|
|
256
|
+
acpDescription: "Show background jobs",
|
|
257
|
+
getTuiAutocompleteDescription: runtime => {
|
|
258
|
+
const snapshot = runtime.ctx.session.getAsyncJobSnapshot({ recentLimit: 5 });
|
|
259
|
+
if (!snapshot || (snapshot.running.length === 0 && snapshot.recent.length === 0)) return "Jobs: none";
|
|
260
|
+
return `Jobs: ${snapshot.running.length} running, ${snapshot.recent.length} recent`;
|
|
261
|
+
},
|
|
262
|
+
handle: async (_command, runtime) => {
|
|
263
|
+
const snapshot = runtime.session.getAsyncJobSnapshot({ recentLimit: 5 });
|
|
264
|
+
if (!snapshot || (snapshot.running.length === 0 && snapshot.recent.length === 0)) {
|
|
265
|
+
await runtime.output(
|
|
266
|
+
"No background jobs running. (Background jobs run async tools — e.g. long-running bash, debug, or task subagents that would otherwise tie up a turn. They appear here while alive and for ~5 minutes after.)",
|
|
267
|
+
);
|
|
268
|
+
return commandConsumed();
|
|
269
|
+
}
|
|
270
|
+
const now = Date.now();
|
|
271
|
+
const lines: string[] = ["Background Jobs", `Running: ${snapshot.running.length}`];
|
|
272
|
+
if (snapshot.running.length > 0) {
|
|
273
|
+
lines.push("", "Running Jobs");
|
|
274
|
+
for (const job of snapshot.running) {
|
|
275
|
+
lines.push(` [${job.id}] ${job.type} (${job.status}) — ${formatDuration(now - job.startTime)}`);
|
|
276
|
+
lines.push(` ${job.label}`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (snapshot.recent.length > 0) {
|
|
280
|
+
lines.push("", "Recent Jobs");
|
|
281
|
+
for (const job of snapshot.recent) {
|
|
282
|
+
lines.push(` [${job.id}] ${job.type} (${job.status}) — ${formatDuration(now - job.startTime)}`);
|
|
283
|
+
lines.push(` ${job.label}`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
await runtime.output(lines.join("\n"));
|
|
287
|
+
return commandConsumed();
|
|
288
|
+
},
|
|
289
|
+
handleTui: async (_command, runtime) => {
|
|
290
|
+
await runtime.ctx.handleJobsCommand();
|
|
291
|
+
runtime.ctx.editor.setText("");
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
name: "usage",
|
|
296
|
+
description: "Show provider usage and limits",
|
|
297
|
+
acpDescription: "Show token usage",
|
|
298
|
+
acpInputHint: "[show|reset [account|active]]",
|
|
299
|
+
subcommands: [
|
|
300
|
+
{ name: "show", description: "Show provider usage and limits" },
|
|
301
|
+
{ name: "reset", description: "Spend a saved Codex rate-limit reset", usage: "[account|active]" },
|
|
302
|
+
],
|
|
303
|
+
allowArgs: true,
|
|
304
|
+
handle: async (command, runtime) => {
|
|
305
|
+
const { verb, rest } = parseSubcommand(command.args);
|
|
306
|
+
if (!verb || (verb === "show" && !rest)) {
|
|
307
|
+
await runtime.output(await buildUsageReportText(runtime));
|
|
308
|
+
return commandConsumed();
|
|
309
|
+
}
|
|
310
|
+
if (verb === "reset") {
|
|
311
|
+
await handleUsageResetCommand(rest, runtime.session, runtime.output);
|
|
312
|
+
return commandConsumed();
|
|
313
|
+
}
|
|
314
|
+
return usage("Usage: /usage [show|reset [account|active]]", runtime);
|
|
315
|
+
},
|
|
316
|
+
handleTui: async (command, runtime) => {
|
|
317
|
+
const { verb, rest } = parseSubcommand(command.args);
|
|
318
|
+
if (!verb || (verb === "show" && !rest)) {
|
|
319
|
+
await runtime.ctx.handleUsageCommand();
|
|
320
|
+
runtime.ctx.editor.setText("");
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (verb === "reset") {
|
|
324
|
+
if (rest) {
|
|
325
|
+
await handleUsageResetCommand(rest, runtime.ctx.session, text => runtime.ctx.showStatus(text));
|
|
326
|
+
} else {
|
|
327
|
+
await runtime.ctx.showResetUsageSelector();
|
|
328
|
+
}
|
|
329
|
+
runtime.ctx.editor.setText("");
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
runtime.ctx.showStatus("Usage: /usage [show|reset [account|active]]");
|
|
333
|
+
runtime.ctx.editor.setText("");
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
name: "stats",
|
|
338
|
+
description: "Launch the local stats dashboard",
|
|
339
|
+
inlineHint: "[--port <port>]",
|
|
340
|
+
allowArgs: true,
|
|
341
|
+
handle: async (command, runtime) => {
|
|
342
|
+
const parsed = parseStatsDashboardArgs(command.args);
|
|
343
|
+
if ("error" in parsed) return usage(parsed.error, runtime);
|
|
344
|
+
|
|
345
|
+
await runtime.output("Syncing session files...");
|
|
346
|
+
try {
|
|
347
|
+
const result = await launchStatsDashboard(parsed);
|
|
348
|
+
await runtime.output(result.message);
|
|
349
|
+
} catch (error) {
|
|
350
|
+
await runtime.output(`Stats dashboard failed: ${errorMessage(error)}`);
|
|
351
|
+
}
|
|
352
|
+
return commandConsumed();
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
name: "changelog",
|
|
357
|
+
description: "Show changelog entries",
|
|
358
|
+
acpDescription: "Show changelog",
|
|
359
|
+
acpInputHint: "[full]",
|
|
360
|
+
subcommands: [{ name: "full", description: "Show complete changelog" }],
|
|
361
|
+
allowArgs: true,
|
|
362
|
+
handle: async (command, runtime) => {
|
|
363
|
+
const changelogPath = getChangelogPath();
|
|
364
|
+
const allEntries = await parseChangelog(changelogPath);
|
|
365
|
+
const showFull = command.args.trim().toLowerCase() === "full";
|
|
366
|
+
const entriesToShow = showFull ? allEntries : allEntries.slice(0, RECENT_CHANGELOG_ENTRY_LIMIT);
|
|
367
|
+
if (entriesToShow.length === 0) {
|
|
368
|
+
await runtime.output("No changelog entries found.");
|
|
369
|
+
return commandConsumed();
|
|
370
|
+
}
|
|
371
|
+
await runtime.output(renderChangelogEntries(entriesToShow).markdown);
|
|
372
|
+
return commandConsumed();
|
|
373
|
+
},
|
|
374
|
+
handleTui: async (command, runtime) => {
|
|
375
|
+
const showFull = command.args.split(/\s+/).filter(Boolean).includes("full");
|
|
376
|
+
await runtime.ctx.handleChangelogCommand(showFull);
|
|
377
|
+
runtime.ctx.editor.setText("");
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
name: "hotkeys",
|
|
382
|
+
description: "Show all keyboard shortcuts",
|
|
383
|
+
handleTui: (_command, runtime) => {
|
|
384
|
+
runtime.ctx.handleHotkeysCommand();
|
|
385
|
+
runtime.ctx.editor.setText("");
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
name: "tools",
|
|
390
|
+
description: "Show tools currently visible to the agent",
|
|
391
|
+
acpDescription: "Show available tools",
|
|
392
|
+
getTuiAutocompleteDescription: runtime => {
|
|
393
|
+
const active = runtime.ctx.session.getActiveToolNames().length;
|
|
394
|
+
const all = runtime.ctx.session.getAllToolNames().length;
|
|
395
|
+
return all === 0 ? "Tools: none available" : `Tools: ${active} active / ${all} available`;
|
|
396
|
+
},
|
|
397
|
+
handle: async (_command, runtime) => {
|
|
398
|
+
const active = runtime.session.getActiveToolNames();
|
|
399
|
+
const all = runtime.session.getAllToolNames();
|
|
400
|
+
if (all.length === 0) {
|
|
401
|
+
await runtime.output("No tools are available.");
|
|
402
|
+
return commandConsumed();
|
|
403
|
+
}
|
|
404
|
+
const lines = all.map(name => `${active.includes(name) ? "*" : "-"} ${name}`);
|
|
405
|
+
for (const mounted of runtime.session.getXdevToolEntries()) {
|
|
406
|
+
lines.push(`~ xd://${mounted.name}`);
|
|
407
|
+
}
|
|
408
|
+
await runtime.output(lines.join("\n"));
|
|
409
|
+
return commandConsumed();
|
|
410
|
+
},
|
|
411
|
+
handleTui: (_command, runtime) => {
|
|
412
|
+
runtime.ctx.handleToolsCommand();
|
|
413
|
+
runtime.ctx.editor.setText("");
|
|
414
|
+
},
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
name: "context",
|
|
418
|
+
description: "Show estimated context usage breakdown",
|
|
419
|
+
acpDescription: "Show context usage",
|
|
420
|
+
getTuiAutocompleteDescription: runtime => {
|
|
421
|
+
const usage = runtime.ctx.session.getContextUsage();
|
|
422
|
+
if (!usage) return "Context: unavailable";
|
|
423
|
+
return `Context: ${Math.round(usage.percent)}% (${formatTokenCount(usage.tokens)}/${formatTokenCount(usage.contextWindow)})`;
|
|
424
|
+
},
|
|
425
|
+
handle: async (_command, runtime) => {
|
|
426
|
+
await runtime.output(buildContextReportText(runtime));
|
|
427
|
+
return commandConsumed();
|
|
428
|
+
},
|
|
429
|
+
handleTui: (_command, runtime) => {
|
|
430
|
+
runtime.ctx.handleContextCommand();
|
|
431
|
+
runtime.ctx.editor.setText("");
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
name: "extensions",
|
|
436
|
+
aliases: ["status"],
|
|
437
|
+
description: "Open Extension Control Center dashboard",
|
|
438
|
+
handleTui: (_command, runtime) => {
|
|
439
|
+
runtime.ctx.showExtensionsDashboard();
|
|
440
|
+
runtime.ctx.editor.setText("");
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
name: "agents",
|
|
445
|
+
description: "Open Agent Control Center dashboard",
|
|
446
|
+
handleTui: (_command, runtime) => {
|
|
447
|
+
runtime.ctx.showAgentsDashboard();
|
|
448
|
+
runtime.ctx.editor.setText("");
|
|
449
|
+
},
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
name: "branch",
|
|
453
|
+
description: "Create a new branch from a previous message",
|
|
454
|
+
handleTui: (_command, runtime) => {
|
|
455
|
+
if (settings.get("doubleEscapeAction") === "tree") {
|
|
456
|
+
runtime.ctx.showTreeSelector();
|
|
457
|
+
} else {
|
|
458
|
+
runtime.ctx.showUserMessageSelector();
|
|
459
|
+
}
|
|
460
|
+
runtime.ctx.editor.setText("");
|
|
461
|
+
},
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
name: "fork",
|
|
465
|
+
description: "Create a new fork from a previous message",
|
|
466
|
+
handleTui: async (_command, runtime) => {
|
|
467
|
+
runtime.ctx.editor.setText("");
|
|
468
|
+
await runtime.ctx.handleForkCommand();
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
name: "tree",
|
|
473
|
+
description: "Navigate session tree (switch branches)",
|
|
474
|
+
handleTui: (_command, runtime) => {
|
|
475
|
+
runtime.ctx.showTreeSelector();
|
|
476
|
+
runtime.ctx.editor.setText("");
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
name: "login",
|
|
481
|
+
description: "Login with OAuth provider",
|
|
482
|
+
inlineHint: "[provider|redirect URL]",
|
|
483
|
+
allowArgs: true,
|
|
484
|
+
getTuiAutocompleteDescription: runtime =>
|
|
485
|
+
runtime.ctx.oauthManualInput.hasPending()
|
|
486
|
+
? `Login: waiting for ${runtime.ctx.oauthManualInput.pendingProviderId ?? "OAuth"} callback`
|
|
487
|
+
: "Login: choose provider",
|
|
488
|
+
handleTui: (command, runtime) => {
|
|
489
|
+
const manualInput = runtime.ctx.oauthManualInput;
|
|
490
|
+
const args = command.args.trim();
|
|
491
|
+
if (args.length > 0) {
|
|
492
|
+
const matchedProvider = getOAuthProviders().find(provider => provider.id === args);
|
|
493
|
+
if (matchedProvider) {
|
|
494
|
+
if (manualInput.hasPending()) {
|
|
495
|
+
const pendingProvider = manualInput.pendingProviderId;
|
|
496
|
+
const message = pendingProvider
|
|
497
|
+
? `OAuth login already in progress for ${pendingProvider}. Paste the redirect URL with /login <url>.`
|
|
498
|
+
: "OAuth login already in progress. Paste the redirect URL with /login <url>.";
|
|
499
|
+
runtime.ctx.showWarning(message);
|
|
500
|
+
runtime.ctx.editor.setText("");
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
void runtime.ctx.showOAuthSelector("login", matchedProvider.id);
|
|
504
|
+
runtime.ctx.editor.setText("");
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
const submitted = manualInput.submit(args);
|
|
508
|
+
if (submitted) {
|
|
509
|
+
runtime.ctx.showStatus("OAuth callback received; completing login…");
|
|
510
|
+
} else {
|
|
511
|
+
runtime.ctx.showWarning("No OAuth login is waiting for a manual callback.");
|
|
512
|
+
}
|
|
513
|
+
runtime.ctx.editor.setText("");
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (manualInput.hasPending()) {
|
|
518
|
+
const provider = manualInput.pendingProviderId;
|
|
519
|
+
const message = provider
|
|
520
|
+
? `OAuth login already in progress for ${provider}. Paste the redirect URL with /login <url>.`
|
|
521
|
+
: "OAuth login already in progress. Paste the redirect URL with /login <url>.";
|
|
522
|
+
runtime.ctx.showWarning(message);
|
|
523
|
+
runtime.ctx.editor.setText("");
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
void runtime.ctx.showOAuthSelector("login");
|
|
528
|
+
runtime.ctx.editor.setText("");
|
|
529
|
+
},
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
name: "logout",
|
|
533
|
+
description: "Logout from OAuth provider",
|
|
534
|
+
inlineHint: "[provider]",
|
|
535
|
+
allowArgs: true,
|
|
536
|
+
handleTui: (command, runtime) => {
|
|
537
|
+
const providerId = command.args.trim();
|
|
538
|
+
if (providerId) {
|
|
539
|
+
const matchedProvider = getOAuthProviders().find(provider => provider.id === providerId);
|
|
540
|
+
if (!matchedProvider) {
|
|
541
|
+
runtime.ctx.showWarning(`Unknown OAuth provider: ${providerId}`);
|
|
542
|
+
runtime.ctx.editor.setText("");
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
void runtime.ctx.showOAuthSelector("logout", matchedProvider.id);
|
|
546
|
+
runtime.ctx.editor.setText("");
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
void runtime.ctx.showOAuthSelector("logout");
|
|
550
|
+
runtime.ctx.editor.setText("");
|
|
551
|
+
},
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
name: "mcp",
|
|
555
|
+
description: "Manage MCP servers (add, list, remove, test)",
|
|
556
|
+
acpDescription: "Manage MCP servers",
|
|
557
|
+
inlineHint: "<subcommand>",
|
|
558
|
+
subcommands: [
|
|
559
|
+
{
|
|
560
|
+
name: "add",
|
|
561
|
+
description: "Add a new MCP server",
|
|
562
|
+
usage: "<name> [--scope project|user] [--url <url>] [-- <command...>]",
|
|
563
|
+
},
|
|
564
|
+
{ name: "list", description: "List all configured MCP servers" },
|
|
565
|
+
{ name: "remove", description: "Remove an MCP server", usage: "<name> [--scope project|user]" },
|
|
566
|
+
{ name: "test", description: "Test connection to a server", usage: "<name>" },
|
|
567
|
+
{ name: "reauth", description: "Reauthorize OAuth for a server", usage: "<name>" },
|
|
568
|
+
{ name: "unauth", description: "Remove OAuth auth from a server", usage: "<name>" },
|
|
569
|
+
{ name: "enable", description: "Enable an MCP server", usage: "<name>" },
|
|
570
|
+
{ name: "disable", description: "Disable an MCP server", usage: "<name>" },
|
|
571
|
+
{
|
|
572
|
+
name: "smithery-search",
|
|
573
|
+
description: "Search Smithery registry and deploy an MCP server",
|
|
574
|
+
usage: "<keyword> [--scope project|user] [--limit <1-100>] [--semantic]",
|
|
575
|
+
},
|
|
576
|
+
{ name: "smithery-login", description: "Login to Smithery and cache API key" },
|
|
577
|
+
{ name: "smithery-logout", description: "Remove cached Smithery API key" },
|
|
578
|
+
{ name: "reconnect", description: "Reconnect to a specific MCP server", usage: "<name>" },
|
|
579
|
+
{ name: "reload", description: "Force reload MCP runtime tools" },
|
|
580
|
+
{ name: "resources", description: "List available resources from connected servers" },
|
|
581
|
+
{ name: "prompts", description: "List available prompts from connected servers" },
|
|
582
|
+
{ name: "notifications", description: "Show notification capabilities and subscriptions" },
|
|
583
|
+
{ name: "help", description: "Show help message" },
|
|
584
|
+
],
|
|
585
|
+
allowArgs: true,
|
|
586
|
+
handle: handleMcpAcp,
|
|
587
|
+
handleTui: async (command, runtime) => {
|
|
588
|
+
runtime.ctx.editor.setText("");
|
|
589
|
+
await runtime.ctx.handleMCPCommand(command.text);
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
];
|
package/src/task/executor.ts
CHANGED
|
@@ -876,20 +876,23 @@ export function createSubagentSettings(
|
|
|
876
876
|
snapshot["tier.openai"] = subagentTiers.openai ?? "none";
|
|
877
877
|
snapshot["tier.anthropic"] = subagentTiers.anthropic ?? "none";
|
|
878
878
|
snapshot["tier.google"] = subagentTiers.google ?? "none";
|
|
879
|
-
return Settings.isolated(
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
879
|
+
return Settings.isolated(
|
|
880
|
+
{
|
|
881
|
+
...snapshot,
|
|
882
|
+
// Async jobs and bash auto-backgrounding are inherited from the parent:
|
|
883
|
+
// background jobs are owner-routed to the subagent's own session, and
|
|
884
|
+
// the run driver's quiescence barrier + teardown reap guarantee no
|
|
885
|
+
// owner job outlives the run, so worktree capture/cleanup stays
|
|
886
|
+
// race-free (previously both were force-disabled here).
|
|
887
|
+
|
|
888
|
+
// Subagents run headless — there is no UI to confirm prompts against, so
|
|
889
|
+
// the parent task approval is the authorization boundary. Use yolo mode
|
|
890
|
+
// to preserve unattended subagent execution. User `tools.approval` policies still apply.
|
|
891
|
+
"tools.approvalMode": "yolo",
|
|
892
|
+
...overrides,
|
|
893
|
+
},
|
|
894
|
+
{ storage: baseSettings.getStorage() },
|
|
895
|
+
);
|
|
893
896
|
}
|
|
894
897
|
|
|
895
898
|
export type AbortReason = "signal" | "terminate" | "timeout" | "budget";
|
package/src/task/index.ts
CHANGED
|
@@ -441,16 +441,19 @@ export function composeSpawnAdvisory(args: {
|
|
|
441
441
|
class TaskJobError extends Error {}
|
|
442
442
|
|
|
443
443
|
/**
|
|
444
|
-
* Process-level
|
|
444
|
+
* Process-level create-time discovery memo and published reload snapshots,
|
|
445
|
+
* keyed by resolved cwd.
|
|
445
446
|
*
|
|
446
447
|
* `TaskTool.create` runs for every (sub)agent session in this process and the
|
|
447
448
|
* walk-up + plugin-registry scan in `discoverAgents` is identical for a given
|
|
448
|
-
* cwd, so repeat creations reuse the first scan.
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
*
|
|
449
|
+
* cwd, so repeat creations reuse the first scan. Explicit plugin reloads
|
|
450
|
+
* replace the matching snapshot so already-created tools advertise the latest
|
|
451
|
+
* definitions. Execution-time discovery (`#runSpawn`) intentionally stays
|
|
452
|
+
* fresh. The memo also tracks the live `discoverAgents` binding: test spies
|
|
453
|
+
* swap that binding, which invalidates both caches automatically.
|
|
452
454
|
*/
|
|
453
455
|
const discoveryMemo = new Map<string, Promise<DiscoveryResult>>();
|
|
456
|
+
const discoverySnapshots = new Map<string, AgentDefinition[]>();
|
|
454
457
|
let discoveryMemoFn: typeof discoverAgents | undefined;
|
|
455
458
|
|
|
456
459
|
function discoverAgentsForCreate(cwd: string): Promise<DiscoveryResult> {
|
|
@@ -458,6 +461,7 @@ function discoverAgentsForCreate(cwd: string): Promise<DiscoveryResult> {
|
|
|
458
461
|
if (discoveryMemoFn !== fn) {
|
|
459
462
|
discoveryMemoFn = fn;
|
|
460
463
|
discoveryMemo.clear();
|
|
464
|
+
discoverySnapshots.clear();
|
|
461
465
|
}
|
|
462
466
|
const key = path.resolve(cwd);
|
|
463
467
|
let pending = discoveryMemo.get(key);
|
|
@@ -471,6 +475,17 @@ function discoverAgentsForCreate(cwd: string): Promise<DiscoveryResult> {
|
|
|
471
475
|
return pending;
|
|
472
476
|
}
|
|
473
477
|
|
|
478
|
+
/** Rescan one cwd and publish its definitions to existing and future task tools. */
|
|
479
|
+
export async function refreshAgentDiscovery(cwd: string): Promise<void> {
|
|
480
|
+
const key = path.resolve(cwd);
|
|
481
|
+
discoveryMemo.delete(key);
|
|
482
|
+
const pending = discoverAgentsForCreate(cwd);
|
|
483
|
+
const { agents } = await pending;
|
|
484
|
+
if (discoveryMemo.get(key) === pending) {
|
|
485
|
+
discoverySnapshots.set(key, agents);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
474
489
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
475
490
|
// Tool Class
|
|
476
491
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -587,7 +602,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
587
602
|
const planMode = this.session.getPlanModeState?.()?.enabled === true;
|
|
588
603
|
const isolationMode = this.session.settings.get("task.isolation.mode");
|
|
589
604
|
return renderDescription({
|
|
590
|
-
agents: this.#discoveredAgents,
|
|
605
|
+
agents: discoverySnapshots.get(path.resolve(this.session.cwd)) ?? this.#discoveredAgents,
|
|
591
606
|
isolationEnabled: !planMode && isolationMode !== "none",
|
|
592
607
|
applyIsolatedChanges: this.session.settings.get("task.isolation.apply"),
|
|
593
608
|
disabledAgents,
|