@oh-my-pi/pi-coding-agent 15.10.12 → 15.11.1
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 +90 -4
- package/dist/cli.js +869 -825
- package/dist/types/async/index.d.ts +0 -1
- package/dist/types/capability/mcp.d.ts +1 -0
- package/dist/types/cli/gallery-fixtures/types.d.ts +5 -0
- package/dist/types/config/keybindings.d.ts +6 -1
- package/dist/types/config/settings-schema.d.ts +66 -34
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/shared-events.d.ts +2 -2
- package/dist/types/internal-urls/history-protocol.d.ts +14 -0
- package/dist/types/internal-urls/index.d.ts +1 -0
- package/dist/types/internal-urls/types.d.ts +1 -1
- package/dist/types/irc/bus.d.ts +66 -0
- package/dist/types/mcp/oauth-discovery.d.ts +2 -0
- package/dist/types/mcp/oauth-flow.d.ts +6 -1
- package/dist/types/mcp/transports/stdio.d.ts +1 -0
- package/dist/types/mcp/types.d.ts +2 -0
- package/dist/types/modes/components/agent-hub.d.ts +30 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -0
- package/dist/types/modes/components/compaction-summary-message.d.ts +10 -4
- package/dist/types/modes/components/custom-editor.d.ts +2 -0
- package/dist/types/modes/components/mcp-add-wizard.d.ts +2 -1
- package/dist/types/modes/components/settings-selector.d.ts +1 -0
- package/dist/types/modes/components/status-line/types.d.ts +3 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -0
- package/dist/types/modes/components/transcript-container.d.ts +3 -2
- package/dist/types/modes/components/ttsr-notification.d.ts +5 -1
- package/dist/types/modes/components/welcome.d.ts +3 -9
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/controllers/tool-args-reveal.d.ts +43 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -2
- package/dist/types/modes/theme/theme.d.ts +3 -1
- package/dist/types/modes/types.d.ts +3 -2
- package/dist/types/modes/utils/ui-helpers.d.ts +1 -1
- package/dist/types/registry/agent-lifecycle.d.ts +51 -0
- package/dist/types/registry/agent-registry.d.ts +16 -5
- package/dist/types/session/agent-session.d.ts +35 -30
- package/dist/types/session/messages.d.ts +2 -4
- package/dist/types/session/session-history-format.d.ts +12 -0
- package/dist/types/session/session-manager.d.ts +21 -3
- package/dist/types/session/streaming-output.d.ts +23 -0
- package/dist/types/task/executor.d.ts +11 -2
- package/dist/types/task/index.d.ts +11 -4
- package/dist/types/task/output-manager.d.ts +0 -7
- package/dist/types/task/repair-args.d.ts +8 -7
- package/dist/types/task/types.d.ts +55 -51
- package/dist/types/tools/browser/tab-worker.d.ts +3 -1
- package/dist/types/tools/find.d.ts +0 -11
- package/dist/types/tools/grouped-file-output.d.ts +0 -49
- package/dist/types/tools/index.d.ts +1 -3
- package/dist/types/tools/irc.d.ts +76 -38
- package/dist/types/tools/job.d.ts +7 -1
- package/dist/types/tools/render-utils.d.ts +22 -0
- package/examples/extensions/with-deps/package.json +1 -0
- package/package.json +11 -10
- package/scripts/bundle-dist.ts +28 -19
- package/src/async/index.ts +0 -1
- package/src/capability/mcp.ts +1 -0
- package/src/cli/gallery-cli.ts +6 -5
- package/src/cli/gallery-fixtures/agentic.ts +230 -115
- package/src/cli/gallery-fixtures/types.ts +5 -0
- package/src/cli.ts +20 -6
- package/src/commit/agentic/tools/analyze-file.ts +38 -19
- package/src/config/keybindings.ts +6 -1
- package/src/config/mcp-schema.json +4 -0
- package/src/config/settings-schema.ts +68 -41
- package/src/config/settings.ts +7 -0
- package/src/edit/renderer.ts +96 -46
- package/src/eval/__tests__/agent-bridge.test.ts +5 -3
- package/src/eval/agent-bridge.ts +3 -16
- package/src/eval/js/shared/prelude.txt +1 -1
- package/src/eval/py/prelude.py +5 -6
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +44 -14
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/shared-events.ts +2 -2
- package/src/internal-urls/docs-index.generated.ts +9 -9
- package/src/internal-urls/history-protocol.ts +113 -0
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/router.ts +3 -1
- package/src/internal-urls/types.ts +1 -1
- package/src/irc/bus.ts +292 -0
- package/src/main.ts +8 -60
- package/src/mcp/manager.ts +3 -0
- package/src/mcp/oauth-discovery.ts +27 -2
- package/src/mcp/oauth-flow.ts +47 -1
- package/src/mcp/transports/stdio.ts +3 -0
- package/src/mcp/types.ts +2 -0
- package/src/modes/components/{session-observer-overlay.ts → agent-hub.ts} +586 -367
- package/src/modes/components/assistant-message.ts +15 -0
- package/src/modes/components/btw-panel.ts +5 -1
- package/src/modes/components/compaction-summary-message.ts +68 -32
- package/src/modes/components/custom-editor.ts +10 -0
- package/src/modes/components/mcp-add-wizard.ts +13 -0
- package/src/modes/components/settings-selector.ts +2 -0
- package/src/modes/components/status-line/component.ts +22 -12
- package/src/modes/components/status-line/types.ts +3 -0
- package/src/modes/components/tool-execution.ts +31 -1
- package/src/modes/components/transcript-container.ts +99 -18
- package/src/modes/components/tree-selector.ts +6 -1
- package/src/modes/components/ttsr-notification.ts +72 -30
- package/src/modes/components/welcome.ts +9 -33
- package/src/modes/controllers/event-controller.ts +93 -4
- package/src/modes/controllers/extension-ui-controller.ts +8 -8
- package/src/modes/controllers/input-controller.ts +18 -2
- package/src/modes/controllers/mcp-command-controller.ts +34 -2
- package/src/modes/controllers/selector-controller.ts +25 -17
- package/src/modes/controllers/tool-args-reveal.ts +174 -0
- package/src/modes/interactive-mode.ts +17 -15
- package/src/modes/theme/theme.ts +24 -5
- package/src/modes/types.ts +3 -5
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +51 -49
- package/src/prompts/system/irc-incoming.md +3 -4
- package/src/prompts/system/orchestrate-notice.md +2 -2
- package/src/prompts/system/subagent-system-prompt.md +0 -5
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/prompts/system/workflow-notice.md +2 -2
- package/src/prompts/tools/eval.md +3 -3
- package/src/prompts/tools/irc.md +29 -19
- package/src/prompts/tools/read.md +2 -2
- package/src/prompts/tools/task-summary.md +5 -16
- package/src/prompts/tools/task.md +43 -29
- package/src/registry/agent-lifecycle.ts +218 -0
- package/src/registry/agent-registry.ts +16 -5
- package/src/sdk.ts +29 -9
- package/src/session/agent-session.ts +268 -241
- package/src/session/messages.ts +11 -78
- package/src/session/session-history-format.ts +246 -0
- package/src/session/session-manager.ts +59 -5
- package/src/session/streaming-output.ts +60 -0
- package/src/task/executor.ts +855 -466
- package/src/task/index.ts +723 -794
- package/src/task/output-manager.ts +0 -11
- package/src/task/render.ts +142 -66
- package/src/task/repair-args.ts +21 -9
- package/src/task/types.ts +73 -66
- package/src/tools/ask.ts +4 -2
- package/src/tools/bash.ts +15 -5
- package/src/tools/browser/tab-worker.ts +26 -7
- package/src/tools/browser.ts +28 -1
- package/src/tools/find.ts +2 -27
- package/src/tools/grouped-file-output.ts +1 -118
- package/src/tools/index.ts +4 -12
- package/src/tools/irc.ts +596 -171
- package/src/tools/job.ts +41 -7
- package/src/tools/read.ts +57 -1
- package/src/tools/render-utils.ts +56 -0
- package/src/tools/renderers.ts +2 -0
- package/src/tools/resolve.ts +4 -1
- package/src/tools/write.ts +65 -47
- package/src/web/search/providers/anthropic.ts +29 -4
- package/dist/types/async/support.d.ts +0 -2
- package/dist/types/modes/components/session-observer-overlay.d.ts +0 -11
- package/dist/types/task/simple-mode.d.ts +0 -8
- package/src/async/support.ts +0 -5
- package/src/task/simple-mode.ts +0 -27
package/src/tools/job.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { Component } from "@oh-my-pi/pi-tui";
|
|
|
3
3
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
4
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
5
5
|
import * as z from "zod/v4";
|
|
6
|
-
import {
|
|
6
|
+
import type { AsyncJob, AsyncJobManager } from "../async";
|
|
7
7
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
8
8
|
import type { Theme } from "../modes/theme/theme";
|
|
9
9
|
import jobDescription from "../prompts/tools/job.md" with { type: "text" };
|
|
@@ -65,6 +65,19 @@ export interface JobToolDetails {
|
|
|
65
65
|
cancelled?: { id: string; status: CancelStatus }[];
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* A poll snapshot where every watched job is still running and nothing was
|
|
70
|
+
* cancelled — pure "still waiting" noise once a newer poll exists. The TUI
|
|
71
|
+
* keeps such a block un-finalized (displaceable) so a follow-up `job` call
|
|
72
|
+
* replaces it instead of stacking another waiting frame in the transcript.
|
|
73
|
+
*/
|
|
74
|
+
export function isWaitingPollDetails(details: unknown): boolean {
|
|
75
|
+
const d = details as JobToolDetails | undefined;
|
|
76
|
+
if (!d || !Array.isArray(d.jobs) || d.jobs.length === 0) return false;
|
|
77
|
+
if (d.cancelled?.length) return false;
|
|
78
|
+
return d.jobs.every(job => job?.status === "running");
|
|
79
|
+
}
|
|
80
|
+
|
|
68
81
|
export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
69
82
|
readonly name = "job";
|
|
70
83
|
readonly approval = "read" as const;
|
|
@@ -78,11 +91,6 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
78
91
|
this.description = prompt.render(jobDescription);
|
|
79
92
|
}
|
|
80
93
|
|
|
81
|
-
static createIf(session: ToolSession): JobTool | null {
|
|
82
|
-
if (!isBackgroundJobSupportEnabled(session.settings)) return null;
|
|
83
|
-
return new JobTool(session);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
94
|
async execute(
|
|
87
95
|
_toolCallId: string,
|
|
88
96
|
params: JobParams,
|
|
@@ -378,6 +386,30 @@ function statusToColor(status: JobSnapshot["status"]): ToolUIColor {
|
|
|
378
386
|
}
|
|
379
387
|
}
|
|
380
388
|
|
|
389
|
+
/**
|
|
390
|
+
* Task job results are delivered in the model-facing `<task-result>` envelope
|
|
391
|
+
* (prompts/tools/task-summary.md) so the parent agent can parse status and the
|
|
392
|
+
* `agent://` pointer. The wrapper markup is noise to a human — preview the
|
|
393
|
+
* inner <output>/<preview> body instead.
|
|
394
|
+
*/
|
|
395
|
+
function stripTaskResultEnvelope(text: string): string {
|
|
396
|
+
if (!text.startsWith("<task-result")) return text;
|
|
397
|
+
const body = /<(output|preview)(?:\s[^>]*)?>\n?([\s\S]*?)\n?<\/\1>/.exec(text)?.[2];
|
|
398
|
+
return body?.trim() || text;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Pretty-printed JSON output wastes the collapsed one-line preview on a lone
|
|
403
|
+
* "{" — flatten structured-looking bodies onto a single line. Slice first:
|
|
404
|
+
* downstream truncation keeps at most a few hundred columns, so collapsing
|
|
405
|
+
* whitespace across a multi-KB body would be pure waste.
|
|
406
|
+
*/
|
|
407
|
+
function flattenStructuredPreview(text: string): string {
|
|
408
|
+
const first = text[0];
|
|
409
|
+
if (first !== "{" && first !== "[") return text;
|
|
410
|
+
return text.slice(0, PREVIEW_LINES_EXPANDED * PREVIEW_LINE_WIDTH * 2).replace(/\s+/g, " ");
|
|
411
|
+
}
|
|
412
|
+
|
|
381
413
|
function describeTarget(args: JobRenderArgs | undefined): string {
|
|
382
414
|
const poll = args?.poll ?? [];
|
|
383
415
|
const cancel = args?.cancel ?? [];
|
|
@@ -494,7 +526,9 @@ export const jobToolRenderer = {
|
|
|
494
526
|
lines.push(` ${uiTheme.fg("toolOutput", visibleLabelLines[i]!)}`);
|
|
495
527
|
}
|
|
496
528
|
|
|
497
|
-
const preview =
|
|
529
|
+
const preview = flattenStructuredPreview(
|
|
530
|
+
stripTaskResultEnvelope(job.errorText?.trim() || job.resultText?.trim() || ""),
|
|
531
|
+
);
|
|
498
532
|
if (preview) {
|
|
499
533
|
const maxLines = expanded ? PREVIEW_LINES_EXPANDED : PREVIEW_LINES_COLLAPSED;
|
|
500
534
|
const previewLines = getPreviewLines(preview, maxLines, PREVIEW_LINE_WIDTH, Ellipsis.Unicode);
|
package/src/tools/read.ts
CHANGED
|
@@ -736,6 +736,17 @@ interface ResolvedSqliteReadPath {
|
|
|
736
736
|
/** Per-execute memo of suffix-glob lookups; `null` records a confirmed miss. */
|
|
737
737
|
type SuffixMatchCache = Map<string, { absolutePath: string; displayPath: string } | null>;
|
|
738
738
|
|
|
739
|
+
/**
|
|
740
|
+
* Repeated whole-file reads of the same path pin stale copies in context.
|
|
741
|
+
* From this per-session read count onward, file reads carry a trailing nudge
|
|
742
|
+
* to prefer narrower re-reads.
|
|
743
|
+
*/
|
|
744
|
+
const REPEAT_READ_NOTICE_THRESHOLD = 3;
|
|
745
|
+
|
|
746
|
+
function formatRepeatReadNotice(count: number): string {
|
|
747
|
+
return `[note: read #${count} of this file this session — after edits, prefer the context echoed in the edit result or a narrow range re-read]`;
|
|
748
|
+
}
|
|
749
|
+
|
|
739
750
|
/**
|
|
740
751
|
* Read tool implementation.
|
|
741
752
|
*
|
|
@@ -754,6 +765,8 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
754
765
|
readonly #autoResizeImages: boolean;
|
|
755
766
|
readonly #defaultLimit: number;
|
|
756
767
|
readonly #inspectImageEnabled: boolean;
|
|
768
|
+
/** Successful file reads per resolved base path (selector stripped) this session. */
|
|
769
|
+
readonly #readCounts = new Map<string, number>();
|
|
757
770
|
|
|
758
771
|
constructor(private readonly session: ToolSession) {
|
|
759
772
|
const displayMode = resolveFileDisplayMode(session);
|
|
@@ -772,6 +785,19 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
772
785
|
});
|
|
773
786
|
}
|
|
774
787
|
|
|
788
|
+
/**
|
|
789
|
+
* Count a file read of `absolutePath` and return the repeat-read nudge once
|
|
790
|
+
* the per-session count reaches {@link REPEAT_READ_NOTICE_THRESHOLD}.
|
|
791
|
+
* Non-file sources (URLs, internal resources, directories, archives,
|
|
792
|
+
* SQLite, images) are never counted.
|
|
793
|
+
*/
|
|
794
|
+
#repeatReadNotice(absolutePath: string): string | undefined {
|
|
795
|
+
const count = (this.#readCounts.get(absolutePath) ?? 0) + 1;
|
|
796
|
+
this.#readCounts.set(absolutePath, count);
|
|
797
|
+
if (count < REPEAT_READ_NOTICE_THRESHOLD) return undefined;
|
|
798
|
+
return formatRepeatReadNotice(count);
|
|
799
|
+
}
|
|
800
|
+
|
|
775
801
|
async #tryReadDelimitedPaths(
|
|
776
802
|
readPath: string,
|
|
777
803
|
signal?: AbortSignal,
|
|
@@ -948,6 +974,8 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
948
974
|
ignoreResultLimits?: boolean;
|
|
949
975
|
raw?: boolean;
|
|
950
976
|
immutable?: boolean;
|
|
977
|
+
/** Trailing repeat-read nudge; appended at the very end of the text. */
|
|
978
|
+
repeatNotice?: string;
|
|
951
979
|
},
|
|
952
980
|
): AgentToolResult<ReadToolDetails> {
|
|
953
981
|
const displayMode = resolveFileDisplayMode(this.session, { raw: options.raw, immutable: options.immutable });
|
|
@@ -1092,6 +1120,9 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1092
1120
|
: formatLineEntries(buildLineEntries(endLine), startLineDisplay);
|
|
1093
1121
|
}
|
|
1094
1122
|
|
|
1123
|
+
if (options.repeatNotice) {
|
|
1124
|
+
outputText += `\n${options.repeatNotice}`;
|
|
1125
|
+
}
|
|
1095
1126
|
resultBuilder.text(outputText);
|
|
1096
1127
|
if (truncationInfo) {
|
|
1097
1128
|
resultBuilder.truncation(truncationInfo.result, truncationInfo.options);
|
|
@@ -1117,6 +1148,8 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1117
1148
|
entityLabel: string;
|
|
1118
1149
|
raw?: boolean;
|
|
1119
1150
|
immutable?: boolean;
|
|
1151
|
+
/** Trailing repeat-read nudge; appended at the very end of the text. */
|
|
1152
|
+
repeatNotice?: string;
|
|
1120
1153
|
},
|
|
1121
1154
|
): AgentToolResult<ReadToolDetails> {
|
|
1122
1155
|
const displayMode = resolveFileDisplayMode(this.session, { raw: options.raw, immutable: options.immutable });
|
|
@@ -1177,8 +1210,11 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1177
1210
|
const bound = range.endLine !== undefined ? `${range.startLine}-${range.endLine}` : `${range.startLine}`;
|
|
1178
1211
|
notices.push(`[Range ${bound} is beyond end of ${options.entityLabel} (${totalLines} lines total); skipped]`);
|
|
1179
1212
|
}
|
|
1180
|
-
|
|
1213
|
+
let finalText =
|
|
1181
1214
|
notices.length > 0 ? (outputText ? `${outputText}\n${notices.join("\n")}` : notices.join("\n")) : outputText;
|
|
1215
|
+
if (options.repeatNotice) {
|
|
1216
|
+
finalText = finalText ? `${finalText}\n${options.repeatNotice}` : options.repeatNotice;
|
|
1217
|
+
}
|
|
1182
1218
|
resultBuilder.text(finalText);
|
|
1183
1219
|
return resultBuilder.done();
|
|
1184
1220
|
}
|
|
@@ -1196,6 +1232,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1196
1232
|
parsed: ParsedSelector,
|
|
1197
1233
|
displayMode: { hashLines: boolean; lineNumbers: boolean },
|
|
1198
1234
|
suffixResolution: { from: string; to: string } | undefined,
|
|
1235
|
+
repeatNotice: string | undefined,
|
|
1199
1236
|
signal: AbortSignal | undefined,
|
|
1200
1237
|
): Promise<{
|
|
1201
1238
|
outputText: string;
|
|
@@ -1215,6 +1252,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1215
1252
|
sourcePath: absolutePath,
|
|
1216
1253
|
entityLabel: "file",
|
|
1217
1254
|
raw: rawSelector,
|
|
1255
|
+
repeatNotice,
|
|
1218
1256
|
});
|
|
1219
1257
|
if (suffixResolution) {
|
|
1220
1258
|
const notice = `[Path '${suffixResolution.from}' not found; resolved to '${suffixResolution.to}' via suffix match]`;
|
|
@@ -1896,6 +1934,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1896
1934
|
let details: ReadToolDetails = {};
|
|
1897
1935
|
let sourcePath: string | undefined;
|
|
1898
1936
|
let columnTruncated = 0;
|
|
1937
|
+
let repeatNotice: string | undefined;
|
|
1899
1938
|
let truncationInfo:
|
|
1900
1939
|
| { result: TruncationResult; options: { direction: "head"; startLine?: number; totalFileLines?: number } }
|
|
1901
1940
|
| undefined;
|
|
@@ -1960,11 +1999,13 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1960
1999
|
}
|
|
1961
2000
|
} else if (isNotebookPath(absolutePath) && !isRawSelector(parsed)) {
|
|
1962
2001
|
const notebookText = await readEditableNotebookText(absolutePath, localReadPath);
|
|
2002
|
+
repeatNotice = this.#repeatReadNotice(absolutePath);
|
|
1963
2003
|
if (isMultiRange(parsed) && parsed.kind === "lines") {
|
|
1964
2004
|
return this.#buildInMemoryMultiRangeResult(notebookText, parsed.ranges, {
|
|
1965
2005
|
details: { resolvedPath: absolutePath },
|
|
1966
2006
|
sourcePath: absolutePath,
|
|
1967
2007
|
entityLabel: "notebook",
|
|
2008
|
+
repeatNotice,
|
|
1968
2009
|
});
|
|
1969
2010
|
}
|
|
1970
2011
|
const { offset, limit } = selToOffsetLimit(parsed);
|
|
@@ -1972,11 +2013,13 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1972
2013
|
details: { resolvedPath: absolutePath },
|
|
1973
2014
|
sourcePath: absolutePath,
|
|
1974
2015
|
entityLabel: "notebook",
|
|
2016
|
+
repeatNotice,
|
|
1975
2017
|
});
|
|
1976
2018
|
} else if (shouldConvertWithMarkit) {
|
|
1977
2019
|
// Convert document via markit.
|
|
1978
2020
|
const result = await convertFileWithMarkit(absolutePath, signal);
|
|
1979
2021
|
if (result.ok) {
|
|
2022
|
+
repeatNotice = this.#repeatReadNotice(absolutePath);
|
|
1980
2023
|
// Route the converted markdown through the in-memory text builder
|
|
1981
2024
|
// so line-range selectors (`file.pdf:50-100`, `:5-16,40-80`) and
|
|
1982
2025
|
// raw mode apply against the converted output. Without this,
|
|
@@ -1987,6 +2030,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1987
2030
|
details: { resolvedPath: absolutePath },
|
|
1988
2031
|
sourcePath: absolutePath,
|
|
1989
2032
|
entityLabel: "document",
|
|
2033
|
+
repeatNotice,
|
|
1990
2034
|
});
|
|
1991
2035
|
}
|
|
1992
2036
|
const { offset, limit } = selToOffsetLimit(parsed);
|
|
@@ -1995,6 +2039,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1995
2039
|
sourcePath: absolutePath,
|
|
1996
2040
|
entityLabel: "document",
|
|
1997
2041
|
raw: isRawSelector(parsed),
|
|
2042
|
+
repeatNotice,
|
|
1998
2043
|
});
|
|
1999
2044
|
} else if (result.error) {
|
|
2000
2045
|
content = [{ type: "text", text: `[Cannot read ${ext} file: ${result.error || "conversion failed"}]` }];
|
|
@@ -2002,6 +2047,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
2002
2047
|
content = [{ type: "text", text: `[Cannot read ${ext} file: conversion failed]` }];
|
|
2003
2048
|
}
|
|
2004
2049
|
} else {
|
|
2050
|
+
repeatNotice = this.#repeatReadNotice(absolutePath);
|
|
2005
2051
|
if (
|
|
2006
2052
|
parsed.kind === "none" &&
|
|
2007
2053
|
this.session.settings.get("read.summarize.enabled") &&
|
|
@@ -2043,6 +2089,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
2043
2089
|
parsed,
|
|
2044
2090
|
displayMode,
|
|
2045
2091
|
suffixResolution,
|
|
2092
|
+
repeatNotice,
|
|
2046
2093
|
undefined, // plain-file read: deterministic and fast, never abort mid-read
|
|
2047
2094
|
);
|
|
2048
2095
|
if (multiResult.bridgeResult) return multiResult.bridgeResult;
|
|
@@ -2066,6 +2113,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
2066
2113
|
sourcePath: absolutePath,
|
|
2067
2114
|
entityLabel: "file",
|
|
2068
2115
|
raw: isRawSelector(parsed),
|
|
2116
|
+
repeatNotice,
|
|
2069
2117
|
});
|
|
2070
2118
|
if (suffixResolution) {
|
|
2071
2119
|
const notice = `[Path '${suffixResolution.from}' not found; resolved to '${suffixResolution.to}' via suffix match]`;
|
|
@@ -2367,6 +2415,14 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
2367
2415
|
content = [{ type: "text", text: notice }, ...content];
|
|
2368
2416
|
}
|
|
2369
2417
|
}
|
|
2418
|
+
if (repeatNotice) {
|
|
2419
|
+
// Trailing nudge goes at the very end of the textual result so it never
|
|
2420
|
+
// disturbs hashline tag headers or inline notices.
|
|
2421
|
+
const lastText = content.findLast((c): c is TextContent => c.type === "text");
|
|
2422
|
+
if (lastText) {
|
|
2423
|
+
lastText.text = `${lastText.text}\n${repeatNotice}`;
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2370
2426
|
const resultBuilder = toolResult(details).content(content);
|
|
2371
2427
|
if (sourcePath) {
|
|
2372
2428
|
resultBuilder.sourcePath(sourcePath);
|
|
@@ -779,6 +779,62 @@ export function createCachedComponent(
|
|
|
779
779
|
};
|
|
780
780
|
}
|
|
781
781
|
|
|
782
|
+
/**
|
|
783
|
+
* Single-slot memo for an expensive rendered string (syntax highlighting, diff
|
|
784
|
+
* coloring) keyed by the exact inputs that shape the bytes: theme instance,
|
|
785
|
+
* expanded state, a caller-chosen salt (path/language), and the source content.
|
|
786
|
+
* Field-wise comparison instead of a concatenated key string: a cache hit costs
|
|
787
|
+
* one string value-compare (engines short-circuit on length) and a miss never
|
|
788
|
+
* allocates a key. Comparing the {@link Theme} by reference is sound because
|
|
789
|
+
* theme switches replace the instance wholesale (`setTheme`/`previewTheme`/
|
|
790
|
+
* `setSymbolPreset` in modes/theme/theme.ts) — themes are never mutated in
|
|
791
|
+
* place.
|
|
792
|
+
*/
|
|
793
|
+
export interface RenderedStringCache {
|
|
794
|
+
theme: Theme | null;
|
|
795
|
+
expanded: boolean;
|
|
796
|
+
salt: string;
|
|
797
|
+
content: string;
|
|
798
|
+
value: string;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
export function createRenderedStringCache(): RenderedStringCache {
|
|
802
|
+
return { theme: null, expanded: false, salt: "", content: "", value: "" };
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/** Drop the memo so the next lookup re-renders (e.g. the render function identity changed). */
|
|
806
|
+
export function invalidateRenderedStringCache(cache: RenderedStringCache): void {
|
|
807
|
+
cache.theme = null;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
export function cachedRenderedString(
|
|
811
|
+
cache: RenderedStringCache | undefined,
|
|
812
|
+
theme: Theme,
|
|
813
|
+
expanded: boolean,
|
|
814
|
+
salt: string,
|
|
815
|
+
content: string,
|
|
816
|
+
render: () => string,
|
|
817
|
+
): string {
|
|
818
|
+
if (
|
|
819
|
+
cache !== undefined &&
|
|
820
|
+
cache.theme === theme &&
|
|
821
|
+
cache.expanded === expanded &&
|
|
822
|
+
cache.salt === salt &&
|
|
823
|
+
cache.content === content
|
|
824
|
+
) {
|
|
825
|
+
return cache.value;
|
|
826
|
+
}
|
|
827
|
+
const value = render();
|
|
828
|
+
if (cache !== undefined) {
|
|
829
|
+
cache.theme = theme;
|
|
830
|
+
cache.expanded = expanded;
|
|
831
|
+
cache.salt = salt;
|
|
832
|
+
cache.content = content;
|
|
833
|
+
cache.value = value;
|
|
834
|
+
}
|
|
835
|
+
return value;
|
|
836
|
+
}
|
|
837
|
+
|
|
782
838
|
/**
|
|
783
839
|
* Append the indented bullet list of parse errors (capped at
|
|
784
840
|
* {@link PARSE_ERRORS_LIMIT}) to `lines`, with an overflow summary line if the
|
package/src/tools/renderers.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { evalToolRenderer } from "./eval-render";
|
|
|
21
21
|
import { findToolRenderer } from "./find";
|
|
22
22
|
import { githubToolRenderer } from "./gh-renderer";
|
|
23
23
|
import { inspectImageToolRenderer } from "./inspect-image-renderer";
|
|
24
|
+
import { ircToolRenderer } from "./irc";
|
|
24
25
|
import { jobToolRenderer } from "./job";
|
|
25
26
|
import { recallToolRenderer, reflectToolRenderer, retainToolRenderer } from "./memory-render";
|
|
26
27
|
import { readToolRenderer } from "./read";
|
|
@@ -58,6 +59,7 @@ export const toolRenderers: Record<string, ToolRenderer> = {
|
|
|
58
59
|
search: searchToolRenderer as ToolRenderer,
|
|
59
60
|
lsp: lspToolRenderer as ToolRenderer,
|
|
60
61
|
inspect_image: inspectImageToolRenderer as ToolRenderer,
|
|
62
|
+
irc: ircToolRenderer as ToolRenderer,
|
|
61
63
|
read: readToolRenderer as ToolRenderer,
|
|
62
64
|
job: jobToolRenderer as ToolRenderer,
|
|
63
65
|
resolve: resolveToolRenderer as ToolRenderer,
|
package/src/tools/resolve.ts
CHANGED
|
@@ -241,7 +241,10 @@ export const resolveToolRenderer = {
|
|
|
241
241
|
const isApply = action === "apply" && !result.isError;
|
|
242
242
|
const isFailedApply = action === "apply" && result.isError;
|
|
243
243
|
const bgColor = result.isError ? "error" : isApply ? "success" : "warning";
|
|
244
|
-
|
|
244
|
+
// Bare symbol: the line is wrapped in inverse(fg(...)), so any embedded fg
|
|
245
|
+
// reset (styledSymbol/status glyphs carry their own \x1b[39m) would drop the
|
|
246
|
+
// inverse block back to the default background mid-line.
|
|
247
|
+
const icon = uiTheme.symbol(isApply ? "tool.resolve" : "status.error");
|
|
245
248
|
const verb = isApply ? "Accept" : isFailedApply ? "Failed" : "Discard";
|
|
246
249
|
const separator = ": ";
|
|
247
250
|
const separatorIndex = label.indexOf(separator);
|
package/src/tools/write.ts
CHANGED
|
@@ -37,12 +37,15 @@ import { type OutputMeta, outputMeta } from "./output-meta";
|
|
|
37
37
|
import { formatPathRelativeToCwd, isInternalUrlPath } from "./path-utils";
|
|
38
38
|
import { enforcePlanModeWrite, resolvePlanPath } from "./plan-mode-guard";
|
|
39
39
|
import {
|
|
40
|
+
cachedRenderedString,
|
|
41
|
+
createRenderedStringCache,
|
|
40
42
|
formatDiagnostics,
|
|
41
43
|
formatErrorDetail,
|
|
42
44
|
formatExpandHint,
|
|
43
45
|
formatMoreItems,
|
|
44
46
|
formatStatusIcon,
|
|
45
47
|
getLspBatchRequest,
|
|
48
|
+
type RenderedStringCache,
|
|
46
49
|
replaceTabs,
|
|
47
50
|
shortenPath,
|
|
48
51
|
} from "./render-utils";
|
|
@@ -1042,37 +1045,40 @@ function formatStreamingContent(
|
|
|
1042
1045
|
language: string | undefined,
|
|
1043
1046
|
uiTheme: Theme,
|
|
1044
1047
|
spinnerFrame?: number,
|
|
1048
|
+
cache?: RenderedStringCache,
|
|
1045
1049
|
): string {
|
|
1046
1050
|
if (!content) return "";
|
|
1047
|
-
const
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1051
|
+
const bodyText = cachedRenderedString(cache, uiTheme, expanded, language ?? "", content, () => {
|
|
1052
|
+
const lines = normalizeDisplayText(content).split("\n");
|
|
1053
|
+
const totalLines = lines.length;
|
|
1054
|
+
// Collapsed: follow the streaming edge with a bounded tail window so the box
|
|
1055
|
+
// stays short enough not to strand its scrolled-off head above the viewport
|
|
1056
|
+
// while the block is volatile. `Ctrl+O` (expanded) lifts the cap for a
|
|
1057
|
+
// deliberate full view — matching the eval streaming preview.
|
|
1058
|
+
const startIndex = expanded ? 0 : Math.max(0, totalLines - WRITE_STREAMING_PREVIEW_LINES);
|
|
1059
|
+
const visibleLines = lines.slice(startIndex);
|
|
1060
|
+
const hidden = startIndex;
|
|
1061
|
+
const highlighted = highlightCode(visibleLines.join("\n"), language);
|
|
1062
|
+
const lineNumberWidth = Math.max(WRITE_GUTTER_MIN_WIDTH, String(totalLines).length);
|
|
1063
|
+
|
|
1064
|
+
let text = "\n\n";
|
|
1065
|
+
if (hidden > 0) {
|
|
1066
|
+
text += `${uiTheme.fg("dim", `… (${hidden} earlier line${hidden === 1 ? "" : "s"})`)}\n`;
|
|
1067
|
+
}
|
|
1068
|
+
for (let i = 0; i < highlighted.length; i++) {
|
|
1069
|
+
const lineNum = startIndex + i + 1;
|
|
1070
|
+
const gutter = uiTheme.fg("dim", `${String(lineNum).padStart(lineNumberWidth, " ")} `);
|
|
1071
|
+
const body = replaceTabs(highlighted[i] ?? "");
|
|
1072
|
+
text += `${gutter}${body}\n`;
|
|
1073
|
+
}
|
|
1074
|
+
return text;
|
|
1075
|
+
});
|
|
1069
1076
|
// The animated glyph lives on this trailing line — inside the transcript's
|
|
1070
1077
|
// volatile-tail holdback — never in the header: an animating head row pins
|
|
1071
1078
|
// the native-scrollback commit boundary at the top of the block, so a long
|
|
1072
1079
|
// expanded preview could never scroll-append mid-stream.
|
|
1073
1080
|
const spinner = spinnerFrame !== undefined ? `${formatStatusIcon("running", uiTheme, spinnerFrame)} ` : "";
|
|
1074
|
-
|
|
1075
|
-
return text;
|
|
1081
|
+
return `${bodyText}${spinner}${uiTheme.fg("dim", `… (streaming)`)}`;
|
|
1076
1082
|
}
|
|
1077
1083
|
|
|
1078
1084
|
function renderContentPreview(
|
|
@@ -1080,29 +1086,32 @@ function renderContentPreview(
|
|
|
1080
1086
|
expanded: boolean,
|
|
1081
1087
|
language: string | undefined,
|
|
1082
1088
|
uiTheme: Theme,
|
|
1089
|
+
cache?: RenderedStringCache,
|
|
1083
1090
|
): string {
|
|
1084
1091
|
if (!content) return "";
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1092
|
+
return cachedRenderedString(cache, uiTheme, expanded, language ?? "", content, () => {
|
|
1093
|
+
const rawLines = normalizeDisplayText(content).split("\n");
|
|
1094
|
+
const totalLines = rawLines.length;
|
|
1095
|
+
const maxLines = expanded ? totalLines : Math.min(totalLines, WRITE_PREVIEW_LINES);
|
|
1096
|
+
const visibleLines = rawLines.slice(0, maxLines);
|
|
1097
|
+
const highlighted = highlightCode(visibleLines.join("\n"), language);
|
|
1098
|
+
const lineNumberWidth = Math.max(WRITE_GUTTER_MIN_WIDTH, String(totalLines).length);
|
|
1099
|
+
const hidden = totalLines - maxLines;
|
|
1100
|
+
|
|
1101
|
+
let text = "\n\n";
|
|
1102
|
+
for (let i = 0; i < highlighted.length; i++) {
|
|
1103
|
+
const lineNum = i + 1;
|
|
1104
|
+
const gutter = uiTheme.fg("dim", `${String(lineNum).padStart(lineNumberWidth, " ")} `);
|
|
1105
|
+
const body = replaceTabs(highlighted[i] ?? "");
|
|
1106
|
+
text += `${gutter}${body}\n`;
|
|
1107
|
+
}
|
|
1108
|
+
if (!expanded && hidden > 0) {
|
|
1109
|
+
const hint = formatExpandHint(uiTheme, expanded, hidden > 0);
|
|
1110
|
+
const moreLine = `${formatMoreItems(hidden, "line")}${hint ? ` ${hint}` : ""}`;
|
|
1111
|
+
text += uiTheme.fg("dim", moreLine);
|
|
1112
|
+
}
|
|
1113
|
+
return text.trimEnd();
|
|
1114
|
+
});
|
|
1106
1115
|
}
|
|
1107
1116
|
|
|
1108
1117
|
export const writeToolRenderer = {
|
|
@@ -1125,9 +1134,17 @@ export const writeToolRenderer = {
|
|
|
1125
1134
|
},
|
|
1126
1135
|
uiTheme,
|
|
1127
1136
|
);
|
|
1137
|
+
const streamingCache = createRenderedStringCache();
|
|
1128
1138
|
return framedBlock(uiTheme, width => {
|
|
1129
1139
|
const body = args.content
|
|
1130
|
-
? formatStreamingContent(
|
|
1140
|
+
? formatStreamingContent(
|
|
1141
|
+
args.content,
|
|
1142
|
+
Boolean(options?.expanded),
|
|
1143
|
+
lang,
|
|
1144
|
+
uiTheme,
|
|
1145
|
+
options?.spinnerFrame,
|
|
1146
|
+
streamingCache,
|
|
1147
|
+
)
|
|
1131
1148
|
: "";
|
|
1132
1149
|
const bodyLines = body ? body.split("\n") : [];
|
|
1133
1150
|
while (bodyLines.length > 0 && bodyLines[0].trim() === "") bodyLines.shift();
|
|
@@ -1189,9 +1206,10 @@ export const writeToolRenderer = {
|
|
|
1189
1206
|
);
|
|
1190
1207
|
const diagnostics = result.details?.diagnostics;
|
|
1191
1208
|
|
|
1209
|
+
const previewCache = createRenderedStringCache();
|
|
1192
1210
|
return framedBlock(uiTheme, width => {
|
|
1193
1211
|
const { expanded } = options;
|
|
1194
|
-
let body = renderContentPreview(fileContent, expanded, lang, uiTheme);
|
|
1212
|
+
let body = renderContentPreview(fileContent, expanded, lang, uiTheme, previewCache);
|
|
1195
1213
|
if (diagnostics) {
|
|
1196
1214
|
const diagText = formatDiagnostics(diagnostics, expanded, uiTheme, fp =>
|
|
1197
1215
|
uiTheme.getLangIcon(getLanguageFromPath(fp)),
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
buildAnthropicSystemBlocks,
|
|
15
15
|
buildAnthropicUrl,
|
|
16
16
|
type FetchImpl,
|
|
17
|
+
resolveAnthropicMetadataUserId,
|
|
17
18
|
stripClaudeToolPrefix,
|
|
18
19
|
withAuth,
|
|
19
20
|
wrapFetchForCch,
|
|
@@ -82,6 +83,7 @@ function buildSystemBlocks(
|
|
|
82
83
|
* @param auth - Authentication configuration (API key or OAuth)
|
|
83
84
|
* @param model - Model identifier to use
|
|
84
85
|
* @param query - Search query from the user
|
|
86
|
+
* @param metadataUserId - Optional Anthropic Messages metadata.user_id (already shaped for OAuth)
|
|
85
87
|
* @param systemPrompt - Optional system prompt for guiding response style
|
|
86
88
|
* @returns Raw API response from Anthropic
|
|
87
89
|
* @throws {SearchProviderError} If the API request fails
|
|
@@ -90,6 +92,7 @@ async function callSearch(
|
|
|
90
92
|
auth: AnthropicAuthConfig,
|
|
91
93
|
model: string,
|
|
92
94
|
query: string,
|
|
95
|
+
metadataUserId?: string,
|
|
93
96
|
systemPrompt?: string,
|
|
94
97
|
maxTokens?: number,
|
|
95
98
|
temperature?: number,
|
|
@@ -113,6 +116,10 @@ async function callSearch(
|
|
|
113
116
|
],
|
|
114
117
|
};
|
|
115
118
|
|
|
119
|
+
if (metadataUserId) {
|
|
120
|
+
body.metadata = { user_id: metadataUserId };
|
|
121
|
+
}
|
|
122
|
+
|
|
116
123
|
if (temperature !== undefined) {
|
|
117
124
|
body.temperature = temperature;
|
|
118
125
|
}
|
|
@@ -273,19 +280,37 @@ export async function searchAnthropic(
|
|
|
273
280
|
const model = getModel();
|
|
274
281
|
const systemPrompt = "authStorage" in params ? params.systemPrompt : params.system_prompt;
|
|
275
282
|
const maxTokens = "authStorage" in params ? params.maxOutputTokens : params.max_tokens;
|
|
283
|
+
const callerSessionId = "authStorage" in params ? params.sessionId : undefined;
|
|
284
|
+
const accountId =
|
|
285
|
+
"authStorage" in params ? params.authStorage.getOAuthAccountId("anthropic", params.sessionId) : undefined;
|
|
276
286
|
const response = await withAuth(
|
|
277
287
|
keyOrResolver,
|
|
278
|
-
key =>
|
|
279
|
-
|
|
280
|
-
|
|
288
|
+
key => {
|
|
289
|
+
const auth = buildAnthropicAuthConfig(key, searchBaseUrl);
|
|
290
|
+
// Mirror the main Messages path: OAuth requests need a Claude-Code-shaped
|
|
291
|
+
// metadata.user_id (`{session_id, account_uuid?, device_id}`) so the
|
|
292
|
+
// CC billing header + system fingerprint installed by
|
|
293
|
+
// `buildAnthropicSearchHeaders`/`buildSystemBlocks` line up with the
|
|
294
|
+
// attribution Anthropic and enterprise gateways expect. API-key tokens
|
|
295
|
+
// forward the raw session id verbatim.
|
|
296
|
+
const metadataUserId = resolveAnthropicMetadataUserId(
|
|
297
|
+
callerSessionId,
|
|
298
|
+
auth.isOAuth,
|
|
299
|
+
callerSessionId,
|
|
300
|
+
accountId,
|
|
301
|
+
);
|
|
302
|
+
return callSearch(
|
|
303
|
+
auth,
|
|
281
304
|
model,
|
|
282
305
|
params.query,
|
|
306
|
+
metadataUserId,
|
|
283
307
|
systemPrompt,
|
|
284
308
|
maxTokens,
|
|
285
309
|
params.temperature,
|
|
286
310
|
params.signal,
|
|
287
311
|
params.fetch,
|
|
288
|
-
)
|
|
312
|
+
);
|
|
313
|
+
},
|
|
289
314
|
{
|
|
290
315
|
signal: params.signal,
|
|
291
316
|
missingKeyMessage:
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { Container } from "@oh-my-pi/pi-tui";
|
|
2
|
-
import type { KeyId } from "../../config/keybindings";
|
|
3
|
-
import type { SessionObserverRegistry } from "../session-observer-registry";
|
|
4
|
-
export declare class SessionObserverOverlayComponent extends Container {
|
|
5
|
-
#private;
|
|
6
|
-
constructor(registry: SessionObserverRegistry, onDone: () => void, observeKeys: KeyId[]);
|
|
7
|
-
render(width: number): readonly string[];
|
|
8
|
-
/** Rebuild content from live registry data */
|
|
9
|
-
refreshFromRegistry(): void;
|
|
10
|
-
handleInput(keyData: string): void;
|
|
11
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export declare const TASK_SIMPLE_MODES: readonly ["default", "schema-free", "independent"];
|
|
2
|
-
export type TaskSimpleMode = (typeof TASK_SIMPLE_MODES)[number];
|
|
3
|
-
interface TaskSimpleModeCapabilities {
|
|
4
|
-
contextEnabled: boolean;
|
|
5
|
-
customSchemaEnabled: boolean;
|
|
6
|
-
}
|
|
7
|
-
export declare function getTaskSimpleModeCapabilities(mode: TaskSimpleMode): TaskSimpleModeCapabilities;
|
|
8
|
-
export {};
|
package/src/async/support.ts
DELETED