@oh-my-pi/pi-coding-agent 16.2.6 → 16.2.8
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 +46 -0
- package/dist/cli.js +2781 -2660
- package/dist/types/cli/bench-cli.d.ts +3 -3
- package/dist/types/commands/bench.d.ts +1 -1
- package/dist/types/config/service-tier.d.ts +39 -24
- package/dist/types/config/settings-schema.d.ts +92 -36
- package/dist/types/edit/hashline/filesystem.d.ts +1 -0
- package/dist/types/mcp/config-writer.d.ts +48 -0
- package/dist/types/mcp/types.d.ts +3 -0
- package/dist/types/session/agent-session.d.ts +33 -13
- package/dist/types/session/messages.d.ts +1 -1
- package/dist/types/session/session-context.d.ts +2 -7
- package/dist/types/session/session-entries.d.ts +2 -2
- package/dist/types/session/session-manager.d.ts +2 -2
- package/dist/types/session/settings-stream-fn.d.ts +2 -2
- package/dist/types/system-prompt.test.d.ts +1 -0
- package/dist/types/task/executor.d.ts +6 -6
- package/dist/types/task/types.d.ts +6 -0
- package/dist/types/task/worktree.d.ts +32 -6
- package/dist/types/tiny/title-client.d.ts +5 -1
- package/dist/types/tools/index.d.ts +3 -3
- package/dist/types/tools/output-schema-validator.d.ts +10 -0
- package/dist/types/utils/git.d.ts +17 -0
- package/package.json +12 -12
- package/src/cli/bench-cli.ts +19 -12
- package/src/cli/tiny-models-cli.ts +18 -4
- package/src/commands/bench.ts +3 -3
- package/src/config/mcp-schema.json +10 -1
- package/src/config/service-tier.ts +85 -56
- package/src/config/settings-schema.ts +81 -37
- package/src/config/settings.ts +47 -0
- package/src/discovery/builtin-rules/go-add-cleanup.md +32 -0
- package/src/discovery/builtin-rules/go-bench-loop.md +36 -0
- package/src/discovery/builtin-rules/go-exp-promoted.md +39 -0
- package/src/discovery/builtin-rules/go-ioutil.md +36 -0
- package/src/discovery/builtin-rules/go-join-hostport.md +29 -0
- package/src/discovery/builtin-rules/go-new-expr.md +44 -0
- package/src/discovery/builtin-rules/go-rand-v2.md +40 -0
- package/src/discovery/builtin-rules/go-range-int.md +45 -0
- package/src/discovery/builtin-rules/index.ts +16 -0
- package/src/edit/hashline/filesystem.ts +12 -0
- package/src/eval/agent-bridge.ts +4 -2
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/main.ts +1 -1
- package/src/mcp/config-writer.ts +121 -0
- package/src/mcp/config.ts +10 -6
- package/src/mcp/types.ts +3 -0
- package/src/modes/components/extensions/extension-dashboard.ts +46 -0
- package/src/modes/components/extensions/state-manager.ts +24 -3
- package/src/modes/controllers/event-controller.ts +7 -0
- package/src/modes/controllers/tool-args-reveal.ts +1 -1
- package/src/modes/utils/transcript-render-helpers.ts +2 -2
- package/src/prompts/tools/bash.md +1 -4
- package/src/sdk.ts +12 -11
- package/src/session/agent-session.ts +294 -82
- package/src/session/messages.ts +1 -1
- package/src/session/session-context.ts +17 -6
- package/src/session/session-entries.ts +2 -2
- package/src/session/session-manager.ts +9 -2
- package/src/session/settings-stream-fn.ts +12 -2
- package/src/slash-commands/builtin-registry.ts +2 -10
- package/src/system-prompt.test.ts +158 -0
- package/src/system-prompt.ts +69 -26
- package/src/task/executor.ts +23 -16
- package/src/task/index.ts +7 -5
- package/src/task/isolation-runner.ts +15 -1
- package/src/task/types.ts +6 -0
- package/src/task/worktree.ts +219 -38
- package/src/tiny/title-client.ts +19 -13
- package/src/tools/grep.ts +19 -1
- package/src/tools/index.ts +3 -3
- package/src/tools/irc.ts +9 -3
- package/src/tools/output-schema-validator.ts +38 -0
- package/src/tools/read.ts +28 -28
- package/src/tools/yield.ts +52 -15
- package/src/utils/file-mentions.ts +10 -1
- package/src/utils/git.ts +38 -0
- package/src/web/search/providers/duckduckgo.ts +17 -3
package/src/main.ts
CHANGED
|
@@ -140,7 +140,7 @@ const HOST_DEFAULTED_SETTING_PATHS: SettingPath[] = [
|
|
|
140
140
|
"advisor.subagents",
|
|
141
141
|
"advisor.syncBacklog",
|
|
142
142
|
"advisor.immuneTurns",
|
|
143
|
-
"
|
|
143
|
+
"tier.advisor",
|
|
144
144
|
];
|
|
145
145
|
|
|
146
146
|
const RPC_BACKGROUND_DEFAULTED_SETTING_PATHS: SettingPath[] = [
|
package/src/mcp/config-writer.ts
CHANGED
|
@@ -227,3 +227,124 @@ export async function setServerDisabled(filePath: string, name: string, disabled
|
|
|
227
227
|
|
|
228
228
|
await writeMCPConfigFile(filePath, updated);
|
|
229
229
|
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Read the user-level force-enable list (allowlist that overrides a
|
|
233
|
+
* non-writable source config's `enabled: false`).
|
|
234
|
+
*/
|
|
235
|
+
export async function readEnabledServers(filePath: string): Promise<string[]> {
|
|
236
|
+
const config = await readMCPConfigFile(filePath);
|
|
237
|
+
return Array.isArray(config.enabledServers) ? config.enabledServers : [];
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Add or remove a server name from the user-level force-enable list.
|
|
242
|
+
* The list overrides a discovered server's `enabled: false` flag but does
|
|
243
|
+
* NOT override the `disabledServers` denylist.
|
|
244
|
+
*/
|
|
245
|
+
export async function setServerForceEnabled(filePath: string, name: string, force: boolean): Promise<void> {
|
|
246
|
+
const config = await readMCPConfigFile(filePath);
|
|
247
|
+
const current = new Set(config.enabledServers ?? []);
|
|
248
|
+
|
|
249
|
+
if (force) {
|
|
250
|
+
current.add(name);
|
|
251
|
+
} else {
|
|
252
|
+
current.delete(name);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const updated: MCPConfigFile = {
|
|
256
|
+
...config,
|
|
257
|
+
enabledServers: current.size > 0 ? Array.from(current).sort() : undefined,
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
if (!updated.enabledServers) {
|
|
261
|
+
delete updated.enabledServers;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
await writeMCPConfigFile(filePath, updated);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** Paths and target state for toggling one MCP server across known config files. */
|
|
268
|
+
export interface SetMcpServerEnabledOptions {
|
|
269
|
+
userPath: string;
|
|
270
|
+
projectPath: string;
|
|
271
|
+
/**
|
|
272
|
+
* Absolute path to the loaded row's source mcp.json. Provide ONLY for
|
|
273
|
+
* formats this codebase owns (native `.omp/mcp.json` and `mcp-json`
|
|
274
|
+
* `mcp.json`/`.mcp.json`). Tool-owned configs (opencode.json, claude.json,
|
|
275
|
+
* settings.json …) MUST be omitted; we never mutate another tool's file.
|
|
276
|
+
*/
|
|
277
|
+
sourcePath?: string;
|
|
278
|
+
name: string;
|
|
279
|
+
enabled: boolean;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Flip a server's enabled/disabled state regardless of where it lives.
|
|
284
|
+
*
|
|
285
|
+
* Resolution order, mirroring `/mcp enable` / `/mcp disable` plus the dashboard
|
|
286
|
+
* fix for non-writable source configs:
|
|
287
|
+
*
|
|
288
|
+
* - Server found in `sourcePath` (writable) → write `enabled` on that entry.
|
|
289
|
+
* - Else server in project mcp.json → write `enabled` there.
|
|
290
|
+
* - Else server in user mcp.json → write `enabled` there.
|
|
291
|
+
* - Else (server defined in a tool-owned source like opencode.json, OR a
|
|
292
|
+
* purely discovered server):
|
|
293
|
+
* - Disable → add to the user-level `disabledServers` denylist.
|
|
294
|
+
* - Enable → add to the user-level `enabledServers` allowlist so the
|
|
295
|
+
* dashboard / runtime override the non-writable source's
|
|
296
|
+
* `enabled: false` flag.
|
|
297
|
+
*
|
|
298
|
+
* Cleanup invariants — on every call:
|
|
299
|
+
* - Re-enable clears any stale denylist entry so a server disabled via
|
|
300
|
+
* `/mcp disable` and re-enabled here doesn't stay suppressed.
|
|
301
|
+
* - Disable clears any stale allowlist entry so re-disabling a
|
|
302
|
+
* force-enabled server actually takes effect.
|
|
303
|
+
*/
|
|
304
|
+
export async function setMcpServerEnabled(options: SetMcpServerEnabledOptions): Promise<void> {
|
|
305
|
+
const { userPath, projectPath, sourcePath, name, enabled } = options;
|
|
306
|
+
const candidatePaths = [...new Set([sourcePath, projectPath, userPath].filter(path => path !== undefined))];
|
|
307
|
+
let updatedInConfig = false;
|
|
308
|
+
|
|
309
|
+
for (const filePath of candidatePaths) {
|
|
310
|
+
const config = await readMCPConfigFile(filePath);
|
|
311
|
+
const server = config.mcpServers?.[name];
|
|
312
|
+
if (server === undefined) continue;
|
|
313
|
+
|
|
314
|
+
await updateMCPServer(filePath, name, { ...server, enabled });
|
|
315
|
+
updatedInConfig = true;
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (enabled) {
|
|
320
|
+
// Either we just wrote `enabled: true` on a writable source, or the
|
|
321
|
+
// server lives in a non-writable source whose `enabled: false` flag we
|
|
322
|
+
// need to override via the user allowlist. Either way the denylist
|
|
323
|
+
// entry (if any) must clear so the row becomes active.
|
|
324
|
+
const denied = await readDisabledServers(userPath);
|
|
325
|
+
if (denied.includes(name)) {
|
|
326
|
+
await setServerDisabled(userPath, name, false);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const forced = await readEnabledServers(userPath);
|
|
330
|
+
const isForced = forced.includes(name);
|
|
331
|
+
if (!updatedInConfig && !isForced) {
|
|
332
|
+
await setServerForceEnabled(userPath, name, true);
|
|
333
|
+
} else if (updatedInConfig && isForced) {
|
|
334
|
+
// Writable source now carries `enabled: true`; the override is
|
|
335
|
+
// redundant. Drop it so the user's allowlist stays tidy.
|
|
336
|
+
await setServerForceEnabled(userPath, name, false);
|
|
337
|
+
}
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Disable path. Clear any force-enable override regardless of source so the
|
|
342
|
+
// disable actually sticks.
|
|
343
|
+
const forced = await readEnabledServers(userPath);
|
|
344
|
+
if (forced.includes(name)) {
|
|
345
|
+
await setServerForceEnabled(userPath, name, false);
|
|
346
|
+
}
|
|
347
|
+
if (!updatedInConfig) {
|
|
348
|
+
await setServerDisabled(userPath, name, true);
|
|
349
|
+
}
|
|
350
|
+
}
|
package/src/mcp/config.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { mcpCapability } from "../capability/mcp";
|
|
|
9
9
|
import type { SourceMeta } from "../capability/types";
|
|
10
10
|
import type { MCPServer } from "../discovery";
|
|
11
11
|
import { loadCapability } from "../discovery";
|
|
12
|
-
import { readDisabledServers } from "./config-writer";
|
|
12
|
+
import { readDisabledServers, readEnabledServers } from "./config-writer";
|
|
13
13
|
import type { MCPServerConfig } from "./types";
|
|
14
14
|
|
|
15
15
|
/** Options for loading MCP configs */
|
|
@@ -105,16 +105,20 @@ export async function loadAllMCPConfigs(cwd: string, options?: LoadMCPConfigsOpt
|
|
|
105
105
|
? result.items
|
|
106
106
|
: result.items.filter(server => server._source.level !== "project");
|
|
107
107
|
|
|
108
|
-
// Load user-level
|
|
109
|
-
|
|
108
|
+
// Load user-level disable/force-enable lists. The denylist always wins; the
|
|
109
|
+
// allowlist overrides a non-writable source config's `enabled: false`.
|
|
110
|
+
const userPath = getMCPConfigPath("user", cwd);
|
|
111
|
+
const [disabledServers, forcedEnabled] = await Promise.all([
|
|
112
|
+
readDisabledServers(userPath).then(list => new Set(list)),
|
|
113
|
+
readEnabledServers(userPath).then(list => new Set(list)),
|
|
114
|
+
]);
|
|
110
115
|
// Convert to legacy format and preserve source metadata
|
|
111
116
|
let configs: Record<string, MCPServerConfig> = {};
|
|
112
117
|
let sources: Record<string, SourceMeta> = {};
|
|
113
118
|
for (const server of servers) {
|
|
114
119
|
const config = convertToLegacyConfig(server);
|
|
115
|
-
if (
|
|
116
|
-
|
|
117
|
-
}
|
|
120
|
+
if (disabledServers.has(server.name)) continue;
|
|
121
|
+
if (config.enabled === false && !forcedEnabled.has(server.name)) continue;
|
|
118
122
|
configs[server.name] = config;
|
|
119
123
|
sources[server.name] = server._source;
|
|
120
124
|
}
|
package/src/mcp/types.ts
CHANGED
|
@@ -111,7 +111,10 @@ export const MCP_CONFIG_SCHEMA_URL =
|
|
|
111
111
|
export interface MCPConfigFile {
|
|
112
112
|
$schema?: string;
|
|
113
113
|
mcpServers?: Record<string, MCPServerConfig>;
|
|
114
|
+
/** Names to hide regardless of any source `enabled` flag. Highest precedence. */
|
|
114
115
|
disabledServers?: string[];
|
|
116
|
+
/** Names to force-enable when a non-writable source reports `enabled: false`. */
|
|
117
|
+
enabledServers?: string[];
|
|
115
118
|
}
|
|
116
119
|
|
|
117
120
|
// =============================================================================
|
|
@@ -24,7 +24,9 @@ import {
|
|
|
24
24
|
truncateToWidth,
|
|
25
25
|
visibleWidth,
|
|
26
26
|
} from "@oh-my-pi/pi-tui";
|
|
27
|
+
import { getMCPConfigPath, logger } from "@oh-my-pi/pi-utils";
|
|
27
28
|
import { Settings } from "../../../config/settings";
|
|
29
|
+
import { setMcpServerEnabled } from "../../../mcp/config-writer";
|
|
28
30
|
import { getTabBarTheme } from "../../../modes/shared";
|
|
29
31
|
import { theme } from "../../../modes/theme/theme";
|
|
30
32
|
import { matchesAppInterrupt } from "../../../modes/utils/keybinding-matchers";
|
|
@@ -263,6 +265,14 @@ export class ExtensionDashboard implements Component {
|
|
|
263
265
|
const sm = this.settings ?? Settings.instance;
|
|
264
266
|
if (!sm) return;
|
|
265
267
|
|
|
268
|
+
// MCP toggles route through the canonical denylist in
|
|
269
|
+
// `~/.omp/agent/mcp.json` so `/mcp list`, the MCP runtime, and this
|
|
270
|
+
// dashboard agree on every server's enabled state (issue #3827).
|
|
271
|
+
if (extensionId.startsWith("mcp:")) {
|
|
272
|
+
void this.#toggleMcpExtension(extensionId, enabled, sm);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
266
276
|
const disabled = ((sm.get("disabledExtensions") as string[]) ?? []).slice();
|
|
267
277
|
if (enabled) {
|
|
268
278
|
const index = disabled.indexOf(extensionId);
|
|
@@ -281,6 +291,42 @@ export class ExtensionDashboard implements Component {
|
|
|
281
291
|
void this.#refreshFromState();
|
|
282
292
|
}
|
|
283
293
|
|
|
294
|
+
async #toggleMcpExtension(extensionId: string, enabled: boolean, sm: Settings): Promise<void> {
|
|
295
|
+
const name = extensionId.slice("mcp:".length);
|
|
296
|
+
try {
|
|
297
|
+
await setMcpServerEnabled({
|
|
298
|
+
userPath: getMCPConfigPath("user", this.cwd),
|
|
299
|
+
projectPath: getMCPConfigPath("project", this.cwd),
|
|
300
|
+
sourcePath: this.#writableMcpSourcePath(extensionId),
|
|
301
|
+
name,
|
|
302
|
+
enabled,
|
|
303
|
+
});
|
|
304
|
+
} catch (error) {
|
|
305
|
+
logger.warn("Failed to persist MCP toggle", { name, enabled, error: String(error) });
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Reconcile `settings.disabledExtensions` with the canonical mcp.json
|
|
309
|
+
// state so a legacy `mcp:<name>` flag from before this routing change
|
|
310
|
+
// doesn't keep the server marked disabled after the user re-enables it
|
|
311
|
+
// via the UI.
|
|
312
|
+
const stored = ((sm.get("disabledExtensions") as string[]) ?? []).slice();
|
|
313
|
+
const had = stored.indexOf(extensionId);
|
|
314
|
+
if (enabled && had !== -1) {
|
|
315
|
+
stored.splice(had, 1);
|
|
316
|
+
sm.set("disabledExtensions", stored);
|
|
317
|
+
this.#applyDisabledExtensions(stored);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
await this.#refreshFromState();
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
#writableMcpSourcePath(extensionId: string): string | undefined {
|
|
324
|
+
const extension = this.#state.extensions.find(ext => ext.id === extensionId);
|
|
325
|
+
if (!extension) return undefined;
|
|
326
|
+
if (extension.source.provider !== "native" && extension.source.provider !== "mcp-json") return undefined;
|
|
327
|
+
return extension.path;
|
|
328
|
+
}
|
|
329
|
+
|
|
284
330
|
async #refreshFromState(): Promise<void> {
|
|
285
331
|
const refreshToken = ++this.#refreshToken;
|
|
286
332
|
// Remember the current tab so it survives the re-sort.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import { fuzzyMatch } from "@oh-my-pi/pi-tui";
|
|
7
|
-
import { logger } from "@oh-my-pi/pi-utils";
|
|
7
|
+
import { getMCPConfigPath, logger } from "@oh-my-pi/pi-utils";
|
|
8
8
|
import type { ContextFile } from "../../../capability/context-file";
|
|
9
9
|
import type { ExtensionModule } from "../../../capability/extension-module";
|
|
10
10
|
import type { Hook } from "../../../capability/hook";
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
isProviderEnabled,
|
|
23
23
|
loadCapability,
|
|
24
24
|
} from "../../../discovery";
|
|
25
|
+
import { readDisabledServers, readEnabledServers } from "../../../mcp/config-writer";
|
|
25
26
|
import type {
|
|
26
27
|
DashboardState,
|
|
27
28
|
Extension,
|
|
@@ -141,12 +142,32 @@ export async function loadAllExtensions(cwd?: string, disabledIds?: string[]): P
|
|
|
141
142
|
logger.warn("Failed to load extension-modules capability", { error: String(error) });
|
|
142
143
|
}
|
|
143
144
|
|
|
144
|
-
// Load MCP servers
|
|
145
|
+
// Load MCP servers. The dashboard mirrors `/mcp list` (issue #3827) by
|
|
146
|
+
// honoring the same disable signals: the dashboard-private settings list,
|
|
147
|
+
// the per-server `enabled: false` flag, and the user-level `disabledServers`
|
|
148
|
+
// denylist that `/mcp disable` writes through `setServerDisabled`. The
|
|
149
|
+
// user-level `enabledServers` allowlist overrides a non-writable source's
|
|
150
|
+
// `enabled: false` (e.g. opencode.json) but never the denylist.
|
|
145
151
|
try {
|
|
152
|
+
const userMcpPath = cwd ? getMCPConfigPath("user", cwd) : undefined;
|
|
153
|
+
const [mcpDisabledNames, mcpForcedEnabled] = await Promise.all([
|
|
154
|
+
userMcpPath
|
|
155
|
+
? readDisabledServers(userMcpPath)
|
|
156
|
+
.then(list => new Set(list))
|
|
157
|
+
.catch(() => new Set<string>())
|
|
158
|
+
: Promise.resolve(new Set<string>()),
|
|
159
|
+
userMcpPath
|
|
160
|
+
? readEnabledServers(userMcpPath)
|
|
161
|
+
.then(list => new Set(list))
|
|
162
|
+
.catch(() => new Set<string>())
|
|
163
|
+
: Promise.resolve(new Set<string>()),
|
|
164
|
+
]);
|
|
146
165
|
const mcps = await loadCapability<MCPServer>("mcps", loadOpts);
|
|
147
166
|
for (const server of mcps.all) {
|
|
148
167
|
const id = makeExtensionId("mcp", server.name);
|
|
149
|
-
const
|
|
168
|
+
const forced = mcpForcedEnabled.has(server.name);
|
|
169
|
+
const sourceSaysDisabled = server.enabled === false && !forced;
|
|
170
|
+
const isDisabled = mcpDisabledNames.has(server.name) || disabledExtensions.has(id) || sourceSaysDisabled;
|
|
150
171
|
const isShadowed = (server as { _shadowed?: boolean })._shadowed;
|
|
151
172
|
const providerEnabled = isProviderEnabled(server._source.provider);
|
|
152
173
|
|
|
@@ -890,6 +890,13 @@ export class EventController {
|
|
|
890
890
|
}
|
|
891
891
|
|
|
892
892
|
async #handleToolExecutionEnd(event: Extract<AgentSessionEvent, { type: "tool_execution_end" }>): Promise<void> {
|
|
893
|
+
// A transient overlay (auto-compaction / auto-retry / handoff) that ran
|
|
894
|
+
// between this tool's start and end could have detached the working
|
|
895
|
+
// loader. `tool_execution_update` already reconciles this so the spinner
|
|
896
|
+
// reappears mid-tool; mirror it here so subagent (`task`) completions —
|
|
897
|
+
// which only fire `tool_execution_end`, never `_update` — do not leave
|
|
898
|
+
// the UI looking idle while the session keeps streaming (#3857).
|
|
899
|
+
this.#ensureWorkingLoaderWhileStreaming();
|
|
893
900
|
if (event.toolName === "read") {
|
|
894
901
|
if (this.#inlineReadToolImages(event.toolCallId, event.result)) {
|
|
895
902
|
const component = this.ctx.pendingTools.get(event.toolCallId);
|
|
@@ -99,9 +99,9 @@ export function buildFileMentionBlock(files: FileMentionMessage["files"], indent
|
|
|
99
99
|
const block = new TranscriptBlock();
|
|
100
100
|
for (const file of files) {
|
|
101
101
|
let suffix: string;
|
|
102
|
-
if (file.skippedReason === "tooLarge") {
|
|
102
|
+
if (file.skippedReason === "tooLarge" || file.skippedReason === "binary") {
|
|
103
103
|
const size = typeof file.byteSize === "number" ? formatBytes(file.byteSize) : "unknown size";
|
|
104
|
-
suffix = `(skipped: ${size})`;
|
|
104
|
+
suffix = file.skippedReason === "binary" ? `(skipped: binary, ${size})` : `(skipped: ${size})`;
|
|
105
105
|
} else {
|
|
106
106
|
suffix = file.image
|
|
107
107
|
? "(image)"
|
|
@@ -29,13 +29,10 @@ Anything below → `eval` cell, not bash:
|
|
|
29
29
|
|
|
30
30
|
<critical>
|
|
31
31
|
- Bash invokes real binaries with simple args; it is NOT a scripting surface. Loops, conditionals, heredocs, inline interpreter scripts (`-e`/`-c`/`--eval`) when an eval runtime exists, several piped stages, or quote/JSON escaping mean you're writing a program → use `eval` cells: restartable, stateful, and free of shell-quoting traps.
|
|
32
|
-
- NEVER shell out to search content or files: `grep/rg` → `grep`.
|
|
33
|
-
- NEVER use `ls` or `find` to list or locate files — `ls` → `read` (a directory path lists entries), `find` → the `glob` tool (globbing). This is non-negotiable, even for a single quick listing.
|
|
34
|
-
- Avoid head/tail/redirections: stderr already merged; long output auto-truncated, FULL capture kept at `artifact://<id>`.
|
|
35
32
|
</critical>
|
|
36
33
|
|
|
37
34
|
<output>
|
|
38
|
-
- Returns output; exit code shown on non-zero exit.
|
|
35
|
+
- Returns output (stderr merged into stdout); exit code shown on non-zero exit.
|
|
39
36
|
- Truncated output → `artifact://<id>` (linked in metadata).
|
|
40
37
|
</output>
|
|
41
38
|
|
package/src/sdk.ts
CHANGED
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
resolveModelRoleValue,
|
|
43
43
|
} from "./config/model-resolver";
|
|
44
44
|
import { loadPromptTemplates as loadPromptTemplatesInternal, type PromptTemplate } from "./config/prompt-templates";
|
|
45
|
+
import { buildServiceTierByFamily } from "./config/service-tier";
|
|
45
46
|
import { Settings, type SkillsSettings } from "./config/settings";
|
|
46
47
|
import { CursorExecHandlers } from "./cursor";
|
|
47
48
|
import "./discovery";
|
|
@@ -1537,7 +1538,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1537
1538
|
getModelString: () => (hasExplicitModel && model ? formatModelString(model) : undefined),
|
|
1538
1539
|
getActiveModelString,
|
|
1539
1540
|
getActiveModel: () => agent?.state.model ?? model,
|
|
1540
|
-
|
|
1541
|
+
getServiceTierByFamily: () => session?.serviceTierByFamily,
|
|
1541
1542
|
getImageAttachments: () => session?.getImageAttachments() ?? [],
|
|
1542
1543
|
getPlanModeState: () => session?.getPlanModeState(),
|
|
1543
1544
|
getPlanReferencePath: () => session?.getPlanReferencePath() ?? "local://PLAN.md",
|
|
@@ -2525,13 +2526,13 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2525
2526
|
const openaiWebsocketSetting = settings.get("providers.openaiWebsockets") ?? "off";
|
|
2526
2527
|
const preferOpenAICodexWebsockets =
|
|
2527
2528
|
openaiWebsocketSetting === "on" ? true : openaiWebsocketSetting === "off" ? false : undefined;
|
|
2528
|
-
const
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2529
|
+
const initialServiceTierByFamily = hasServiceTierEntry
|
|
2530
|
+
? (existingSession.serviceTier ?? {})
|
|
2531
|
+
: buildServiceTierByFamily(
|
|
2532
|
+
settings.get("tier.openai"),
|
|
2533
|
+
settings.get("tier.anthropic"),
|
|
2534
|
+
settings.get("tier.google"),
|
|
2535
|
+
);
|
|
2535
2536
|
|
|
2536
2537
|
// One-shot launch-latency marker: fired the first time the loop dispatches
|
|
2537
2538
|
// a chat request to the provider transport. See onFirstChatDispatch.
|
|
@@ -2579,7 +2580,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2579
2580
|
minP: settings.get("minP") >= 0 ? settings.get("minP") : undefined,
|
|
2580
2581
|
presencePenalty: settings.get("presencePenalty") >= 0 ? settings.get("presencePenalty") : undefined,
|
|
2581
2582
|
repetitionPenalty: settings.get("repetitionPenalty") >= 0 ? settings.get("repetitionPenalty") : undefined,
|
|
2582
|
-
serviceTier: initialServiceTier,
|
|
2583
2583
|
hideThinkingSummary: settings.get("omitThinking"),
|
|
2584
2584
|
kimiApiFormat: settings.get("providers.kimiApiFormat") ?? "anthropic",
|
|
2585
2585
|
preferWebsockets: preferOpenAICodexWebsockets,
|
|
@@ -2639,8 +2639,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2639
2639
|
// classification persists its concrete effort once a real user turn runs.
|
|
2640
2640
|
sessionManager.appendThinkingLevelChange(effectiveThinkingLevel);
|
|
2641
2641
|
}
|
|
2642
|
-
if (
|
|
2643
|
-
sessionManager.appendServiceTierChange(
|
|
2642
|
+
if (Object.keys(initialServiceTierByFamily).length > 0) {
|
|
2643
|
+
sessionManager.appendServiceTierChange(initialServiceTierByFamily);
|
|
2644
2644
|
}
|
|
2645
2645
|
}
|
|
2646
2646
|
|
|
@@ -2691,6 +2691,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2691
2691
|
agent,
|
|
2692
2692
|
pruneToolDescriptions: inlineToolDescriptors,
|
|
2693
2693
|
thinkingLevel: autoThinking ? AUTO_THINKING : effectiveThinkingLevel,
|
|
2694
|
+
serviceTierByFamily: initialServiceTierByFamily,
|
|
2694
2695
|
sessionManager,
|
|
2695
2696
|
settings,
|
|
2696
2697
|
autoApprove: options.autoApprove,
|