@oh-my-pi/pi-coding-agent 16.0.5 → 16.0.7
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 +60 -0
- package/dist/cli.js +1945 -1386
- package/dist/types/advisor/advise-tool.d.ts +22 -19
- package/dist/types/autoresearch/tools/init-experiment.d.ts +13 -17
- package/dist/types/autoresearch/tools/log-experiment.d.ts +17 -19
- package/dist/types/autoresearch/tools/run-experiment.d.ts +3 -4
- package/dist/types/autoresearch/tools/update-notes.d.ts +4 -5
- package/dist/types/cli/ttsr-cli.d.ts +39 -0
- package/dist/types/commands/ttsr.d.ts +57 -0
- package/dist/types/commit/agentic/tools/analyze-file.d.ts +4 -5
- package/dist/types/commit/agentic/tools/git-file-diff.d.ts +4 -5
- package/dist/types/commit/agentic/tools/git-hunk.d.ts +5 -6
- package/dist/types/commit/agentic/tools/git-overview.d.ts +4 -5
- package/dist/types/commit/agentic/tools/propose-changelog.d.ts +23 -24
- package/dist/types/commit/agentic/tools/propose-commit.d.ts +11 -32
- package/dist/types/commit/agentic/tools/recent-commits.d.ts +3 -4
- package/dist/types/commit/agentic/tools/schemas.d.ts +6 -27
- package/dist/types/commit/agentic/tools/split-commit.d.ts +28 -49
- package/dist/types/commit/changelog/generate.d.ts +12 -13
- package/dist/types/commit/shared-llm.d.ts +10 -37
- package/dist/types/config/config-file.d.ts +4 -4
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/models-config-schema.d.ts +625 -990
- package/dist/types/config/models-config.d.ts +229 -217
- package/dist/types/config/settings-schema.d.ts +53 -23
- package/dist/types/edit/hashline/params.d.ts +7 -11
- package/dist/types/edit/index.d.ts +2 -1
- package/dist/types/edit/modes/apply-patch.d.ts +4 -5
- package/dist/types/edit/modes/patch.d.ts +15 -24
- package/dist/types/edit/modes/replace.d.ts +16 -17
- package/dist/types/eval/js/index.d.ts +1 -0
- package/dist/types/extensibility/custom-commands/types.d.ts +6 -3
- package/dist/types/extensibility/custom-tools/types.d.ts +8 -5
- package/dist/types/extensibility/extensions/types.d.ts +6 -3
- package/dist/types/extensibility/hooks/types.d.ts +7 -4
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +13 -5
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +17 -0
- package/dist/types/extensibility/typebox.d.ts +80 -58
- package/dist/types/goals/tools/goal-tool.d.ts +11 -24
- package/dist/types/index.d.ts +2 -0
- package/dist/types/lsp/index.d.ts +11 -26
- package/dist/types/lsp/types.d.ts +12 -28
- package/dist/types/mcp/client.d.ts +8 -0
- package/dist/types/modes/components/btw-panel.d.ts +1 -0
- package/dist/types/modes/components/custom-editor.d.ts +3 -1
- package/dist/types/modes/controllers/btw-controller.d.ts +2 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -0
- package/dist/types/modes/setup-wizard/index.d.ts +1 -0
- package/dist/types/modes/setup-wizard/startup-splash.d.ts +7 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +3 -0
- package/dist/types/sdk.d.ts +5 -0
- package/dist/types/session/agent-session.d.ts +4 -0
- package/dist/types/startup-splash.d.ts +12 -0
- package/dist/types/task/types.d.ts +47 -48
- package/dist/types/tools/ask.d.ts +26 -27
- package/dist/types/tools/ast-edit.d.ts +17 -17
- package/dist/types/tools/ast-grep.d.ts +12 -13
- package/dist/types/tools/bash.d.ts +20 -17
- package/dist/types/tools/browser.d.ts +46 -71
- package/dist/types/tools/checkpoint.d.ts +14 -15
- package/dist/types/tools/debug.d.ts +82 -145
- package/dist/types/tools/eval.d.ts +30 -40
- package/dist/types/tools/find.d.ts +17 -18
- package/dist/types/tools/gh.d.ts +49 -78
- package/dist/types/tools/image-gen.d.ts +20 -36
- package/dist/types/tools/inspect-image.d.ts +10 -11
- package/dist/types/tools/irc.d.ts +22 -33
- package/dist/types/tools/job.d.ts +11 -12
- package/dist/types/tools/learn.d.ts +21 -28
- package/dist/types/tools/manage-skill.d.ts +13 -22
- package/dist/types/tools/memory-edit.d.ts +15 -24
- package/dist/types/tools/memory-recall.d.ts +7 -8
- package/dist/types/tools/memory-reflect.d.ts +9 -10
- package/dist/types/tools/memory-retain.d.ts +13 -14
- package/dist/types/tools/read.d.ts +7 -8
- package/dist/types/tools/resolve.d.ts +11 -18
- package/dist/types/tools/review.d.ts +9 -15
- package/dist/types/tools/search-tool-bm25.d.ts +9 -10
- package/dist/types/tools/search.d.ts +16 -17
- package/dist/types/tools/ssh.d.ts +14 -15
- package/dist/types/tools/todo.d.ts +27 -43
- package/dist/types/tools/tts.d.ts +8 -9
- package/dist/types/tools/write.d.ts +9 -10
- package/dist/types/tui/index.d.ts +1 -0
- package/dist/types/tui/width-aware-text.d.ts +23 -0
- package/dist/types/utils/markit.d.ts +10 -1
- package/dist/types/web/search/index.d.ts +17 -28
- package/dist/types/web/search/providers/perplexity.d.ts +0 -2
- package/dist/types/web/search/types.d.ts +32 -26
- package/package.json +14 -13
- package/scripts/omp +1 -1
- package/src/advisor/__tests__/advisor.test.ts +44 -1
- package/src/advisor/advise-tool.ts +34 -11
- package/src/autoresearch/tools/init-experiment.ts +13 -16
- package/src/autoresearch/tools/log-experiment.ts +15 -18
- package/src/autoresearch/tools/run-experiment.ts +3 -3
- package/src/autoresearch/tools/update-notes.ts +4 -4
- package/src/cli/ttsr-cli.ts +995 -0
- package/src/cli-commands.ts +1 -0
- package/src/cli.ts +7 -1
- package/src/commands/ttsr.ts +125 -0
- package/src/commit/agentic/tools/analyze-file.ts +4 -4
- package/src/commit/agentic/tools/git-file-diff.ts +4 -4
- package/src/commit/agentic/tools/git-hunk.ts +7 -5
- package/src/commit/agentic/tools/git-overview.ts +4 -4
- package/src/commit/agentic/tools/propose-changelog.ts +18 -15
- package/src/commit/agentic/tools/propose-commit.ts +6 -6
- package/src/commit/agentic/tools/recent-commits.ts +3 -3
- package/src/commit/agentic/tools/schemas.ts +8 -20
- package/src/commit/agentic/tools/split-commit.ts +19 -23
- package/src/commit/analysis/summary.ts +7 -5
- package/src/commit/changelog/generate.ts +15 -11
- package/src/commit/shared-llm.ts +17 -24
- package/src/config/config-file.ts +13 -15
- package/src/config/keybindings.ts +6 -0
- package/src/config/models-config-schema.ts +206 -179
- package/src/config/settings-schema.ts +34 -0
- package/src/discovery/builtin-rules/index.ts +2 -0
- package/src/discovery/builtin-rules/ts-import-type.md +2 -2
- package/src/discovery/builtin-rules/ts-no-any.md +11 -2
- package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +55 -0
- package/src/edit/hashline/params.ts +12 -11
- package/src/edit/index.ts +5 -4
- package/src/edit/modes/apply-patch.ts +4 -4
- package/src/edit/modes/patch.ts +15 -18
- package/src/edit/modes/replace.ts +13 -17
- package/src/edit/renderer.ts +0 -1
- package/src/eval/agent-bridge.ts +11 -13
- package/src/eval/completion-bridge.ts +25 -17
- package/src/eval/js/context-manager.ts +17 -2
- package/src/eval/js/index.ts +1 -1
- package/src/eval/py/executor.ts +2 -2
- package/src/extensibility/custom-commands/loader.ts +5 -3
- package/src/extensibility/custom-commands/types.ts +6 -3
- package/src/extensibility/custom-tools/loader.ts +4 -2
- package/src/extensibility/custom-tools/types.ts +8 -5
- package/src/extensibility/extensions/loader.ts +4 -2
- package/src/extensibility/extensions/types.ts +6 -3
- package/src/extensibility/hooks/loader.ts +5 -2
- package/src/extensibility/hooks/types.ts +7 -4
- package/src/extensibility/legacy-pi-ai-shim.ts +42 -5
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +113 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +13 -13
- package/src/extensibility/tool-proxy.ts +4 -1
- package/src/extensibility/typebox.ts +778 -251
- package/src/goals/guided-setup.ts +12 -3
- package/src/goals/tools/goal-tool.ts +6 -6
- package/src/index.ts +2 -0
- package/src/internal-urls/docs-index.generated.ts +11 -9
- package/src/lsp/types.ts +13 -27
- package/src/main.ts +19 -18
- package/src/mcp/client.ts +38 -13
- package/src/mcp/render.ts +102 -89
- package/src/modes/components/agent-hub.ts +11 -4
- package/src/modes/components/btw-panel.ts +5 -1
- package/src/modes/components/custom-editor.ts +18 -0
- package/src/modes/components/status-line/component.ts +8 -1
- package/src/modes/components/tool-execution.ts +17 -10
- package/src/modes/controllers/btw-controller.ts +69 -1
- package/src/modes/controllers/input-controller.ts +29 -0
- package/src/modes/interactive-mode.ts +38 -8
- package/src/modes/setup-wizard/index.ts +1 -0
- package/src/modes/setup-wizard/scenes/sign-in.ts +77 -5
- package/src/modes/setup-wizard/startup-splash.ts +107 -0
- package/src/modes/theme/theme.ts +133 -143
- package/src/modes/types.ts +3 -0
- package/src/modes/utils/context-usage.ts +9 -5
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/sdk.ts +21 -4
- package/src/session/agent-session.ts +173 -33
- package/src/session/session-history-format.ts +11 -2
- package/src/session/snapcompact-inline.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +3 -10
- package/src/startup-splash.ts +19 -0
- package/src/task/executor.ts +11 -6
- package/src/task/types.ts +44 -41
- package/src/tool-discovery/tool-index.ts +17 -4
- package/src/tools/ask.ts +14 -14
- package/src/tools/ast-edit.ts +17 -14
- package/src/tools/ast-grep.ts +10 -9
- package/src/tools/bash.ts +15 -10
- package/src/tools/browser/launch.ts +13 -0
- package/src/tools/browser.ts +26 -32
- package/src/tools/checkpoint.ts +7 -7
- package/src/tools/debug.ts +72 -69
- package/src/tools/eval.ts +18 -19
- package/src/tools/find.ts +20 -13
- package/src/tools/gh.ts +29 -49
- package/src/tools/image-gen.ts +27 -32
- package/src/tools/inspect-image.ts +8 -9
- package/src/tools/irc.ts +12 -12
- package/src/tools/job.ts +6 -6
- package/src/tools/learn.ts +11 -14
- package/src/tools/manage-skill.ts +19 -23
- package/src/tools/memory-edit.ts +8 -8
- package/src/tools/memory-recall.ts +4 -4
- package/src/tools/memory-reflect.ts +5 -5
- package/src/tools/memory-retain.ts +9 -11
- package/src/tools/puppeteer/02_stealth_hairline.txt +1 -1
- package/src/tools/puppeteer/04_stealth_iframe.txt +4 -4
- package/src/tools/puppeteer/05_stealth_webgl.txt +1 -1
- package/src/tools/puppeteer/10_stealth_plugins.txt +6 -4
- package/src/tools/puppeteer/12_stealth_codecs.txt +2 -2
- package/src/tools/puppeteer/13_stealth_worker.txt +1 -1
- package/src/tools/read.ts +169 -13
- package/src/tools/report-tool-issue.ts +6 -6
- package/src/tools/resolve.ts +6 -6
- package/src/tools/review.ts +10 -12
- package/src/tools/search-tool-bm25.ts +5 -5
- package/src/tools/search.ts +20 -29
- package/src/tools/ssh.ts +8 -8
- package/src/tools/todo.ts +16 -19
- package/src/tools/tts.ts +16 -15
- package/src/tools/write.ts +5 -5
- package/src/tui/index.ts +1 -0
- package/src/tui/width-aware-text.ts +58 -0
- package/src/utils/markit.ts +17 -2
- package/src/web/search/index.ts +9 -9
- package/src/web/search/providers/perplexity.ts +373 -126
- package/src/web/search/types.ts +28 -48
|
@@ -32,11 +32,11 @@ import {
|
|
|
32
32
|
AppendOnlyContextManager,
|
|
33
33
|
type AsideMessage,
|
|
34
34
|
type CompactionSummaryMessage,
|
|
35
|
+
countTokens,
|
|
35
36
|
resolveTelemetry,
|
|
36
37
|
STREAM_INTERRUPTED_AFTER_CONTENT_STOP_DETAIL,
|
|
37
38
|
ThinkingLevel,
|
|
38
39
|
} from "@oh-my-pi/pi-agent-core";
|
|
39
|
-
|
|
40
40
|
import {
|
|
41
41
|
AGGRESSIVE_SHAKE_CONFIG,
|
|
42
42
|
AUTO_HANDOFF_THRESHOLD_FOCUS,
|
|
@@ -104,7 +104,7 @@ import {
|
|
|
104
104
|
} from "@oh-my-pi/pi-ai";
|
|
105
105
|
import { getSupportedEfforts } from "@oh-my-pi/pi-catalog/model-thinking";
|
|
106
106
|
import { modelsAreEqual } from "@oh-my-pi/pi-catalog/models";
|
|
107
|
-
import {
|
|
107
|
+
import { MacOSPowerAssertion } from "@oh-my-pi/pi-natives";
|
|
108
108
|
import {
|
|
109
109
|
extractRetryHint,
|
|
110
110
|
formatDuration,
|
|
@@ -150,7 +150,7 @@ import {
|
|
|
150
150
|
resolveModelRoleValue,
|
|
151
151
|
resolveRoleSelection,
|
|
152
152
|
} from "../config/model-resolver";
|
|
153
|
-
import { MODEL_ROLE_IDS } from "../config/model-roles";
|
|
153
|
+
import { MODEL_ROLE_IDS, MODEL_ROLES } from "../config/model-roles";
|
|
154
154
|
import { expandPromptTemplate, type PromptTemplate } from "../config/prompt-templates";
|
|
155
155
|
import type { Settings, SkillsSettings } from "../config/settings";
|
|
156
156
|
import { onAppendOnlyModeChanged } from "../config/settings";
|
|
@@ -1755,6 +1755,24 @@ export class AgentSession {
|
|
|
1755
1755
|
systemPrompt.push(this.#advisorWatchdogPrompt);
|
|
1756
1756
|
}
|
|
1757
1757
|
const advisorSessionId = this.sessionId ? `${this.sessionId}-advisor` : undefined;
|
|
1758
|
+
|
|
1759
|
+
// Thread the primary's telemetry into the advisor loop so the advisor
|
|
1760
|
+
// model's GenAI spans + usage/cost hooks fire like every other model call,
|
|
1761
|
+
// stamped with the advisor's own identity. `conversationId` is cleared so
|
|
1762
|
+
// the advisor loop falls back to its own `-advisor` session id for
|
|
1763
|
+
// `gen_ai.conversation.id` instead of inheriting the primary's
|
|
1764
|
+
// conversation; undefined telemetry stays undefined (zero-overhead no-op).
|
|
1765
|
+
const advisorTelemetry = this.agent.telemetry
|
|
1766
|
+
? {
|
|
1767
|
+
...this.agent.telemetry,
|
|
1768
|
+
agent: {
|
|
1769
|
+
id: advisorSessionId,
|
|
1770
|
+
name: MODEL_ROLES.advisor.name,
|
|
1771
|
+
description: formatModelString(advisorSel.model),
|
|
1772
|
+
},
|
|
1773
|
+
conversationId: undefined,
|
|
1774
|
+
}
|
|
1775
|
+
: undefined;
|
|
1758
1776
|
const advisorAgent = new Agent({
|
|
1759
1777
|
initialState: {
|
|
1760
1778
|
systemPrompt,
|
|
@@ -1766,6 +1784,7 @@ export class AgentSession {
|
|
|
1766
1784
|
sessionId: advisorSessionId,
|
|
1767
1785
|
getApiKey: requestModel => this.#modelRegistry.resolver(requestModel, advisorSessionId),
|
|
1768
1786
|
intentTracing: false,
|
|
1787
|
+
telemetry: advisorTelemetry,
|
|
1769
1788
|
});
|
|
1770
1789
|
advisorAgent.setDisableReasoning(shouldDisableReasoning(advisorThinkingLevel));
|
|
1771
1790
|
|
|
@@ -1941,24 +1960,26 @@ export class AgentSession {
|
|
|
1941
1960
|
|
|
1942
1961
|
let compactResult: CompactionResult | undefined;
|
|
1943
1962
|
let lastError: unknown;
|
|
1963
|
+
const advisorSessionId = this.sessionId ? `${this.sessionId}-advisor` : undefined;
|
|
1964
|
+
// Instrument the advisor's overflow-compaction one-shot like the primary
|
|
1965
|
+
// compaction path so the advisor model's maintenance call also emits spans.
|
|
1966
|
+
const telemetry = resolveTelemetry(advisor.telemetry, advisorSessionId);
|
|
1944
1967
|
|
|
1945
1968
|
for (const candidate of candidates) {
|
|
1946
|
-
const apiKey = await this.#modelRegistry.getApiKey(
|
|
1947
|
-
candidate,
|
|
1948
|
-
this.sessionId ? `${this.sessionId}-advisor` : undefined,
|
|
1949
|
-
);
|
|
1969
|
+
const apiKey = await this.#modelRegistry.getApiKey(candidate, advisorSessionId);
|
|
1950
1970
|
if (!apiKey) continue;
|
|
1951
1971
|
|
|
1952
1972
|
try {
|
|
1953
1973
|
compactResult = await compact(
|
|
1954
1974
|
preparation,
|
|
1955
1975
|
candidate,
|
|
1956
|
-
this.#modelRegistry.resolver(candidate,
|
|
1976
|
+
this.#modelRegistry.resolver(candidate, advisorSessionId),
|
|
1957
1977
|
undefined,
|
|
1958
1978
|
undefined,
|
|
1959
1979
|
{
|
|
1960
1980
|
thinkingLevel: advisorCompactionThinkingLevel,
|
|
1961
1981
|
convertToLlm: messages => this.#convertToLlmForSideRequest(messages),
|
|
1982
|
+
telemetry,
|
|
1962
1983
|
},
|
|
1963
1984
|
);
|
|
1964
1985
|
break;
|
|
@@ -2478,6 +2499,13 @@ export class AgentSession {
|
|
|
2478
2499
|
});
|
|
2479
2500
|
this.#retryAttempt = 0;
|
|
2480
2501
|
}
|
|
2502
|
+
if (assistantMsg.provider === "opencode-go") {
|
|
2503
|
+
this.#modelRegistry.authStorage.recordUsageCost(assistantMsg.provider, assistantMsg.usage.cost.total, {
|
|
2504
|
+
sessionId: this.#activeProviderSessionId(),
|
|
2505
|
+
recordedAt: assistantMsg.timestamp,
|
|
2506
|
+
baseUrl: this.#modelRegistry.getProviderBaseUrl?.(assistantMsg.provider),
|
|
2507
|
+
});
|
|
2508
|
+
}
|
|
2481
2509
|
}
|
|
2482
2510
|
if (event.message.role === "toolResult") {
|
|
2483
2511
|
const { toolName, details, isError, content } = event.message as {
|
|
@@ -4422,7 +4450,7 @@ export class AgentSession {
|
|
|
4422
4450
|
}
|
|
4423
4451
|
return new Proxy(tool, {
|
|
4424
4452
|
get: (target, prop) => {
|
|
4425
|
-
if (prop !== "execute") return
|
|
4453
|
+
if (prop !== "execute") return target[prop as keyof T];
|
|
4426
4454
|
return async (
|
|
4427
4455
|
toolCallId: string,
|
|
4428
4456
|
args: unknown,
|
|
@@ -4964,20 +4992,15 @@ export class AgentSession {
|
|
|
4964
4992
|
const antigravityEndpointMode =
|
|
4965
4993
|
provider === "google-antigravity" ? this.settings.get("providers.antigravityEndpoint") : undefined;
|
|
4966
4994
|
|
|
4967
|
-
if (
|
|
4968
|
-
!sessionOnPayload &&
|
|
4969
|
-
!sessionOnResponse &&
|
|
4970
|
-
!sessionMetadata &&
|
|
4971
|
-
!sessionOnSseEvent &&
|
|
4972
|
-
!openrouterVariant &&
|
|
4973
|
-
!antigravityEndpointMode
|
|
4974
|
-
)
|
|
4975
|
-
return options;
|
|
4976
|
-
|
|
4977
4995
|
const preparedOptions: SimpleStreamOptions = {
|
|
4978
4996
|
...options,
|
|
4979
4997
|
...(openrouterVariant !== undefined && { openrouterVariant }),
|
|
4980
4998
|
...(antigravityEndpointMode !== undefined && { antigravityEndpointMode }),
|
|
4999
|
+
loopGuard: {
|
|
5000
|
+
enabled: this.settings.get("model.loopGuard.enabled"),
|
|
5001
|
+
checkAssistantContent: this.settings.get("model.loopGuard.checkAssistantContent"),
|
|
5002
|
+
...options.loopGuard,
|
|
5003
|
+
},
|
|
4981
5004
|
};
|
|
4982
5005
|
|
|
4983
5006
|
// Stamp session metadata (e.g. user_id={session_id}) onto direct-call requests so
|
|
@@ -9225,7 +9248,9 @@ export class AgentSession {
|
|
|
9225
9248
|
let compactResult: CompactionResult | undefined;
|
|
9226
9249
|
let lastError: unknown;
|
|
9227
9250
|
|
|
9228
|
-
for (
|
|
9251
|
+
for (let candidateIndex = 0; candidateIndex < candidates.length; candidateIndex++) {
|
|
9252
|
+
const candidate = candidates[candidateIndex];
|
|
9253
|
+
const hasMoreCandidates = candidateIndex < candidates.length - 1;
|
|
9229
9254
|
const apiKey = await this.#modelRegistry.getApiKey(candidate, this.sessionId);
|
|
9230
9255
|
if (!apiKey) continue;
|
|
9231
9256
|
|
|
@@ -9264,6 +9289,20 @@ export class AgentSession {
|
|
|
9264
9289
|
lastError = this.#buildCompactionAuthError();
|
|
9265
9290
|
break;
|
|
9266
9291
|
}
|
|
9292
|
+
if (this.#isCompactionSummarizationTimeoutMessage(message)) {
|
|
9293
|
+
logger.warn(
|
|
9294
|
+
hasMoreCandidates
|
|
9295
|
+
? "Auto-compaction summarization timed out, trying next model"
|
|
9296
|
+
: "Auto-compaction summarization timed out, not retrying same model",
|
|
9297
|
+
{
|
|
9298
|
+
error: message,
|
|
9299
|
+
model: `${candidate.provider}/${candidate.id}`,
|
|
9300
|
+
},
|
|
9301
|
+
);
|
|
9302
|
+
lastError = error;
|
|
9303
|
+
break;
|
|
9304
|
+
}
|
|
9305
|
+
|
|
9267
9306
|
const retryAfterMs = this.#parseRetryAfterMsFromError(message);
|
|
9268
9307
|
const shouldRetry =
|
|
9269
9308
|
retrySettings.enabled &&
|
|
@@ -9281,19 +9320,15 @@ export class AgentSession {
|
|
|
9281
9320
|
|
|
9282
9321
|
// If retry delay is too long (>30s), try next candidate instead of waiting
|
|
9283
9322
|
const maxAcceptableDelayMs = 30_000;
|
|
9284
|
-
if (delayMs > maxAcceptableDelayMs) {
|
|
9285
|
-
|
|
9286
|
-
|
|
9287
|
-
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
|
|
9292
|
-
|
|
9293
|
-
lastError = error;
|
|
9294
|
-
break; // Exit retry loop, continue to next candidate
|
|
9295
|
-
}
|
|
9296
|
-
// No more candidates - we have to wait
|
|
9323
|
+
if (delayMs > maxAcceptableDelayMs && hasMoreCandidates) {
|
|
9324
|
+
logger.warn("Auto-compaction retry delay too long, trying next model", {
|
|
9325
|
+
delayMs,
|
|
9326
|
+
retryAfterMs,
|
|
9327
|
+
error: message,
|
|
9328
|
+
model: `${candidate.provider}/${candidate.id}`,
|
|
9329
|
+
});
|
|
9330
|
+
lastError = error;
|
|
9331
|
+
break; // Exit retry loop, continue to next candidate
|
|
9297
9332
|
}
|
|
9298
9333
|
|
|
9299
9334
|
attempt++;
|
|
@@ -9654,6 +9689,8 @@ export class AgentSession {
|
|
|
9654
9689
|
if (isContextOverflow(message, contextWindow)) return false;
|
|
9655
9690
|
|
|
9656
9691
|
if (this.#isClassifierRefusal(message)) return true;
|
|
9692
|
+
if (this.#isProviderErrorFinishReasonBeforeToolUse(message)) return true;
|
|
9693
|
+
if (this.#isMalformedFunctionCallError(message)) return true;
|
|
9657
9694
|
if (this.#streamInterruptedAfterObservableOutput(message)) return false;
|
|
9658
9695
|
if (this.#isStaleOpenAIResponsesReplayError(message)) return true;
|
|
9659
9696
|
|
|
@@ -9698,6 +9735,17 @@ export class AgentSession {
|
|
|
9698
9735
|
return stopType === "refusal" || stopType === "sensitive";
|
|
9699
9736
|
}
|
|
9700
9737
|
|
|
9738
|
+
#isProviderErrorFinishReasonBeforeToolUse(message: AssistantMessage): boolean {
|
|
9739
|
+
if (!message.errorMessage) return false;
|
|
9740
|
+
if (message.content.some(block => block.type === "toolCall")) return false;
|
|
9741
|
+
return /\bProvider (?:returned error finish_reason|finish_reason:\s*error)\b/i.test(message.errorMessage);
|
|
9742
|
+
}
|
|
9743
|
+
|
|
9744
|
+
#isMalformedFunctionCallError(message: AssistantMessage): boolean {
|
|
9745
|
+
if (!message.errorMessage) return false;
|
|
9746
|
+
return /\bmalformed.?function.?call\b/i.test(message.errorMessage);
|
|
9747
|
+
}
|
|
9748
|
+
|
|
9701
9749
|
#isTransientErrorMessage(errorMessage: string): boolean {
|
|
9702
9750
|
return (
|
|
9703
9751
|
this.#isTransientEnvelopeErrorMessage(errorMessage) || this.#isTransientTransportErrorMessage(errorMessage)
|
|
@@ -9709,6 +9757,10 @@ export class AgentSession {
|
|
|
9709
9757
|
return /anthropic stream envelope error:/i.test(errorMessage) && /before message_start/i.test(errorMessage);
|
|
9710
9758
|
}
|
|
9711
9759
|
|
|
9760
|
+
#isCompactionSummarizationTimeoutMessage(errorMessage: string): boolean {
|
|
9761
|
+
return /\b(?:operation\s+)?timed?\s*out\b|\btimeout\b|\bstream stall\b/i.test(errorMessage);
|
|
9762
|
+
}
|
|
9763
|
+
|
|
9712
9764
|
#isTransientTransportErrorMessage(errorMessage: string): boolean {
|
|
9713
9765
|
// Match: overloaded_error, provider returned error, rate limit, 429, 500, 502, 503, 504,
|
|
9714
9766
|
// service unavailable, provider-suggested retry, network/connection/socket errors, fetch failed,
|
|
@@ -11157,6 +11209,94 @@ export class AgentSession {
|
|
|
11157
11209
|
return { selectedText, cancelled: false };
|
|
11158
11210
|
}
|
|
11159
11211
|
|
|
11212
|
+
async branchFromBtw(
|
|
11213
|
+
question: string,
|
|
11214
|
+
assistantMessage: AssistantMessage,
|
|
11215
|
+
): Promise<{ cancelled: boolean; sessionFile: string | undefined }> {
|
|
11216
|
+
const previousSessionFile = this.sessionFile;
|
|
11217
|
+
if (!this.sessionManager.getSessionFile()) {
|
|
11218
|
+
throw new Error("Cannot branch /btw: session is not persisted");
|
|
11219
|
+
}
|
|
11220
|
+
|
|
11221
|
+
const leafId = this.sessionManager.getLeafId();
|
|
11222
|
+
if (!leafId) {
|
|
11223
|
+
throw new Error("Cannot branch /btw: current session has no leaf");
|
|
11224
|
+
}
|
|
11225
|
+
|
|
11226
|
+
if (
|
|
11227
|
+
this.isBashRunning ||
|
|
11228
|
+
this.isEvalRunning ||
|
|
11229
|
+
this.isCompacting ||
|
|
11230
|
+
this.isGeneratingHandoff ||
|
|
11231
|
+
this.isRetrying
|
|
11232
|
+
) {
|
|
11233
|
+
throw new Error("Cannot branch /btw while session maintenance or user work is still running");
|
|
11234
|
+
}
|
|
11235
|
+
|
|
11236
|
+
if (this.#extensionRunner?.hasHandlers("session_before_branch")) {
|
|
11237
|
+
const result = (await this.#extensionRunner.emit({
|
|
11238
|
+
type: "session_before_branch",
|
|
11239
|
+
entryId: leafId,
|
|
11240
|
+
})) as SessionBeforeBranchResult | undefined;
|
|
11241
|
+
|
|
11242
|
+
if (result?.cancel) {
|
|
11243
|
+
return { cancelled: true, sessionFile: previousSessionFile };
|
|
11244
|
+
}
|
|
11245
|
+
}
|
|
11246
|
+
|
|
11247
|
+
await this.#cancelPostPromptTasks();
|
|
11248
|
+
if (
|
|
11249
|
+
this.isBashRunning ||
|
|
11250
|
+
this.isEvalRunning ||
|
|
11251
|
+
this.isCompacting ||
|
|
11252
|
+
this.isGeneratingHandoff ||
|
|
11253
|
+
this.isRetrying
|
|
11254
|
+
) {
|
|
11255
|
+
throw new Error("Cannot branch /btw while session maintenance or user work is still running");
|
|
11256
|
+
}
|
|
11257
|
+
|
|
11258
|
+
this.#pendingNextTurnMessages = [];
|
|
11259
|
+
this.#scheduledHiddenNextTurnGeneration = undefined;
|
|
11260
|
+
this.agent.replaceQueues([], []);
|
|
11261
|
+
if (this.isStreaming) {
|
|
11262
|
+
await this.abort({ goalReason: "internal", reason: "branching /btw" });
|
|
11263
|
+
this.agent.replaceQueues([], []);
|
|
11264
|
+
}
|
|
11265
|
+
await this.sessionManager.flush();
|
|
11266
|
+
this.#cancelOwnAsyncJobs();
|
|
11267
|
+
|
|
11268
|
+
this.sessionManager.createBranchedSession(leafId);
|
|
11269
|
+
this.sessionManager.appendMessage({
|
|
11270
|
+
role: "user",
|
|
11271
|
+
content: [{ type: "text", text: question }],
|
|
11272
|
+
timestamp: Date.now(),
|
|
11273
|
+
});
|
|
11274
|
+
this.sessionManager.appendMessage(assistantMessage);
|
|
11275
|
+
this.#syncTodoPhasesFromBranch();
|
|
11276
|
+
this.#freshProviderSessionId = undefined;
|
|
11277
|
+
this.#syncAgentSessionId();
|
|
11278
|
+
this.#rekeyHindsightMemoryForCurrentSessionId();
|
|
11279
|
+
this.#rekeyMnemopiMemoryForCurrentSessionId();
|
|
11280
|
+
this.#resetHindsightConversationTrackingIfHindsight();
|
|
11281
|
+
this.#resetMnemopiConversationTrackingIfMnemopi();
|
|
11282
|
+
|
|
11283
|
+
const sessionContext = this.buildDisplaySessionContext();
|
|
11284
|
+
await this.#restoreMCPSelectionsForSessionContext(sessionContext);
|
|
11285
|
+
|
|
11286
|
+
if (this.#extensionRunner) {
|
|
11287
|
+
await this.#extensionRunner.emit({
|
|
11288
|
+
type: "session_branch",
|
|
11289
|
+
previousSessionFile,
|
|
11290
|
+
});
|
|
11291
|
+
}
|
|
11292
|
+
|
|
11293
|
+
this.agent.replaceMessages(sessionContext.messages);
|
|
11294
|
+
this.#advisorRuntime?.reset();
|
|
11295
|
+
this.#closeCodexProviderSessionsForHistoryRewrite();
|
|
11296
|
+
|
|
11297
|
+
return { cancelled: false, sessionFile: this.sessionFile };
|
|
11298
|
+
}
|
|
11299
|
+
|
|
11160
11300
|
// =========================================================================
|
|
11161
11301
|
// Tree Navigation
|
|
11162
11302
|
// =========================================================================
|
|
@@ -45,6 +45,7 @@ const PRIMARY_ARG_KEYS = [
|
|
|
45
45
|
"query",
|
|
46
46
|
"prompt",
|
|
47
47
|
"assignment",
|
|
48
|
+
"note",
|
|
48
49
|
"message",
|
|
49
50
|
"op",
|
|
50
51
|
"name",
|
|
@@ -74,8 +75,16 @@ function lineCount(text: string): number {
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
/** Pick the most informative scalar argument of a tool call. */
|
|
77
|
-
function primaryArg(args: Record<string, unknown> | undefined): string {
|
|
78
|
+
function primaryArg(name: string, args: Record<string, unknown> | undefined): string {
|
|
78
79
|
if (!args || typeof args !== "object") return "";
|
|
80
|
+
// Advisor note is the most informative summary; preserve severity too.
|
|
81
|
+
if (name === "advise") {
|
|
82
|
+
const note = typeof args.note === "string" ? args.note : "";
|
|
83
|
+
const severity = typeof args.severity === "string" ? args.severity : "";
|
|
84
|
+
if (note && severity) return oneLine(`${severity}: ${note}`);
|
|
85
|
+
if (note) return oneLine(note);
|
|
86
|
+
if (severity) return oneLine(severity);
|
|
87
|
+
}
|
|
79
88
|
for (const key of PRIMARY_ARG_KEYS) {
|
|
80
89
|
const value = args[key];
|
|
81
90
|
if (typeof value === "string" && value.length > 0) return oneLine(value);
|
|
@@ -108,7 +117,7 @@ function toolCallLine(
|
|
|
108
117
|
result: ToolResultMessage | undefined,
|
|
109
118
|
includeToolIntent?: boolean,
|
|
110
119
|
): string {
|
|
111
|
-
const head = `→ ${name}(${primaryArg(args)})`;
|
|
120
|
+
const head = `→ ${name}(${primaryArg(name, args)})`;
|
|
112
121
|
let base: string;
|
|
113
122
|
if (!result) {
|
|
114
123
|
base = `${head} ⇒ pending`;
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
* estimate (`estimateInlineSavings`) so the two can never disagree.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { countTokens } from "@oh-my-pi/pi-agent-core";
|
|
17
18
|
import type { Context, ImageContent, Model, TextContent, ToolResultMessage, UserMessage } from "@oh-my-pi/pi-ai";
|
|
18
|
-
import { countTokens } from "@oh-my-pi/pi-natives";
|
|
19
19
|
import * as snapcompact from "@oh-my-pi/snapcompact";
|
|
20
20
|
import contextFramesNote from "../prompts/system/snapcompact-context-frames-note.md" with { type: "text" };
|
|
21
21
|
import contextStub from "../prompts/system/snapcompact-context-stub.md" with { type: "text" };
|
|
@@ -240,13 +240,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
240
240
|
allowArgs: true,
|
|
241
241
|
handleTui: async (command, runtime) => {
|
|
242
242
|
const hadArgs = !!command.args;
|
|
243
|
-
// Capture state BEFORE the call: when plan mode is already active,
|
|
244
|
-
// handlePlanModeCommand may exit it (on confirmed exit) or leave it on (on cancel
|
|
245
|
-
// or warning). In every "already active" case the typed args are NOT consumed,
|
|
246
|
-
// so preserve them in history regardless of the user's confirm/cancel choice.
|
|
247
|
-
const wasPlanModeEnabled = runtime.ctx.planModeEnabled;
|
|
248
243
|
await runtime.ctx.handlePlanModeCommand(command.args || undefined);
|
|
249
|
-
if (hadArgs
|
|
244
|
+
if (hadArgs) {
|
|
250
245
|
runtime.ctx.editor.addToHistory(command.text);
|
|
251
246
|
}
|
|
252
247
|
runtime.ctx.editor.setText("");
|
|
@@ -275,10 +270,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
275
270
|
allowArgs: true,
|
|
276
271
|
handleTui: async (command, runtime) => {
|
|
277
272
|
const hadArgs = !!command.args;
|
|
278
|
-
// Capture state BEFORE the call (see /plan above for rationale).
|
|
279
|
-
const wasGoalModeEnabled = runtime.ctx.goalModeEnabled;
|
|
280
273
|
await runtime.ctx.handleGoalModeCommand(command.args || undefined);
|
|
281
|
-
if (hadArgs
|
|
274
|
+
if (hadArgs) {
|
|
282
275
|
runtime.ctx.editor.addToHistory(command.text);
|
|
283
276
|
}
|
|
284
277
|
runtime.ctx.editor.setText("");
|
|
@@ -308,7 +301,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
308
301
|
{
|
|
309
302
|
name: "model",
|
|
310
303
|
aliases: ["models"],
|
|
311
|
-
description: "
|
|
304
|
+
description: "Switch model for this session",
|
|
312
305
|
acpDescription: "Show current model selection",
|
|
313
306
|
handle: async (command, runtime) => {
|
|
314
307
|
if (command.args) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Inputs used to decide whether the optional startup splash may run for this process. */
|
|
2
|
+
export interface StartupSplashDecisionOptions {
|
|
3
|
+
readonly configured: boolean;
|
|
4
|
+
readonly isInteractive: boolean;
|
|
5
|
+
readonly resuming: boolean;
|
|
6
|
+
readonly quiet: boolean;
|
|
7
|
+
readonly timing: boolean;
|
|
8
|
+
readonly stdinIsTTY: boolean | undefined;
|
|
9
|
+
readonly stdoutIsTTY: boolean | undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Returns true only for explicitly enabled, normal interactive TTY startup. */
|
|
13
|
+
export function shouldShowStartupSplash(options: StartupSplashDecisionOptions): boolean {
|
|
14
|
+
if (!options.configured) return false;
|
|
15
|
+
if (!options.isInteractive) return false;
|
|
16
|
+
if (options.resuming || options.quiet) return false;
|
|
17
|
+
if (options.timing) return false;
|
|
18
|
+
return options.stdinIsTTY === true && options.stdoutIsTTY === true;
|
|
19
|
+
}
|
package/src/task/executor.ts
CHANGED
|
@@ -516,6 +516,7 @@ export function finalizeSubprocessOutput(args: FinalizeSubprocessOutputArgs): Fi
|
|
|
516
516
|
const { yieldItems, reportFindings, doneAborted, signalAborted, outputSchema } = args;
|
|
517
517
|
let abortedViaYield = false;
|
|
518
518
|
const hasYield = Array.isArray(yieldItems) && yieldItems.length > 0;
|
|
519
|
+
const hadFailureBeforeYield = exitCode !== 0 && stderr.trim().length > 0;
|
|
519
520
|
|
|
520
521
|
if (hasYield) {
|
|
521
522
|
const lastYield = yieldItems[yieldItems.length - 1];
|
|
@@ -553,12 +554,16 @@ export function finalizeSubprocessOutput(args: FinalizeSubprocessOutputArgs): Fi
|
|
|
553
554
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
554
555
|
rawOutput = `{"error":"Failed to serialize yield data: ${errorMessage}"}`;
|
|
555
556
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
557
|
+
if (!hadFailureBeforeYield) {
|
|
558
|
+
exitCode = 0;
|
|
559
|
+
stderr = overridden
|
|
560
|
+
? SUBAGENT_WARNING_SCHEMA_OVERRIDDEN
|
|
561
|
+
: schemaError
|
|
562
|
+
? `invalid output schema: ${schemaError}`
|
|
563
|
+
: "";
|
|
564
|
+
} else if (!stderr) {
|
|
565
|
+
stderr = "Subagent failed after yielding a result.";
|
|
566
|
+
}
|
|
562
567
|
}
|
|
563
568
|
}
|
|
564
569
|
}
|
package/src/task/types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import type { Usage } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import { $env } from "@oh-my-pi/pi-utils";
|
|
4
|
-
import {
|
|
4
|
+
import { type } from "arktype";
|
|
5
5
|
import type { AgentSessionEvent } from "../session/agent-session";
|
|
6
6
|
import type { NestedRepoPatch } from "./worktree";
|
|
7
7
|
|
|
@@ -78,37 +78,23 @@ export interface SubagentLifecyclePayload {
|
|
|
78
78
|
export const ROLE_LABEL_MAX = 80;
|
|
79
79
|
/** Schema bound on the raw `role` input, before it is label-normalized at every use site. */
|
|
80
80
|
export const ROLE_INPUT_MAX = 256;
|
|
81
|
+
const ROLE_INPUT_SCHEMA = `string <= ${ROLE_INPUT_MAX}` as const;
|
|
81
82
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
),
|
|
98
|
-
assignment: z.string().describe("the work; self-contained instructions"),
|
|
99
|
-
};
|
|
100
|
-
const isolatedShape = {
|
|
101
|
-
isolated: z.boolean().optional().describe("run in isolated env; returns patches"),
|
|
102
|
-
};
|
|
103
|
-
const agentShape = {
|
|
104
|
-
agent: z.string().describe("agent type to spawn"),
|
|
105
|
-
};
|
|
106
|
-
const contextShape = {
|
|
107
|
-
context: z.string().describe("shared background prepended to each assignment"),
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
export const taskItemSchema = z.object(taskItemShape);
|
|
111
|
-
const taskItemSchemaIsolated = z.object({ ...taskItemShape, ...isolatedShape });
|
|
83
|
+
export const taskItemSchema = type({
|
|
84
|
+
"id?": "string",
|
|
85
|
+
"description?": "string",
|
|
86
|
+
"role?": ROLE_INPUT_SCHEMA,
|
|
87
|
+
assignment: "string",
|
|
88
|
+
"+": "delete",
|
|
89
|
+
});
|
|
90
|
+
const taskItemSchemaIsolated = type({
|
|
91
|
+
"id?": "string",
|
|
92
|
+
"description?": "string",
|
|
93
|
+
"role?": ROLE_INPUT_SCHEMA,
|
|
94
|
+
assignment: "string",
|
|
95
|
+
"isolated?": "boolean",
|
|
96
|
+
"+": "delete",
|
|
97
|
+
});
|
|
112
98
|
|
|
113
99
|
/** Single task item. Fields are optional defensively: args stream in token by token. */
|
|
114
100
|
export interface TaskItem {
|
|
@@ -124,17 +110,34 @@ export interface TaskItem {
|
|
|
124
110
|
isolated?: boolean;
|
|
125
111
|
}
|
|
126
112
|
|
|
127
|
-
export const taskSchema =
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
113
|
+
export const taskSchema = type({
|
|
114
|
+
agent: "string",
|
|
115
|
+
"id?": "string",
|
|
116
|
+
"description?": "string",
|
|
117
|
+
"role?": ROLE_INPUT_SCHEMA,
|
|
118
|
+
assignment: "string",
|
|
119
|
+
"isolated?": "boolean",
|
|
120
|
+
"+": "delete",
|
|
121
|
+
});
|
|
122
|
+
const taskSchemaNoIsolation = type({
|
|
123
|
+
agent: "string",
|
|
124
|
+
"id?": "string",
|
|
125
|
+
"description?": "string",
|
|
126
|
+
"role?": ROLE_INPUT_SCHEMA,
|
|
127
|
+
assignment: "string",
|
|
128
|
+
"+": "delete",
|
|
129
|
+
});
|
|
130
|
+
const taskSchemaBatch = type({
|
|
131
|
+
agent: "string",
|
|
132
|
+
context: "string",
|
|
133
|
+
tasks: taskItemSchemaIsolated.array(),
|
|
134
|
+
"+": "delete",
|
|
133
135
|
});
|
|
134
|
-
const taskSchemaBatchNoIsolation =
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
tasks:
|
|
136
|
+
const taskSchemaBatchNoIsolation = type({
|
|
137
|
+
agent: "string",
|
|
138
|
+
context: "string",
|
|
139
|
+
tasks: taskItemSchema.array(),
|
|
140
|
+
"+": "delete",
|
|
138
141
|
});
|
|
139
142
|
const ALL_TASK_SCHEMAS = [taskSchema, taskSchemaNoIsolation, taskSchemaBatch, taskSchemaBatchNoIsolation] as const;
|
|
140
143
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
2
|
-
import {
|
|
2
|
+
import type { Tool as AiTool } from "@oh-my-pi/pi-ai";
|
|
3
|
+
import { toolWireSchema } from "@oh-my-pi/pi-ai/utils/schema";
|
|
3
4
|
|
|
4
5
|
// ─── Generic Tool Discovery Types ────────────────────────────────────────────
|
|
5
6
|
|
|
@@ -65,8 +66,13 @@ export function isMCPToolName(name: string): boolean {
|
|
|
65
66
|
return name.startsWith("mcp__");
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
function getSchemaPropertyKeys(
|
|
69
|
-
|
|
69
|
+
function getSchemaPropertyKeys(tool: Pick<AiTool, "name" | "description" | "parameters">): string[] {
|
|
70
|
+
let parameters: unknown = tool.parameters;
|
|
71
|
+
try {
|
|
72
|
+
parameters = toolWireSchema(tool as AiTool);
|
|
73
|
+
} catch {
|
|
74
|
+
// Schema may contain functions or cycles; fall back to the raw shape.
|
|
75
|
+
}
|
|
70
76
|
if (!parameters || typeof parameters !== "object" || Array.isArray(parameters)) return [];
|
|
71
77
|
const properties = (parameters as { properties?: unknown }).properties;
|
|
72
78
|
if (!properties || typeof properties !== "object" || Array.isArray(properties)) return [];
|
|
@@ -149,7 +155,14 @@ export function getDiscoverableTool(
|
|
|
149
155
|
source,
|
|
150
156
|
serverName: typeof toolRecord.mcpServerName === "string" ? toolRecord.mcpServerName : undefined,
|
|
151
157
|
mcpToolName: typeof toolRecord.mcpToolName === "string" ? toolRecord.mcpToolName : undefined,
|
|
152
|
-
schemaKeys:
|
|
158
|
+
schemaKeys:
|
|
159
|
+
toolRecord.parameters === undefined
|
|
160
|
+
? []
|
|
161
|
+
: getSchemaPropertyKeys({
|
|
162
|
+
name: tool.name,
|
|
163
|
+
description: rawDescription,
|
|
164
|
+
parameters: toolRecord.parameters as AiTool["parameters"],
|
|
165
|
+
}),
|
|
153
166
|
};
|
|
154
167
|
}
|
|
155
168
|
|
package/src/tools/ask.ts
CHANGED
|
@@ -19,7 +19,7 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
19
19
|
import type { ToolExample } from "@oh-my-pi/pi-ai";
|
|
20
20
|
import { type Component, Markdown, type MarkdownTheme, renderInlineMarkdown, TERMINAL, Text } from "@oh-my-pi/pi-tui";
|
|
21
21
|
import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
22
|
-
import {
|
|
22
|
+
import { type as arkType } from "arktype";
|
|
23
23
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
24
24
|
import type { ExtensionUISelectItem } from "../extensibility/extensions";
|
|
25
25
|
import { getMarkdownTheme, type Theme, theme } from "../modes/theme/theme";
|
|
@@ -34,24 +34,24 @@ import { ToolAbortError } from "./tool-errors";
|
|
|
34
34
|
// Types
|
|
35
35
|
// =============================================================================
|
|
36
36
|
|
|
37
|
-
const OptionItem =
|
|
38
|
-
label:
|
|
39
|
-
description:
|
|
37
|
+
const OptionItem = arkType({
|
|
38
|
+
label: arkType("string").describe("display label"),
|
|
39
|
+
"description?": arkType("string").describe("optional explanatory text displayed below the label"),
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
const QuestionItem =
|
|
43
|
-
id:
|
|
44
|
-
question:
|
|
45
|
-
options:
|
|
46
|
-
multi:
|
|
47
|
-
recommended:
|
|
42
|
+
const QuestionItem = arkType({
|
|
43
|
+
id: arkType("string").describe("question id"),
|
|
44
|
+
question: arkType("string").describe("question text"),
|
|
45
|
+
options: OptionItem.array().describe("available options"),
|
|
46
|
+
"multi?": arkType("boolean").describe("allow multiple selections"),
|
|
47
|
+
"recommended?": arkType("number").describe("recommended option index"),
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
const askSchema =
|
|
51
|
-
questions:
|
|
50
|
+
const askSchema = arkType({
|
|
51
|
+
questions: QuestionItem.array().atLeastLength(1).describe("questions to ask"),
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
-
export type AskToolInput =
|
|
54
|
+
export type AskToolInput = typeof askSchema.infer;
|
|
55
55
|
|
|
56
56
|
/** Result for a single question */
|
|
57
57
|
export interface QuestionResult {
|
|
@@ -424,7 +424,7 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
424
424
|
readonly parameters = askSchema;
|
|
425
425
|
readonly strict = true;
|
|
426
426
|
|
|
427
|
-
readonly examples: readonly ToolExample<
|
|
427
|
+
readonly examples: readonly ToolExample<typeof askSchema.infer>[] = [
|
|
428
428
|
{
|
|
429
429
|
caption: "Single question",
|
|
430
430
|
call: {
|