@oh-my-pi/pi-coding-agent 15.10.4 → 15.10.6
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 +74 -0
- package/dist/types/capability/rule-buckets.d.ts +1 -1
- package/dist/types/capability/rule.d.ts +6 -1
- package/dist/types/cli/update-cli.d.ts +11 -1
- package/dist/types/config/model-registry.d.ts +18 -1
- package/dist/types/discovery/at-imports.d.ts +15 -0
- package/dist/types/edit/diff.d.ts +3 -2
- package/dist/types/eval/__tests__/helpers-local-roots.test.d.ts +1 -0
- package/dist/types/eval/backend.d.ts +7 -0
- package/dist/types/eval/js/context-manager.d.ts +1 -0
- package/dist/types/eval/js/executor.d.ts +2 -0
- package/dist/types/eval/js/index.d.ts +1 -1
- package/dist/types/eval/js/shared/helpers.d.ts +6 -0
- package/dist/types/eval/js/shared/runtime.d.ts +5 -0
- package/dist/types/eval/js/worker-protocol.d.ts +6 -0
- package/dist/types/eval/py/executor.d.ts +7 -0
- package/dist/types/eval/py/index.d.ts +1 -1
- package/dist/types/exa/index.d.ts +1 -19
- package/dist/types/exa/mcp-client.d.ts +10 -3
- package/dist/types/exa/types.d.ts +0 -83
- package/dist/types/export/ttsr.d.ts +14 -0
- package/dist/types/extensibility/extensions/types.d.ts +8 -1
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +1 -1
- package/dist/types/internal-urls/local-protocol.d.ts +10 -0
- package/dist/types/mcp/oauth-flow.d.ts +2 -2
- package/dist/types/modes/components/custom-editor.d.ts +3 -0
- package/dist/types/modes/components/{status-line.d.ts → status-line/component.d.ts} +2 -32
- package/dist/types/modes/components/status-line/index.d.ts +1 -0
- package/dist/types/modes/components/status-line/types.d.ts +31 -2
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +8 -0
- package/dist/types/modes/image-references.d.ts +8 -3
- package/dist/types/modes/interactive-mode.d.ts +9 -1
- package/dist/types/modes/theme/theme.d.ts +2 -1
- package/dist/types/modes/types.d.ts +3 -1
- package/dist/types/modes/utils/ui-helpers.d.ts +2 -2
- package/dist/types/session/agent-session.d.ts +0 -2
- package/dist/types/task/render.d.ts +1 -0
- package/dist/types/tools/ask.d.ts +1 -0
- package/dist/types/tools/browser/tab-worker.d.ts +15 -0
- package/dist/types/tools/index.d.ts +17 -2
- package/dist/types/tools/render-utils.d.ts +1 -1
- package/dist/types/tools/tool-timeouts.d.ts +1 -1
- package/dist/types/utils/block-context.d.ts +35 -0
- package/dist/types/utils/git.d.ts +6 -0
- package/dist/types/utils/image-loading.d.ts +12 -0
- package/package.json +29 -9
- package/src/capability/rule-buckets.ts +4 -2
- package/src/capability/rule.ts +10 -1
- package/src/cli/auth-broker-cli.ts +6 -7
- package/src/cli/auth-gateway-cli.ts +4 -3
- package/src/cli/list-models.ts +5 -0
- package/src/cli/update-cli.ts +138 -16
- package/src/commit/agentic/tools/split-commit.ts +8 -1
- package/src/config/model-provider-priority.ts +1 -0
- package/src/config/model-registry.ts +81 -2
- package/src/debug/index.ts +4 -8
- package/src/discovery/at-imports.ts +273 -0
- package/src/discovery/builtin-rules/index.ts +4 -0
- package/src/discovery/builtin-rules/ts-no-test-timers.md +55 -0
- package/src/discovery/builtin-rules/ts-redundant-clear-guard.md +75 -0
- package/src/discovery/helpers.ts +2 -1
- package/src/edit/diff.ts +114 -4
- package/src/edit/hashline/diff.ts +1 -1
- package/src/edit/hashline/execute.ts +1 -1
- package/src/edit/modes/patch.ts +6 -2
- package/src/edit/modes/replace.ts +1 -1
- package/src/edit/renderer.ts +12 -2
- package/src/eval/__tests__/helpers-local-roots.test.ts +58 -0
- package/src/eval/backend.ts +15 -0
- package/src/eval/js/context-manager.ts +4 -2
- package/src/eval/js/executor.ts +3 -0
- package/src/eval/js/index.ts +7 -1
- package/src/eval/js/shared/helpers.ts +53 -6
- package/src/eval/js/shared/runtime.ts +8 -0
- package/src/eval/js/worker-core.ts +1 -0
- package/src/eval/js/worker-protocol.ts +6 -0
- package/src/eval/py/executor.ts +12 -0
- package/src/eval/py/index.ts +7 -1
- package/src/eval/py/prelude.py +43 -4
- package/src/eval/py/runner.py +1 -0
- package/src/exa/index.ts +1 -26
- package/src/exa/mcp-client.ts +10 -10
- package/src/exa/types.ts +0 -97
- package/src/export/ttsr.ts +122 -1
- package/src/extensibility/extensions/types.ts +8 -1
- package/src/extensibility/legacy-pi-ai-shim.ts +1 -1
- package/src/extensibility/plugins/doctor.ts +1 -1
- package/src/extensibility/plugins/legacy-pi-compat.ts +6 -5
- package/src/goals/tools/goal-tool.ts +1 -1
- package/src/internal-urls/docs-index.generated.ts +7 -6
- package/src/internal-urls/local-protocol.ts +13 -0
- package/src/lsp/render.ts +8 -6
- package/src/mcp/oauth-flow.ts +3 -3
- package/src/mcp/render.ts +7 -1
- package/src/modes/components/agent-dashboard.ts +6 -4
- package/src/modes/components/custom-editor.ts +12 -6
- package/src/modes/components/login-dialog.ts +1 -1
- package/src/modes/components/oauth-selector.ts +4 -4
- package/src/modes/components/read-tool-group.ts +10 -3
- package/src/modes/components/{status-line.ts → status-line/component.ts} +18 -40
- package/src/modes/components/status-line/index.ts +1 -0
- package/src/modes/components/status-line/types.ts +23 -8
- package/src/modes/components/tool-execution.ts +1 -1
- package/src/modes/components/transcript-container.ts +17 -10
- package/src/modes/components/user-message.ts +6 -3
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/event-controller.ts +8 -0
- package/src/modes/controllers/extension-ui-controller.ts +143 -127
- package/src/modes/controllers/input-controller.ts +60 -11
- package/src/modes/controllers/mcp-command-controller.ts +52 -17
- package/src/modes/controllers/selector-controller.ts +4 -11
- package/src/modes/controllers/ssh-command-controller.ts +2 -2
- package/src/modes/image-references.ts +13 -7
- package/src/modes/interactive-mode.ts +35 -3
- package/src/modes/rpc/rpc-mode.ts +1 -1
- package/src/modes/setup-wizard/scenes/sign-in.ts +3 -11
- package/src/modes/theme/theme.ts +95 -1
- package/src/modes/types.ts +3 -1
- package/src/modes/utils/ui-helpers.ts +14 -5
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/eval.md +4 -4
- package/src/sdk.ts +31 -14
- package/src/session/agent-session.ts +290 -196
- package/src/session/session-manager.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +9 -1
- package/src/system-prompt.ts +15 -9
- package/src/task/index.ts +9 -1
- package/src/task/render.ts +36 -14
- package/src/tools/ask.ts +14 -5
- package/src/tools/bash-interactive.ts +1 -1
- package/src/tools/bash.ts +14 -2
- package/src/tools/browser/render.ts +5 -2
- package/src/tools/browser/tab-worker.ts +211 -91
- package/src/tools/debug.ts +5 -2
- package/src/tools/eval-render.ts +6 -3
- package/src/tools/eval.ts +1 -1
- package/src/tools/gh-renderer.ts +29 -15
- package/src/tools/index.ts +32 -4
- package/src/tools/inspect-image-renderer.ts +12 -5
- package/src/tools/job.ts +9 -6
- package/src/tools/memory-render.ts +19 -5
- package/src/tools/read.ts +165 -18
- package/src/tools/render-utils.ts +3 -1
- package/src/tools/resolve.ts +1 -1
- package/src/tools/review.ts +1 -1
- package/src/tools/ssh.ts +4 -1
- package/src/tools/todo.ts +8 -1
- package/src/tools/tool-timeouts.ts +1 -1
- package/src/tools/write.ts +1 -1
- package/src/tui/code-cell.ts +1 -1
- package/src/utils/block-context.ts +312 -0
- package/src/utils/git.ts +41 -0
- package/src/utils/image-loading.ts +31 -1
- package/src/web/search/providers/codex.ts +1 -1
- package/src/web/search/render.ts +14 -6
- package/dist/types/exa/factory.d.ts +0 -13
- package/dist/types/exa/render.d.ts +0 -19
- package/dist/types/exa/researcher.d.ts +0 -9
- package/dist/types/exa/search.d.ts +0 -9
- package/dist/types/exa/websets.d.ts +0 -9
- package/src/exa/factory.ts +0 -60
- package/src/exa/render.ts +0 -244
- package/src/exa/researcher.ts +0 -36
- package/src/exa/search.ts +0 -47
- package/src/exa/websets.ts +0 -248
|
@@ -3,30 +3,36 @@ import { logger } from "@oh-my-pi/pi-utils";
|
|
|
3
3
|
import { type BlobPutResult, blobExtensionForImageMimeType } from "../session/blob-store";
|
|
4
4
|
import { fileHyperlink } from "../tui/hyperlink";
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
/** Matches `[Image #N]`/`[Image #N, WxH]` and `[Paste #N, +X lines]`/`[Paste #N, Y chars]` tokens.
|
|
7
|
+
* Group 1 is the kind (`Image`/`Paste`), group 2 the 1-based index. The optional metadata
|
|
8
|
+
* tail (`, …`) is captured loosely (no `]`/newline) so future label tweaks keep matching. */
|
|
9
|
+
export const PLACEHOLDER_REGEX = /\[(Image|Paste) #([1-9]\d*)(?:,[^\]\n]*)?\]/g;
|
|
7
10
|
|
|
8
11
|
type ImageBlobWriter = (data: Buffer, options?: { extension?: string }) => Promise<BlobPutResult>;
|
|
9
12
|
type ImageBlobWriterSync = (data: Buffer, options?: { extension?: string }) => BlobPutResult;
|
|
10
13
|
|
|
11
|
-
export
|
|
14
|
+
export type PlaceholderKind = "image" | "paste";
|
|
15
|
+
|
|
16
|
+
export interface PlaceholderRenderers {
|
|
12
17
|
renderText: (text: string) => string;
|
|
13
|
-
renderReference: (label: string, index: number) => string;
|
|
18
|
+
renderReference: (label: string, kind: PlaceholderKind, index: number) => string;
|
|
14
19
|
}
|
|
15
20
|
|
|
16
|
-
export function
|
|
17
|
-
|
|
21
|
+
export function renderPlaceholders(text: string, renderers: PlaceholderRenderers): string {
|
|
22
|
+
PLACEHOLDER_REGEX.lastIndex = 0;
|
|
18
23
|
let result = "";
|
|
19
24
|
let last = 0;
|
|
20
25
|
let matched = false;
|
|
21
26
|
|
|
22
27
|
for (;;) {
|
|
23
|
-
const match =
|
|
28
|
+
const match = PLACEHOLDER_REGEX.exec(text);
|
|
24
29
|
if (match === null) break;
|
|
25
30
|
matched = true;
|
|
26
31
|
if (match.index > last) {
|
|
27
32
|
result += renderers.renderText(text.slice(last, match.index));
|
|
28
33
|
}
|
|
29
|
-
|
|
34
|
+
const kind: PlaceholderKind = match[1] === "Paste" ? "paste" : "image";
|
|
35
|
+
result += renderers.renderReference(match[0], kind, Number(match[2]));
|
|
30
36
|
last = match.index + match[0].length;
|
|
31
37
|
}
|
|
32
38
|
|
|
@@ -65,7 +65,12 @@ import { BUILTIN_SLASH_COMMANDS, loadSlashCommands } from "../extensibility/slas
|
|
|
65
65
|
import type { Goal, GoalModeState } from "../goals/state";
|
|
66
66
|
import { resolveLocalUrlToPath } from "../internal-urls";
|
|
67
67
|
import { LSP_STARTUP_EVENT_CHANNEL, type LspStartupEvent } from "../lsp/startup-events";
|
|
68
|
-
import {
|
|
68
|
+
import {
|
|
69
|
+
humanizePlanTitle,
|
|
70
|
+
type PlanApprovalDetails,
|
|
71
|
+
resolveApprovedPlan,
|
|
72
|
+
resolvePlanTitle,
|
|
73
|
+
} from "../plan-mode/approved-plan";
|
|
69
74
|
import planModeApprovedPrompt from "../prompts/system/plan-mode-approved.md" with { type: "text" };
|
|
70
75
|
import planModeCompactInstructionsPrompt from "../prompts/system/plan-mode-compact-instructions.md" with {
|
|
71
76
|
type: "text",
|
|
@@ -2265,6 +2270,33 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2265
2270
|
await this.#startGoalFromObjective(objective);
|
|
2266
2271
|
}
|
|
2267
2272
|
|
|
2273
|
+
/** Manually (re-)open the plan-review overlay — bound to `/plan-review`. Lets
|
|
2274
|
+
* the operator pull the review back up after dismissing it, or review a plan
|
|
2275
|
+
* the agent wrote without calling `resolve`. There is no fixed plan filename:
|
|
2276
|
+
* `getPlanReferencePath()` is empty until a plan is actually approved (and does
|
|
2277
|
+
* not survive a restart), so this drives off the newest `local://<slug>-plan.md`
|
|
2278
|
+
* the agent wrote — the files persist in the session artifacts dir, so the scan
|
|
2279
|
+
* works before any review and across restarts. */
|
|
2280
|
+
async openPlanReview(): Promise<void> {
|
|
2281
|
+
if (!this.planModeEnabled) {
|
|
2282
|
+
this.showWarning("Plan mode is not active.");
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
2285
|
+
const noPlan = "No plan to review yet — write one to a local://<slug>-plan.md file first.";
|
|
2286
|
+
const [planFilePath] = await this.#listLocalPlanFiles();
|
|
2287
|
+
if (!planFilePath) {
|
|
2288
|
+
this.showWarning(noPlan);
|
|
2289
|
+
return;
|
|
2290
|
+
}
|
|
2291
|
+
const planContent = await this.#readPlanFile(planFilePath);
|
|
2292
|
+
if (planContent === null) {
|
|
2293
|
+
this.showWarning(noPlan);
|
|
2294
|
+
return;
|
|
2295
|
+
}
|
|
2296
|
+
const { title } = resolvePlanTitle({ planContent, planFilePath });
|
|
2297
|
+
await this.handlePlanApproval({ planFilePath, title, planExists: true });
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2268
2300
|
async handlePlanApproval(details: PlanApprovalDetails): Promise<void> {
|
|
2269
2301
|
if (!this.planModeEnabled) {
|
|
2270
2302
|
this.showWarning("Plan mode is not active.");
|
|
@@ -2806,8 +2838,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2806
2838
|
this.#uiHelpers.updatePendingMessagesDisplay();
|
|
2807
2839
|
}
|
|
2808
2840
|
|
|
2809
|
-
queueCompactionMessage(text: string, mode: "steer" | "followUp"): void {
|
|
2810
|
-
this.#uiHelpers.queueCompactionMessage(text, mode);
|
|
2841
|
+
queueCompactionMessage(text: string, mode: "steer" | "followUp", images?: ImageContent[]): void {
|
|
2842
|
+
this.#uiHelpers.queueCompactionMessage(text, mode, images);
|
|
2811
2843
|
}
|
|
2812
2844
|
|
|
2813
2845
|
flushCompactionQueue(options?: { willRetry?: boolean }): Promise<void> {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - Events: AgentSessionEvent objects streamed as they occur
|
|
11
11
|
* - Extension UI: Extension UI requests are emitted, client responds with extension_ui_response
|
|
12
12
|
*/
|
|
13
|
-
import { getOAuthProviders } from "@oh-my-pi/pi-ai/
|
|
13
|
+
import { getOAuthProviders } from "@oh-my-pi/pi-ai/oauth";
|
|
14
14
|
import { $env, readJsonl, Snowflake } from "@oh-my-pi/pi-utils";
|
|
15
15
|
import {
|
|
16
16
|
type ExtensionUIContext,
|
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
import type { AuthStorage } from "@oh-my-pi/pi-ai";
|
|
2
|
-
import
|
|
2
|
+
import { PASTE_CODE_LOGIN_PROVIDERS } from "@oh-my-pi/pi-ai";
|
|
3
|
+
import type { OAuthProvider } from "@oh-my-pi/pi-ai/oauth/types";
|
|
3
4
|
import { Input, matchesKey, wrapTextWithAnsi } from "@oh-my-pi/pi-tui";
|
|
4
5
|
import { getAgentDbPath } from "@oh-my-pi/pi-utils";
|
|
5
6
|
import { OAuthSelectorComponent } from "../../components/oauth-selector";
|
|
6
7
|
import { theme } from "../../theme/theme";
|
|
7
8
|
import type { SetupSceneHost, SetupTab } from "./types";
|
|
8
9
|
|
|
9
|
-
/** Providers whose OAuth flow needs a pasted code/redirect URL rather than a callback server. */
|
|
10
|
-
const CALLBACK_SERVER_PROVIDERS: Partial<Record<OAuthProvider, true>> = {
|
|
11
|
-
anthropic: true,
|
|
12
|
-
"openai-codex": true,
|
|
13
|
-
"gitlab-duo": true,
|
|
14
|
-
"google-gemini-cli": true,
|
|
15
|
-
"google-antigravity": true,
|
|
16
|
-
};
|
|
17
|
-
|
|
18
10
|
function loginUrlLink(url: string): string {
|
|
19
11
|
return `\x1b]8;;${url}\x07Open login URL\x1b]8;;\x07`;
|
|
20
12
|
}
|
|
@@ -119,7 +111,7 @@ export class SignInTab implements SetupTab {
|
|
|
119
111
|
|
|
120
112
|
async #login(providerId: string): Promise<void> {
|
|
121
113
|
if (this.#loggingInProvider || this.#disposed) return;
|
|
122
|
-
const useManualInput =
|
|
114
|
+
const useManualInput = PASTE_CODE_LOGIN_PROVIDERS.has(providerId);
|
|
123
115
|
this.#selector.stopValidation();
|
|
124
116
|
this.#loggingInProvider = providerId;
|
|
125
117
|
this.#statusLines = [theme.fg("dim", "Starting OAuth flow…")];
|
package/src/modes/theme/theme.ts
CHANGED
|
@@ -43,6 +43,7 @@ export type SymbolKey =
|
|
|
43
43
|
| "status.running"
|
|
44
44
|
| "status.shadowed"
|
|
45
45
|
| "status.aborted"
|
|
46
|
+
| "status.done"
|
|
46
47
|
// Navigation
|
|
47
48
|
| "nav.cursor"
|
|
48
49
|
| "nav.selected"
|
|
@@ -197,7 +198,29 @@ export type SymbolKey =
|
|
|
197
198
|
| "tab.tools"
|
|
198
199
|
| "tab.memory"
|
|
199
200
|
| "tab.tasks"
|
|
200
|
-
| "tab.providers"
|
|
201
|
+
| "tab.providers"
|
|
202
|
+
// Tool identity icons
|
|
203
|
+
| "tool.write"
|
|
204
|
+
| "tool.edit"
|
|
205
|
+
| "tool.bash"
|
|
206
|
+
| "tool.ssh"
|
|
207
|
+
| "tool.lsp"
|
|
208
|
+
| "tool.gh"
|
|
209
|
+
| "tool.webSearch"
|
|
210
|
+
| "tool.exa"
|
|
211
|
+
| "tool.browser"
|
|
212
|
+
| "tool.eval"
|
|
213
|
+
| "tool.debug"
|
|
214
|
+
| "tool.mcp"
|
|
215
|
+
| "tool.job"
|
|
216
|
+
| "tool.task"
|
|
217
|
+
| "tool.todo"
|
|
218
|
+
| "tool.memory"
|
|
219
|
+
| "tool.ask"
|
|
220
|
+
| "tool.resolve"
|
|
221
|
+
| "tool.review"
|
|
222
|
+
| "tool.inspectImage"
|
|
223
|
+
| "tool.goal";
|
|
201
224
|
|
|
202
225
|
type SymbolMap = Record<SymbolKey, string>;
|
|
203
226
|
|
|
@@ -213,6 +236,7 @@ const UNICODE_SYMBOLS: SymbolMap = {
|
|
|
213
236
|
"status.running": "⟳",
|
|
214
237
|
"status.shadowed": "◌",
|
|
215
238
|
"status.aborted": "⏹",
|
|
239
|
+
"status.done": "•",
|
|
216
240
|
// Navigation
|
|
217
241
|
"nav.cursor": "❯",
|
|
218
242
|
"nav.selected": "➤",
|
|
@@ -368,6 +392,28 @@ const UNICODE_SYMBOLS: SymbolMap = {
|
|
|
368
392
|
"tab.memory": "🧠",
|
|
369
393
|
"tab.tasks": "📦",
|
|
370
394
|
"tab.providers": "🌐",
|
|
395
|
+
// Tool identity icons (per-tool signature glyph on the success header)
|
|
396
|
+
"tool.write": "✎",
|
|
397
|
+
"tool.edit": "✎",
|
|
398
|
+
"tool.bash": "❯",
|
|
399
|
+
"tool.ssh": "⇄",
|
|
400
|
+
"tool.lsp": "💡",
|
|
401
|
+
"tool.gh": "⎇",
|
|
402
|
+
"tool.webSearch": "⌕",
|
|
403
|
+
"tool.exa": "🔭",
|
|
404
|
+
"tool.browser": "🌐",
|
|
405
|
+
"tool.eval": "▶",
|
|
406
|
+
"tool.debug": "🐞",
|
|
407
|
+
"tool.mcp": "🔌",
|
|
408
|
+
"tool.job": "⚙",
|
|
409
|
+
"tool.task": "⇶",
|
|
410
|
+
"tool.todo": "☑",
|
|
411
|
+
"tool.memory": "🧠",
|
|
412
|
+
"tool.ask": "?",
|
|
413
|
+
"tool.resolve": "✓",
|
|
414
|
+
"tool.review": "◉",
|
|
415
|
+
"tool.inspectImage": "🖼",
|
|
416
|
+
"tool.goal": "◎",
|
|
371
417
|
};
|
|
372
418
|
|
|
373
419
|
const NERD_SYMBOLS: SymbolMap = {
|
|
@@ -392,6 +438,8 @@ const NERD_SYMBOLS: SymbolMap = {
|
|
|
392
438
|
"status.shadowed": "◐",
|
|
393
439
|
// pick: | alt:
|
|
394
440
|
"status.aborted": "\uf04d",
|
|
441
|
+
// pick: • | alt: ● ·
|
|
442
|
+
"status.done": "•",
|
|
395
443
|
// Navigation
|
|
396
444
|
// pick: | alt:
|
|
397
445
|
"nav.cursor": "\uf054",
|
|
@@ -638,6 +686,28 @@ const NERD_SYMBOLS: SymbolMap = {
|
|
|
638
686
|
"tab.memory": "",
|
|
639
687
|
"tab.tasks": "",
|
|
640
688
|
"tab.providers": "",
|
|
689
|
+
// Tool identity icons (per-tool signature glyph on the success header)
|
|
690
|
+
"tool.write": "\uEA7F",
|
|
691
|
+
"tool.edit": "\uEA73",
|
|
692
|
+
"tool.bash": "\uEBCA",
|
|
693
|
+
"tool.ssh": "\uEB3A",
|
|
694
|
+
"tool.lsp": "\uEA61",
|
|
695
|
+
"tool.gh": "\uEA84",
|
|
696
|
+
"tool.webSearch": "\uEB01",
|
|
697
|
+
"tool.exa": "\uEB68",
|
|
698
|
+
"tool.browser": "\uEAAE",
|
|
699
|
+
"tool.eval": "\uEBAF",
|
|
700
|
+
"tool.debug": "\uEAD8",
|
|
701
|
+
"tool.mcp": "\uEB2D",
|
|
702
|
+
"tool.job": "\uEBA2",
|
|
703
|
+
"tool.task": "\uEA7E",
|
|
704
|
+
"tool.todo": "\uEAB3",
|
|
705
|
+
"tool.memory": "\uEACE",
|
|
706
|
+
"tool.ask": "\uEAC7",
|
|
707
|
+
"tool.resolve": "\uEBB1",
|
|
708
|
+
"tool.review": "\uEA70",
|
|
709
|
+
"tool.inspectImage": "\uEAEA",
|
|
710
|
+
"tool.goal": "\uEBF8",
|
|
641
711
|
};
|
|
642
712
|
|
|
643
713
|
const ASCII_SYMBOLS: SymbolMap = {
|
|
@@ -652,6 +722,7 @@ const ASCII_SYMBOLS: SymbolMap = {
|
|
|
652
722
|
"status.running": "[~]",
|
|
653
723
|
"status.shadowed": "[/]",
|
|
654
724
|
"status.aborted": "[-]",
|
|
725
|
+
"status.done": "*",
|
|
655
726
|
// Navigation
|
|
656
727
|
"nav.cursor": ">",
|
|
657
728
|
"nav.selected": "->",
|
|
@@ -805,6 +876,28 @@ const ASCII_SYMBOLS: SymbolMap = {
|
|
|
805
876
|
"tab.memory": "[Y]",
|
|
806
877
|
"tab.tasks": "[K]",
|
|
807
878
|
"tab.providers": "[P]",
|
|
879
|
+
// Tool identity icons (per-tool signature glyph on the success header)
|
|
880
|
+
"tool.write": "+f",
|
|
881
|
+
"tool.edit": "~",
|
|
882
|
+
"tool.bash": "$",
|
|
883
|
+
"tool.ssh": "ssh",
|
|
884
|
+
"tool.lsp": "lsp",
|
|
885
|
+
"tool.gh": "gh",
|
|
886
|
+
"tool.webSearch": "web",
|
|
887
|
+
"tool.exa": "exa",
|
|
888
|
+
"tool.browser": "[w]",
|
|
889
|
+
"tool.eval": ">_",
|
|
890
|
+
"tool.debug": "dbg",
|
|
891
|
+
"tool.mcp": "<>",
|
|
892
|
+
"tool.job": "job",
|
|
893
|
+
"tool.task": ">>>",
|
|
894
|
+
"tool.todo": "[x]",
|
|
895
|
+
"tool.memory": "mem",
|
|
896
|
+
"tool.ask": "[?]",
|
|
897
|
+
"tool.resolve": "[v]",
|
|
898
|
+
"tool.review": "rev",
|
|
899
|
+
"tool.inspectImage": "[i]",
|
|
900
|
+
"tool.goal": "(o)",
|
|
808
901
|
};
|
|
809
902
|
|
|
810
903
|
const SYMBOL_PRESETS: Record<SymbolPreset, SymbolMap> = {
|
|
@@ -1485,6 +1578,7 @@ export class Theme {
|
|
|
1485
1578
|
running: this.#symbols["status.running"],
|
|
1486
1579
|
shadowed: this.#symbols["status.shadowed"],
|
|
1487
1580
|
aborted: this.#symbols["status.aborted"],
|
|
1581
|
+
done: this.#symbols["status.done"],
|
|
1488
1582
|
};
|
|
1489
1583
|
}
|
|
1490
1584
|
|
package/src/modes/types.ts
CHANGED
|
@@ -35,6 +35,7 @@ import type { Theme } from "./theme/theme";
|
|
|
35
35
|
export type CompactionQueuedMessage = {
|
|
36
36
|
text: string;
|
|
37
37
|
mode: "steer" | "followUp";
|
|
38
|
+
images?: ImageContent[];
|
|
38
39
|
};
|
|
39
40
|
|
|
40
41
|
export type SubmittedUserInput = {
|
|
@@ -180,7 +181,7 @@ export interface InteractiveModeContext {
|
|
|
180
181
|
showNewVersionNotification(newVersion: string): void;
|
|
181
182
|
clearEditor(): void;
|
|
182
183
|
updatePendingMessagesDisplay(): void;
|
|
183
|
-
queueCompactionMessage(text: string, mode: "steer" | "followUp"): void;
|
|
184
|
+
queueCompactionMessage(text: string, mode: "steer" | "followUp", images?: ImageContent[]): void;
|
|
184
185
|
flushCompactionQueue(options?: { willRetry?: boolean }): Promise<void>;
|
|
185
186
|
flushPendingBashComponents(): void;
|
|
186
187
|
flushPendingModelSwitch(): Promise<void>;
|
|
@@ -317,6 +318,7 @@ export interface InteractiveModeContext {
|
|
|
317
318
|
disableLoopMode(): void;
|
|
318
319
|
pauseLoop(): void;
|
|
319
320
|
handlePlanApproval(details: PlanApprovalDetails): Promise<void>;
|
|
321
|
+
openPlanReview(): Promise<void>;
|
|
320
322
|
|
|
321
323
|
// Hook UI methods
|
|
322
324
|
initHooksAndCustomTools(): Promise<void>;
|
|
@@ -161,7 +161,7 @@ export class UiHelpers {
|
|
|
161
161
|
const typeLabel = job.type ? `[${job.type}]` : "[job]";
|
|
162
162
|
const duration = typeof job.durationMs === "number" ? formatDuration(job.durationMs) : undefined;
|
|
163
163
|
const line = [
|
|
164
|
-
theme.fg("success", `${theme.status.
|
|
164
|
+
theme.fg("success", `${theme.status.done} Background job completed`),
|
|
165
165
|
theme.fg("dim", typeLabel),
|
|
166
166
|
theme.fg("accent", jobId),
|
|
167
167
|
duration ? theme.fg("dim", `(${duration})`) : undefined,
|
|
@@ -630,12 +630,18 @@ export class UiHelpers {
|
|
|
630
630
|
}
|
|
631
631
|
}
|
|
632
632
|
|
|
633
|
-
queueCompactionMessage(text: string, mode: "steer" | "followUp"): void {
|
|
634
|
-
|
|
633
|
+
queueCompactionMessage(text: string, mode: "steer" | "followUp", images?: ImageContent[]): void {
|
|
634
|
+
const queuedImages = images && images.length > 0 ? images : undefined;
|
|
635
|
+
this.ctx.compactionQueuedMessages.push({ text, mode, images: queuedImages } as CompactionQueuedMessage);
|
|
635
636
|
this.ctx.editor.addToHistory(text);
|
|
636
637
|
this.ctx.editor.setText("");
|
|
638
|
+
this.ctx.editor.imageLinks = undefined;
|
|
639
|
+
this.ctx.pendingImages = [];
|
|
640
|
+
this.ctx.pendingImageLinks = [];
|
|
637
641
|
this.ctx.updatePendingMessagesDisplay();
|
|
638
|
-
this.ctx.showStatus(
|
|
642
|
+
this.ctx.showStatus(
|
|
643
|
+
queuedImages ? "Queued message with image for after compaction" : "Queued message for after compaction",
|
|
644
|
+
);
|
|
639
645
|
}
|
|
640
646
|
|
|
641
647
|
async #deliverQueuedMessage(message: CompactionQueuedMessage): Promise<void> {
|
|
@@ -644,7 +650,9 @@ export class UiHelpers {
|
|
|
644
650
|
return;
|
|
645
651
|
}
|
|
646
652
|
await this.ctx.withLocalSubmission(message.text, () =>
|
|
647
|
-
message.mode === "followUp"
|
|
653
|
+
message.mode === "followUp"
|
|
654
|
+
? this.ctx.session.followUp(message.text, message.images)
|
|
655
|
+
: this.ctx.session.steer(message.text, message.images),
|
|
648
656
|
);
|
|
649
657
|
}
|
|
650
658
|
|
|
@@ -738,6 +746,7 @@ export class UiHelpers {
|
|
|
738
746
|
const promptPromise = this.ctx.session
|
|
739
747
|
.prompt(firstPrompt.text, {
|
|
740
748
|
streamingBehavior: firstPrompt.mode === "followUp" ? "followUp" : "steer",
|
|
749
|
+
images: firstPrompt.images,
|
|
741
750
|
})
|
|
742
751
|
.catch((error: unknown) => {
|
|
743
752
|
disposeFirstPrompt();
|
|
@@ -29,7 +29,7 @@ Executes bash command in shell session for terminal operations like git, bun, ca
|
|
|
29
29
|
|
|
30
30
|
- `timeout` (seconds) caps the **wall-clock duration** of the command. When it elapses the process is killed and the call returns with a timeout annotation. Range: `1`–`3600`s; default `300`s (see `clampTimeout("bash", …)` in `tool-timeouts.ts`).
|
|
31
31
|
- `async: true` only defers **reporting** of the result — it does NOT disable, extend, or detach the timeout. A daemon started with `async: true` is still killed when `timeout` elapses, regardless of how long the agent waits before reading the result.
|
|
32
|
-
- For long-running daemons (dev servers, watchers):
|
|
32
|
+
- For long-running daemons (dev servers, watchers): pass an explicit large `timeout` (up to `3600`). The shell session persists across calls, so a backgrounded job (`cmd &`) keeps running between bash calls on its own.
|
|
33
33
|
{{/if}}
|
|
34
34
|
{{#if autoBackgroundEnabled}}
|
|
35
35
|
|
|
@@ -8,7 +8,7 @@ Cell fields:
|
|
|
8
8
|
- `language` — {{#if py}}`"py"` for the IPython kernel{{/if}}{{#ifAll py js}}, {{/ifAll}}{{#if js}}`"js"` for the persistent JavaScript VM{{/if}}.
|
|
9
9
|
- `code` — cell body, verbatim. Newlines, quotes, and indentation are JSON-encoded; no fences, no headers.
|
|
10
10
|
- `title` (optional) — short label shown in the transcript (e.g. `"imports"`, `"load config"`).
|
|
11
|
-
- `timeout` (optional) — per-cell wall-clock budget in seconds (1-
|
|
11
|
+
- `timeout` (optional) — per-cell wall-clock budget in seconds (1-3600). Default 30. It bounds the cell's **own** work, but is paused while an `agent()`/`parallel()`/`completion()` call is in flight — so a long fanout or a slow completion runs to completion, while the cell itself is still bounded. Compute, `print`/stdout, `log()`/`phase()`, and ordinary tool calls all count against the budget; raise `timeout` for a cell that does heavy local work or long non-agent tool calls.
|
|
12
12
|
- `reset` (optional) — wipe this cell's language kernel before running.{{#ifAll py js}} Reset is per-language: a `py` cell's reset does not touch the JavaScript VM and vice versa.{{/ifAll}}
|
|
13
13
|
|
|
14
14
|
**Work incrementally:**
|
|
@@ -29,11 +29,11 @@ display(value) → None
|
|
|
29
29
|
print(value, ...) → None
|
|
30
30
|
Print to the cell's text output.
|
|
31
31
|
read(path, offset?=1, limit?=None) → str
|
|
32
|
-
Read file contents as text. offset/limit are 1-indexed line bounds.
|
|
32
|
+
Read file contents as text. offset/limit are 1-indexed line bounds. Accepts `local://…` (resolved to the session-local root, same place `read local://…` reads).
|
|
33
33
|
write(path, content) → str
|
|
34
|
-
Write content to a file (creates parent directories). Returns the resolved path.
|
|
34
|
+
Write content to a file (creates parent directories). Returns the resolved path. Accepts `local://…` to persist artifacts across turns / share with subagents.
|
|
35
35
|
append(path, content) → str
|
|
36
|
-
Append content to a file. Returns the resolved path.
|
|
36
|
+
Append content to a file. Returns the resolved path. Accepts `local://…`.
|
|
37
37
|
tree(path?=".", max_depth?=3, show_hidden?=False) → str
|
|
38
38
|
Render a directory tree.
|
|
39
39
|
diff(a, b) → str
|
package/src/sdk.ts
CHANGED
|
@@ -154,6 +154,7 @@ import {
|
|
|
154
154
|
EditTool,
|
|
155
155
|
EvalTool,
|
|
156
156
|
FindTool,
|
|
157
|
+
filterInitialToolsForDiscoveryAll,
|
|
157
158
|
getSearchTools,
|
|
158
159
|
HIDDEN_TOOLS,
|
|
159
160
|
isImageProviderPreference,
|
|
@@ -1427,7 +1428,11 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1427
1428
|
const getArtifactsDir = () => sessionManager.getArtifactsDir();
|
|
1428
1429
|
if (!options.parentTaskPrefix) {
|
|
1429
1430
|
setActiveSkills(skills);
|
|
1430
|
-
|
|
1431
|
+
// Include TTSR rules so `rule://<name>` can resolve them too. They are
|
|
1432
|
+
// registered with the manager and bucketed out before rulebook/always,
|
|
1433
|
+
// so without this a TTSR-only rule (e.g. a triggered builtin) is not
|
|
1434
|
+
// addressable and `rule://` reports "Available: none".
|
|
1435
|
+
setActiveRules([...rulebookRules, ...alwaysApplyRules, ...ttsrManager.getRules()]);
|
|
1431
1436
|
if (asyncJobManager) AsyncJobManager.setInstance(asyncJobManager);
|
|
1432
1437
|
}
|
|
1433
1438
|
const localProtocolOptions = options.localProtocolOptions ?? {
|
|
@@ -1570,6 +1575,16 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1570
1575
|
}
|
|
1571
1576
|
extensionsResult.runtime.pendingProviderRegistrations = [];
|
|
1572
1577
|
}
|
|
1578
|
+
// Discover runtime (extension) provider catalogs now that they are
|
|
1579
|
+
// registered. The startup refreshInBackground() ran before extensions
|
|
1580
|
+
// loaded, so dynamic extension providers are only discovered here. Runs in
|
|
1581
|
+
// the background (cache-aware) so startup is never blocked on the fetch; the
|
|
1582
|
+
// model list re-renders when the catalog arrives, like other dynamic providers.
|
|
1583
|
+
void modelRegistry.refreshRuntimeProviders().catch(error => {
|
|
1584
|
+
logger.warn("runtime provider discovery failed", {
|
|
1585
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1586
|
+
});
|
|
1587
|
+
});
|
|
1573
1588
|
|
|
1574
1589
|
// Retry session-model candidates now that extension providers are
|
|
1575
1590
|
// registered. The initial restore runs before extensions load, so a role
|
|
@@ -1951,19 +1966,21 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1951
1966
|
// from the initial set unless they were explicitly requested or restored from persistence.
|
|
1952
1967
|
// The model finds them via search_tool_bm25 and activates them on demand.
|
|
1953
1968
|
if (effectiveDiscoveryMode === "all") {
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
//
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1969
|
+
// Tools a forced tool_choice will target must stay active, or the named
|
|
1970
|
+
// choice references a tool absent from the request (provider 400). Eager
|
|
1971
|
+
// todos force a named `todo` choice on the first turn.
|
|
1972
|
+
const forceActive = new Set<string>();
|
|
1973
|
+
if (settings.get("todo.eager") && settings.get("todo.enabled") && toolRegistry.has("todo")) {
|
|
1974
|
+
forceActive.add("todo");
|
|
1975
|
+
}
|
|
1976
|
+
initialToolNames = filterInitialToolsForDiscoveryAll(initialToolNames, {
|
|
1977
|
+
loadModeOf: name => toolRegistry.get(name)?.loadMode,
|
|
1978
|
+
essentialNames: new Set(computeEssentialBuiltinNames(settings)),
|
|
1979
|
+
explicitlyRequested: new Set(options.toolNames?.map(name => name.toLowerCase()) ?? []),
|
|
1980
|
+
// Back-compat: persisted activations live under selectedMCPToolNames today (built-in
|
|
1981
|
+
// activation persistence is a follow-up). MCP names won't collide with built-in names.
|
|
1982
|
+
restored: new Set(existingSession.selectedMCPToolNames),
|
|
1983
|
+
forceActive,
|
|
1967
1984
|
});
|
|
1968
1985
|
}
|
|
1969
1986
|
|