@oh-my-pi/pi-coding-agent 17.2.13 → 17.2.14
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 +6 -0
- package/dist/{CHANGELOG-d8xh7keh.md → CHANGELOG-za420td8.md} +6 -0
- package/dist/cli.js +3336 -3327
- package/dist/types/config/settings-schema.d.ts +10 -0
- package/dist/types/session/agent-session-types.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +2 -0
- package/dist/types/session/session-tools.d.ts +11 -0
- package/dist/types/tools/builtin-names.d.ts +1 -2
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/think.d.ts +38 -0
- package/package.json +13 -13
- package/src/cli/gallery-fixtures/agentic.ts +16 -0
- package/src/config/settings-schema.ts +11 -0
- package/src/modes/controllers/selector-controller.ts +5 -0
- package/src/prompts/system/system-prompt.md +9 -0
- package/src/sdk.ts +7 -1
- package/src/session/agent-session-types.ts +2 -0
- package/src/session/agent-session.ts +25 -0
- package/src/session/session-tools.ts +35 -0
- package/src/tools/builtin-names.ts +1 -2
- package/src/tools/index.ts +8 -0
- package/src/tools/renderers.ts +2 -0
- package/src/tools/think.ts +62 -0
|
@@ -1026,6 +1026,16 @@ export declare const SETTINGS_SCHEMA: {
|
|
|
1026
1026
|
readonly description: "Instruct upstream providers to completely omit thinking summaries from responses (where supported)";
|
|
1027
1027
|
};
|
|
1028
1028
|
};
|
|
1029
|
+
readonly externalThinking: {
|
|
1030
|
+
readonly type: "boolean";
|
|
1031
|
+
readonly default: false;
|
|
1032
|
+
readonly ui: {
|
|
1033
|
+
readonly tab: "model";
|
|
1034
|
+
readonly group: "Thinking";
|
|
1035
|
+
readonly label: "External Thinking";
|
|
1036
|
+
readonly description: "Use a private think tool and send reasoning effort off to GPT Responses models";
|
|
1037
|
+
};
|
|
1038
|
+
};
|
|
1029
1039
|
readonly "model.loopGuard.enabled": {
|
|
1030
1040
|
readonly type: "boolean";
|
|
1031
1041
|
readonly default: true;
|
|
@@ -138,6 +138,8 @@ export interface AgentSessionConfig {
|
|
|
138
138
|
createMemoryTools?: () => Promise<AgentTool[]>;
|
|
139
139
|
/** Creates the built-in `computer` tool for session-scoped runtime enablement (see {@link AgentSession.setComputerToolEnabled}). */
|
|
140
140
|
createComputerTool?: () => Promise<AgentTool | null>;
|
|
141
|
+
/** Creates the private `think` scratchpad tool for runtime setting changes. */
|
|
142
|
+
createThinkTool?: () => Promise<AgentTool | null>;
|
|
141
143
|
/** Creates the built-in `inspect_image` tool for session-scoped runtime enablement (see {@link AgentSession.setInspectImageMode}). */
|
|
142
144
|
createInspectImageTool?: () => Promise<AgentTool | null>;
|
|
143
145
|
/** Model registry for API key resolution and model discovery. */
|
|
@@ -331,6 +331,8 @@ export declare class AgentSession {
|
|
|
331
331
|
* tool (e.g. restricted child sessions have no factory).
|
|
332
332
|
*/
|
|
333
333
|
setComputerToolEnabled(enabled: boolean): Promise<boolean>;
|
|
334
|
+
/** Applies the external-thinking setting to the private scratchpad tool immediately. */
|
|
335
|
+
setThinkToolEnabled(enabled: boolean): Promise<boolean>;
|
|
334
336
|
/**
|
|
335
337
|
* Session-scoped inspect_image mode (`/vision`). `auto` clears the override
|
|
336
338
|
* and returns to the persisted `inspect_image.mode` setting; `on`/`off`
|
|
@@ -43,6 +43,8 @@ interface SessionToolsOptions {
|
|
|
43
43
|
toolRegistry?: Map<string, AgentTool>;
|
|
44
44
|
createVibeTools?: () => AgentTool[];
|
|
45
45
|
createComputerTool?: () => Promise<AgentTool | null>;
|
|
46
|
+
/** Creates the private `think` scratchpad tool for runtime setting changes. */
|
|
47
|
+
createThinkTool?: () => Promise<AgentTool | null>;
|
|
46
48
|
/** Creates the built-in `inspect_image` tool for session-scoped runtime enablement (see {@link SessionTools.setInspectImageMode}). */
|
|
47
49
|
createInspectImageTool?: () => Promise<AgentTool | null>;
|
|
48
50
|
builtInToolNames?: Iterable<string>;
|
|
@@ -232,6 +234,15 @@ export declare class SessionTools {
|
|
|
232
234
|
* tool (e.g. restricted child sessions have no factory).
|
|
233
235
|
*/
|
|
234
236
|
setComputerToolEnabled(enabled: boolean): Promise<boolean>;
|
|
237
|
+
/**
|
|
238
|
+
* Session-scoped enable/disable for the private `think` scratchpad tool.
|
|
239
|
+
*
|
|
240
|
+
* Enabling constructs the tool once and refreshes the model's tool contract;
|
|
241
|
+
* disabling removes it from the active set while preserving its registry entry.
|
|
242
|
+
*
|
|
243
|
+
* @returns false when enabling was requested but this session cannot build the tool.
|
|
244
|
+
*/
|
|
245
|
+
setThinkToolEnabled(enabled: boolean): Promise<boolean>;
|
|
235
246
|
/** Current effective inspect_image state for `/vision status`. */
|
|
236
247
|
inspectImageState(): {
|
|
237
248
|
mode: InspectImageMode;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export declare const BUILTIN_TOOL_NAMES: readonly ["read", "bash", "edit", "ast_grep", "ast_edit", "ask", "debug", "eval", "github", "glob", "grep", "lsp", "inspect_image", "browser", "computer", "checkpoint", "rewind", "security_scan", "task", "hub", "todo", "web_search", "write", "memory_edit", "retain", "recall", "reflect", "learn", "manage_skill"];
|
|
2
2
|
export type BuiltinToolName = (typeof BUILTIN_TOOL_NAMES)[number];
|
|
3
|
-
|
|
4
|
-
export declare const HIDDEN_TOOL_NAMES: readonly ["yield", "goal"];
|
|
3
|
+
export declare const HIDDEN_TOOL_NAMES: readonly ["yield", "goal", "think"];
|
|
5
4
|
export type HiddenToolName = (typeof HIDDEN_TOOL_NAMES)[number];
|
|
6
5
|
/** Return the canonical tool name for current and legacy built-in tool IDs. */
|
|
7
6
|
export declare function normalizeToolName(name: string): string;
|
|
@@ -67,6 +67,7 @@ export * from "./report-tool-issue.js";
|
|
|
67
67
|
export * from "./resolve.js";
|
|
68
68
|
export * from "./review.js";
|
|
69
69
|
export * from "./security-scan.js";
|
|
70
|
+
export * from "./think.js";
|
|
70
71
|
export * from "./todo.js";
|
|
71
72
|
export * from "./tts.js";
|
|
72
73
|
export * from "./vibe.js";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
|
|
2
|
+
import { type Component } from "@oh-my-pi/pi-tui";
|
|
3
|
+
import type { RenderResultOptions } from "../extensibility/custom-tools/types.js";
|
|
4
|
+
import { type Theme } from "../modes/theme/theme.js";
|
|
5
|
+
declare const thinkSchema: import("@oh-my-pi/omptype").FluentType<{
|
|
6
|
+
thoughts: string;
|
|
7
|
+
}, {
|
|
8
|
+
thoughts: string;
|
|
9
|
+
}>;
|
|
10
|
+
type ThinkParams = typeof thinkSchema.infer;
|
|
11
|
+
export type ThinkRenderArgs = {
|
|
12
|
+
thoughts?: string;
|
|
13
|
+
};
|
|
14
|
+
export declare const thinkToolRenderer: {
|
|
15
|
+
inline: boolean;
|
|
16
|
+
renderCall(args: ThinkRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component;
|
|
17
|
+
renderResult(): Component;
|
|
18
|
+
};
|
|
19
|
+
interface ThinkToolDetails {
|
|
20
|
+
recorded: true;
|
|
21
|
+
}
|
|
22
|
+
/** Records private intermediate reasoning while native GPT reasoning is disabled. */
|
|
23
|
+
export declare class ThinkTool implements AgentTool<typeof thinkSchema, ThinkToolDetails> {
|
|
24
|
+
readonly name = "think";
|
|
25
|
+
readonly approval: "read";
|
|
26
|
+
readonly label = "Think";
|
|
27
|
+
readonly summary = "Record private intermediate reasoning before answering";
|
|
28
|
+
readonly description = "Use this private scratchpad to plan, derive, or check work before answering. Record only materially new reasoning. The user does not see this tool activity.";
|
|
29
|
+
readonly parameters: import("@oh-my-pi/omptype").FluentType<{
|
|
30
|
+
thoughts: string;
|
|
31
|
+
}, {
|
|
32
|
+
thoughts: string;
|
|
33
|
+
}>;
|
|
34
|
+
readonly strict = true;
|
|
35
|
+
readonly intent: "omit";
|
|
36
|
+
execute(_toolCallId: string, _params: ThinkParams): Promise<AgentToolResult<ThinkToolDetails>>;
|
|
37
|
+
}
|
|
38
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
4
|
-
"version": "17.2.
|
|
4
|
+
"version": "17.2.14",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -50,18 +50,18 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@babel/parser": "^7.29.7",
|
|
53
|
-
"@oh-my-pi/hashline": "17.2.
|
|
54
|
-
"@oh-my-pi/omp-stats": "17.2.
|
|
55
|
-
"@oh-my-pi/omptype": "17.2.
|
|
56
|
-
"@oh-my-pi/pi-agent-core": "17.2.
|
|
57
|
-
"@oh-my-pi/pi-ai": "17.2.
|
|
58
|
-
"@oh-my-pi/pi-catalog": "17.2.
|
|
59
|
-
"@oh-my-pi/pi-mnemopi": "17.2.
|
|
60
|
-
"@oh-my-pi/pi-natives": "17.2.
|
|
61
|
-
"@oh-my-pi/pi-tui": "17.2.
|
|
62
|
-
"@oh-my-pi/pi-utils": "17.2.
|
|
63
|
-
"@oh-my-pi/pi-wire": "17.2.
|
|
64
|
-
"@oh-my-pi/snapcompact": "17.2.
|
|
53
|
+
"@oh-my-pi/hashline": "17.2.14",
|
|
54
|
+
"@oh-my-pi/omp-stats": "17.2.14",
|
|
55
|
+
"@oh-my-pi/omptype": "17.2.14",
|
|
56
|
+
"@oh-my-pi/pi-agent-core": "17.2.14",
|
|
57
|
+
"@oh-my-pi/pi-ai": "17.2.14",
|
|
58
|
+
"@oh-my-pi/pi-catalog": "17.2.14",
|
|
59
|
+
"@oh-my-pi/pi-mnemopi": "17.2.14",
|
|
60
|
+
"@oh-my-pi/pi-natives": "17.2.14",
|
|
61
|
+
"@oh-my-pi/pi-tui": "17.2.14",
|
|
62
|
+
"@oh-my-pi/pi-utils": "17.2.14",
|
|
63
|
+
"@oh-my-pi/pi-wire": "17.2.14",
|
|
64
|
+
"@oh-my-pi/snapcompact": "17.2.14",
|
|
65
65
|
"@opentelemetry/api": "^1.9.1",
|
|
66
66
|
"@opentelemetry/api-logs": "^0.220.0",
|
|
67
67
|
"@opentelemetry/context-async-hooks": "^2.9.0",
|
|
@@ -361,6 +361,22 @@ export const agenticFixtures: Record<string, GalleryFixture> = {
|
|
|
361
361
|
},
|
|
362
362
|
},
|
|
363
363
|
|
|
364
|
+
think: {
|
|
365
|
+
label: "Think",
|
|
366
|
+
// Streaming: scratchpad text still arriving.
|
|
367
|
+
streamingArgs: {
|
|
368
|
+
thoughts: "The retry loop re-reads the config after every failure — that explains the doubled latency.",
|
|
369
|
+
},
|
|
370
|
+
args: {
|
|
371
|
+
thoughts:
|
|
372
|
+
"The retry loop re-reads the config after every failure — that explains the doubled latency. Cache the parsed config outside the loop, then re-check the invalidation path before answering.",
|
|
373
|
+
},
|
|
374
|
+
result: {
|
|
375
|
+
content: [{ type: "text", text: "------" }],
|
|
376
|
+
details: { recorded: true },
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
|
|
364
380
|
hub_jobs: {
|
|
365
381
|
label: "Hub jobs",
|
|
366
382
|
renderer: "hub",
|
|
@@ -1139,6 +1139,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
1139
1139
|
},
|
|
1140
1140
|
},
|
|
1141
1141
|
|
|
1142
|
+
externalThinking: {
|
|
1143
|
+
type: "boolean",
|
|
1144
|
+
default: false,
|
|
1145
|
+
ui: {
|
|
1146
|
+
tab: "model",
|
|
1147
|
+
group: "Thinking",
|
|
1148
|
+
label: "External Thinking",
|
|
1149
|
+
description: "Use a private think tool and send reasoning effort off to GPT Responses models",
|
|
1150
|
+
},
|
|
1151
|
+
},
|
|
1152
|
+
|
|
1142
1153
|
"model.loopGuard.enabled": {
|
|
1143
1154
|
type: "boolean",
|
|
1144
1155
|
default: true,
|
|
@@ -479,6 +479,11 @@ export class SelectorController {
|
|
|
479
479
|
this.ctx.showError(`Failed to apply vision mode: ${err}`);
|
|
480
480
|
});
|
|
481
481
|
break;
|
|
482
|
+
case "externalThinking":
|
|
483
|
+
void this.ctx.session.setThinkToolEnabled(value as boolean).catch(err => {
|
|
484
|
+
this.ctx.showError(`Failed to apply external thinking: ${err}`);
|
|
485
|
+
});
|
|
486
|
+
break;
|
|
482
487
|
|
|
483
488
|
case "autocompleteMaxVisible":
|
|
484
489
|
this.ctx.editor.setAutocompleteMaxVisible(typeof value === "number" ? value : Number(value));
|
|
@@ -101,6 +101,15 @@ Invalid args return the schema in the error — fix and retry
|
|
|
101
101
|
TOOL POLICY
|
|
102
102
|
==============
|
|
103
103
|
|
|
104
|
+
{{#has tools "think"}}
|
|
105
|
+
# Reasoning
|
|
106
|
+
`{{toolRefs.think}}` is your scratchpad and it is where your reasoning actually happens — whatever you do not write there, you have not worked out. Its content is private; the user never sees it.
|
|
107
|
+
- MUST call `{{toolRefs.think}}` before the first action of a turn, and again before any step that is expensive to undo: an edit, a destructive command, a final answer.
|
|
108
|
+
- Restate what is actually being asked and the constraints given. Split it into ordered sub-problems and solve each explicitly, writing the intermediate result instead of jumping to the conclusion. When a step splits into cases, enumerate them and resolve each.
|
|
109
|
+
- Then check the work: verify each claim against the constraints, test one boundary or degenerate case, and look specifically for the error you would most plausibly have made. If a check fails, redo that step — NEVER patch the conclusion.
|
|
110
|
+
- Call again only for materially new state: a tool result that changes the plan, a failed check, a sub-problem you had not opened. NEVER use it to narrate progress or restate what you already recorded.
|
|
111
|
+
{{/has}}
|
|
112
|
+
|
|
104
113
|
# General
|
|
105
114
|
Use tools whenever they improve correctness, completeness, or grounding.
|
|
106
115
|
- SHOULD resolve prerequisites before acting.
|
package/src/sdk.ts
CHANGED
|
@@ -3237,7 +3237,12 @@ async function createAgentSessionScoped(options: CreateAgentSessionOptions): Pro
|
|
|
3237
3237
|
});
|
|
3238
3238
|
}
|
|
3239
3239
|
}
|
|
3240
|
-
|
|
3240
|
+
const externalThinking =
|
|
3241
|
+
settings.get("externalThinking") && agent.state.tools.some(tool => tool.name === "think");
|
|
3242
|
+
return settingsAwareStreamFn(streamModel, context, {
|
|
3243
|
+
...streamOptions,
|
|
3244
|
+
forceReasoningOff: externalThinking || streamOptions?.forceReasoningOff,
|
|
3245
|
+
});
|
|
3241
3246
|
},
|
|
3242
3247
|
cursorExecHandlers,
|
|
3243
3248
|
getCursorTools: () => (toolSession.xdev ? listXdevTools(toolSession.xdev) : []),
|
|
@@ -3393,6 +3398,7 @@ async function createAgentSessionScoped(options: CreateAgentSessionOptions): Pro
|
|
|
3393
3398
|
createComputerTool: restrictToolNames
|
|
3394
3399
|
? undefined
|
|
3395
3400
|
: async () => (await BUILTIN_TOOLS.computer(toolSession)) ?? null,
|
|
3401
|
+
createThinkTool: async () => (await HIDDEN_TOOLS.think(toolSession)) ?? null,
|
|
3396
3402
|
createInspectImageTool: restrictToolNames
|
|
3397
3403
|
? undefined
|
|
3398
3404
|
: async () => (await BUILTIN_TOOLS.inspect_image(toolSession)) ?? null,
|
|
@@ -164,6 +164,8 @@ export interface AgentSessionConfig {
|
|
|
164
164
|
createMemoryTools?: () => Promise<AgentTool[]>;
|
|
165
165
|
/** Creates the built-in `computer` tool for session-scoped runtime enablement (see {@link AgentSession.setComputerToolEnabled}). */
|
|
166
166
|
createComputerTool?: () => Promise<AgentTool | null>;
|
|
167
|
+
/** Creates the private `think` scratchpad tool for runtime setting changes. */
|
|
168
|
+
createThinkTool?: () => Promise<AgentTool | null>;
|
|
167
169
|
/** Creates the built-in `inspect_image` tool for session-scoped runtime enablement (see {@link AgentSession.setInspectImageMode}). */
|
|
168
170
|
createInspectImageTool?: () => Promise<AgentTool | null>;
|
|
169
171
|
/** Model registry for API key resolution and model discovery. */
|
|
@@ -1241,6 +1241,7 @@ export class AgentSession {
|
|
|
1241
1241
|
toolRegistry: config.toolRegistry,
|
|
1242
1242
|
createVibeTools: config.createVibeTools,
|
|
1243
1243
|
createComputerTool: config.createComputerTool,
|
|
1244
|
+
createThinkTool: config.createThinkTool,
|
|
1244
1245
|
createInspectImageTool: config.createInspectImageTool,
|
|
1245
1246
|
builtInToolNames: config.builtInToolNames,
|
|
1246
1247
|
mcpManagerToolNames: config.mcpManagerToolNames,
|
|
@@ -4419,6 +4420,11 @@ export class AgentSession {
|
|
|
4419
4420
|
return this.#tools.setComputerToolEnabled(enabled);
|
|
4420
4421
|
}
|
|
4421
4422
|
|
|
4423
|
+
/** Applies the external-thinking setting to the private scratchpad tool immediately. */
|
|
4424
|
+
setThinkToolEnabled(enabled: boolean): Promise<boolean> {
|
|
4425
|
+
return this.#tools.setThinkToolEnabled(enabled);
|
|
4426
|
+
}
|
|
4427
|
+
|
|
4422
4428
|
/**
|
|
4423
4429
|
* Session-scoped inspect_image mode (`/vision`). `auto` clears the override
|
|
4424
4430
|
* and returns to the persisted `inspect_image.mode` setting; `on`/`off`
|
|
@@ -5176,6 +5182,18 @@ export class AgentSession {
|
|
|
5176
5182
|
|
|
5177
5183
|
// Skip eager preludes when the user has already queued a directive
|
|
5178
5184
|
const hasPendingUserDirective = this.#toolChoiceQueue.inspect().includes("user-force");
|
|
5185
|
+
const activeModel = this.agent.state.model;
|
|
5186
|
+
const externalThinkingToolChoice =
|
|
5187
|
+
!options?.synthetic &&
|
|
5188
|
+
!hasPendingUserDirective &&
|
|
5189
|
+
this.settings.get("externalThinking") &&
|
|
5190
|
+
this.getEnabledToolNames().includes("think") &&
|
|
5191
|
+
activeModel &&
|
|
5192
|
+
(activeModel.api === "openai-responses" ||
|
|
5193
|
+
activeModel.api === "azure-openai-responses" ||
|
|
5194
|
+
activeModel.api === "openai-codex-responses")
|
|
5195
|
+
? buildNamedToolChoice("think", activeModel)
|
|
5196
|
+
: undefined;
|
|
5179
5197
|
const eagerTodoPrelude =
|
|
5180
5198
|
!options?.synthetic && !hasPendingUserDirective ? this.#todo.createEagerTodoPrelude(expandedText) : undefined;
|
|
5181
5199
|
const eagerTaskPrelude =
|
|
@@ -5193,6 +5211,12 @@ export class AgentSession {
|
|
|
5193
5211
|
: undefined;
|
|
5194
5212
|
|
|
5195
5213
|
const promptAttribution = options?.attribution ?? (options?.synthetic ? "agent" : "user");
|
|
5214
|
+
if (externalThinkingToolChoice) {
|
|
5215
|
+
this.#toolChoiceQueue.pushOnce(externalThinkingToolChoice, {
|
|
5216
|
+
label: "external-thinking",
|
|
5217
|
+
now: true,
|
|
5218
|
+
});
|
|
5219
|
+
}
|
|
5196
5220
|
const message = options?.synthetic
|
|
5197
5221
|
? { role: "developer" as const, content: userContent, attribution: promptAttribution, timestamp: Date.now() }
|
|
5198
5222
|
: { role: "user" as const, content: userContent, attribution: promptAttribution, timestamp: Date.now() };
|
|
@@ -5223,6 +5247,7 @@ export class AgentSession {
|
|
|
5223
5247
|
// Clean up residual eager-todo directive if the prompt never consumed it
|
|
5224
5248
|
// (e.g., compaction aborted, validation failed).
|
|
5225
5249
|
this.#toolChoiceQueue.removeByLabel("eager-todo");
|
|
5250
|
+
this.#toolChoiceQueue.removeByLabel("external-thinking");
|
|
5226
5251
|
}
|
|
5227
5252
|
return true;
|
|
5228
5253
|
}
|
|
@@ -68,6 +68,8 @@ interface SessionToolsOptions {
|
|
|
68
68
|
toolRegistry?: Map<string, AgentTool>;
|
|
69
69
|
createVibeTools?: () => AgentTool[];
|
|
70
70
|
createComputerTool?: () => Promise<AgentTool | null>;
|
|
71
|
+
/** Creates the private `think` scratchpad tool for runtime setting changes. */
|
|
72
|
+
createThinkTool?: () => Promise<AgentTool | null>;
|
|
71
73
|
/** Creates the built-in `inspect_image` tool for session-scoped runtime enablement (see {@link SessionTools.setInspectImageMode}). */
|
|
72
74
|
createInspectImageTool?: () => Promise<AgentTool | null>;
|
|
73
75
|
builtInToolNames?: Iterable<string>;
|
|
@@ -184,6 +186,7 @@ export class SessionTools {
|
|
|
184
186
|
#toolRegistry: Map<string, AgentTool>;
|
|
185
187
|
#createVibeTools: (() => AgentTool[]) | undefined;
|
|
186
188
|
#createComputerTool: SessionToolsOptions["createComputerTool"];
|
|
189
|
+
#createThinkTool: SessionToolsOptions["createThinkTool"];
|
|
187
190
|
#createInspectImageTool: SessionToolsOptions["createInspectImageTool"];
|
|
188
191
|
#installedVibeToolNames = new Set<string>();
|
|
189
192
|
#builtInToolNames: Set<string>;
|
|
@@ -241,6 +244,7 @@ export class SessionTools {
|
|
|
241
244
|
this.#toolRegistry = options.toolRegistry ?? new Map();
|
|
242
245
|
this.#createVibeTools = options.createVibeTools;
|
|
243
246
|
this.#createComputerTool = options.createComputerTool;
|
|
247
|
+
this.#createThinkTool = options.createThinkTool;
|
|
244
248
|
this.#createInspectImageTool = options.createInspectImageTool;
|
|
245
249
|
this.#builtInToolNames = new Set(options.builtInToolNames ?? []);
|
|
246
250
|
this.#mcpManagerToolNames = new Set(options.mcpManagerToolNames ?? []);
|
|
@@ -1158,6 +1162,37 @@ export class SessionTools {
|
|
|
1158
1162
|
});
|
|
1159
1163
|
}
|
|
1160
1164
|
|
|
1165
|
+
/**
|
|
1166
|
+
* Session-scoped enable/disable for the private `think` scratchpad tool.
|
|
1167
|
+
*
|
|
1168
|
+
* Enabling constructs the tool once and refreshes the model's tool contract;
|
|
1169
|
+
* disabling removes it from the active set while preserving its registry entry.
|
|
1170
|
+
*
|
|
1171
|
+
* @returns false when enabling was requested but this session cannot build the tool.
|
|
1172
|
+
*/
|
|
1173
|
+
setThinkToolEnabled(enabled: boolean): Promise<boolean> {
|
|
1174
|
+
return this.runToolRegistryMutation(async () => {
|
|
1175
|
+
const active = this.getEnabledToolNames();
|
|
1176
|
+
if (!enabled) {
|
|
1177
|
+
if (active.includes("think")) {
|
|
1178
|
+
await this.#applyActiveToolsByName(active.filter(name => name !== "think"));
|
|
1179
|
+
}
|
|
1180
|
+
return true;
|
|
1181
|
+
}
|
|
1182
|
+
if (!this.#toolRegistry.has("think")) {
|
|
1183
|
+
const tool = await this.#createThinkTool?.();
|
|
1184
|
+
if (tool?.name !== "think") return false;
|
|
1185
|
+
const wrapped = this.#wrapRuntimeTool(tool);
|
|
1186
|
+
this.#toolRegistry.set(wrapped.name, wrapped);
|
|
1187
|
+
this.#builtInToolNames.add(wrapped.name);
|
|
1188
|
+
}
|
|
1189
|
+
if (!active.includes("think")) {
|
|
1190
|
+
await this.#applyActiveToolsByName([...active, "think"]);
|
|
1191
|
+
}
|
|
1192
|
+
return true;
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1161
1196
|
/** Current effective inspect_image state for `/vision status`. */
|
|
1162
1197
|
inspectImageState(): { mode: InspectImageMode; active: boolean; model: string | undefined } {
|
|
1163
1198
|
const model = this.#host.model();
|
|
@@ -32,8 +32,7 @@ export const BUILTIN_TOOL_NAMES = [
|
|
|
32
32
|
|
|
33
33
|
export type BuiltinToolName = (typeof BUILTIN_TOOL_NAMES)[number];
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
export const HIDDEN_TOOL_NAMES = ["yield", "goal"] as const;
|
|
35
|
+
export const HIDDEN_TOOL_NAMES = ["yield", "goal", "think"] as const;
|
|
37
36
|
|
|
38
37
|
export type HiddenToolName = (typeof HIDDEN_TOOL_NAMES)[number];
|
|
39
38
|
|
package/src/tools/index.ts
CHANGED
|
@@ -62,6 +62,7 @@ import { wrapToolWithMetaNotice } from "./output-meta";
|
|
|
62
62
|
import { ReadTool } from "./read";
|
|
63
63
|
import type { PlanProposalHandler } from "./resolve";
|
|
64
64
|
import { SecurityScanTool } from "./security-scan";
|
|
65
|
+
import { ThinkTool } from "./think";
|
|
65
66
|
import { type TodoPhase, TodoTool } from "./todo";
|
|
66
67
|
import { WriteTool } from "./write";
|
|
67
68
|
import { isMountableUnderXdev, type XdevState } from "./xdev";
|
|
@@ -102,6 +103,7 @@ export * from "./report-tool-issue";
|
|
|
102
103
|
export * from "./resolve";
|
|
103
104
|
export * from "./review";
|
|
104
105
|
export * from "./security-scan";
|
|
106
|
+
export * from "./think";
|
|
105
107
|
export * from "./todo";
|
|
106
108
|
export * from "./tts";
|
|
107
109
|
export * from "./vibe";
|
|
@@ -444,6 +446,7 @@ export const BUILTIN_TOOLS: Record<BuiltinToolName, ToolFactory> = {
|
|
|
444
446
|
};
|
|
445
447
|
|
|
446
448
|
export const HIDDEN_TOOLS: Record<HiddenToolName, ToolFactory> = {
|
|
449
|
+
think: () => new ThinkTool(),
|
|
447
450
|
yield: s => new YieldTool(s),
|
|
448
451
|
goal: s => new GoalTool(s),
|
|
449
452
|
};
|
|
@@ -562,6 +565,9 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
562
565
|
if (session.settings.get("memory.backend") === "mnemopi" && !requestedTools.includes("memory_edit")) {
|
|
563
566
|
requestedTools.push("memory_edit");
|
|
564
567
|
}
|
|
568
|
+
if (session.settings.get("externalThinking") && !requestedTools.includes("think")) {
|
|
569
|
+
requestedTools.push("think");
|
|
570
|
+
}
|
|
565
571
|
// Auto-learn tools are gated by `autolearn.enabled` but, like the memory
|
|
566
572
|
// tools above, must also be force-included into an explicit requestedTools
|
|
567
573
|
// list so a restricted top-level session whose controller/guidance is
|
|
@@ -604,6 +610,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
604
610
|
if (name === "inspect_image") return isInspectImageToolActive(session);
|
|
605
611
|
if (name === "web_search") return session.settings.get("web_search.enabled");
|
|
606
612
|
if (name === "security_scan") return session.settings.get("security.enabled");
|
|
613
|
+
if (name === "think") return session.settings.get("externalThinking");
|
|
607
614
|
if (name === "ask") return session.settings.get("ask.enabled");
|
|
608
615
|
if (name === "browser") return session.settings.get("browser.enabled");
|
|
609
616
|
if (name === "computer") return session.settings.get("computer.enabled");
|
|
@@ -650,6 +657,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
650
657
|
...Object.entries(BUILTIN_TOOLS)
|
|
651
658
|
.filter(([name]) => isToolAllowed(name))
|
|
652
659
|
.map(([name, factory]) => [name, factory] as const),
|
|
660
|
+
...(session.settings.get("externalThinking") ? ([["think", HIDDEN_TOOLS.think]] as const) : []),
|
|
653
661
|
...(includeYield ? ([["yield", HIDDEN_TOOLS.yield]] as const) : []),
|
|
654
662
|
...(goalModeActive ? ([["goal", HIDDEN_TOOLS.goal]] as const) : []),
|
|
655
663
|
];
|
package/src/tools/renderers.ts
CHANGED
|
@@ -27,6 +27,7 @@ import { inspectImageToolRenderer } from "./inspect-image-renderer";
|
|
|
27
27
|
import { recallToolRenderer, reflectToolRenderer, retainToolRenderer } from "./memory-render";
|
|
28
28
|
import { readToolRenderer } from "./read";
|
|
29
29
|
import { resolveRenderer } from "./resolve";
|
|
30
|
+
import { thinkToolRenderer } from "./think";
|
|
30
31
|
import { todoToolRenderer } from "./todo";
|
|
31
32
|
import { createVibeToolRenderer } from "./vibe";
|
|
32
33
|
import { writeToolRenderer } from "./write";
|
|
@@ -115,6 +116,7 @@ export const toolRenderers: Record<string, ToolRenderer> = {
|
|
|
115
116
|
get task(): ToolRenderer {
|
|
116
117
|
return taskToolRenderer as ToolRenderer;
|
|
117
118
|
},
|
|
119
|
+
think: thinkToolRenderer as ToolRenderer,
|
|
118
120
|
todo: todoToolRenderer as ToolRenderer,
|
|
119
121
|
github: githubToolRenderer as ToolRenderer,
|
|
120
122
|
goal: goalToolRenderer as ToolRenderer,
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type } from "@oh-my-pi/omptype";
|
|
2
|
+
import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
|
|
3
|
+
import { type Component, Markdown } from "@oh-my-pi/pi-tui";
|
|
4
|
+
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
5
|
+
import { getMarkdownTheme, type Theme } from "../modes/theme/theme";
|
|
6
|
+
|
|
7
|
+
const thinkSchema = type({
|
|
8
|
+
thoughts: type("string").describe("private scratchpad reasoning to retain before the next response"),
|
|
9
|
+
"+": "reject",
|
|
10
|
+
}).describe("record private intermediate reasoning before answering");
|
|
11
|
+
|
|
12
|
+
type ThinkParams = typeof thinkSchema.infer;
|
|
13
|
+
|
|
14
|
+
export type ThinkRenderArgs = {
|
|
15
|
+
thoughts?: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const thinkToolRenderer = {
|
|
19
|
+
inline: true,
|
|
20
|
+
renderCall(args: ThinkRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
|
|
21
|
+
const thoughts =
|
|
22
|
+
typeof args === "object" && args !== null && "thoughts" in args && typeof args.thoughts === "string"
|
|
23
|
+
? args.thoughts
|
|
24
|
+
: "";
|
|
25
|
+
return new Markdown(thoughts, 1, 0, getMarkdownTheme(), {
|
|
26
|
+
color: (text: string) => uiTheme.fg("thinkingText", text),
|
|
27
|
+
italic: true,
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
renderResult(): Component {
|
|
31
|
+
return undefined as unknown as Component;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
interface ThinkToolDetails {
|
|
36
|
+
recorded: true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Records private intermediate reasoning while native GPT reasoning is disabled. */
|
|
40
|
+
export class ThinkTool implements AgentTool<typeof thinkSchema, ThinkToolDetails> {
|
|
41
|
+
readonly name = "think";
|
|
42
|
+
readonly approval = "read" as const;
|
|
43
|
+
readonly label = "Think";
|
|
44
|
+
readonly summary = "Record private intermediate reasoning before answering";
|
|
45
|
+
readonly description =
|
|
46
|
+
"Use this private scratchpad to plan, derive, or check work before answering. Record only materially new reasoning. The user does not see this tool activity.";
|
|
47
|
+
readonly parameters = thinkSchema;
|
|
48
|
+
readonly strict = true;
|
|
49
|
+
readonly intent = "omit" as const;
|
|
50
|
+
|
|
51
|
+
async execute(_toolCallId: string, _params: ThinkParams): Promise<AgentToolResult<ThinkToolDetails>> {
|
|
52
|
+
return {
|
|
53
|
+
content: [
|
|
54
|
+
{
|
|
55
|
+
type: "text",
|
|
56
|
+
text: "------",
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
details: { recorded: true },
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|