@cydm/pie 1.0.46 → 1.0.47

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/dist/cli.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  getThemesDir,
20
20
  migrateConfigFromAgentDir,
21
21
  truncateHeadTail
22
- } from "./chunks/chunk-75JWZPRK.js";
22
+ } from "./chunks/chunk-U4B6RJ6Z.js";
23
23
  import {
24
24
  AGENTS_CONTEXT_FILE_NAME,
25
25
  AgentSessionController,
@@ -66,10 +66,11 @@ import {
66
66
  resolveExtensionIdentity,
67
67
  restoreExecutionState,
68
68
  selectToolsForRuntimePolicy,
69
+ setEnablementInMap,
69
70
  setExtensionEnabledInSettings,
70
71
  shouldPreserveExecutionStateForUserText,
71
72
  supersedeExecutionState
72
- } from "./chunks/chunk-PKR3PO73.js";
73
+ } from "./chunks/chunk-IJMP3JTX.js";
73
74
  import "./chunks/chunk-FKAO25XH.js";
74
75
  import {
75
76
  Deref,
@@ -1236,12 +1237,12 @@ var require_supports_color = __commonJS({
1236
1237
  "use strict";
1237
1238
  var os9 = __require("os");
1238
1239
  var tty = __require("tty");
1239
- var hasFlag2 = require_has_flag();
1240
+ var hasFlag = require_has_flag();
1240
1241
  var { env } = process;
1241
1242
  var forceColor;
1242
- if (hasFlag2("no-color") || hasFlag2("no-colors") || hasFlag2("color=false") || hasFlag2("color=never")) {
1243
+ if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
1243
1244
  forceColor = 0;
1244
- } else if (hasFlag2("color") || hasFlag2("colors") || hasFlag2("color=true") || hasFlag2("color=always")) {
1245
+ } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
1245
1246
  forceColor = 1;
1246
1247
  }
1247
1248
  if ("FORCE_COLOR" in env) {
@@ -1268,10 +1269,10 @@ var require_supports_color = __commonJS({
1268
1269
  if (forceColor === 0) {
1269
1270
  return 0;
1270
1271
  }
1271
- if (hasFlag2("color=16m") || hasFlag2("color=full") || hasFlag2("color=truecolor")) {
1272
+ if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
1272
1273
  return 3;
1273
1274
  }
1274
- if (hasFlag2("color=256")) {
1275
+ if (hasFlag("color=256")) {
1275
1276
  return 2;
1276
1277
  }
1277
1278
  if (haveStream && !streamIsTTY && forceColor === void 0) {
@@ -46223,6 +46224,13 @@ var ModelRegistry = class {
46223
46224
  // src/core/settings-manager.ts
46224
46225
  import * as fs4 from "fs";
46225
46226
  import * as path4 from "path";
46227
+ function getDisabledToolNames(settingsManager) {
46228
+ return new Set(typeof settingsManager?.getDisabledTools === "function" ? settingsManager.getDisabledTools() : []);
46229
+ }
46230
+ function filterEnabledTools(tools, settingsManager) {
46231
+ const disabled = getDisabledToolNames(settingsManager);
46232
+ return disabled.size > 0 ? tools.filter((tool) => !disabled.has(tool.name)) : [...tools];
46233
+ }
46226
46234
  var SettingsManager = class {
46227
46235
  settingsPath;
46228
46236
  legacyConfigPath;
@@ -46379,6 +46387,44 @@ var SettingsManager = class {
46379
46387
  this.writeSettingsValue(nextSettings);
46380
46388
  this.settings = nextSettings;
46381
46389
  }
46390
+ /** Raw skill enablement map. */
46391
+ getSkillSettings() {
46392
+ return this.settings.skills ?? {};
46393
+ }
46394
+ /** Whether a skill is enabled; absent entries mean "enabled". */
46395
+ isSkillEnabled(name) {
46396
+ return isExtensionEnabled(name, this.getSkillSettings());
46397
+ }
46398
+ /** Set or clear (enabled=undefined) a skill enablement override. */
46399
+ setSkillEnabled(name, enabled) {
46400
+ const nextSettings = { ...this.settings };
46401
+ setEnablementInMap(nextSettings, "skills", name, enabled);
46402
+ this.writeSettingsValue(nextSettings);
46403
+ this.settings = nextSettings;
46404
+ }
46405
+ /** Disabled runtime tool names (may contain names no longer installed). */
46406
+ getDisabledTools() {
46407
+ return [...this.settings.tools?.disabledTools ?? []];
46408
+ }
46409
+ /** Whether a runtime tool is enabled; absent means "enabled". */
46410
+ isToolEnabled(name) {
46411
+ return !this.getDisabledTools().includes(name);
46412
+ }
46413
+ /** Enable/disable a runtime tool by name (persisted in settings.json). */
46414
+ setToolEnabled(name, enabled) {
46415
+ const disabled = this.getDisabledTools().filter((tool) => tool !== name);
46416
+ if (!enabled) disabled.push(name);
46417
+ disabled.sort();
46418
+ const nextTools = { ...this.settings.tools ?? {} };
46419
+ delete nextTools.disabledTools;
46420
+ if (disabled.length > 0) nextTools.disabledTools = disabled;
46421
+ const nextSettings = { ...this.settings, tools: nextTools };
46422
+ if (Object.keys(nextTools).length === 0) {
46423
+ delete nextSettings.tools;
46424
+ }
46425
+ this.writeSettingsValue(nextSettings);
46426
+ this.settings = nextSettings;
46427
+ }
46382
46428
  };
46383
46429
 
46384
46430
  // src/doctor.ts
@@ -46772,28 +46818,54 @@ function formatDoctorReport(report) {
46772
46818
  }
46773
46819
 
46774
46820
  // src/permissions-help.ts
46821
+ var MODE_RULES = {
46822
+ default: [
46823
+ "File tools read and write inside the current workspace.",
46824
+ "~/.pie is allowlisted for Pie configuration and session support.",
46825
+ "Shell commands run from the workspace and return structured output, timeout, and truncation details.",
46826
+ "web_research is read-only, uses provider-native web/search/fetch capabilities, and does not grant bash/curl web scraping permission.",
46827
+ "Protected paths from extensions can block reads/writes even when tools are present."
46828
+ ],
46829
+ plan: [
46830
+ "The agent should inspect and plan before making changes.",
46831
+ "Risky write/shell actions should be deferred until the user approves the plan."
46832
+ ],
46833
+ yolo: [
46834
+ "/settings/yolo relaxes filesystem sandbox directory restrictions.",
46835
+ "It does not make destructive shell commands safe; use it only in trusted workspaces."
46836
+ ],
46837
+ subagents: [
46838
+ "Subagents inherit the parent tool set, then runtime policy removes write and high-risk tools.",
46839
+ "Subagents are expected to return evidence and hand off required writes to the parent agent."
46840
+ ]
46841
+ };
46842
+ var MODE_TITLES = {
46843
+ default: "Default mode",
46844
+ plan: "Plan mode",
46845
+ yolo: "YOLO mode",
46846
+ subagents: "Subagents"
46847
+ };
46848
+ function buildPermissionsData() {
46849
+ return {
46850
+ modes: Object.fromEntries(Object.entries(MODE_RULES).map(([key, rules]) => [key, [...rules]])),
46851
+ tools: Object.entries(BUILTIN_TOOL_CAPABILITY_METADATA).map(([name, meta]) => ({
46852
+ name,
46853
+ ...meta.riskClass ? { riskClass: meta.riskClass } : {},
46854
+ ...meta.permissionScope ? { permissionScope: meta.permissionScope } : {},
46855
+ ...meta.availableInPlanMode ? { availableInPlanMode: true } : {},
46856
+ ...meta.allowedForSubagentByDefault ? { allowedForSubagentByDefault: true } : {},
46857
+ ...meta.highRisk ? { highRisk: true } : {}
46858
+ }))
46859
+ };
46860
+ }
46775
46861
  function formatPermissionsHelp() {
46776
- const toolLines = Object.entries(BUILTIN_TOOL_CAPABILITY_METADATA).map(([name, meta]) => ` - ${name}: ${meta.riskClass ?? "unknown"}${meta.permissionScope ? `, scope=${meta.permissionScope}` : ""}${meta.availableInPlanMode ? ", plan" : ""}${meta.allowedForSubagentByDefault ? ", subagent" : ""}${meta.highRisk ? ", high-risk" : ""}`).join("\n");
46862
+ const data = buildPermissionsData();
46863
+ const modeSections = Object.entries(data.modes).map(([key, rules]) => `${MODE_TITLES[key]}:
46864
+ ${rules.map((rule) => ` - ${rule}`).join("\n")}`).join("\n\n");
46865
+ const toolLines = data.tools.map((tool) => ` - ${tool.name}: ${tool.riskClass ?? "unknown"}${tool.permissionScope ? `, scope=${tool.permissionScope}` : ""}${tool.availableInPlanMode ? ", plan" : ""}${tool.allowedForSubagentByDefault ? ", subagent" : ""}${tool.highRisk ? ", high-risk" : ""}`).join("\n");
46777
46866
  return `Pie permissions and safety model
46778
46867
 
46779
- Default mode:
46780
- - File tools read and write inside the current workspace.
46781
- - ~/.pie is allowlisted for Pie configuration and session support.
46782
- - Shell commands run from the workspace and return structured output, timeout, and truncation details.
46783
- - web_research is read-only, uses provider-native web/search/fetch capabilities, and does not grant bash/curl web scraping permission.
46784
- - Protected paths from extensions can block reads/writes even when tools are present.
46785
-
46786
- Plan mode:
46787
- - The agent should inspect and plan before making changes.
46788
- - Risky write/shell actions should be deferred until the user approves the plan.
46789
-
46790
- YOLO mode:
46791
- - /settings/yolo relaxes filesystem sandbox directory restrictions.
46792
- - It does not make destructive shell commands safe; use it only in trusted workspaces.
46793
-
46794
- Subagents:
46795
- - Subagents inherit the parent tool set, then runtime policy removes write and high-risk tools.
46796
- - Subagents are expected to return evidence and hand off required writes to the parent agent.
46868
+ ${modeSections}
46797
46869
 
46798
46870
  Tool policy snapshot:
46799
46871
  ${toolLines}
@@ -47042,30 +47114,677 @@ async function probeModelsConfig(options) {
47042
47114
  message: passed ? `web_search probe passed using ${attempts.find((attempt) => attempt.status === "passed")?.route}.` : "web_search probe failed for all configured candidate routes."
47043
47115
  };
47044
47116
  }
47045
- function parseModelsCommandArgs(args) {
47046
- const parsed = { force: false, stdout: false, json: false };
47047
- for (let i = 0; i < args.length; i += 1) {
47048
- const arg = args[i];
47049
- if (arg === "--force") {
47050
- parsed.force = true;
47051
- } else if (arg === "--json") {
47052
- parsed.json = true;
47053
- } else if (arg === "--stdout") {
47054
- parsed.stdout = true;
47055
- } else if (arg === "--probe" && args[i + 1]) {
47056
- parsed.probe = args[i + 1] === "web_search" ? "web_search" : void 0;
47057
- i += 1;
47058
- } else if (arg.startsWith("--probe=")) {
47059
- const value = arg.slice("--probe=".length);
47060
- parsed.probe = value === "web_search" ? "web_search" : void 0;
47061
- } else if (arg === "--path" && args[i + 1]) {
47062
- parsed.path = args[i + 1];
47063
- i += 1;
47064
- } else if (arg.startsWith("--path=")) {
47065
- parsed.path = arg.slice("--path=".length);
47117
+
47118
+ // src/command-args.ts
47119
+ var HELP_FLAG_NAMES = /* @__PURE__ */ new Set(["--help", "-h"]);
47120
+ function parseCommandArgs(args, spec) {
47121
+ const flags = /* @__PURE__ */ new Map();
47122
+ const positionals = /* @__PURE__ */ new Map();
47123
+ const flagByName = /* @__PURE__ */ new Map();
47124
+ for (const flag of spec.flags) {
47125
+ flagByName.set(flag.name, flag);
47126
+ for (const alias of flag.aliases ?? []) {
47127
+ flagByName.set(alias, flag);
47128
+ }
47129
+ }
47130
+ let help = false;
47131
+ let positionalIndex = 0;
47132
+ for (let index = 0; index < args.length; index += 1) {
47133
+ const arg = args[index];
47134
+ if (HELP_FLAG_NAMES.has(arg)) {
47135
+ help = true;
47136
+ continue;
47137
+ }
47138
+ if (arg.startsWith("--")) {
47139
+ const equals = arg.indexOf("=");
47140
+ const head = equals >= 0 ? arg.slice(0, equals) : arg;
47141
+ const inlineValue = equals >= 0 ? arg.slice(equals + 1) : void 0;
47142
+ if (HELP_FLAG_NAMES.has(head)) {
47143
+ return {
47144
+ help,
47145
+ flags,
47146
+ positionals,
47147
+ error: {
47148
+ code: "unknown_flag",
47149
+ flag: arg,
47150
+ message: `Unknown flag: ${arg}. Use --help (no value) instead.`
47151
+ }
47152
+ };
47153
+ }
47154
+ const flagSpec = flagByName.get(head);
47155
+ if (!flagSpec) {
47156
+ return {
47157
+ help,
47158
+ flags,
47159
+ positionals,
47160
+ error: {
47161
+ code: "unknown_flag",
47162
+ flag: arg,
47163
+ message: `Unknown flag: ${arg}. Run 'pie ${spec.path} --help' for supported flags.`
47164
+ }
47165
+ };
47166
+ }
47167
+ if (flagSpec.kind === "boolean") {
47168
+ if (inlineValue !== void 0) {
47169
+ return {
47170
+ help,
47171
+ flags,
47172
+ positionals,
47173
+ error: {
47174
+ code: "unknown_flag",
47175
+ flag: arg,
47176
+ message: `Flag ${flagSpec.name} does not take a value.`
47177
+ }
47178
+ };
47179
+ }
47180
+ flags.set(flagSpec.name, true);
47181
+ continue;
47182
+ }
47183
+ let value = inlineValue;
47184
+ if (value === void 0) {
47185
+ const next = args[index + 1];
47186
+ if (next === void 0 || next.startsWith("--")) {
47187
+ return {
47188
+ help,
47189
+ flags,
47190
+ positionals,
47191
+ error: {
47192
+ code: "missing_value",
47193
+ flag: flagSpec.name,
47194
+ message: `Flag ${flagSpec.name} requires a value.`
47195
+ }
47196
+ };
47197
+ }
47198
+ value = next;
47199
+ index += 1;
47200
+ }
47201
+ if (flagSpec.repeatable) {
47202
+ const existing = flags.get(flagSpec.name);
47203
+ const collected = Array.isArray(existing) ? existing : [];
47204
+ collected.push(value);
47205
+ flags.set(flagSpec.name, collected);
47206
+ } else {
47207
+ flags.set(flagSpec.name, value);
47208
+ }
47209
+ continue;
47210
+ }
47211
+ const slot = spec.positional[positionalIndex];
47212
+ if (!slot) {
47213
+ return {
47214
+ help,
47215
+ flags,
47216
+ positionals,
47217
+ error: {
47218
+ code: "unexpected_positional",
47219
+ positional: arg,
47220
+ message: `Unexpected argument: ${arg}. Run 'pie ${spec.usage}' to see expected arguments.`
47221
+ }
47222
+ };
47223
+ }
47224
+ if (slot.variadic) {
47225
+ const collected = getPositionals({ help, flags, positionals }, slot.name);
47226
+ collected.push(arg);
47227
+ positionals.set(slot.name, collected);
47228
+ continue;
47229
+ }
47230
+ positionals.set(slot.name, arg);
47231
+ positionalIndex += 1;
47232
+ }
47233
+ if (help) return { help, flags, positionals };
47234
+ for (const slot of spec.positional) {
47235
+ if (!slot.required) continue;
47236
+ const value = positionals.get(slot.name);
47237
+ if (value === void 0 || Array.isArray(value) && value.length === 0) {
47238
+ return {
47239
+ help,
47240
+ flags,
47241
+ positionals,
47242
+ error: {
47243
+ code: "missing_positional",
47244
+ positional: slot.name,
47245
+ message: `Missing required argument: <${slot.name}>.`
47246
+ }
47247
+ };
47248
+ }
47249
+ }
47250
+ return { help, flags, positionals };
47251
+ }
47252
+ function getFlagString(parsed, name) {
47253
+ const value = parsed.flags.get(name);
47254
+ if (typeof value === "string") return value;
47255
+ return void 0;
47256
+ }
47257
+ function getFlagStrings(parsed, name) {
47258
+ const value = parsed.flags.get(name);
47259
+ return Array.isArray(value) ? value : [];
47260
+ }
47261
+ function getFlagBool(parsed, name) {
47262
+ return parsed.flags.get(name) === true;
47263
+ }
47264
+ function getPositional(parsed, name) {
47265
+ const value = parsed.positionals.get(name);
47266
+ if (typeof value === "string") return value;
47267
+ return void 0;
47268
+ }
47269
+ function getPositionals(parsed, name) {
47270
+ const value = parsed.positionals.get(name);
47271
+ return Array.isArray(value) ? value : [];
47272
+ }
47273
+ function renderCommandUsage(spec) {
47274
+ return `Usage: pie ${spec.usage}`;
47275
+ }
47276
+ function renderCommandHelp(spec) {
47277
+ const lines = [renderCommandUsage(spec), "", spec.summary];
47278
+ if (spec.positional.length > 0) {
47279
+ lines.push("", "Arguments:");
47280
+ for (const slot of spec.positional) {
47281
+ const marker = slot.required ? `<${slot.name}>` : `[${slot.name}]`;
47282
+ lines.push(
47283
+ ` ${marker}${slot.variadic ? "..." : ""} ${slot.description}`
47284
+ );
47285
+ }
47286
+ }
47287
+ if (spec.flags.length > 0) {
47288
+ lines.push("", "Options:");
47289
+ for (const flag of spec.flags) {
47290
+ const names = [flag.name, ...flag.aliases ?? []].join(", ");
47291
+ const valueHint = flag.kind === "string" ? " <value>" : "";
47292
+ lines.push(` ${names}${valueHint} ${flag.description}`);
47293
+ }
47294
+ }
47295
+ lines.push("", " --help, -h Show this help");
47296
+ return lines.join("\n");
47297
+ }
47298
+
47299
+ // src/command-specs.ts
47300
+ var SESSIONS_LIST_SPEC = {
47301
+ path: "sessions list",
47302
+ summary: "List sessions, most recently updated first.",
47303
+ usage: "sessions list [--json] [--limit <n>] [--cursor <cursor>] [--cwd <path> | --all] [--fields <list>] [--full]",
47304
+ flags: [
47305
+ { name: "--json", kind: "boolean", description: "Emit the machine envelope (compact JSON)." },
47306
+ { name: "--limit", kind: "string", description: "Page size, 1-1000 (default 50)." },
47307
+ { name: "--cursor", kind: "string", description: "Resume from meta.nextCursor of a previous page." },
47308
+ { name: "--cwd", kind: "string", description: "Only sessions whose cwd matches this path." },
47309
+ { name: "--all", kind: "boolean", description: "Ignore cwd scoping (JSON mode defaults to the current directory)." },
47310
+ { name: "--fields", kind: "string", description: "Extra row fields (comma list): createdAt, cwd, activeEntryId, rootEntryId, sourceSessionId, sourceEntryId, sourceActiveEntryId, forkBaseEntryId, or all." },
47311
+ { name: "--full", kind: "boolean", description: "Disable name truncation." }
47312
+ ],
47313
+ positional: []
47314
+ };
47315
+ var SESSIONS_READ_SPEC = {
47316
+ path: "sessions read",
47317
+ summary: "Read a session's active transcript path.",
47318
+ usage: "sessions read <sessionId> [--json] [--limit <n>] [--before <cursor>] [--fields <list>] [--full]",
47319
+ flags: [
47320
+ { name: "--json", kind: "boolean", description: "Emit the machine envelope (compact JSON)." },
47321
+ { name: "--limit", kind: "string", description: "Max events per page, 1-1000 (default 200)." },
47322
+ { name: "--before", kind: "string", description: "Event entryId or sequence number to read up to (exclusive)." },
47323
+ { name: "--fields", kind: "string", description: "Extra event fields (comma list): timestamp, turnId, message, or all." },
47324
+ { name: "--full", kind: "boolean", description: "Disable content truncation." }
47325
+ ],
47326
+ positional: [
47327
+ { name: "sessionId", required: true, description: "Session identifier." }
47328
+ ]
47329
+ };
47330
+ var SESSIONS_RENAME_SPEC = {
47331
+ path: "sessions rename",
47332
+ summary: "Rename a session.",
47333
+ usage: "sessions rename <sessionId> <name...> [--json] [--cwd <path>]",
47334
+ flags: [
47335
+ { name: "--json", kind: "boolean", description: "Emit the machine envelope (compact JSON)." },
47336
+ { name: "--cwd", kind: "string", description: "Also update the session's cwd." }
47337
+ ],
47338
+ positional: [
47339
+ { name: "sessionId", required: true, description: "Session identifier." },
47340
+ { name: "name", required: true, variadic: true, description: "New session name." }
47341
+ ]
47342
+ };
47343
+ var SESSIONS_REWIND_SPEC = {
47344
+ path: "sessions rewind",
47345
+ summary: "Move a session's active head back to an earlier entry.",
47346
+ usage: "sessions rewind <sessionId> --to <entryId> [--json]",
47347
+ flags: [
47348
+ { name: "--json", kind: "boolean", description: "Emit the machine envelope (compact JSON)." },
47349
+ { name: "--to", kind: "string", description: "Target entryId on the active path." }
47350
+ ],
47351
+ positional: [
47352
+ { name: "sessionId", required: true, description: "Session identifier." }
47353
+ ]
47354
+ };
47355
+ var SESSIONS_FORK_SPEC = {
47356
+ path: "sessions fork",
47357
+ summary: "Fork a session at an entry into a new session.",
47358
+ usage: "sessions fork <sessionId> --from <entryId> --target <newSessionId> --name <name> [--cwd <path>] [--force] [--json]",
47359
+ flags: [
47360
+ { name: "--json", kind: "boolean", description: "Emit the machine envelope (compact JSON)." },
47361
+ { name: "--from", kind: "string", description: "EntryId to fork at." },
47362
+ { name: "--target", kind: "string", description: "Identifier for the new session." },
47363
+ { name: "--name", kind: "string", description: "Name for the new session." },
47364
+ { name: "--cwd", kind: "string", description: "cwd recorded on the new session." },
47365
+ { name: "--force", kind: "boolean", description: "Overwrite the target session if it already exists." }
47366
+ ],
47367
+ positional: [
47368
+ { name: "sessionId", required: true, description: "Source session identifier." }
47369
+ ]
47370
+ };
47371
+ var DOCTOR_SPEC = {
47372
+ path: "doctor",
47373
+ summary: "Diagnose local Pie configuration and runtime dependencies.",
47374
+ usage: "doctor [--json]",
47375
+ flags: [
47376
+ { name: "--json", kind: "boolean", description: "Emit the machine envelope (compact JSON)." }
47377
+ ],
47378
+ positional: []
47379
+ };
47380
+ var MODELS_INIT_SPEC = {
47381
+ path: "models init",
47382
+ summary: "Create a starter models.json template.",
47383
+ usage: "models init [--path <path>] [--force] [--stdout] [--json]",
47384
+ flags: [
47385
+ { name: "--json", kind: "boolean", description: "Emit the machine envelope (compact JSON)." },
47386
+ { name: "--path", kind: "string", description: "Target models.json path (default ~/.pie/models.json)." },
47387
+ { name: "--force", kind: "boolean", description: "Overwrite an existing file." },
47388
+ { name: "--stdout", kind: "boolean", description: "Print the template instead of writing a file." }
47389
+ ],
47390
+ positional: []
47391
+ };
47392
+ var MODELS_VALIDATE_SPEC = {
47393
+ path: "models validate",
47394
+ summary: "Validate models.json configuration.",
47395
+ usage: "models validate [--path <path>] [--probe web_search] [--json]",
47396
+ flags: [
47397
+ { name: "--json", kind: "boolean", description: "Emit the machine envelope (compact JSON)." },
47398
+ { name: "--path", kind: "string", description: "models.json path to validate (default ~/.pie/models.json)." },
47399
+ { name: "--probe", kind: "string", description: "Run a live capability probe (web_search)." }
47400
+ ],
47401
+ positional: []
47402
+ };
47403
+ var EXTENSIONS_LIST_SPEC = {
47404
+ path: "extensions list",
47405
+ summary: "List discovered extensions.",
47406
+ usage: "extensions list [--json] [--extension-path <dir>] [--fields <list>]",
47407
+ flags: [
47408
+ { name: "--json", kind: "boolean", description: "Emit the machine envelope (compact JSON)." },
47409
+ { name: "--extension-path", kind: "string", repeatable: true, description: "Extra extension search directory (repeatable)." },
47410
+ { name: "--no-builtin-extensions", kind: "boolean", description: "Skip built-in extensions." },
47411
+ { name: "--no-extensions", kind: "boolean", description: "Skip all discovered extensions." },
47412
+ { name: "--fields", kind: "string", description: "Extra row fields (comma list): entryPath, description, or all." }
47413
+ ],
47414
+ positional: []
47415
+ };
47416
+ var EXTENSIONS_TOGGLE_SPEC = {
47417
+ path: "extensions enable/disable",
47418
+ summary: "Toggle extension enablement in user or project settings.",
47419
+ usage: "extensions <enable|disable> <name> [--project | --user] [--json]",
47420
+ flags: [
47421
+ { name: "--json", kind: "boolean", description: "Emit the machine envelope (compact JSON)." },
47422
+ { name: "--project", kind: "boolean", description: "Write ./.pie/settings.json (default is user scope)." },
47423
+ { name: "--user", kind: "boolean", description: "Write ~/.pie/settings.json (default)." }
47424
+ ],
47425
+ positional: [
47426
+ { name: "name", required: true, description: "Extension identity." }
47427
+ ]
47428
+ };
47429
+ var PERMISSIONS_SPEC = {
47430
+ path: "permissions",
47431
+ summary: "Explain Pie tool and shell safety defaults.",
47432
+ usage: "permissions [--json]",
47433
+ flags: [
47434
+ { name: "--json", kind: "boolean", description: "Emit the machine envelope (compact JSON)." }
47435
+ ],
47436
+ positional: []
47437
+ };
47438
+ var CAPABILITIES_SPEC = {
47439
+ path: "capabilities",
47440
+ summary: "Report command surface, flags, and output schema generation for agents.",
47441
+ usage: "capabilities [--json]",
47442
+ flags: [
47443
+ { name: "--json", kind: "boolean", description: "Emit the machine envelope (compact JSON)." }
47444
+ ],
47445
+ positional: []
47446
+ };
47447
+ var CHAT_SPEC = {
47448
+ path: "chat",
47449
+ summary: "Interactive chat (default), print mode, or JSONL protocol mode.",
47450
+ usage: "chat [prompt] [--raw-output | --json-output | --json-events] [--session-id <id>] ...",
47451
+ flags: [
47452
+ { name: "--raw-output", kind: "boolean", description: "Print assistant text only in non-interactive mode." },
47453
+ { name: "--json-output", kind: "boolean", description: "Print structured JSON in non-interactive mode." },
47454
+ { name: "--json-events", kind: "boolean", description: "Run JSONL protocol mode over stdin/stdout." },
47455
+ { name: "--event-detail", kind: "string", description: "JSON-events payload detail: compact (default) caps tool results and summaries; full keeps historical caps." },
47456
+ { name: "--session-id", kind: "string", description: "Reuse or create a specific session." },
47457
+ { name: "--resume-required", kind: "boolean", description: "Fail if --session-id cannot be resumed." },
47458
+ { name: "--developer-instructions", kind: "string", description: "Append stable host/developer instructions." },
47459
+ { name: "--developer-instructions-file", kind: "string", description: "Read developer instructions from a file." },
47460
+ { name: "--extension-path", kind: "string", repeatable: true, description: "Extra extension search directory (repeatable)." },
47461
+ { name: "--no-builtin-extensions", kind: "boolean", description: "Skip built-in extensions." },
47462
+ { name: "--no-extensions", kind: "boolean", description: "Skip all discovered extensions." }
47463
+ ],
47464
+ positional: [
47465
+ { name: "prompt", required: false, description: "Initial prompt (starts print mode instead of the TUI)." }
47466
+ ]
47467
+ };
47468
+ var MANAGEMENT_COMMAND_SPECS = [
47469
+ CHAT_SPEC,
47470
+ DOCTOR_SPEC,
47471
+ MODELS_INIT_SPEC,
47472
+ MODELS_VALIDATE_SPEC,
47473
+ SESSIONS_LIST_SPEC,
47474
+ SESSIONS_READ_SPEC,
47475
+ SESSIONS_RENAME_SPEC,
47476
+ SESSIONS_REWIND_SPEC,
47477
+ SESSIONS_FORK_SPEC,
47478
+ EXTENSIONS_LIST_SPEC,
47479
+ EXTENSIONS_TOGGLE_SPEC,
47480
+ PERMISSIONS_SPEC,
47481
+ CAPABILITIES_SPEC
47482
+ ];
47483
+
47484
+ // src/output/envelope.ts
47485
+ var OUTPUT_SCHEMA_VERSION = 1;
47486
+ var EXIT_BUSINESS_FAILURE = 1;
47487
+ var EXIT_USAGE_ERROR = 2;
47488
+ function buildEnvelope(kind, options) {
47489
+ const envelope = {
47490
+ v: OUTPUT_SCHEMA_VERSION,
47491
+ ok: options.ok !== false,
47492
+ kind
47493
+ };
47494
+ if (options.data !== void 0) envelope.data = options.data;
47495
+ if (options.meta && Object.keys(options.meta).length > 0) envelope.meta = options.meta;
47496
+ if (options.help && options.help.length > 0) envelope.help = options.help;
47497
+ if (options.error) envelope.error = options.error;
47498
+ return JSON.stringify(envelope);
47499
+ }
47500
+ function successEnvelope(kind, data, meta, help) {
47501
+ const envelope = {
47502
+ v: OUTPUT_SCHEMA_VERSION,
47503
+ ok: true,
47504
+ kind
47505
+ };
47506
+ if (data !== void 0) envelope.data = data;
47507
+ if (meta && Object.keys(meta).length > 0) envelope.meta = meta;
47508
+ if (help && help.length > 0) envelope.help = help;
47509
+ return JSON.stringify(envelope);
47510
+ }
47511
+ function errorEnvelope(kind, code, message, hint) {
47512
+ const envelope = {
47513
+ v: OUTPUT_SCHEMA_VERSION,
47514
+ ok: false,
47515
+ kind,
47516
+ error: {
47517
+ code,
47518
+ message,
47519
+ ...hint ? { hint } : {}
47520
+ }
47521
+ };
47522
+ return JSON.stringify(envelope);
47523
+ }
47524
+ function printSuccessEnvelope(kind, data, meta, help) {
47525
+ process.stdout.write(`${successEnvelope(kind, data, meta, help)}
47526
+ `);
47527
+ }
47528
+ var CommandError = class extends Error {
47529
+ kind;
47530
+ code;
47531
+ usage;
47532
+ hint;
47533
+ constructor(kind, code, message, options = {}) {
47534
+ super(message);
47535
+ this.name = "CommandError";
47536
+ this.kind = kind;
47537
+ this.code = code;
47538
+ this.usage = options.usage === true;
47539
+ this.hint = options.hint;
47540
+ }
47541
+ };
47542
+ function isCommandError(value) {
47543
+ return value instanceof CommandError;
47544
+ }
47545
+ function emitCommandError(error, options) {
47546
+ const commandError = isCommandError(error) ? error : new CommandError(options.fallbackKind, "internal_error", error instanceof Error ? error.message : String(error));
47547
+ if (options.json) {
47548
+ process.stdout.write(`${errorEnvelope(commandError.kind, commandError.code, commandError.message, commandError.hint)}
47549
+ `);
47550
+ } else {
47551
+ console.error(commandError.message);
47552
+ if (commandError.hint) {
47553
+ console.error(`Hint: ${commandError.hint}`);
47554
+ }
47555
+ }
47556
+ process.exitCode = commandError.usage ? EXIT_USAGE_ERROR : EXIT_BUSINESS_FAILURE;
47557
+ }
47558
+ function truncateText(text, maxChars) {
47559
+ const chars = Array.from(text);
47560
+ if (chars.length <= maxChars) {
47561
+ return { text, truncated: false, fullLength: chars.length };
47562
+ }
47563
+ return { text: chars.slice(0, maxChars).join(""), truncated: true, fullLength: chars.length };
47564
+ }
47565
+
47566
+ // src/capabilities.ts
47567
+ function buildCapabilitiesReport() {
47568
+ return {
47569
+ name: "pie",
47570
+ version: VERSION,
47571
+ outputSchemaVersion: OUTPUT_SCHEMA_VERSION,
47572
+ outputFormats: ["human", "json"],
47573
+ exitCodes: { success: 0, businessFailure: 1, usageError: 2 },
47574
+ commands: MANAGEMENT_COMMAND_SPECS.map((spec) => ({
47575
+ path: spec.path,
47576
+ summary: spec.summary,
47577
+ flags: spec.flags.map((flag) => ({
47578
+ name: flag.name,
47579
+ kind: flag.kind,
47580
+ description: flag.description
47581
+ })),
47582
+ positional: spec.positional.map((slot) => ({
47583
+ name: slot.name,
47584
+ required: slot.required,
47585
+ ...slot.variadic ? { variadic: true } : {},
47586
+ description: slot.description
47587
+ }))
47588
+ }))
47589
+ };
47590
+ }
47591
+ function formatCapabilitiesHelp(report) {
47592
+ const lines = [
47593
+ `pie ${report.version} capabilities`,
47594
+ `output schema: v${report.outputSchemaVersion} (formats: ${report.outputFormats.join(", ")})`,
47595
+ `exit codes: ${report.exitCodes.success} success, ${report.exitCodes.businessFailure} business failure, ${report.exitCodes.usageError} usage error`,
47596
+ "",
47597
+ "Commands:"
47598
+ ];
47599
+ for (const command of report.commands) {
47600
+ lines.push(` pie ${command.path} ${command.summary}`);
47601
+ }
47602
+ lines.push("", "Per-command flags: pie <command> --help");
47603
+ return lines.join("\n");
47604
+ }
47605
+
47606
+ // src/commands/management.ts
47607
+ function emitTopLevelUsageError(kind, code, message, args) {
47608
+ emitCommandError(new CommandError(kind, code, message, { usage: true }), {
47609
+ json: args.includes("--json"),
47610
+ fallbackKind: kind
47611
+ });
47612
+ }
47613
+ async function runDoctorCommand(args) {
47614
+ const parsed = parseCommandArgs(args, DOCTOR_SPEC);
47615
+ if (parsed.error) {
47616
+ emitTopLevelUsageError("doctor", parsed.error.code, parsed.error.message, args);
47617
+ return;
47618
+ }
47619
+ if (parsed.help) {
47620
+ console.log(renderCommandHelp(DOCTOR_SPEC));
47621
+ return;
47622
+ }
47623
+ const json = getFlagBool(parsed, "--json");
47624
+ const report = await runDoctor();
47625
+ if (json) {
47626
+ process.stdout.write(`${buildEnvelope("doctor", {
47627
+ ok: report.ok,
47628
+ data: { summary: report.summary, checks: report.checks },
47629
+ ...report.ok ? {} : { error: { code: "doctor_failed", message: `${report.summary.fail} check(s) failed. Run pie doctor for details.` } }
47630
+ })}
47631
+ `);
47632
+ } else {
47633
+ console.log(formatDoctorReport(report));
47634
+ }
47635
+ if (!report.ok) {
47636
+ process.exitCode = EXIT_BUSINESS_FAILURE;
47637
+ }
47638
+ }
47639
+ function printModelsUsage() {
47640
+ console.log(`Usage:
47641
+ pie models init [--path <path>] [--force] [--stdout] [--json]
47642
+ pie models validate [--path <path>] [--probe web_search] [--json]`);
47643
+ }
47644
+ async function runModelsCommand(args) {
47645
+ const subcommand = args[0];
47646
+ if (!subcommand || subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
47647
+ printModelsUsage();
47648
+ return;
47649
+ }
47650
+ const rest = args.slice(1);
47651
+ if (subcommand === "init") {
47652
+ await runModelsInit(rest, args);
47653
+ return;
47654
+ }
47655
+ if (subcommand === "validate") {
47656
+ await runModelsValidate(rest, args);
47657
+ return;
47658
+ }
47659
+ emitTopLevelUsageError("models", "unknown_command", `Unknown models command: ${subcommand}`, args);
47660
+ }
47661
+ async function runModelsInit(rest, allArgs) {
47662
+ const parsed = parseCommandArgs(rest, MODELS_INIT_SPEC);
47663
+ if (parsed.error) {
47664
+ emitTopLevelUsageError("models.init", parsed.error.code, `${parsed.error.message}
47665
+ ${renderCommandUsage(MODELS_INIT_SPEC)}`, allArgs);
47666
+ return;
47667
+ }
47668
+ if (parsed.help) {
47669
+ console.log(renderCommandHelp(MODELS_INIT_SPEC));
47670
+ return;
47671
+ }
47672
+ const json = getFlagBool(parsed, "--json");
47673
+ try {
47674
+ const result = initModelsConfig({
47675
+ path: getFlagString(parsed, "--path"),
47676
+ force: getFlagBool(parsed, "--force"),
47677
+ stdout: getFlagBool(parsed, "--stdout")
47678
+ });
47679
+ if (getFlagBool(parsed, "--stdout")) {
47680
+ process.stdout.write(result.content);
47681
+ return;
47682
+ }
47683
+ if (json) {
47684
+ printSuccessEnvelope("models.init", { path: result.path, wrote: result.wrote });
47685
+ } else {
47686
+ console.log(`Wrote models.json template: ${result.path}`);
47687
+ }
47688
+ } catch (error) {
47689
+ emitCommandError(error, { json, fallbackKind: "models.init" });
47690
+ }
47691
+ }
47692
+ async function runModelsValidate(rest, allArgs) {
47693
+ const parsed = parseCommandArgs(rest, MODELS_VALIDATE_SPEC);
47694
+ if (parsed.error) {
47695
+ emitTopLevelUsageError("models.validate", parsed.error.code, `${parsed.error.message}
47696
+ ${renderCommandUsage(MODELS_VALIDATE_SPEC)}`, allArgs);
47697
+ return;
47698
+ }
47699
+ if (parsed.help) {
47700
+ console.log(renderCommandHelp(MODELS_VALIDATE_SPEC));
47701
+ return;
47702
+ }
47703
+ const json = getFlagBool(parsed, "--json");
47704
+ const probePurpose = getFlagString(parsed, "--probe");
47705
+ const result = validateModelsConfig({ path: getFlagString(parsed, "--path") });
47706
+ const probe = result.ok && probePurpose ? await probeModelsConfig({ path: getFlagString(parsed, "--path"), probe: probePurpose }) : void 0;
47707
+ if (json) {
47708
+ const { ok: _ok, ...fields } = result;
47709
+ process.stdout.write(`${buildEnvelope("models.validate", {
47710
+ ok: result.ok && probe?.status !== "failed",
47711
+ data: { ...fields, ...probe ? { probe } : {} },
47712
+ ...result.ok ? {} : { error: { code: "models_invalid", message: result.message } },
47713
+ ...probe?.status === "failed" ? { error: { code: "probe_failed", message: probe.message } } : {}
47714
+ })}
47715
+ `);
47716
+ if (!result.ok || probe?.status === "failed") process.exitCode = EXIT_BUSINESS_FAILURE;
47717
+ return;
47718
+ }
47719
+ if (!result.ok) {
47720
+ console.error(result.message);
47721
+ process.exitCode = EXIT_BUSINESS_FAILURE;
47722
+ return;
47723
+ }
47724
+ console.log(result.message);
47725
+ if (result.profiles.length > 0) {
47726
+ console.log(`Profiles: ${result.profiles.join(", ")}`);
47727
+ console.log(`Models: ${result.modelCount}`);
47728
+ }
47729
+ if (result.defaultModel) {
47730
+ console.log(`Default: ${result.defaultModel}`);
47731
+ }
47732
+ if (result.modelClasses && result.modelClasses.length > 0) {
47733
+ console.log(`Model classes: ${result.modelClasses.join(", ")}`);
47734
+ }
47735
+ if (result.webSearchModels && result.webSearchModels.length > 0) {
47736
+ console.log(`Web search: ${result.webSearchModels.join(", ")}`);
47737
+ }
47738
+ if (result.webSearchRouteCandidates && result.webSearchRouteCandidates.length > 0) {
47739
+ console.log(`Web search routes: ${result.webSearchRouteCandidates.join(", ")}`);
47740
+ }
47741
+ if (result.fileCapabilityModels && result.fileCapabilityModels.length > 0) {
47742
+ console.log(`File capabilities: ${result.fileCapabilityModels.join(", ")}`);
47743
+ }
47744
+ if (result.toolModelRoutes && result.toolModelRoutes.length > 0) {
47745
+ console.log(`Tool model routes: ${result.toolModelRoutes.join(", ")}`);
47746
+ }
47747
+ if (probePurpose) {
47748
+ const probeReport = probe ?? await probeModelsConfig({ path: getFlagString(parsed, "--path"), probe: probePurpose });
47749
+ console.log(`Probe ${probeReport.purpose}: ${probeReport.message}`);
47750
+ for (const attempt of probeReport.attempts) {
47751
+ console.log(`- ${attempt.status} ${attempt.route} sources=${attempt.sourceCount}${attempt.failureCategory ? ` failure=${attempt.failureCategory}` : ""}`);
47066
47752
  }
47753
+ if (probeReport.status === "failed") process.exitCode = EXIT_BUSINESS_FAILURE;
47754
+ }
47755
+ }
47756
+ function runPermissionsCommand(args) {
47757
+ const parsed = parseCommandArgs(args, PERMISSIONS_SPEC);
47758
+ if (parsed.error) {
47759
+ emitTopLevelUsageError("permissions", parsed.error.code, parsed.error.message, args);
47760
+ return;
47761
+ }
47762
+ if (parsed.help) {
47763
+ console.log(renderCommandHelp(PERMISSIONS_SPEC));
47764
+ return;
47765
+ }
47766
+ if (getFlagBool(parsed, "--json")) {
47767
+ printSuccessEnvelope("permissions", buildPermissionsData());
47768
+ return;
47067
47769
  }
47068
- return parsed;
47770
+ console.log(formatPermissionsHelp());
47771
+ }
47772
+ function runCapabilitiesCommand(args) {
47773
+ const parsed = parseCommandArgs(args, CAPABILITIES_SPEC);
47774
+ if (parsed.error) {
47775
+ emitTopLevelUsageError("capabilities", parsed.error.code, parsed.error.message, args);
47776
+ return;
47777
+ }
47778
+ if (parsed.help) {
47779
+ console.log(renderCommandHelp(CAPABILITIES_SPEC));
47780
+ return;
47781
+ }
47782
+ const report = buildCapabilitiesReport();
47783
+ if (getFlagBool(parsed, "--json")) {
47784
+ printSuccessEnvelope("capabilities", report, void 0, ["try: pie sessions list --json"]);
47785
+ return;
47786
+ }
47787
+ console.log(formatCapabilitiesHelp(report));
47069
47788
  }
47070
47789
 
47071
47790
  // src/commands/sessions.ts
@@ -47097,7 +47816,7 @@ async function loadRequiredSession(store, sessionId) {
47097
47816
  async function rewindStoredSession(params) {
47098
47817
  const data = await loadRequiredSession(params.store, params.sessionId);
47099
47818
  if (!data.entries.some((entry) => entry.id === params.entryId)) {
47100
- throw new SessionControlError("checkpoint_not_found", `Entry not found: ${params.entryId}`);
47819
+ throw new SessionControlError("entry_not_found", `Entry not found: ${params.entryId}`);
47101
47820
  }
47102
47821
  try {
47103
47822
  await params.store.save(params.sessionId, data.entries, {
@@ -47112,9 +47831,7 @@ async function rewindStoredSession(params) {
47112
47831
  return {
47113
47832
  sessionId: params.sessionId,
47114
47833
  entryId: params.entryId,
47115
- checkpointId: params.entryId,
47116
47834
  activeEntryId: params.entryId,
47117
- headEntryId: params.entryId,
47118
47835
  metadata
47119
47836
  };
47120
47837
  } catch (error) {
@@ -47124,12 +47841,14 @@ async function rewindStoredSession(params) {
47124
47841
  }
47125
47842
  async function forkStoredSession(params) {
47126
47843
  try {
47127
- const existingTarget = await maybeLoadExistingForkTarget(params);
47128
- if (existingTarget) return existingTarget;
47844
+ if (!params.force) {
47845
+ const existingTarget = await maybeLoadExistingForkTarget(params);
47846
+ if (existingTarget) return existingTarget;
47847
+ }
47129
47848
  const source = await loadRequiredSession(params.store, params.sourceSessionId);
47130
47849
  const pathEntries = pathToEntry(source, params.sourceEntryId);
47131
47850
  if (pathEntries.length === 0 || pathEntries[pathEntries.length - 1]?.id !== params.sourceEntryId) {
47132
- throw new SessionControlError("checkpoint_not_found", `Entry not found: ${params.sourceEntryId}`);
47851
+ throw new SessionControlError("entry_not_found", `Entry not found: ${params.sourceEntryId}`);
47133
47852
  }
47134
47853
  const now = params.now ?? Date.now();
47135
47854
  const idMap = /* @__PURE__ */ new Map();
@@ -47202,11 +47921,8 @@ function buildForkResult(params) {
47202
47921
  return {
47203
47922
  sourceSessionId: params.sourceSessionId,
47204
47923
  sourceEntryId: params.sourceEntryId,
47205
- sourceCheckpointId: params.sourceEntryId,
47206
47924
  targetSessionId: params.metadata.id,
47207
47925
  targetActiveEntryId,
47208
- targetHeadEntryId: targetActiveEntryId,
47209
- headEntryId: targetActiveEntryId,
47210
47926
  sourceActiveEntryId: params.sourceActiveEntryId,
47211
47927
  forkBaseEntryId,
47212
47928
  metadata: params.metadata
@@ -47229,238 +47945,312 @@ function cloneJson(value) {
47229
47945
  }
47230
47946
 
47231
47947
  // src/commands/sessions.ts
47948
+ var NAME_MAX_CHARS = 24;
47949
+ var CONTENT_MAX_CHARS = 80;
47950
+ var LIST_ROW_EXTRA_FIELDS = [
47951
+ "createdAt",
47952
+ "cwd",
47953
+ "activeEntryId",
47954
+ "rootEntryId",
47955
+ "sourceSessionId",
47956
+ "sourceEntryId",
47957
+ "sourceActiveEntryId",
47958
+ "forkBaseEntryId"
47959
+ ];
47960
+ var READ_EVENT_EXTRA_FIELDS = ["timestamp", "turnId", "message"];
47961
+ var SUBCOMMANDS = {
47962
+ list: { spec: SESSIONS_LIST_SPEC, kind: "sessions.list", run: listSessions },
47963
+ read: { spec: SESSIONS_READ_SPEC, kind: "sessions.read", run: readSession },
47964
+ rename: { spec: SESSIONS_RENAME_SPEC, kind: "sessions.rename", run: renameSession },
47965
+ rewind: { spec: SESSIONS_REWIND_SPEC, kind: "sessions.rewind", run: rewindSession },
47966
+ fork: { spec: SESSIONS_FORK_SPEC, kind: "sessions.fork", run: forkSession }
47967
+ };
47232
47968
  async function runSessionsCommand(args) {
47233
- const command = args[0] || "help";
47234
- const rest = args.slice(1);
47969
+ const first = args[0];
47970
+ if (!first || first === "help" || first === "--help" || first === "-h") {
47971
+ printSessionsUsage();
47972
+ return;
47973
+ }
47974
+ const entry = SUBCOMMANDS[first];
47235
47975
  try {
47236
- if (command === "list") {
47237
- await listSessions(rest);
47238
- return;
47239
- }
47240
- if (command === "read") {
47241
- await readSession(rest);
47242
- return;
47243
- }
47244
- if (command === "rename") {
47245
- await renameSession(rest);
47246
- return;
47247
- }
47248
- if (command === "rewind") {
47249
- await rewindSession(rest);
47250
- return;
47976
+ if (!entry) {
47977
+ throw new CommandError("sessions", "unknown_command", `Unknown sessions command: ${first}`, {
47978
+ usage: true,
47979
+ hint: "subcommands: list, read, rename, rewind, fork"
47980
+ });
47251
47981
  }
47252
- if (command === "fork") {
47253
- await forkSession(rest);
47254
- return;
47982
+ const parsed = parseCommandArgs(args.slice(1), entry.spec);
47983
+ if (parsed.error) {
47984
+ throw new CommandError(entry.kind, parsed.error.code, `${parsed.error.message}
47985
+ ${renderCommandUsage(entry.spec)}`, { usage: true });
47255
47986
  }
47256
- if (command === "help" || command === "--help" || command === "-h") {
47257
- printSessionsUsage();
47987
+ if (parsed.help) {
47988
+ console.log(renderCommandHelp(entry.spec));
47258
47989
  return;
47259
47990
  }
47260
- throw new Error(`Unknown sessions command: ${command}`);
47991
+ await entry.run(parsed);
47261
47992
  } catch (error) {
47262
- if (hasFlag(args, "--json")) {
47263
- console.log(JSON.stringify({ ok: false, error: error instanceof Error ? error.message : String(error) }));
47264
- } else {
47265
- console.error(error instanceof Error ? error.message : String(error));
47266
- }
47267
- process.exitCode = 1;
47993
+ const mapped = toSessionCommandError(error, entry?.kind ?? "sessions");
47994
+ emitCommandError(mapped, { json: args.includes("--json"), fallbackKind: entry?.kind ?? "sessions" });
47268
47995
  }
47269
47996
  }
47270
- async function listSessions(args) {
47271
- const json = hasFlag(args, "--json");
47272
- const limit = numericFlag(args, "--limit", 50);
47273
- const cwd = stringFlag(args, "--cwd");
47274
- const cursor = stringFlag(args, "--cursor");
47997
+ function toSessionCommandError(error, kind) {
47998
+ if (error instanceof SessionControlError) {
47999
+ const hints = {
48000
+ session_not_found: "list sessions: pie sessions list --json",
48001
+ target_exists: "choose a new --target, or add --force to overwrite"
48002
+ };
48003
+ return new CommandError(kind, error.code, error.message, { hint: hints[error.code] });
48004
+ }
48005
+ return error;
48006
+ }
48007
+ async function listSessions(parsed) {
48008
+ const json = getFlagBool(parsed, "--json");
48009
+ const all = getFlagBool(parsed, "--all");
48010
+ const cwdFlag = getFlagString(parsed, "--cwd");
48011
+ const limit = parseLimitFlag(parsed, "sessions.list", 50);
48012
+ const cursor = getFlagString(parsed, "--cursor");
48013
+ const fields = parseExtraFields(getFlagString(parsed, "--fields"), LIST_ROW_EXTRA_FIELDS, "sessions.list");
48014
+ const full = getFlagBool(parsed, "--full");
48015
+ const cwdFilter = cwdFlag ?? (json && !all ? process.cwd() : void 0);
47275
48016
  const store = new FileSessionStore(getSessionsDir());
47276
48017
  const cursorState = decodeListCursor(cursor);
47277
- const queryKey = listCursorQueryKey({ cwd });
48018
+ const queryKey = listCursorQueryKey({ cwd: cwdFilter });
47278
48019
  const after = cursorState.q === queryKey ? cursorState.after : void 0;
47279
- const page = await store.listMetadataPage({
47280
- limit,
47281
- after,
47282
- filter: (metadata) => !cwd || cwdMatches(typeof metadata.cwd === "string" ? metadata.cwd : void 0, cwd)
47283
- });
47284
- const sessions = page.metadata.map(sessionSummary);
47285
- const hasMore = page.hasMore;
47286
- const lastSession = sessions.at(-1);
47287
- const nextCursor = hasMore && lastSession ? encodeListCursor({ v: 2, q: queryKey, after: { updatedAt: lastSession.updatedAt, id: lastSession.id } }) : void 0;
47288
- if (json) {
47289
- console.log(JSON.stringify({ ok: true, sessions, hasMore, nextCursor }, null, 2));
47290
- return;
47291
- }
47292
- if (sessions.length === 0) {
47293
- console.log("No sessions.");
48020
+ const matchesCwd = cwdFilter ? (metadata) => cwdMatches(typeof metadata.cwd === "string" ? metadata.cwd : void 0, cwdFilter) : void 0;
48021
+ const page = await store.listMetadataPage({ limit, after, filter: matchesCwd });
48022
+ if (!json) {
48023
+ if (page.metadata.length === 0) {
48024
+ console.log("No sessions.");
48025
+ return;
48026
+ }
48027
+ for (const session of page.metadata) {
48028
+ console.log(`${session.id} ${session.name} ${new Date(session.updatedAt).toISOString()}${session.cwd ? ` ${session.cwd}` : ""}`);
48029
+ }
47294
48030
  return;
47295
48031
  }
47296
- for (const session of sessions) {
47297
- console.log(`${session.id} ${session.name} ${new Date(session.updatedAt).toISOString()}${session.cwd ? ` ${session.cwd}` : ""}`);
48032
+ const rows = page.metadata.map((metadata) => listRow(metadata, fields, full));
48033
+ const hasMore = page.hasMore;
48034
+ const lastSession = page.metadata.at(-1);
48035
+ const nextCursor = hasMore && lastSession ? encodeListCursor({ v: 2, q: queryKey, after: { updatedAt: lastSession.updatedAt, id: lastSession.id } }) : void 0;
48036
+ const total = matchesCwd ? (await store.listMetadata()).filter(matchesCwd).length : (await store.listMetadata()).length;
48037
+ const meta = {
48038
+ count: rows.length,
48039
+ total,
48040
+ hasMore,
48041
+ scope: cwdFilter ? "cwd" : "all",
48042
+ ...cwdFilter ? { cwd: cwdFilter } : {},
48043
+ ...nextCursor ? { nextCursor } : {}
48044
+ };
48045
+ const help = [];
48046
+ if (nextCursor) help.push("next page: pass meta.nextCursor to --cursor");
48047
+ if (rows.some((row) => row.truncated === true)) help.push("full names: add --full");
48048
+ if (cwdFilter) help.push("all projects: add --all");
48049
+ if (rows.length === 0) help.push("sessions are created by running pie chat in a project directory");
48050
+ else if (!nextCursor) help.push("open a session: pie sessions read <id> --json");
48051
+ printSuccessEnvelope("sessions.list", rows, meta, help.slice(0, 3));
48052
+ }
48053
+ function listRow(metadata, fields, full) {
48054
+ const name = full ? { text: metadata.name, truncated: false } : truncateText(metadata.name, NAME_MAX_CHARS);
48055
+ const row = {
48056
+ id: metadata.id,
48057
+ name: name.text,
48058
+ updatedAt: metadata.updatedAt,
48059
+ count: metadata.entryCount
48060
+ };
48061
+ if (name.truncated) row.truncated = true;
48062
+ const optional = {
48063
+ createdAt: metadata.createdAt,
48064
+ cwd: typeof metadata.cwd === "string" ? metadata.cwd : void 0,
48065
+ activeEntryId: metadata.activeEntryId ?? void 0,
48066
+ rootEntryId: metadata.rootEntryId ?? void 0,
48067
+ sourceSessionId: typeof metadata.sourceSessionId === "string" ? metadata.sourceSessionId : void 0,
48068
+ sourceEntryId: typeof metadata.sourceEntryId === "string" ? metadata.sourceEntryId : void 0,
48069
+ sourceActiveEntryId: typeof metadata.sourceActiveEntryId === "string" ? metadata.sourceActiveEntryId : void 0,
48070
+ forkBaseEntryId: typeof metadata.forkBaseEntryId === "string" ? metadata.forkBaseEntryId : void 0
48071
+ };
48072
+ for (const field of fields) {
48073
+ const value = optional[field];
48074
+ if (value !== void 0) row[field] = value;
48075
+ }
48076
+ return row;
48077
+ }
48078
+ async function readSession(parsed) {
48079
+ const json = getFlagBool(parsed, "--json");
48080
+ const limit = parseLimitFlag(parsed, "sessions.read", 200);
48081
+ const before = getFlagString(parsed, "--before");
48082
+ const fields = parseExtraFields(getFlagString(parsed, "--fields"), READ_EVENT_EXTRA_FIELDS, "sessions.read");
48083
+ const full = getFlagBool(parsed, "--full");
48084
+ const sessionId = getPositional(parsed, "sessionId");
48085
+ if (!sessionId) {
48086
+ throw new CommandError("sessions.read", "missing_positional", renderCommandUsage(SESSIONS_READ_SPEC), { usage: true });
47298
48087
  }
47299
- }
47300
- async function readSession(args) {
47301
- const json = hasFlag(args, "--json");
47302
- const includeMessage = hasFlag(args, "--raw") || hasFlag(args, "--include-message");
47303
- const sessionId = positionalArgs(args, /* @__PURE__ */ new Set(["--limit", "--before"]))[0];
47304
- if (!sessionId) throw new Error("Usage: pie sessions read <sessionId> [--json] [--raw|--include-message] [--limit <n>] [--before <cursor>]");
47305
- const limit = numericFlag(args, "--limit", 200);
47306
- const before = stringFlag(args, "--before");
47307
48088
  const store = new FileSessionStore(getSessionsDir());
47308
- const data = await store.load(sessionId);
47309
- if (!data) throw new Error(`Session not found: ${sessionId}`);
48089
+ const data = await loadSession(store, sessionId, "sessions.read");
47310
48090
  const events = activeTranscriptEvents(activePathEntries(data));
47311
48091
  const end = resolveBeforeCursor(events, before);
47312
48092
  const start = Math.max(0, end - limit);
47313
48093
  const page = events.slice(start, end);
47314
- const response = {
47315
- ok: true,
47316
- session: sessionSummary(data.metadata),
47317
- currentHead: data.metadata.activeEntryId,
48094
+ if (!json) {
48095
+ console.log(`${data.metadata.id}: ${data.metadata.name}`);
48096
+ for (const entry of page) {
48097
+ console.log(`${entry.logicalSeq} ${entry.type}${entry.role ? `:${entry.role}` : ""} ${entry.content ?? ""}`);
48098
+ }
48099
+ return;
48100
+ }
48101
+ const rows = page.map((event) => readEventRow(event, fields, full));
48102
+ const meta = {
48103
+ count: rows.length,
47318
48104
  activeEntryId: data.metadata.activeEntryId,
47319
- events: json && !includeMessage ? page.map(stableTranscriptEvent) : page,
47320
48105
  hasMoreBefore: start > 0,
47321
- nextBefore: start > 0 ? String(start) : void 0
48106
+ ...start > 0 ? { nextBefore: String(start) } : {}
47322
48107
  };
47323
- if (json) {
47324
- console.log(JSON.stringify(response, null, 2));
47325
- return;
47326
- }
47327
- console.log(`${response.session.id}: ${response.session.name}`);
47328
- for (const entry of page) {
47329
- console.log(`${entry.logicalSeq} ${entry.type}${entry.role ? `:${entry.role}` : ""} ${entry.content ?? ""}`);
48108
+ const help = [];
48109
+ if (start > 0) help.push("earlier events: pass meta.nextBefore to --before");
48110
+ if (rows.some((row) => row.truncated === true)) help.push("full content: add --full or --fields message");
48111
+ printSuccessEnvelope("sessions.read", { session: fullSessionRow(data.metadata), events: rows }, meta, help);
48112
+ }
48113
+ function readEventRow(event, fields, full) {
48114
+ const row = {
48115
+ seq: event.logicalSeq,
48116
+ entryId: event.entryId,
48117
+ type: event.type
48118
+ };
48119
+ if (event.role) row.role = event.role;
48120
+ if (event.content !== void 0) {
48121
+ if (full) {
48122
+ row.content = event.content;
48123
+ } else {
48124
+ const content = truncateText(event.content, CONTENT_MAX_CHARS);
48125
+ row.content = content.text;
48126
+ if (content.truncated) row.truncated = true;
48127
+ }
47330
48128
  }
48129
+ if (fields.includes("timestamp")) row.timestamp = event.timestamp;
48130
+ if (fields.includes("turnId") && event.turnId) row.turnId = event.turnId;
48131
+ if (fields.includes("message") && event.message !== void 0) row.message = event.message;
48132
+ return row;
47331
48133
  }
47332
- async function renameSession(args) {
47333
- const json = hasFlag(args, "--json");
47334
- const cwd = stringFlag(args, "--cwd");
47335
- const positionals = positionalArgs(args, /* @__PURE__ */ new Set(["--cwd"]));
47336
- const sessionId = positionals[0];
47337
- const name = positionals.slice(1).join(" ").trim();
47338
- if (!sessionId || !name) throw new Error("Usage: pie sessions rename <sessionId> <name> [--json] [--cwd <path>]");
48134
+ async function renameSession(parsed) {
48135
+ const json = getFlagBool(parsed, "--json");
48136
+ const cwd = getFlagString(parsed, "--cwd");
48137
+ const sessionId = getPositional(parsed, "sessionId");
48138
+ const name = getPositionals(parsed, "name").join(" ").trim();
48139
+ if (!sessionId || !name) {
48140
+ throw new CommandError("sessions.rename", "missing_positional", renderCommandUsage(SESSIONS_RENAME_SPEC), { usage: true });
48141
+ }
47339
48142
  const store = new FileSessionStore(getSessionsDir());
47340
- const data = await store.load(sessionId);
47341
- if (!data) throw new Error(`Session not found: ${sessionId}`);
48143
+ const data = await loadSession(store, sessionId, "sessions.rename");
47342
48144
  await store.save(sessionId, data.entries, { ...data.metadata, name, nameSource: "manual", ...cwd ? { cwd } : {}, updatedAt: Date.now() });
47343
48145
  const updated = await store.getMetadata(sessionId);
47344
- if (!updated) throw new Error(`Session not found after rename: ${sessionId}`);
47345
- const response = { ok: true, session: sessionSummary(updated) };
48146
+ if (!updated) throw new CommandError("sessions.rename", "session_not_found", `Session not found after rename: ${sessionId}`);
47346
48147
  if (json) {
47347
- console.log(JSON.stringify(response, null, 2));
48148
+ printSuccessEnvelope("sessions.rename", { session: fullSessionRow(updated) });
47348
48149
  return;
47349
48150
  }
47350
48151
  console.log(`Renamed ${sessionId} to "${name}".`);
47351
48152
  }
47352
- async function rewindSession(args) {
47353
- const json = hasFlag(args, "--json");
47354
- const sessionId = positionalArgs(args, /* @__PURE__ */ new Set(["--to"]))[0];
47355
- const entryId = stringFlag(args, "--to");
47356
- if (!sessionId || !entryId) throw new Error("Usage: pie sessions rewind <sessionId> --to <entryId> [--json]");
48153
+ async function rewindSession(parsed) {
48154
+ const json = getFlagBool(parsed, "--json");
48155
+ const sessionId = getPositional(parsed, "sessionId");
48156
+ const entryId = getFlagString(parsed, "--to");
48157
+ if (!sessionId || !entryId) {
48158
+ throw new CommandError("sessions.rewind", "missing_positional", renderCommandUsage(SESSIONS_REWIND_SPEC), { usage: true });
48159
+ }
47357
48160
  const store = new FileSessionStore(getSessionsDir());
47358
48161
  const result = await rewindStoredSession({ store, sessionId, entryId });
47359
- const response = {
47360
- ok: true,
47361
- session: sessionSummary(result.metadata),
47362
- activeEntryId: result.activeEntryId,
47363
- currentHead: result.headEntryId
47364
- };
47365
48162
  if (json) {
47366
- console.log(JSON.stringify(response, null, 2));
48163
+ printSuccessEnvelope("sessions.rewind", {
48164
+ session: fullSessionRow(result.metadata),
48165
+ activeEntryId: result.activeEntryId
48166
+ });
47367
48167
  return;
47368
48168
  }
47369
48169
  console.log(`Rewound ${sessionId} to ${entryId}.`);
47370
48170
  }
47371
- async function forkSession(args) {
47372
- const json = hasFlag(args, "--json");
47373
- const sourceSessionId = positionalArgs(args, /* @__PURE__ */ new Set(["--from", "--target", "--name", "--cwd"]))[0];
47374
- const sourceEntryId = stringFlag(args, "--from");
47375
- const targetSessionId = stringFlag(args, "--target");
47376
- const name = stringFlag(args, "--name");
47377
- const cwd = stringFlag(args, "--cwd");
47378
- if (!sourceSessionId || !sourceEntryId || !targetSessionId || !name) {
47379
- throw new Error("Usage: pie sessions fork <sessionId> --from <entryId> --target <newSessionId> --name <name> [--cwd <path>] [--json]");
48171
+ async function forkSession(parsed) {
48172
+ const json = getFlagBool(parsed, "--json");
48173
+ const sessionId = getPositional(parsed, "sessionId");
48174
+ const sourceEntryId = getFlagString(parsed, "--from");
48175
+ const targetSessionId = getFlagString(parsed, "--target");
48176
+ const name = getFlagString(parsed, "--name");
48177
+ const cwd = getFlagString(parsed, "--cwd");
48178
+ const force = getFlagBool(parsed, "--force");
48179
+ if (!sessionId || !sourceEntryId || !targetSessionId || !name) {
48180
+ throw new CommandError("sessions.fork", "missing_positional", renderCommandUsage(SESSIONS_FORK_SPEC), { usage: true });
47380
48181
  }
47381
48182
  const store = new FileSessionStore(getSessionsDir());
47382
48183
  const result = await forkStoredSession({
47383
48184
  store,
47384
- sourceSessionId,
48185
+ sourceSessionId: sessionId,
47385
48186
  sourceEntryId,
47386
48187
  targetSessionId,
47387
48188
  name,
47388
- cwd
48189
+ cwd,
48190
+ force
47389
48191
  });
47390
- const response = {
47391
- ok: true,
47392
- session: sessionSummary(result.metadata),
47393
- sourceSessionId,
47394
- sourceEntryId,
47395
- sourceActiveEntryId: result.sourceActiveEntryId,
47396
- forkBaseEntryId: result.forkBaseEntryId,
47397
- activeEntryId: result.targetActiveEntryId,
47398
- currentHead: result.targetHeadEntryId
47399
- };
47400
48192
  if (json) {
47401
- console.log(JSON.stringify(response, null, 2));
48193
+ printSuccessEnvelope("sessions.fork", {
48194
+ session: fullSessionRow(result.metadata),
48195
+ sourceSessionId: result.sourceSessionId,
48196
+ sourceEntryId: result.sourceEntryId,
48197
+ activeEntryId: result.targetActiveEntryId,
48198
+ forkBaseEntryId: result.forkBaseEntryId
48199
+ });
47402
48200
  return;
47403
48201
  }
47404
- console.log(`Forked ${sourceSessionId} at ${sourceEntryId} into ${targetSessionId}.`);
47405
- }
47406
- function printSessionsUsage() {
47407
- console.log(`Usage:
47408
- pie sessions list [--json] [--limit <n>] [--cursor <cursor>] [--cwd <path>]
47409
- pie sessions read <sessionId> [--json] [--raw|--include-message] [--limit <n>] [--before <cursor>]
47410
- pie sessions rename <sessionId> <name> [--json] [--cwd <path>]
47411
- pie sessions rewind <sessionId> --to <entryId> [--json]
47412
- pie sessions fork <sessionId> --from <entryId> --target <newSessionId> --name <name> [--cwd <path>] [--json]`);
47413
- }
47414
- function hasFlag(args, flag) {
47415
- return args.includes(flag);
47416
- }
47417
- function stringFlag(args, flag) {
47418
- const index = args.indexOf(flag);
47419
- if (index < 0) return void 0;
47420
- const value = args[index + 1];
47421
- return value && !value.startsWith("-") ? value : void 0;
47422
- }
47423
- function numericFlag(args, flag, fallback) {
47424
- const index = args.indexOf(flag);
47425
- if (index < 0) return fallback;
47426
- const value = args[index + 1];
47427
- if (!value) throw new Error(`${flag} requires a value.`);
47428
- const parsed = Number(value);
47429
- const max = 1e3;
47430
- if (!Number.isInteger(parsed) || parsed <= 0 || parsed > max) {
47431
- throw new Error(`${flag} must be a positive integer no greater than ${max}.`);
47432
- }
47433
- return parsed;
48202
+ console.log(`Forked ${sessionId} at ${sourceEntryId} into ${targetSessionId}.`);
47434
48203
  }
47435
- function positionalArgs(args, valueFlags) {
47436
- const result = [];
47437
- for (let index = 0; index < args.length; index += 1) {
47438
- const arg = args[index];
47439
- if (arg.startsWith("--")) {
47440
- if (valueFlags.has(arg)) index += 1;
47441
- continue;
47442
- }
47443
- result.push(arg);
47444
- }
47445
- return result;
48204
+ async function loadSession(store, sessionId, kind) {
48205
+ const data = await store.load(sessionId);
48206
+ if (!data) throw new CommandError(kind, "session_not_found", `Session not found: ${sessionId}`, { hint: "list sessions: pie sessions list --json" });
48207
+ return data;
47446
48208
  }
47447
- function sessionSummary(metadata) {
48209
+ function fullSessionRow(metadata) {
47448
48210
  return {
47449
48211
  id: metadata.id,
47450
48212
  name: metadata.name,
47451
48213
  createdAt: metadata.createdAt,
47452
48214
  updatedAt: metadata.updatedAt,
47453
- cwd: typeof metadata.cwd === "string" ? metadata.cwd : void 0,
48215
+ ...typeof metadata.cwd === "string" ? { cwd: metadata.cwd } : {},
47454
48216
  entryCount: metadata.entryCount,
47455
- messageCount: metadata.messageCount,
47456
48217
  activeEntryId: metadata.activeEntryId,
47457
- rootEntryId: metadata.rootEntryId,
47458
- sourceSessionId: typeof metadata.sourceSessionId === "string" ? metadata.sourceSessionId : void 0,
47459
- sourceEntryId: typeof metadata.sourceEntryId === "string" ? metadata.sourceEntryId : void 0,
47460
- sourceActiveEntryId: typeof metadata.sourceActiveEntryId === "string" ? metadata.sourceActiveEntryId : void 0,
47461
- forkBaseEntryId: typeof metadata.forkBaseEntryId === "string" ? metadata.forkBaseEntryId : void 0
48218
+ ...typeof metadata.rootEntryId === "string" ? { rootEntryId: metadata.rootEntryId } : {},
48219
+ ...typeof metadata.sourceSessionId === "string" ? { sourceSessionId: metadata.sourceSessionId } : {},
48220
+ ...typeof metadata.sourceEntryId === "string" ? { sourceEntryId: metadata.sourceEntryId } : {},
48221
+ ...typeof metadata.sourceActiveEntryId === "string" ? { sourceActiveEntryId: metadata.sourceActiveEntryId } : {},
48222
+ ...typeof metadata.forkBaseEntryId === "string" ? { forkBaseEntryId: metadata.forkBaseEntryId } : {}
47462
48223
  };
47463
48224
  }
48225
+ function parseLimitFlag(parsed, kind, fallback) {
48226
+ const raw = getFlagString(parsed, "--limit");
48227
+ if (raw === void 0) return fallback;
48228
+ const value = Number(raw);
48229
+ const max = 1e3;
48230
+ if (!Number.isInteger(value) || value <= 0 || value > max) {
48231
+ throw new CommandError(kind, "invalid_argument", `--limit must be a positive integer no greater than ${max}.`, { usage: true });
48232
+ }
48233
+ return value;
48234
+ }
48235
+ function parseExtraFields(raw, allowed, kind) {
48236
+ if (!raw) return [];
48237
+ if (raw === "all") return [...allowed];
48238
+ const requested = raw.split(",").map((value) => value.trim()).filter(Boolean);
48239
+ for (const field of requested) {
48240
+ if (!allowed.includes(field)) {
48241
+ throw new CommandError(kind, "invalid_argument", `Unknown --fields entry: ${field}. Allowed: ${allowed.join(", ")}, all.`, { usage: true });
48242
+ }
48243
+ }
48244
+ return [...new Set(requested)];
48245
+ }
48246
+ function printSessionsUsage() {
48247
+ console.log(`Usage:
48248
+ pie sessions list [--json] [--limit <n>] [--cursor <cursor>] [--cwd <path> | --all] [--fields <list>] [--full]
48249
+ pie sessions read <sessionId> [--json] [--limit <n>] [--before <cursor>] [--fields <list>] [--full]
48250
+ pie sessions rename <sessionId> <name> [--json] [--cwd <path>]
48251
+ pie sessions rewind <sessionId> --to <entryId> [--json]
48252
+ pie sessions fork <sessionId> --from <entryId> --target <newSessionId> --name <name> [--cwd <path>] [--force] [--json]`);
48253
+ }
47464
48254
  function cwdMatches(sessionCwd, requestedCwd) {
47465
48255
  if (!sessionCwd) return false;
47466
48256
  if (sessionCwd === requestedCwd) return true;
@@ -47479,12 +48269,9 @@ function activeTranscriptEvents(entries) {
47479
48269
  const turnId = isRecord2(entry.message) && typeof entry.message.turnId === "string" ? entry.message.turnId : void 0;
47480
48270
  return {
47481
48271
  logicalSeq: index + 1,
47482
- checkpointId: entry.id,
47483
48272
  entryId: entry.id,
47484
- id: entry.id,
47485
48273
  type: entry.type,
47486
48274
  timestamp: entry.timestamp,
47487
- occurredAt: entry.timestamp,
47488
48275
  role,
47489
48276
  turnId,
47490
48277
  content: textFromMessage(entry.message),
@@ -47492,20 +48279,6 @@ function activeTranscriptEvents(entries) {
47492
48279
  };
47493
48280
  });
47494
48281
  }
47495
- function stableTranscriptEvent(event) {
47496
- return {
47497
- logicalSeq: event.logicalSeq,
47498
- checkpointId: event.checkpointId,
47499
- entryId: event.entryId,
47500
- id: event.id,
47501
- type: event.type,
47502
- timestamp: event.timestamp,
47503
- occurredAt: event.occurredAt,
47504
- ...event.role ? { role: event.role } : {},
47505
- ...event.turnId ? { turnId: event.turnId } : {},
47506
- ...event.content ? { content: event.content } : {}
47507
- };
47508
- }
47509
48282
  function activePathEntries(data) {
47510
48283
  const activeEntryId = data.metadata.activeEntryId;
47511
48284
  if (!activeEntryId) return [];
@@ -47517,7 +48290,7 @@ function resolveBeforeCursor(events, cursor) {
47517
48290
  if (Number.isInteger(numeric) && numeric >= 0) {
47518
48291
  return Math.max(0, Math.min(events.length, numeric));
47519
48292
  }
47520
- const index = events.findIndex((event) => event.entryId === cursor || event.checkpointId === cursor);
48293
+ const index = events.findIndex((event) => event.entryId === cursor);
47521
48294
  return index >= 0 ? index : events.length;
47522
48295
  }
47523
48296
  function cloneJson2(value) {
@@ -48249,31 +49022,7 @@ function parseExtensionFlags(argv) {
48249
49022
  }
48250
49023
 
48251
49024
  // src/commands/extensions.ts
48252
- function parseExtensionsArgs(args) {
48253
- const extensionPaths = [];
48254
- let json = false;
48255
- let scope = "user";
48256
- let name;
48257
- const rest = [];
48258
- for (let i = 0; i < args.length; i += 1) {
48259
- const arg = args[i];
48260
- if (arg === "--json") {
48261
- json = true;
48262
- } else if (arg === "--project") {
48263
- scope = "project";
48264
- } else if (arg === "--user") {
48265
- scope = "user";
48266
- } else if (arg === "--extension-path" && args[i + 1]) {
48267
- extensionPaths.push(args[i + 1]);
48268
- i += 1;
48269
- } else if (!name && !arg.startsWith("-")) {
48270
- name = arg;
48271
- } else {
48272
- rest.push(arg);
48273
- }
48274
- }
48275
- return { json, scope, name, extensionPaths, rest };
48276
- }
49025
+ var EXTENSION_ROW_EXTRA_FIELDS = ["entryPath", "description"];
48277
49026
  function listExtensions(settingsManager, extensionPaths) {
48278
49027
  const cwd = process.cwd();
48279
49028
  return discoverAllExtensions(
@@ -48294,40 +49043,112 @@ function formatExtensionList(infos) {
48294
49043
  });
48295
49044
  return lines.join("\n");
48296
49045
  }
49046
+ function printExtensionsUsage() {
49047
+ console.log(`Usage:
49048
+ pie extensions list [--json] [--extension-path <dir>] [--fields <list>]
49049
+ pie extensions enable <name> [--project|--user] [--json]
49050
+ pie extensions disable <name> [--project|--user] [--json]
49051
+
49052
+ Enablement overrides are stored sparsely; absent entries default to enabled.
49053
+ Project scope writes ./.pie/settings.json and overrides user scope.`);
49054
+ }
48297
49055
  async function runExtensionsCommand(args) {
48298
49056
  const subcommand = args[0] || "list";
48299
- const parsed = parseExtensionsArgs(args.slice(1));
48300
- const settingsManager = new SettingsManager();
48301
- if (subcommand === "list") {
48302
- const infos = listExtensions(settingsManager, parsed.extensionPaths);
48303
- if (parsed.json) {
48304
- console.log(JSON.stringify({ extensions: infos }, null, 2));
49057
+ if (subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
49058
+ printExtensionsUsage();
49059
+ return;
49060
+ }
49061
+ const rest = args.slice(1);
49062
+ try {
49063
+ if (subcommand === "list") {
49064
+ await runExtensionsList(rest);
48305
49065
  return;
48306
49066
  }
49067
+ if (subcommand === "enable" || subcommand === "disable") {
49068
+ await runExtensionsToggle(subcommand, rest);
49069
+ return;
49070
+ }
49071
+ throw new CommandError("extensions", "unknown_command", `Unknown extensions command: ${subcommand}`, {
49072
+ usage: true,
49073
+ hint: "subcommands: list, enable, disable"
49074
+ });
49075
+ } catch (error) {
49076
+ emitCommandError(error, { json: args.includes("--json"), fallbackKind: "extensions" });
49077
+ }
49078
+ }
49079
+ async function runExtensionsList(rest) {
49080
+ const parsed = parseCommandArgs(rest, EXTENSIONS_LIST_SPEC);
49081
+ if (parsed.error) {
49082
+ throw new CommandError("extensions.list", parsed.error.code, parsed.error.message, { usage: true });
49083
+ }
49084
+ if (parsed.help) {
49085
+ console.log(renderCommandHelp(EXTENSIONS_LIST_SPEC));
49086
+ return;
49087
+ }
49088
+ const json = getFlagBool(parsed, "--json");
49089
+ const settingsManager = new SettingsManager();
49090
+ const infos = listExtensions(settingsManager, getFlagStrings(parsed, "--extension-path"));
49091
+ if (!json) {
48307
49092
  console.log(formatExtensionList(infos));
48308
49093
  return;
48309
49094
  }
48310
- if (subcommand === "enable" || subcommand === "disable") {
48311
- if (!parsed.name) {
48312
- console.error(`Usage: pie extensions ${subcommand} <name> [--project|--user]`);
48313
- process.exitCode = 1;
48314
- return;
48315
- }
48316
- const enabled = subcommand === "enable";
48317
- settingsManager.setExtensionEnabled(parsed.name, enabled, parsed.scope);
48318
- const scopeLabel = parsed.scope === "project" ? "./.pie/settings.json" : "~/.pie/settings.json";
48319
- console.log(
48320
- `Extension "${parsed.name}" ${enabled ? "enabled" : "disabled"} in ${scopeLabel}. Takes effect on next start.`
48321
- );
49095
+ const fields = parseExtensionFields(getFlagString(parsed, "--fields"));
49096
+ const rows = infos.map((info) => {
49097
+ const row = {
49098
+ identity: info.identity ?? null,
49099
+ source: info.source,
49100
+ enabled: info.enabled
49101
+ };
49102
+ if (fields.includes("entryPath")) row.entryPath = info.entryPath;
49103
+ if (fields.includes("description") && info.description) row.description = info.description;
49104
+ return row;
49105
+ });
49106
+ printSuccessEnvelope("extensions.list", rows, { count: rows.length, total: rows.length }, [
49107
+ "toggle: pie extensions enable|disable <name>"
49108
+ ]);
49109
+ }
49110
+ async function runExtensionsToggle(subcommand, rest) {
49111
+ const parsed = parseCommandArgs(rest, EXTENSIONS_TOGGLE_SPEC);
49112
+ if (parsed.error) {
49113
+ throw new CommandError(`extensions.${subcommand}`, parsed.error.code, parsed.error.message, { usage: true });
49114
+ }
49115
+ if (parsed.help) {
49116
+ console.log(renderCommandHelp(EXTENSIONS_TOGGLE_SPEC));
48322
49117
  return;
48323
49118
  }
48324
- console.log(`Usage:
48325
- pie extensions list [--json] [--extension-path <dir>]
48326
- pie extensions enable <name> [--project|--user]
48327
- pie extensions disable <name> [--project|--user]
48328
-
48329
- Enablement overrides are stored sparsely; absent entries default to enabled.
48330
- Project scope writes ./.pie/settings.json and overrides user scope.`);
49119
+ const json = getFlagBool(parsed, "--json");
49120
+ const name = getPositional(parsed, "name");
49121
+ if (!name) {
49122
+ throw new CommandError(`extensions.${subcommand}`, "missing_positional", `Usage: pie extensions ${subcommand} <name> [--project|--user]`, { usage: true });
49123
+ }
49124
+ const project = getFlagBool(parsed, "--project");
49125
+ const user = getFlagBool(parsed, "--user");
49126
+ if (project && user) {
49127
+ throw new CommandError(`extensions.${subcommand}`, "invalid_argument", "Choose one scope: --project or --user.", { usage: true });
49128
+ }
49129
+ const scope = project ? "project" : "user";
49130
+ const enabled = subcommand === "enable";
49131
+ const settingsManager = new SettingsManager();
49132
+ settingsManager.setExtensionEnabled(name, enabled, scope);
49133
+ if (json) {
49134
+ printSuccessEnvelope(`extensions.${subcommand}`, { identity: name, enabled, scope });
49135
+ return;
49136
+ }
49137
+ const scopeLabel = scope === "project" ? "./.pie/settings.json" : "~/.pie/settings.json";
49138
+ console.log(
49139
+ `Extension "${name}" ${enabled ? "enabled" : "disabled"} in ${scopeLabel}. Takes effect on next start.`
49140
+ );
49141
+ }
49142
+ function parseExtensionFields(raw) {
49143
+ if (!raw) return [];
49144
+ if (raw === "all") return [...EXTENSION_ROW_EXTRA_FIELDS];
49145
+ const requested = raw.split(",").map((value) => value.trim()).filter(Boolean);
49146
+ for (const field of requested) {
49147
+ if (!EXTENSION_ROW_EXTRA_FIELDS.includes(field)) {
49148
+ throw new CommandError("extensions.list", "invalid_argument", `Unknown --fields entry: ${field}. Allowed: ${EXTENSION_ROW_EXTRA_FIELDS.join(", ")}, all.`, { usage: true });
49149
+ }
49150
+ }
49151
+ return requested;
48331
49152
  }
48332
49153
 
48333
49154
  // src/interactive-mode.ts
@@ -55703,6 +56524,267 @@ function wrapToolsWithExtensions(tools, runner) {
55703
56524
  return tools.map((tool) => wrapToolWithExtensions(tool, runner));
55704
56525
  }
55705
56526
 
56527
+ // src/core/defaults.ts
56528
+ var DEFAULT_THINKING_LEVEL = "medium";
56529
+
56530
+ // src/core/model-resolver.ts
56531
+ function normalizeProviderName(provider) {
56532
+ return provider.toLowerCase().replace(/[^a-z]/g, "");
56533
+ }
56534
+ function selectFallbackModel(registry, preferred) {
56535
+ const availableModels = registry.getAvailable();
56536
+ if (preferred?.provider && preferred?.modelId) {
56537
+ const exact = availableModels.find(
56538
+ (model) => model.provider === preferred.provider && model.id === preferred.modelId
56539
+ );
56540
+ if (exact) return exact;
56541
+ }
56542
+ if (preferred?.provider) {
56543
+ const sameProvider = availableModels.find((model) => model.provider === preferred.provider);
56544
+ if (sameProvider) return sameProvider;
56545
+ }
56546
+ return availableModels[0] || registry.getAll()[0];
56547
+ }
56548
+ function resolveConfiguredModel(registry, provider, modelId) {
56549
+ const exact = registry.find(provider, modelId);
56550
+ if (exact) {
56551
+ return { model: exact, provider, modelId, corrected: false };
56552
+ }
56553
+ const allModels = registry.getAll();
56554
+ if (allModels.length === 0) {
56555
+ throw new Error("No registered models are available.");
56556
+ }
56557
+ const sameIdMatches = allModels.filter((model) => model.id === modelId);
56558
+ const normalizedProviderMatches = sameIdMatches.filter(
56559
+ (model) => normalizeProviderName(model.provider) === normalizeProviderName(provider)
56560
+ );
56561
+ if (normalizedProviderMatches.length === 1) {
56562
+ const matched = normalizedProviderMatches[0];
56563
+ return {
56564
+ model: matched,
56565
+ provider: matched.provider,
56566
+ modelId: matched.id,
56567
+ corrected: true,
56568
+ warning: `Configured model "${provider}/${modelId}" was not found. Using "${matched.provider}/${matched.id}" instead.`
56569
+ };
56570
+ }
56571
+ if (sameIdMatches.length === 1) {
56572
+ const matched = sameIdMatches[0];
56573
+ return {
56574
+ model: matched,
56575
+ provider: matched.provider,
56576
+ modelId: matched.id,
56577
+ corrected: true,
56578
+ warning: `Configured model "${provider}/${modelId}" was not found. Reusing the available model "${matched.provider}/${matched.id}".`
56579
+ };
56580
+ }
56581
+ const providerModels = registry.getByProvider(provider);
56582
+ if (providerModels.length > 0) {
56583
+ const fallback2 = providerModels[0];
56584
+ return {
56585
+ model: fallback2,
56586
+ provider: fallback2.provider,
56587
+ modelId: fallback2.id,
56588
+ corrected: true,
56589
+ warning: `Configured model "${provider}/${modelId}" was not found. Falling back to "${fallback2.provider}/${fallback2.id}".`
56590
+ };
56591
+ }
56592
+ const fallback = selectFallbackModel(registry);
56593
+ if (!fallback) {
56594
+ throw new Error("No registered models are available.");
56595
+ }
56596
+ return {
56597
+ model: fallback,
56598
+ provider: fallback.provider,
56599
+ modelId: fallback.id,
56600
+ corrected: true,
56601
+ warning: `Configured model "${provider}/${modelId}" was not found. Falling back to "${fallback.provider}/${fallback.id}".`
56602
+ };
56603
+ }
56604
+ function findInitialModel(options) {
56605
+ const configDefaults = options.registry.getConfigDefaults();
56606
+ const configuredProvider = options.configuredProvider || configDefaults?.provider;
56607
+ const configuredModelId = options.configuredModelId || configDefaults?.modelId;
56608
+ const resolved = configuredProvider && configuredModelId ? resolveConfiguredModel(options.registry, configuredProvider, configuredModelId) : void 0;
56609
+ const apiKey = resolved ? options.registry.getApiKey(resolved.model) : void 0;
56610
+ if (!resolved || !apiKey) {
56611
+ const fallback = selectFallbackModel(options.registry, {
56612
+ provider: configuredProvider,
56613
+ modelId: configuredModelId
56614
+ });
56615
+ if (!fallback) {
56616
+ return {
56617
+ model: void 0,
56618
+ apiKey: void 0,
56619
+ provider: void 0,
56620
+ modelId: void 0,
56621
+ unconfigured: true,
56622
+ warning: "No models are configured. Add one in ~/.pie/models.json before sending messages."
56623
+ };
56624
+ }
56625
+ const fallbackKey = options.registry.getApiKey(fallback);
56626
+ if (!fallbackKey) {
56627
+ throw new Error(`No API key found for provider "${resolved?.provider || fallback.provider}".`);
56628
+ }
56629
+ return {
56630
+ model: fallback,
56631
+ apiKey: fallbackKey,
56632
+ provider: fallback.provider,
56633
+ modelId: fallback.id,
56634
+ warning: resolved ? resolved.warning || `No API key found for "${resolved.provider}". Using "${fallback.provider}/${fallback.id}" instead.` : configuredProvider && configuredModelId ? `Configured model "${configuredProvider}/${configuredModelId}" was not found. Using "${fallback.provider}/${fallback.id}" instead.` : void 0
56635
+ };
56636
+ }
56637
+ return {
56638
+ model: resolved.model,
56639
+ apiKey,
56640
+ provider: resolved.provider,
56641
+ modelId: resolved.modelId,
56642
+ warning: resolved.warning
56643
+ };
56644
+ }
56645
+ function restoreModelFromSession(options) {
56646
+ const savedProvider = options.savedProvider;
56647
+ const restored = options.registry.find(savedProvider, options.savedModelId);
56648
+ if (restored && options.registry.getApiKey(restored)) {
56649
+ return { model: restored };
56650
+ }
56651
+ if (options.currentModel) {
56652
+ return {
56653
+ model: options.currentModel,
56654
+ fallbackMessage: `Could not restore model ${savedProvider}/${options.savedModelId}. Using ${options.currentModel.provider}/${options.currentModel.id}.`
56655
+ };
56656
+ }
56657
+ const fallback = selectFallbackModel(options.registry, {
56658
+ provider: savedProvider,
56659
+ modelId: options.savedModelId
56660
+ });
56661
+ return fallback ? {
56662
+ model: fallback,
56663
+ fallbackMessage: `Could not restore model ${savedProvider}/${options.savedModelId}. Using ${fallback.provider}/${fallback.id}.`
56664
+ } : { model: void 0 };
56665
+ }
56666
+
56667
+ // src/core/session-model-metadata.ts
56668
+ function getSavedSessionModelSelection(activeSession) {
56669
+ if (!activeSession) return {};
56670
+ const provider = typeof activeSession.metadata.provider === "string" ? activeSession.metadata.provider : typeof activeSession.metadata.defaultProvider === "string" ? activeSession.metadata.defaultProvider : void 0;
56671
+ const modelId = typeof activeSession.metadata.modelId === "string" ? activeSession.metadata.modelId : typeof activeSession.metadata.defaultModel === "string" ? activeSession.metadata.defaultModel : void 0;
56672
+ const thinkingLevel = typeof activeSession.metadata.thinkingLevel === "string" ? activeSession.metadata.thinkingLevel : void 0;
56673
+ return { provider, modelId, thinkingLevel };
56674
+ }
56675
+ function syncSessionModelMetadata(sessionManager, model, thinkingLevel) {
56676
+ const activeSession = sessionManager.getActiveSession();
56677
+ if (!activeSession || !model) return;
56678
+ activeSession.metadata.provider = model.provider;
56679
+ activeSession.metadata.modelId = model.id;
56680
+ activeSession.metadata.defaultProvider = model.provider;
56681
+ activeSession.metadata.defaultModel = model.id;
56682
+ activeSession.metadata.thinkingLevel = thinkingLevel;
56683
+ activeSession.isDirty = true;
56684
+ }
56685
+ function restoreSessionModelState(options) {
56686
+ const { provider, modelId, thinkingLevel } = getSavedSessionModelSelection(options.activeSession);
56687
+ if (!provider || !modelId) {
56688
+ return {
56689
+ model: options.currentModel,
56690
+ thinkingLevel
56691
+ };
56692
+ }
56693
+ const restored = restoreModelFromSession({
56694
+ registry: options.registry,
56695
+ savedProvider: provider,
56696
+ savedModelId: modelId,
56697
+ currentModel: options.currentModel
56698
+ });
56699
+ return {
56700
+ model: restored.model,
56701
+ thinkingLevel,
56702
+ fallbackMessage: restored.fallbackMessage
56703
+ };
56704
+ }
56705
+
56706
+ // src/core/interactive-model-state.ts
56707
+ var THINKING_LEVELS = [
56708
+ "off",
56709
+ "minimal",
56710
+ "low",
56711
+ "medium",
56712
+ "high",
56713
+ "xhigh"
56714
+ ];
56715
+ var THINKING_LEVEL_DESCRIPTIONS = {
56716
+ off: "No reasoning",
56717
+ minimal: "Very brief reasoning (~1k tokens)",
56718
+ low: "Light reasoning (~2k tokens)",
56719
+ medium: "Moderate reasoning (~8k tokens)",
56720
+ high: "Deep reasoning (~16k tokens)",
56721
+ xhigh: "Maximum reasoning (~32k tokens)"
56722
+ };
56723
+ function isThinkingLevel(value) {
56724
+ return THINKING_LEVELS.includes(value);
56725
+ }
56726
+ function resolveInitialThinkingLevel(options) {
56727
+ const sessionLevel = options.session?.metadata.thinkingLevel;
56728
+ if (typeof sessionLevel === "string" && isThinkingLevel(sessionLevel)) {
56729
+ return sessionLevel;
56730
+ }
56731
+ return options.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
56732
+ }
56733
+ function maskApiKey(key) {
56734
+ if (key.length <= 6) return "***";
56735
+ return key.slice(0, 6) + "***";
56736
+ }
56737
+ function applyThinkingLevel(options) {
56738
+ options.agent.setThinkingLevel(options.level);
56739
+ options.settingsManager.setDefaultThinkingLevel(options.level);
56740
+ syncSessionModelMetadata(options.sessionManager, options.model, options.level);
56741
+ }
56742
+ function switchInteractiveModel(options) {
56743
+ const [provider, modelId] = options.fullModelId.split("/");
56744
+ const model = options.registry.find(provider, modelId);
56745
+ if (!model) {
56746
+ throw new Error(`Model "${options.fullModelId}" not found`);
56747
+ }
56748
+ const apiKey = options.registry.getApiKey(model);
56749
+ options.agent.setModel(model);
56750
+ if (apiKey) {
56751
+ options.agent.apiKey = apiKey;
56752
+ fileLogger.log("cli", `Switched model to ${options.fullModelId}, updated API key`, {
56753
+ keyPrefix: maskApiKey(apiKey)
56754
+ });
56755
+ } else {
56756
+ fileLogger.log("cli", `Switched model to ${options.fullModelId}, no API key found`);
56757
+ }
56758
+ options.settingsManager.setDefaultModelAndProvider(provider, modelId);
56759
+ syncSessionModelMetadata(options.sessionManager, model, options.thinkingLevel);
56760
+ return { model, apiKey };
56761
+ }
56762
+ function restoreInteractiveSessionState(options) {
56763
+ const restored = restoreSessionModelState({
56764
+ activeSession: options.activeSession,
56765
+ registry: options.registry,
56766
+ currentModel: options.currentModel
56767
+ });
56768
+ const thinkingLevel = restored.thinkingLevel && isThinkingLevel(restored.thinkingLevel) ? restored.thinkingLevel : void 0;
56769
+ if (thinkingLevel) {
56770
+ options.agent.setThinkingLevel(thinkingLevel);
56771
+ }
56772
+ let apiKey;
56773
+ if (restored.model) {
56774
+ options.agent.setModel(restored.model);
56775
+ apiKey = options.registry.getApiKey(restored.model);
56776
+ if (apiKey) {
56777
+ options.agent.apiKey = apiKey;
56778
+ }
56779
+ }
56780
+ return {
56781
+ model: restored.model,
56782
+ apiKey,
56783
+ thinkingLevel,
56784
+ fallbackMessage: restored.fallbackMessage
56785
+ };
56786
+ }
56787
+
55706
56788
  // src/keybindings.ts
55707
56789
  import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
55708
56790
  import { join as join7 } from "path";
@@ -56829,23 +57911,23 @@ function parseThemeJson(label, json) {
56829
57911
  otherErrors.push(` - ${e.path}: ${e.message}`);
56830
57912
  }
56831
57913
  }
56832
- let errorMessage3 = `Invalid theme "${label}":
57914
+ let errorMessage4 = `Invalid theme "${label}":
56833
57915
  `;
56834
57916
  if (missingColors.length > 0) {
56835
- errorMessage3 += "\nMissing required color tokens:\n";
56836
- errorMessage3 += missingColors.map((c) => ` - ${c}`).join("\n");
56837
- errorMessage3 += `
57917
+ errorMessage4 += "\nMissing required color tokens:\n";
57918
+ errorMessage4 += missingColors.map((c) => ` - ${c}`).join("\n");
57919
+ errorMessage4 += `
56838
57920
 
56839
57921
  Please add these colors to your theme's "colors" object.`;
56840
- errorMessage3 += "\nSee the built-in themes (dark.json, light.json) for reference values.";
57922
+ errorMessage4 += "\nSee the built-in themes (dark.json, light.json) for reference values.";
56841
57923
  }
56842
57924
  if (otherErrors.length > 0) {
56843
- errorMessage3 += `
57925
+ errorMessage4 += `
56844
57926
 
56845
57927
  Other errors:
56846
57928
  ${otherErrors.join("\n")}`;
56847
57929
  }
56848
- throw new Error(errorMessage3);
57930
+ throw new Error(errorMessage4);
56849
57931
  }
56850
57932
  return json;
56851
57933
  }
@@ -61265,257 +62347,6 @@ var SettingsSelectorComponent = class extends Container {
61265
62347
  }
61266
62348
  };
61267
62349
 
61268
- // src/core/model-resolver.ts
61269
- function normalizeProviderName(provider) {
61270
- return provider.toLowerCase().replace(/[^a-z]/g, "");
61271
- }
61272
- function selectFallbackModel(registry, preferred) {
61273
- const availableModels = registry.getAvailable();
61274
- if (preferred?.provider && preferred?.modelId) {
61275
- const exact = availableModels.find(
61276
- (model) => model.provider === preferred.provider && model.id === preferred.modelId
61277
- );
61278
- if (exact) return exact;
61279
- }
61280
- if (preferred?.provider) {
61281
- const sameProvider = availableModels.find((model) => model.provider === preferred.provider);
61282
- if (sameProvider) return sameProvider;
61283
- }
61284
- return availableModels[0] || registry.getAll()[0];
61285
- }
61286
- function resolveConfiguredModel(registry, provider, modelId) {
61287
- const exact = registry.find(provider, modelId);
61288
- if (exact) {
61289
- return { model: exact, provider, modelId, corrected: false };
61290
- }
61291
- const allModels = registry.getAll();
61292
- if (allModels.length === 0) {
61293
- throw new Error("No registered models are available.");
61294
- }
61295
- const sameIdMatches = allModels.filter((model) => model.id === modelId);
61296
- const normalizedProviderMatches = sameIdMatches.filter(
61297
- (model) => normalizeProviderName(model.provider) === normalizeProviderName(provider)
61298
- );
61299
- if (normalizedProviderMatches.length === 1) {
61300
- const matched = normalizedProviderMatches[0];
61301
- return {
61302
- model: matched,
61303
- provider: matched.provider,
61304
- modelId: matched.id,
61305
- corrected: true,
61306
- warning: `Configured model "${provider}/${modelId}" was not found. Using "${matched.provider}/${matched.id}" instead.`
61307
- };
61308
- }
61309
- if (sameIdMatches.length === 1) {
61310
- const matched = sameIdMatches[0];
61311
- return {
61312
- model: matched,
61313
- provider: matched.provider,
61314
- modelId: matched.id,
61315
- corrected: true,
61316
- warning: `Configured model "${provider}/${modelId}" was not found. Reusing the available model "${matched.provider}/${matched.id}".`
61317
- };
61318
- }
61319
- const providerModels = registry.getByProvider(provider);
61320
- if (providerModels.length > 0) {
61321
- const fallback2 = providerModels[0];
61322
- return {
61323
- model: fallback2,
61324
- provider: fallback2.provider,
61325
- modelId: fallback2.id,
61326
- corrected: true,
61327
- warning: `Configured model "${provider}/${modelId}" was not found. Falling back to "${fallback2.provider}/${fallback2.id}".`
61328
- };
61329
- }
61330
- const fallback = selectFallbackModel(registry);
61331
- if (!fallback) {
61332
- throw new Error("No registered models are available.");
61333
- }
61334
- return {
61335
- model: fallback,
61336
- provider: fallback.provider,
61337
- modelId: fallback.id,
61338
- corrected: true,
61339
- warning: `Configured model "${provider}/${modelId}" was not found. Falling back to "${fallback.provider}/${fallback.id}".`
61340
- };
61341
- }
61342
- function findInitialModel(options) {
61343
- const configDefaults = options.registry.getConfigDefaults();
61344
- const configuredProvider = options.configuredProvider || configDefaults?.provider;
61345
- const configuredModelId = options.configuredModelId || configDefaults?.modelId;
61346
- const resolved = configuredProvider && configuredModelId ? resolveConfiguredModel(options.registry, configuredProvider, configuredModelId) : void 0;
61347
- const apiKey = resolved ? options.registry.getApiKey(resolved.model) : void 0;
61348
- if (!resolved || !apiKey) {
61349
- const fallback = selectFallbackModel(options.registry, {
61350
- provider: configuredProvider,
61351
- modelId: configuredModelId
61352
- });
61353
- if (!fallback) {
61354
- return {
61355
- model: void 0,
61356
- apiKey: void 0,
61357
- provider: void 0,
61358
- modelId: void 0,
61359
- unconfigured: true,
61360
- warning: "No models are configured. Add one in ~/.pie/models.json before sending messages."
61361
- };
61362
- }
61363
- const fallbackKey = options.registry.getApiKey(fallback);
61364
- if (!fallbackKey) {
61365
- throw new Error(`No API key found for provider "${resolved?.provider || fallback.provider}".`);
61366
- }
61367
- return {
61368
- model: fallback,
61369
- apiKey: fallbackKey,
61370
- provider: fallback.provider,
61371
- modelId: fallback.id,
61372
- warning: resolved ? resolved.warning || `No API key found for "${resolved.provider}". Using "${fallback.provider}/${fallback.id}" instead.` : configuredProvider && configuredModelId ? `Configured model "${configuredProvider}/${configuredModelId}" was not found. Using "${fallback.provider}/${fallback.id}" instead.` : void 0
61373
- };
61374
- }
61375
- return {
61376
- model: resolved.model,
61377
- apiKey,
61378
- provider: resolved.provider,
61379
- modelId: resolved.modelId,
61380
- warning: resolved.warning
61381
- };
61382
- }
61383
- function restoreModelFromSession(options) {
61384
- const savedProvider = options.savedProvider;
61385
- const restored = options.registry.find(savedProvider, options.savedModelId);
61386
- if (restored && options.registry.getApiKey(restored)) {
61387
- return { model: restored };
61388
- }
61389
- if (options.currentModel) {
61390
- return {
61391
- model: options.currentModel,
61392
- fallbackMessage: `Could not restore model ${savedProvider}/${options.savedModelId}. Using ${options.currentModel.provider}/${options.currentModel.id}.`
61393
- };
61394
- }
61395
- const fallback = selectFallbackModel(options.registry, {
61396
- provider: savedProvider,
61397
- modelId: options.savedModelId
61398
- });
61399
- return fallback ? {
61400
- model: fallback,
61401
- fallbackMessage: `Could not restore model ${savedProvider}/${options.savedModelId}. Using ${fallback.provider}/${fallback.id}.`
61402
- } : { model: void 0 };
61403
- }
61404
-
61405
- // src/core/session-model-metadata.ts
61406
- function getSavedSessionModelSelection(activeSession) {
61407
- if (!activeSession) return {};
61408
- const provider = typeof activeSession.metadata.provider === "string" ? activeSession.metadata.provider : typeof activeSession.metadata.defaultProvider === "string" ? activeSession.metadata.defaultProvider : void 0;
61409
- const modelId = typeof activeSession.metadata.modelId === "string" ? activeSession.metadata.modelId : typeof activeSession.metadata.defaultModel === "string" ? activeSession.metadata.defaultModel : void 0;
61410
- const thinkingLevel = typeof activeSession.metadata.thinkingLevel === "string" ? activeSession.metadata.thinkingLevel : void 0;
61411
- return { provider, modelId, thinkingLevel };
61412
- }
61413
- function syncSessionModelMetadata(sessionManager, model, thinkingLevel) {
61414
- const activeSession = sessionManager.getActiveSession();
61415
- if (!activeSession || !model) return;
61416
- activeSession.metadata.provider = model.provider;
61417
- activeSession.metadata.modelId = model.id;
61418
- activeSession.metadata.defaultProvider = model.provider;
61419
- activeSession.metadata.defaultModel = model.id;
61420
- activeSession.metadata.thinkingLevel = thinkingLevel;
61421
- activeSession.isDirty = true;
61422
- }
61423
- function restoreSessionModelState(options) {
61424
- const { provider, modelId, thinkingLevel } = getSavedSessionModelSelection(options.activeSession);
61425
- if (!provider || !modelId) {
61426
- return {
61427
- model: options.currentModel,
61428
- thinkingLevel
61429
- };
61430
- }
61431
- const restored = restoreModelFromSession({
61432
- registry: options.registry,
61433
- savedProvider: provider,
61434
- savedModelId: modelId,
61435
- currentModel: options.currentModel
61436
- });
61437
- return {
61438
- model: restored.model,
61439
- thinkingLevel,
61440
- fallbackMessage: restored.fallbackMessage
61441
- };
61442
- }
61443
-
61444
- // src/core/interactive-model-state.ts
61445
- var THINKING_LEVELS = [
61446
- "off",
61447
- "minimal",
61448
- "low",
61449
- "medium",
61450
- "high",
61451
- "xhigh"
61452
- ];
61453
- var THINKING_LEVEL_DESCRIPTIONS = {
61454
- off: "No reasoning",
61455
- minimal: "Very brief reasoning (~1k tokens)",
61456
- low: "Light reasoning (~2k tokens)",
61457
- medium: "Moderate reasoning (~8k tokens)",
61458
- high: "Deep reasoning (~16k tokens)",
61459
- xhigh: "Maximum reasoning (~32k tokens)"
61460
- };
61461
- function isThinkingLevel(value) {
61462
- return THINKING_LEVELS.includes(value);
61463
- }
61464
- function maskApiKey(key) {
61465
- if (key.length <= 6) return "***";
61466
- return key.slice(0, 6) + "***";
61467
- }
61468
- function applyThinkingLevel(options) {
61469
- options.agent.setThinkingLevel(options.level);
61470
- options.settingsManager.setDefaultThinkingLevel(options.level);
61471
- syncSessionModelMetadata(options.sessionManager, options.model, options.level);
61472
- }
61473
- function switchInteractiveModel(options) {
61474
- const [provider, modelId] = options.fullModelId.split("/");
61475
- const model = options.registry.find(provider, modelId);
61476
- if (!model) {
61477
- throw new Error(`Model "${options.fullModelId}" not found`);
61478
- }
61479
- const apiKey = options.registry.getApiKey(model);
61480
- options.agent.setModel(model);
61481
- if (apiKey) {
61482
- options.agent.apiKey = apiKey;
61483
- fileLogger.log("cli", `Switched model to ${options.fullModelId}, updated API key`, {
61484
- keyPrefix: maskApiKey(apiKey)
61485
- });
61486
- } else {
61487
- fileLogger.log("cli", `Switched model to ${options.fullModelId}, no API key found`);
61488
- }
61489
- options.settingsManager.setDefaultModelAndProvider(provider, modelId);
61490
- syncSessionModelMetadata(options.sessionManager, model, options.thinkingLevel);
61491
- return { model, apiKey };
61492
- }
61493
- function restoreInteractiveSessionState(options) {
61494
- const restored = restoreSessionModelState({
61495
- activeSession: options.activeSession,
61496
- registry: options.registry,
61497
- currentModel: options.currentModel
61498
- });
61499
- const thinkingLevel = restored.thinkingLevel && isThinkingLevel(restored.thinkingLevel) ? restored.thinkingLevel : void 0;
61500
- if (thinkingLevel) {
61501
- options.agent.setThinkingLevel(thinkingLevel);
61502
- }
61503
- let apiKey;
61504
- if (restored.model) {
61505
- options.agent.setModel(restored.model);
61506
- apiKey = options.registry.getApiKey(restored.model);
61507
- if (apiKey) {
61508
- options.agent.apiKey = apiKey;
61509
- }
61510
- }
61511
- return {
61512
- model: restored.model,
61513
- apiKey,
61514
- thinkingLevel,
61515
- fallbackMessage: restored.fallbackMessage
61516
- };
61517
- }
61518
-
61519
62350
  // src/core/session-display.ts
61520
62351
  function deriveSessionTitle(options) {
61521
62352
  const { activeSession, messages } = options;
@@ -61833,7 +62664,10 @@ var BUILTIN_SLASH_COMMANDS = [
61833
62664
  { name: "sessions/info", description: "Show session info" },
61834
62665
  { name: "sessions/fork", description: "Fork from a message" },
61835
62666
  { name: "settings", description: "Open settings menu" },
61836
- { name: "settings/extensions", description: "Manage extensions" },
62667
+ { name: "extensions", description: "Manage extensions (list/enable/disable)" },
62668
+ { name: "extensions/list", description: "List discovered extensions (including disabled)" },
62669
+ { name: "extensions/enable", description: "Enable an extension by identity (writes settings)" },
62670
+ { name: "extensions/disable", description: "Disable an extension by identity (writes settings)" },
61837
62671
  { name: "settings/help", description: "Show help information (keyboard shortcuts and commands)" },
61838
62672
  { name: "settings/hotkeys", description: "Show keyboard shortcuts" },
61839
62673
  { name: "settings/model", description: "Select AI model" },
@@ -61841,14 +62675,19 @@ var BUILTIN_SLASH_COMMANDS = [
61841
62675
  { name: "settings/thinking", description: "Set thinking level" },
61842
62676
  { name: "settings/calm", description: "Toggle calm mode: show only questions and answers" },
61843
62677
  { name: "settings/yolo", description: "Toggle or set YOLO mode" },
61844
- { name: "settings/debugs/cache-test", description: "Run provider cache diagnostics with a dedicated 2-request probe" },
61845
- { name: "settings/debugs/context", description: "Append a context summary to ~/.pie/logs/pie-cli.log" },
61846
- { name: "settings/debugs/package", description: "Append a package summary to ~/.pie/logs/pie-cli.log" },
62678
+ { name: "debugs/cache-test", description: "Run provider cache diagnostics with a dedicated 2-request probe" },
62679
+ { name: "debugs/context", description: "Append a context summary to ~/.pie/logs/pie-cli.log" },
62680
+ { name: "debugs/package", description: "Append a package summary to ~/.pie/logs/pie-cli.log" },
61847
62681
  { name: "settings/quit", description: "Exit Pie" },
61848
62682
  { name: "skills", description: "Open skills menu" },
61849
62683
  { name: "skills/list", description: "List available skills" },
62684
+ { name: "skills/enable", description: "Enable a skill by name" },
62685
+ { name: "skills/disable", description: "Disable a skill by name" },
61850
62686
  { name: "skills/reload", description: "Reload skills from disk" },
61851
62687
  { name: "skills/use", description: "Use a skill" },
62688
+ { name: "tools", description: "List tools and manage enable/disable toggles" },
62689
+ { name: "tools/enable", description: "Enable a tool by name" },
62690
+ { name: "tools/disable", description: "Disable a tool by name" },
61852
62691
  { name: "tools/subagent", description: "Spawn subagents in parallel to handle tasks" }
61853
62692
  ];
61854
62693
 
@@ -63958,7 +64797,7 @@ async function handleResumeCommand(host) {
63958
64797
  return { component: selector, focus: selector.getSelectList() };
63959
64798
  });
63960
64799
  }
63961
- async function loadSession(host, sessionId) {
64800
+ async function loadSession2(host, sessionId) {
63962
64801
  const beforeSwitch = await host.extensionRunner?.emitSessionBeforeSwitch({
63963
64802
  type: "session:before_switch",
63964
64803
  reason: "resume",
@@ -64431,6 +65270,14 @@ function handleSkillsCommand(host) {
64431
65270
  host.chatContainer.addChild(new Text(theme.fg("dim", ` \u2022 ${skill.name}: ${skill.description}`), 1, 0));
64432
65271
  }
64433
65272
  }
65273
+ const disabledNames = Object.entries(host.options.settingsManager.getSkillSettings()).filter(([, entry]) => entry === false || entry && typeof entry === "object" && entry.enabled === false).map(([name]) => name).filter((name) => !host.skills.some((skill) => skill.name === name));
65274
+ if (disabledNames.length > 0) {
65275
+ host.chatContainer.addChild(new Spacer(1));
65276
+ host.chatContainer.addChild(new Text(theme.fg("dim", "Disabled skills:"), 1, 0));
65277
+ for (const name of disabledNames) {
65278
+ host.chatContainer.addChild(new Text(theme.fg("dim", ` \u2022 ${name} (disabled; /skills/enable ${name})`), 1, 0));
65279
+ }
65280
+ }
64434
65281
  host.chatContainer.addChild(new Spacer(1));
64435
65282
  host.chatContainer.addChild(new Text(theme.fg("dim", "Usage: /skills/skill:<name> <your request>"), 1, 0));
64436
65283
  host.chatContainer.addChild(new Text(theme.fg("dim", "Example: /skills/skill:browser-tools navigate to google.com"), 1, 0));
@@ -64457,7 +65304,11 @@ function handleSkillCommand(host, name) {
64457
65304
  async function handleSkillInvoke(host, skillName, args) {
64458
65305
  const skill = host.skills.find((s) => s.name === skillName);
64459
65306
  if (!skill) {
64460
- host.showError(`Skill not found: ${skillName}`);
65307
+ if (!host.options.settingsManager.isSkillEnabled(skillName)) {
65308
+ host.showError(`Skill disabled: ${skillName}. Use /skills/enable ${skillName} to restore it.`);
65309
+ } else {
65310
+ host.showError(`Skill not found: ${skillName}`);
65311
+ }
64461
65312
  return;
64462
65313
  }
64463
65314
  const skillText = `skill:${skillName} ${args}`.trim();
@@ -64541,6 +65392,30 @@ function stripFrontmatter(host, content) {
64541
65392
  }
64542
65393
  return content;
64543
65394
  }
65395
+ async function handleSkillToggleCommand(host, args, enabled) {
65396
+ const name = args.trim();
65397
+ if (!name) {
65398
+ host.showError(`Usage: /skills/${enabled ? "enable" : "disable"} <name>`);
65399
+ return;
65400
+ }
65401
+ const builtinDir = path25.join(getBuiltinDir(), "skills");
65402
+ const userDir = path25.join(os7.homedir(), ".pie", "skills");
65403
+ const projectDir = path25.join(process.cwd(), ".pie", "skills");
65404
+ const discovered = loadSkills({ dirs: buildSkillDirs({ builtinDir, userDir, projectDir, cwd: process.cwd() }) }).skills;
65405
+ if (!discovered.some((skill) => skill.name === name)) {
65406
+ host.showError(`Skill not found: ${name}`);
65407
+ return;
65408
+ }
65409
+ try {
65410
+ host.options.settingsManager.setSkillEnabled(name, enabled);
65411
+ } catch (error) {
65412
+ host.showError(`Failed to update skill "${name}": ${errorMessage2(error)}`);
65413
+ return;
65414
+ }
65415
+ await handleSkillsReload(host);
65416
+ host.chatContainer.addChild(new Text(theme.fg("accent", `Skill "${name}" ${enabled ? "enabled" : "disabled"} in ~/.pie/settings.json`), 1, 0));
65417
+ host.ui.requestRender();
65418
+ }
64544
65419
  async function handleSkillsCreate(host, args) {
64545
65420
  console.error(`[SkillsCreate] Called with args: "${args}"`);
64546
65421
  try {
@@ -64636,7 +65511,7 @@ async function handleSkillsReload(host) {
64636
65511
  const userDir = path25.join(os7.homedir(), ".pie", "skills");
64637
65512
  const projectDir = path25.join(process.cwd(), ".pie", "skills");
64638
65513
  const result = loadSkills({ dirs: buildSkillDirs({ builtinDir, userDir, projectDir, cwd: process.cwd() }) });
64639
- host.skills = result.skills;
65514
+ host.skills = result.skills.filter((skill) => host.options.settingsManager.isSkillEnabled(skill.name));
64640
65515
  host.skillsSection = formatSkillsForPrompt(host.skills);
64641
65516
  host.applyRuntimeToolMode(host.allTools.length > 0 ? host.allTools : host.agent.state.tools);
64642
65517
  host.updateSystemPromptTools();
@@ -64671,6 +65546,67 @@ async function handleSkillsReload(host) {
64671
65546
  }
64672
65547
  }
64673
65548
 
65549
+ // src/commands/interactive-tools-command.ts
65550
+ function errorMessage3(error) {
65551
+ return error instanceof Error ? error.message : String(error);
65552
+ }
65553
+ function collectKnownToolNames(host) {
65554
+ const liveNames = (host.allTools.length > 0 ? host.allTools : host.agent?.state.tools ?? []).map((tool) => tool.name);
65555
+ const disabledNames = host.options.settingsManager.getDisabledTools();
65556
+ const disabled = disabledNames.filter((name) => !liveNames.includes(name));
65557
+ return { active: [...liveNames].sort(), disabled: [...disabled].sort() };
65558
+ }
65559
+ function handleToolsCommand(host, args) {
65560
+ const parts = args.trim().split(/\s+/).filter(Boolean);
65561
+ const subcommand = parts[0];
65562
+ if (subcommand === "enable" || subcommand === "disable") {
65563
+ const name = parts.slice(1).join(" ").trim();
65564
+ if (!name) {
65565
+ host.showError(`Usage: /tools ${subcommand} <tool-name>`);
65566
+ return;
65567
+ }
65568
+ const { active: active2, disabled: disabled2 } = collectKnownToolNames(host);
65569
+ if (!active2.includes(name) && !disabled2.includes(name)) {
65570
+ host.showError(`Tool not found: ${name}. Use /tools to list tool names.`);
65571
+ return;
65572
+ }
65573
+ const enabled = subcommand === "enable";
65574
+ try {
65575
+ host.options.settingsManager.setToolEnabled(name, enabled);
65576
+ } catch (error) {
65577
+ host.showError(`Failed to update tool "${name}": ${errorMessage3(error)}`);
65578
+ return;
65579
+ }
65580
+ host.chatContainer.addChild(new Spacer(1));
65581
+ host.chatContainer.addChild(new Text(
65582
+ theme.fg("accent", `Tool "${name}" ${enabled ? "enabled" : "disabled"} in ~/.pie/settings.json. Takes effect on next start.`),
65583
+ 1,
65584
+ 0
65585
+ ));
65586
+ host.ui.requestRender();
65587
+ return;
65588
+ }
65589
+ if (subcommand) {
65590
+ host.showError("Usage: /tools [enable|disable <tool-name>]");
65591
+ return;
65592
+ }
65593
+ const { active, disabled } = collectKnownToolNames(host);
65594
+ host.chatContainer.addChild(new Spacer(1));
65595
+ host.chatContainer.addChild(new Text(theme.bold("Tools"), 1, 0));
65596
+ if (active.length === 0 && disabled.length === 0) {
65597
+ host.chatContainer.addChild(new Text(theme.fg("dim", " No tools available."), 1, 0));
65598
+ }
65599
+ for (const name of active) {
65600
+ host.chatContainer.addChild(new Text(theme.fg("dim", ` \u2022 ${name}: enabled`), 1, 0));
65601
+ }
65602
+ for (const name of disabled) {
65603
+ host.chatContainer.addChild(new Text(theme.fg("dim", ` \u2022 ${name}: disabled (/tools/enable ${name})`), 1, 0));
65604
+ }
65605
+ host.chatContainer.addChild(new Spacer(1));
65606
+ host.chatContainer.addChild(new Text(theme.fg("dim", "Usage: /tools/enable <name> | /tools/disable <name> (takes effect on next start)"), 1, 0));
65607
+ host.ui.requestRender();
65608
+ }
65609
+
64674
65610
  // src/commands/interactive-command-handlers.ts
64675
65611
  function showSettingsSelector(host) {
64676
65612
  host.showSelector((done) => {
@@ -65271,19 +66207,19 @@ function registerBuiltinCommands(host) {
65271
66207
  description: "Toggle calm mode: show only questions and answers (on/off/status)",
65272
66208
  handler: (_2, args) => host.handleCalmCommand(args)
65273
66209
  });
65274
- commandTree.register(["settings", "extensions"], {
66210
+ commandTree.register(["extensions"], {
65275
66211
  description: "List extensions and manage enable/disable toggles",
65276
66212
  handler: (_2, args) => host.handleExtensionsCommand(args)
65277
66213
  });
65278
- commandTree.register(["settings", "extensions", "list"], {
66214
+ commandTree.register(["extensions", "list"], {
65279
66215
  description: "List discovered extensions (including disabled)",
65280
66216
  handler: (_2, args) => host.handleExtensionsCommand(args ? `list ${args}` : "list")
65281
66217
  });
65282
- commandTree.register(["settings", "extensions", "enable"], {
66218
+ commandTree.register(["extensions", "enable"], {
65283
66219
  description: "Enable an extension by identity (writes settings)",
65284
66220
  handler: (_2, args) => host.handleExtensionsCommand(`enable ${args || ""}`)
65285
66221
  });
65286
- commandTree.register(["settings", "extensions", "disable"], {
66222
+ commandTree.register(["extensions", "disable"], {
65287
66223
  description: "Disable an extension by identity (writes settings)",
65288
66224
  handler: (_2, args) => host.handleExtensionsCommand(`disable ${args || ""}`)
65289
66225
  });
@@ -65301,6 +66237,14 @@ function registerBuiltinCommands(host) {
65301
66237
  await host.handleSkillsReload();
65302
66238
  }
65303
66239
  });
66240
+ commandTree.register(["skills", "enable"], {
66241
+ description: "Enable a skill by name (writes settings, reloads immediately)",
66242
+ handler: (_2, args) => host.handleSkillToggleCommand(args || "", true)
66243
+ });
66244
+ commandTree.register(["skills", "disable"], {
66245
+ description: "Disable a skill by name (writes settings, reloads immediately)",
66246
+ handler: (_2, args) => host.handleSkillToggleCommand(args || "", false)
66247
+ });
65304
66248
  for (const skill of host.skills) {
65305
66249
  commandTree.register(["skills", `skill:${skill.name}`], {
65306
66250
  description: skill.description,
@@ -65311,15 +66255,27 @@ function registerBuiltinCommands(host) {
65311
66255
  description: "Spawn subagents in parallel to handle tasks",
65312
66256
  handler: (_2, args) => host.handleSubagentCommand(args || "")
65313
66257
  });
65314
- commandTree.register(["settings", "debugs", "cache-test"], {
66258
+ commandTree.register(["tools"], {
66259
+ description: "List runtime tools and manage enable/disable toggles",
66260
+ handler: (_2, args) => host.handleToolsCommand(args || "")
66261
+ });
66262
+ commandTree.register(["tools", "enable"], {
66263
+ description: "Enable a tool by name (writes settings; next start)",
66264
+ handler: (_2, args) => host.handleToolsCommand(`enable ${args || ""}`)
66265
+ });
66266
+ commandTree.register(["tools", "disable"], {
66267
+ description: "Disable a tool by name (writes settings; next start)",
66268
+ handler: (_2, args) => host.handleToolsCommand(`disable ${args || ""}`)
66269
+ });
66270
+ commandTree.register(["debugs", "cache-test"], {
65315
66271
  description: "Run provider cache diagnostics with a dedicated 2-request probe",
65316
66272
  handler: () => host.handleCacheTest()
65317
66273
  });
65318
- commandTree.register(["settings", "debugs", "context"], {
66274
+ commandTree.register(["debugs", "context"], {
65319
66275
  description: "Append a context summary to ~/.pie/logs/pie-cli.log",
65320
66276
  handler: () => host.handleDebugContextCommand()
65321
66277
  });
65322
- commandTree.register(["settings", "debugs", "package"], {
66278
+ commandTree.register(["debugs", "package"], {
65323
66279
  description: "Append a package summary to ~/.pie/logs/pie-cli.log",
65324
66280
  handler: () => host.handleDebugPackageCommand()
65325
66281
  });
@@ -66238,7 +67194,7 @@ function setupInteractiveEditorSubmitHandler(host) {
66238
67194
  host.ui.requestRender();
66239
67195
  await new Promise((resolve7) => setImmediate(resolve7));
66240
67196
  let finalContent = messageContent;
66241
- const reminderPrefix = host.executionReminder;
67197
+ const reminderPrefix = host.executionState.getReminder() ?? host.executionReminder;
66242
67198
  const combinedPrefix = [reminderPrefix].filter((value) => Boolean(value)).join("");
66243
67199
  if (combinedPrefix) {
66244
67200
  if (typeof messageContent === "string") {
@@ -67202,17 +68158,17 @@ var MentionHandler = class {
67202
68158
  } catch (err) {
67203
68159
  this.onInsertMention("", pendingMention);
67204
68160
  this.clearPendingStatus(pendingStatusKey);
67205
- const errorMessage3 = err instanceof Error ? err.message : String(err);
67206
- if (errorMessage3.includes("PLATFORM_NOT_SUPPORTED")) {
68161
+ const errorMessage4 = err instanceof Error ? err.message : String(err);
68162
+ if (errorMessage4.includes("PLATFORM_NOT_SUPPORTED")) {
67207
68163
  this.onError?.("The current AI model does not support file uploads.");
67208
- } else if (errorMessage3.includes("ENOENT")) {
68164
+ } else if (errorMessage4.includes("ENOENT")) {
67209
68165
  this.onError?.("File does not exist or cannot be accessed.");
67210
- } else if (errorMessage3.includes("EACCES") || errorMessage3.includes("PERMISSION")) {
68166
+ } else if (errorMessage4.includes("EACCES") || errorMessage4.includes("PERMISSION")) {
67211
68167
  this.onError?.("Permission denied while accessing the file.");
67212
- } else if (errorMessage3.includes("network") || errorMessage3.includes("ETIMEDOUT") || errorMessage3.includes("ECONNREFUSED")) {
68168
+ } else if (errorMessage4.includes("network") || errorMessage4.includes("ETIMEDOUT") || errorMessage4.includes("ECONNREFUSED")) {
67213
68169
  this.onError?.("Network error. Check the connection and try again.");
67214
68170
  } else {
67215
- this.onError?.(`Upload failed: ${errorMessage3}`);
68171
+ this.onError?.(`Upload failed: ${errorMessage4}`);
67216
68172
  }
67217
68173
  } finally {
67218
68174
  this.clearPendingStatus(pendingStatusKey);
@@ -68412,8 +69368,8 @@ function calculateCliContextUsage(params) {
68412
69368
  };
68413
69369
  }
68414
69370
  function createCliAutoCompactDecision(params) {
68415
- const errorMessage3 = params.message?.role === "assistant" ? params.message.errorMessage : void 0;
68416
- if (errorMessage3 && params.isContextOverflowError(errorMessage3)) {
69371
+ const errorMessage4 = params.message?.role === "assistant" ? params.message.errorMessage : void 0;
69372
+ if (errorMessage4 && params.isContextOverflowError(errorMessage4)) {
68417
69373
  return { reason: "overflow", willRetry: true };
68418
69374
  }
68419
69375
  if (!params.autoCompactEnabled || params.contextWindow <= 0) {
@@ -68499,9 +69455,9 @@ var INVALID_FILE_REFERENCE_PATTERN = /not the owner of this file|owner of this f
68499
69455
  var INVALID_REQUEST_PATTERN = /bad request|\b400\b|invalid request|invalid schema|schema validation|invalid parameter|unsupported parameter|malformed/i;
68500
69456
  var CLI_RETRY_MAX_RETRIES = 3;
68501
69457
  var CLI_RETRY_BASE_DELAY_MS = 2e3;
68502
- function isCliContextOverflowError(errorMessage3) {
69458
+ function isCliContextOverflowError(errorMessage4) {
68503
69459
  return /context length exceeded|context_length_exceeded|maximum context length|maximum context size|too many tokens|input.*too long|prompt.*too long|context window|token limit exceeded|exceeds.{0,20}context/i.test(
68504
- errorMessage3
69460
+ errorMessage4
68505
69461
  );
68506
69462
  }
68507
69463
  function createClassification(kind, message, overrides) {
@@ -68516,56 +69472,56 @@ function createClassification(kind, message, overrides) {
68516
69472
  };
68517
69473
  }
68518
69474
  function classifyCliFailure(context) {
68519
- const errorMessage3 = typeof context === "string" ? context : context.errorMessage;
68520
- if (isCliContextOverflowError(errorMessage3)) {
68521
- return createClassification("context_overflow", errorMessage3, {
69475
+ const errorMessage4 = typeof context === "string" ? context : context.errorMessage;
69476
+ if (isCliContextOverflowError(errorMessage4)) {
69477
+ return createClassification("context_overflow", errorMessage4, {
68522
69478
  compactable: true,
68523
69479
  terminal: false
68524
69480
  });
68525
69481
  }
68526
- if (USER_ABORT_PATTERN.test(errorMessage3)) {
68527
- return createClassification("user_abort", errorMessage3, {
69482
+ if (USER_ABORT_PATTERN.test(errorMessage4)) {
69483
+ return createClassification("user_abort", errorMessage4, {
68528
69484
  userAbort: true,
68529
69485
  terminal: false
68530
69486
  });
68531
69487
  }
68532
- if (INVALID_FILE_REFERENCE_PATTERN.test(errorMessage3)) {
68533
- return createClassification("invalid_file_reference", errorMessage3);
69488
+ if (INVALID_FILE_REFERENCE_PATTERN.test(errorMessage4)) {
69489
+ return createClassification("invalid_file_reference", errorMessage4);
68534
69490
  }
68535
- if (AUTH_CONFIG_PATTERN.test(errorMessage3)) {
68536
- return createClassification("auth_config", errorMessage3);
69491
+ if (AUTH_CONFIG_PATTERN.test(errorMessage4)) {
69492
+ return createClassification("auth_config", errorMessage4);
68537
69493
  }
68538
- if (TRANSIENT_NETWORK_ERROR_PATTERN.test(errorMessage3)) {
68539
- return createClassification("transient_network", errorMessage3, {
69494
+ if (TRANSIENT_NETWORK_ERROR_PATTERN.test(errorMessage4)) {
69495
+ return createClassification("transient_network", errorMessage4, {
68540
69496
  retryable: true,
68541
69497
  terminal: false
68542
69498
  });
68543
69499
  }
68544
- if (errorMessage3.includes(EMPTY_ASSISTANT_CONTENT_ERROR)) {
68545
- return createClassification("provider_transient", errorMessage3, {
69500
+ if (errorMessage4.includes(EMPTY_ASSISTANT_CONTENT_ERROR)) {
69501
+ return createClassification("provider_transient", errorMessage4, {
68546
69502
  retryable: true,
68547
69503
  terminal: false
68548
69504
  });
68549
69505
  }
68550
- if (errorMessage3.includes(OPENAI_COMPAT_PROTOCOL_ERROR_CODE)) {
68551
- return createClassification("provider_transient", errorMessage3, {
69506
+ if (errorMessage4.includes(OPENAI_COMPAT_PROTOCOL_ERROR_CODE)) {
69507
+ return createClassification("provider_transient", errorMessage4, {
68552
69508
  retryable: true,
68553
69509
  terminal: false
68554
69510
  });
68555
69511
  }
68556
- if (PROVIDER_TRANSIENT_ERROR_PATTERN.test(errorMessage3)) {
68557
- return createClassification("provider_transient", errorMessage3, {
69512
+ if (PROVIDER_TRANSIENT_ERROR_PATTERN.test(errorMessage4)) {
69513
+ return createClassification("provider_transient", errorMessage4, {
68558
69514
  retryable: true,
68559
69515
  terminal: false
68560
69516
  });
68561
69517
  }
68562
- if (INVALID_REQUEST_PATTERN.test(errorMessage3)) {
68563
- return createClassification("invalid_request", errorMessage3);
69518
+ if (INVALID_REQUEST_PATTERN.test(errorMessage4)) {
69519
+ return createClassification("invalid_request", errorMessage4);
68564
69520
  }
68565
- return createClassification("unknown", errorMessage3);
69521
+ return createClassification("unknown", errorMessage4);
68566
69522
  }
68567
- function isCliRetryableError(errorMessage3) {
68568
- return classifyCliFailure(errorMessage3).retryable;
69523
+ function isCliRetryableError(errorMessage4) {
69524
+ return classifyCliFailure(errorMessage4).retryable;
68569
69525
  }
68570
69526
  function getCliRetryBaseDelayMs() {
68571
69527
  const override = process.env.PIE_TEST_CLI_RETRY_BASE_DELAY_MS;
@@ -68738,23 +69694,23 @@ function setupAutocomplete(host) {
68738
69694
  };
68739
69695
  host.editor.setAutocompleteProvider(provider);
68740
69696
  }
68741
- function isContextOverflowError(errorMessage3) {
68742
- return isCliContextOverflowError(errorMessage3);
69697
+ function isContextOverflowError(errorMessage4) {
69698
+ return isCliContextOverflowError(errorMessage4);
68743
69699
  }
68744
69700
  function shouldAutoCompact(host, contextTokens, contextWindow) {
68745
69701
  if (!host.options.settingsManager.getAutoCompactEnabled()) return false;
68746
69702
  const reserveTokens = host.options.settingsManager.getAutoCompactReserveTokens();
68747
69703
  return shouldCliAutoCompactTokens({ tokens: contextTokens, contextWindow, reserveTokens });
68748
69704
  }
68749
- function isRetryableError(errorMessage3) {
68750
- return isCliRetryableError(errorMessage3);
69705
+ function isRetryableError(errorMessage4) {
69706
+ return isCliRetryableError(errorMessage4);
68751
69707
  }
68752
- function isInvalidFileReferenceError(errorMessage3) {
69708
+ function isInvalidFileReferenceError(errorMessage4) {
68753
69709
  return /not the owner of this file|owner of this file|invalid_authentication_error/i.test(
68754
- errorMessage3
69710
+ errorMessage4
68755
69711
  );
68756
69712
  }
68757
- function recoverFromInvalidFileReferenceError(host, errorMessage3) {
69713
+ function recoverFromInvalidFileReferenceError(host, errorMessage4) {
68758
69714
  if (!host.fileManager || !host.agent) return;
68759
69715
  const messages = host.agent.state.messages.slice();
68760
69716
  const invalidFileIds = /* @__PURE__ */ new Set();
@@ -68772,7 +69728,7 @@ function recoverFromInvalidFileReferenceError(host, errorMessage3) {
68772
69728
  }
68773
69729
  if (invalidFileIds.size === 0) return;
68774
69730
  for (const fileId of invalidFileIds) {
68775
- host.fileManager.markFileReferenceError(fileId, errorMessage3);
69731
+ host.fileManager.markFileReferenceError(fileId, errorMessage4);
68776
69732
  }
68777
69733
  let changed = false;
68778
69734
  const sanitizedMessages = messages.map((message) => {
@@ -69050,9 +70006,9 @@ function showStatus(host, message) {
69050
70006
  host.lastStatusText = text;
69051
70007
  host.ui.requestRender();
69052
70008
  }
69053
- function showError(host, errorMessage3) {
70009
+ function showError(host, errorMessage4) {
69054
70010
  host.chatContainer.addChild(new Spacer(1));
69055
- host.chatContainer.addChild(new Text(theme.fg("error", `Error: ${errorMessage3}`), 1, 0));
70011
+ host.chatContainer.addChild(new Text(theme.fg("error", `Error: ${errorMessage4}`), 1, 0));
69056
70012
  host.ui.requestRender();
69057
70013
  }
69058
70014
  async function executeCommand(host, command) {
@@ -69279,7 +70235,7 @@ function createInteractiveAgentSessionController(host) {
69279
70235
  getAutoContinueMessage: () => host.getTodoAutoContinueMessage(),
69280
70236
  classifyFailure: (context) => classifyCliFailure(context),
69281
70237
  retry: {
69282
- isRetryableError: (errorMessage3) => !host._isContextOverflowError(errorMessage3) && isCliRetryableError(errorMessage3),
70238
+ isRetryableError: (errorMessage4) => !host._isContextOverflowError(errorMessage4) && isCliRetryableError(errorMessage4),
69283
70239
  maxRetries: CLI_RETRY_MAX_RETRIES,
69284
70240
  baseDelayMs: getCliRetryBaseDelayMs()
69285
70241
  },
@@ -69431,6 +70387,13 @@ function createInteractiveAgentSessionController(host) {
69431
70387
  host.appendWarningSummary(outcome.terminalSummary);
69432
70388
  }
69433
70389
  host.setControllerBusy(false);
70390
+ } else if (event.type === "runtime_guard_advisory") {
70391
+ host.noteRuntimeGuardAdvisory(event);
70392
+ host.sessionTrace.noteRuntimeEvent("runtime_guard_advisory", {
70393
+ toolName: event.toolName,
70394
+ repeatCount: event.repeatCount,
70395
+ reason: event.reason
70396
+ });
69434
70397
  } else if (event.type === "runtime_guard_triggered") {
69435
70398
  host.sessionTrace.noteStalled("runtime_guard_triggered", {
69436
70399
  toolName: event.toolName,
@@ -69631,6 +70594,43 @@ function createRuntimeModeTransformContext(options) {
69631
70594
  };
69632
70595
  }
69633
70596
 
70597
+ // src/runtime/guard-advisory-context.ts
70598
+ var GUARD_ADVISORY_MARKER = "<runtime-guard-advisory>";
70599
+ var GUARD_ADVISORY_END_MARKER = "</runtime-guard-advisory>";
70600
+ function isMarkerOnlyUserMessage(message) {
70601
+ if (message.role !== "user") return false;
70602
+ const content = message.content;
70603
+ const text = typeof content === "string" ? content : Array.isArray(content) && content.length === 1 && content[0]?.type === "text" ? content[0].text : "";
70604
+ return text.trim().startsWith(GUARD_ADVISORY_MARKER) && text.trim().endsWith(GUARD_ADVISORY_END_MARKER);
70605
+ }
70606
+ function stripGuardAdvisories(messages) {
70607
+ return messages.filter((message) => !isMarkerOnlyUserMessage(message));
70608
+ }
70609
+ function findLatestUserIndex2(messages) {
70610
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
70611
+ if (messages[index].role === "user") return index;
70612
+ }
70613
+ return -1;
70614
+ }
70615
+ function injectGuardAdvisory(messages, guidance) {
70616
+ const advisoryMessage = {
70617
+ role: "user",
70618
+ content: `${GUARD_ADVISORY_MARKER}
70619
+ ${guidance}
70620
+ ${GUARD_ADVISORY_END_MARKER}`,
70621
+ timestamp: Date.now()
70622
+ };
70623
+ const latestUserIndex = findLatestUserIndex2(messages);
70624
+ if (latestUserIndex < 0) {
70625
+ return [...messages, advisoryMessage];
70626
+ }
70627
+ return [
70628
+ ...messages.slice(0, latestUserIndex),
70629
+ advisoryMessage,
70630
+ ...messages.slice(latestUserIndex)
70631
+ ];
70632
+ }
70633
+
69634
70634
  // src/runtime/tool-policy.ts
69635
70635
  function selectToolsForRuntimeMode(tools, mode) {
69636
70636
  return selectToolsForRuntimePolicy(tools, mode);
@@ -70321,7 +71321,7 @@ async function loadInteractiveExtensions(host) {
70321
71321
  console.error(`[Extension] Failed to load ${path33}: ${error}`);
70322
71322
  }
70323
71323
  }
70324
- const extensionTools = convertExtensionTools(extensions);
71324
+ const extensionTools = filterEnabledTools(convertExtensionTools(extensions), host.options.settingsManager);
70325
71325
  const activeTools = [...host.agent.state.tools, ...extensionTools];
70326
71326
  host.agent.state.tools = wrapToolsWithExtensions(activeTools, host.extensionRunner);
70327
71327
  const availableTools = [
@@ -70390,12 +71390,13 @@ var InteractiveMode = class {
70390
71390
  isProcessing = false;
70391
71391
  _turnIndex = 0;
70392
71392
  _semanticTurnIndex = 0;
70393
- thinkingLevel = "off";
71393
+ thinkingLevel = DEFAULT_THINKING_LEVEL;
70394
71394
  toolOutputExpanded = false;
70395
71395
  planModeEnabled = false;
70396
71396
  pendingPlanModeTransition = null;
70397
71397
  pendingYoloModeTransition = null;
70398
71398
  executionReminder = null;
71399
+ pendingRuntimeGuardAdvisory = null;
70399
71400
  fileManager;
70400
71401
  mentionHandler;
70401
71402
  mentionPickerComponent;
@@ -70705,12 +71706,9 @@ var InteractiveMode = class {
70705
71706
  });
70706
71707
  }
70707
71708
  async init() {
70708
- const savedLevel = this.options.settingsManager.getDefaultThinkingLevel();
70709
- if (savedLevel) {
70710
- this.thinkingLevel = savedLevel;
70711
- }
70712
- const { Agent } = await import("./chunks/src-HYZFAW4V.js");
70713
71709
  const activeSession = this.sessionManager.getActiveSession();
71710
+ this.thinkingLevel = resolveInitialThinkingLevel({ session: activeSession, settingsManager: this.options.settingsManager });
71711
+ const { Agent } = await import("./chunks/src-HYZFAW4V.js");
70714
71712
  this.agent = new Agent({
70715
71713
  initialState: {
70716
71714
  systemPrompt: this.buildSystemPrompt(),
@@ -70721,20 +71719,27 @@ var InteractiveMode = class {
70721
71719
  apiKey: this.options.apiKey,
70722
71720
  sessionId: activeSession?.id,
70723
71721
  streamFn: this.options.streamFn,
70724
- transformContext: createRuntimeModeTransformContext({
70725
- getState: () => this.getRuntimeModeState(),
70726
- consumeTransition: () => this.consumeRuntimeModeTransition(),
70727
- onInjected: (state) => this.sessionTrace.noteRuntimeEvent("runtime_context_injected", {
70728
- yoloMode: state.yoloMode,
70729
- planMode: state.planMode,
70730
- filesystemScope: state.filesystemScope,
70731
- pendingTransition: state.pendingTransition,
70732
- currentDate: state.currentDate,
70733
- timeZone: state.timeZone,
70734
- activeToolCount: state.activeToolNames.length
70735
- }),
70736
- onStripped: (count) => this.sessionTrace.noteRuntimeEvent("stale_runtime_context_stripped", { count })
70737
- })
71722
+ transformContext: async (messages) => {
71723
+ const runtimeModeTransform = createRuntimeModeTransformContext({
71724
+ getState: () => this.getRuntimeModeState(),
71725
+ consumeTransition: () => this.consumeRuntimeModeTransition(),
71726
+ onInjected: (state) => this.sessionTrace.noteRuntimeEvent("runtime_context_injected", {
71727
+ yoloMode: state.yoloMode,
71728
+ planMode: state.planMode,
71729
+ filesystemScope: state.filesystemScope,
71730
+ pendingTransition: state.pendingTransition,
71731
+ currentDate: state.currentDate,
71732
+ timeZone: state.timeZone,
71733
+ activeToolCount: state.activeToolNames.length
71734
+ }),
71735
+ onStripped: (count) => this.sessionTrace.noteRuntimeEvent("stale_runtime_context_stripped", { count })
71736
+ });
71737
+ let transformed = await runtimeModeTransform(messages);
71738
+ const advisory = this.pendingRuntimeGuardAdvisory;
71739
+ this.pendingRuntimeGuardAdvisory = null;
71740
+ transformed = advisory ? injectGuardAdvisory(transformed, advisory) : stripGuardAdvisories(transformed);
71741
+ return transformed;
71742
+ }
70738
71743
  });
70739
71744
  this.agentSessionController = createInteractiveAgentSessionController(this.controllerSetupHost);
70740
71745
  this.updateTerminalTitle();
@@ -70837,6 +71842,10 @@ var InteractiveMode = class {
70837
71842
  calculateContextUsage() {
70838
71843
  return calculateContextUsage(this.contextUiHost);
70839
71844
  }
71845
+ noteRuntimeGuardAdvisory(event) {
71846
+ this.pendingRuntimeGuardAdvisory = event.guidance;
71847
+ this.showStatus(`Runtime guard: ${event.toolName} failed ${event.repeatCount}x identically; advisory queued for next request.`);
71848
+ }
70840
71849
  persistExecutionState() {
70841
71850
  const activeSession = this.sessionManager.getActiveSession();
70842
71851
  if (!activeSession) return;
@@ -70865,20 +71874,20 @@ var InteractiveMode = class {
70865
71874
  setupAgentSubscription() {
70866
71875
  bindInteractiveRuntimeEvents(this.runtimeEventHost);
70867
71876
  }
70868
- _isContextOverflowError(errorMessage3) {
70869
- return isContextOverflowError(errorMessage3);
71877
+ _isContextOverflowError(errorMessage4) {
71878
+ return isContextOverflowError(errorMessage4);
70870
71879
  }
70871
71880
  _shouldAutoCompact(contextTokens, contextWindow) {
70872
71881
  return shouldAutoCompact(this.contextUiHost, contextTokens, contextWindow);
70873
71882
  }
70874
- _isRetryableError(errorMessage3) {
70875
- return isRetryableError(errorMessage3);
71883
+ _isRetryableError(errorMessage4) {
71884
+ return isRetryableError(errorMessage4);
70876
71885
  }
70877
- _isInvalidFileReferenceError(errorMessage3) {
70878
- return isInvalidFileReferenceError(errorMessage3);
71886
+ _isInvalidFileReferenceError(errorMessage4) {
71887
+ return isInvalidFileReferenceError(errorMessage4);
70879
71888
  }
70880
- _recoverFromInvalidFileReferenceError(errorMessage3) {
70881
- return recoverFromInvalidFileReferenceError(this.contextUiHost, errorMessage3);
71889
+ _recoverFromInvalidFileReferenceError(errorMessage4) {
71890
+ return recoverFromInvalidFileReferenceError(this.contextUiHost, errorMessage4);
70882
71891
  }
70883
71892
  async handleCompactCommand() {
70884
71893
  return handleCompactCommand(this.compactCommandHost);
@@ -70969,7 +71978,7 @@ var InteractiveMode = class {
70969
71978
  return handleResumeCommand(this);
70970
71979
  }
70971
71980
  async loadSession(sessionId) {
70972
- return loadSession(this, sessionId);
71981
+ return loadSession2(this, sessionId);
70973
71982
  }
70974
71983
  updateTerminalTitle() {
70975
71984
  return updateTerminalTitle(this);
@@ -71031,6 +72040,12 @@ var InteractiveMode = class {
71031
72040
  async handleSkillsReload() {
71032
72041
  return handleSkillsReload(this);
71033
72042
  }
72043
+ async handleSkillToggleCommand(args, enabled) {
72044
+ return handleSkillToggleCommand(this, args, enabled);
72045
+ }
72046
+ handleToolsCommand(args) {
72047
+ return handleToolsCommand(this, args);
72048
+ }
71034
72049
  isInteractiveCommand(commandPath) {
71035
72050
  const interactiveCommands = [
71036
72051
  "/skills/create",
@@ -71049,8 +72064,8 @@ var InteractiveMode = class {
71049
72064
  showStatus(message) {
71050
72065
  return showStatus(this.uiHelperHost, message);
71051
72066
  }
71052
- showError(errorMessage3) {
71053
- return showError(this.uiHelperHost, errorMessage3);
72067
+ showError(errorMessage4) {
72068
+ return showError(this.uiHelperHost, errorMessage4);
71054
72069
  }
71055
72070
  async handleDirectBash(command) {
71056
72071
  return handleDirectBash(this.directBashHost, command);
@@ -71224,8 +72239,8 @@ async function composeCliRuntime(params) {
71224
72239
  } else {
71225
72240
  await sessionManager.createSession();
71226
72241
  }
71227
- syncSessionModelMetadata(sessionManager, model, settingsManager.getDefaultThinkingLevel() || "off");
71228
72242
  const activeSession = sessionManager.getActiveSession();
72243
+ syncSessionModelMetadata(sessionManager, model, resolveInitialThinkingLevel({ session: activeSession, settingsManager }));
71229
72244
  if (activeSession && activeSession.metadata.cwd !== cwd) {
71230
72245
  activeSession.metadata.cwd = cwd;
71231
72246
  await sessionManager.save();
@@ -71242,7 +72257,7 @@ async function composeCliRuntime(params) {
71242
72257
  keyPrefix: apiKey ? maskApiKey2(apiKey) : "none"
71243
72258
  });
71244
72259
  const projectSkillsDir = path30.join(cwd, ".pie", "skills");
71245
- const { skills, diagnostics } = loadSkills({
72260
+ const { skills: discoveredSkills, diagnostics } = loadSkills({
71246
72261
  dirs: buildSkillDirs({
71247
72262
  builtinDir: paths.builtinSkillsDir,
71248
72263
  userDir: paths.skillsDir,
@@ -71250,6 +72265,7 @@ async function composeCliRuntime(params) {
71250
72265
  cwd
71251
72266
  })
71252
72267
  });
72268
+ const skills = discoveredSkills.filter((skill) => settingsManager.isSkillEnabled(skill.name));
71253
72269
  let interactiveOptions;
71254
72270
  const getCurrentModel = () => interactiveOptions?.model ?? model;
71255
72271
  const cliHostCapabilities = createCliHostCapabilities(cwd, {
@@ -71273,22 +72289,23 @@ async function composeCliRuntime(params) {
71273
72289
  ...createCliFileSystemTools(cwd, fileSystemToolOptions),
71274
72290
  ...cliHostCapabilities.tools
71275
72291
  ];
72292
+ const enabledRawTools = filterEnabledTools(rawTools, settingsManager);
71276
72293
  recordCliCompositionStep(compositionTrace, {
71277
72294
  id: "host_shared_capabilities",
71278
72295
  inputSummary: `cwd=${cwd}`,
71279
- outputSummary: summarizeToolNames(rawTools),
71280
- toolCount: rawTools.length
72296
+ outputSummary: summarizeToolNames(enabledRawTools),
72297
+ toolCount: enabledRawTools.length
71281
72298
  });
71282
- const allTools = createPolicyEnforcedTools(rawTools, {
72299
+ const allTools = createPolicyEnforcedTools(enabledRawTools, {
71283
72300
  getContext: () => ({ mode: "normal", confirmationPolicy: "allow" })
71284
72301
  });
71285
72302
  const tools = [...allTools];
71286
72303
  recordCliCompositionStep(compositionTrace, {
71287
72304
  id: "policy_filtering",
71288
- inputSummary: `tools=${rawTools.length}`,
72305
+ inputSummary: `tools=${enabledRawTools.length}`,
71289
72306
  outputSummary: summarizeToolNames(tools),
71290
72307
  toolCount: tools.length,
71291
- policyDecisions: rawTools.length === allTools.length ? ["allow:default"] : [`filtered:${rawTools.length - allTools.length}`]
72308
+ policyDecisions: enabledRawTools.length === allTools.length ? ["allow:default"] : [`filtered:${enabledRawTools.length - allTools.length}`]
71292
72309
  });
71293
72310
  for (const diag of diagnostics.filter((d) => d.type === "collision")) {
71294
72311
  console.log(`[CLI] ${diag.message}`);
@@ -71473,7 +72490,7 @@ async function loadPrintModeExtensions(params) {
71473
72490
  );
71474
72491
  runner.setUIContext(uiContext);
71475
72492
  await restoreExtensionProviderCredentials(params.modelRegistry);
71476
- const extensionTools = convertExtensionTools(extensions);
72493
+ const extensionTools = filterEnabledTools(convertExtensionTools(extensions), params.settingsManager);
71477
72494
  availableTools = [...availableTools, ...extensionTools];
71478
72495
  return {
71479
72496
  extensions,
@@ -71533,12 +72550,13 @@ async function runPrintMode(params) {
71533
72550
  knowledgeSection: runtime.knowledgeSection,
71534
72551
  tools: printModeTools.map((tool) => ({ name: tool.name, description: tool.description }))
71535
72552
  });
72553
+ const printThinkingLevel = resolveInitialThinkingLevel({ session: runtime.sessionManager.getActiveSession(), settingsManager: runtime.settingsManager });
71536
72554
  agent = new AgentClass({
71537
72555
  initialState: {
71538
72556
  systemPrompt: baseSystemPrompt,
71539
72557
  model: runtime.model,
71540
72558
  tools: printModeTools,
71541
- thinkingLevel: "off"
72559
+ thinkingLevel: printThinkingLevel
71542
72560
  },
71543
72561
  apiKey: runtime.apiKey,
71544
72562
  streamFn: params.streamFn
@@ -71673,21 +72691,21 @@ async function runPrintMode(params) {
71673
72691
  console.log(assistantText);
71674
72692
  }
71675
72693
  } catch (err) {
71676
- const errorMessage3 = err instanceof Error ? err.message : String(err);
71677
- sessionTrace.noteStalled(errorMessage3, { mode: "print" });
72694
+ const errorMessage4 = err instanceof Error ? err.message : String(err);
72695
+ sessionTrace.noteStalled(errorMessage4, { mode: "print" });
71678
72696
  process.exitCode = 1;
71679
72697
  if (params.jsonOutput) {
71680
72698
  console.log(
71681
72699
  JSON.stringify({
71682
72700
  ok: false,
71683
- error: errorMessage3,
72701
+ error: errorMessage4,
71684
72702
  sessionId: runtime.sessionManager.getActiveSession()?.id,
71685
72703
  provider: runtime.provider,
71686
72704
  modelId: runtime.modelId
71687
72705
  })
71688
72706
  );
71689
72707
  }
71690
- console.error("Error:", errorMessage3);
72708
+ console.error("Error:", errorMessage4);
71691
72709
  }
71692
72710
  if (!params.rawOutput && !params.jsonOutput) {
71693
72711
  console.log("");
@@ -71704,6 +72722,9 @@ var MAX_EVENT_OBJECT_KEYS = 50;
71704
72722
  var MAX_EVENT_ARRAY_ITEMS = 50;
71705
72723
  var MAX_EVENT_VALUE_DEPTH = 4;
71706
72724
  var MAX_EVENT_STRING_LENGTH = 4e3;
72725
+ var COMPACT_EVENT_STRING_LENGTH = 800;
72726
+ var FULL_DETAIL = "full";
72727
+ var COMPACT_DETAIL = "compact";
71707
72728
  function createNoOpExtensionUIContext2() {
71708
72729
  return {
71709
72730
  notify: () => {
@@ -71719,10 +72740,10 @@ function writeEvent(event) {
71719
72740
  process.stdout.write(`${JSON.stringify(event)}
71720
72741
  `);
71721
72742
  }
71722
- function toJsonValue(value, depth = 0) {
72743
+ function toJsonValue(value, depth = 0, cap = MAX_EVENT_STRING_LENGTH) {
71723
72744
  if (value === null) return null;
71724
72745
  if (typeof value === "string") {
71725
- return value.length > MAX_EVENT_STRING_LENGTH ? `${value.slice(0, MAX_EVENT_STRING_LENGTH)}...` : value;
72746
+ return value.length > cap ? value.slice(0, cap) : value;
71726
72747
  }
71727
72748
  if (typeof value === "number") return Number.isFinite(value) ? value : String(value);
71728
72749
  if (typeof value === "boolean") return value;
@@ -71730,17 +72751,33 @@ function toJsonValue(value, depth = 0) {
71730
72751
  if (typeof value !== "object") return void 0;
71731
72752
  if (depth >= MAX_EVENT_VALUE_DEPTH) return "[truncated]";
71732
72753
  if (Array.isArray(value)) {
71733
- return value.slice(0, MAX_EVENT_ARRAY_ITEMS).map((item) => toJsonValue(item, depth + 1) ?? null);
72754
+ return value.slice(0, MAX_EVENT_ARRAY_ITEMS).map((item) => toJsonValue(item, depth + 1, cap) ?? null);
71734
72755
  }
71735
72756
  const result = {};
71736
72757
  for (const [key, item] of Object.entries(value).slice(0, MAX_EVENT_OBJECT_KEYS)) {
71737
- result[key] = toJsonValue(item, depth + 1);
72758
+ result[key] = toJsonValue(item, depth + 1, cap);
71738
72759
  }
71739
72760
  return result;
71740
72761
  }
71741
72762
  function isRecord4(value) {
71742
72763
  return !!value && typeof value === "object" && !Array.isArray(value);
71743
72764
  }
72765
+ function truncateForEvent(text, cap) {
72766
+ if (text.length <= cap) {
72767
+ return { value: text, truncated: false, fullLength: text.length };
72768
+ }
72769
+ return { value: text.slice(0, cap), truncated: true, fullLength: text.length };
72770
+ }
72771
+ function hasOversizedValue(value, cap, depth = 0) {
72772
+ if (typeof value === "string") return value.length > cap;
72773
+ if (!value || typeof value !== "object") return false;
72774
+ if (depth >= MAX_EVENT_VALUE_DEPTH) return true;
72775
+ if (Array.isArray(value)) {
72776
+ return value.length > MAX_EVENT_ARRAY_ITEMS || value.some((item) => hasOversizedValue(item, cap, depth + 1));
72777
+ }
72778
+ const entries = Object.entries(value);
72779
+ return entries.length > MAX_EVENT_OBJECT_KEYS || entries.some(([, item]) => hasOversizedValue(item, cap, depth + 1));
72780
+ }
71744
72781
  function parseInput(line) {
71745
72782
  let parsed;
71746
72783
  try {
@@ -71765,8 +72802,7 @@ function parseInput(line) {
71765
72802
  type: "session.rewind",
71766
72803
  sessionId: typeof parsed.sessionId === "string" ? parsed.sessionId : void 0,
71767
72804
  requestId: typeof parsed.requestId === "string" ? parsed.requestId : void 0,
71768
- entryId: typeof parsed.entryId === "string" ? parsed.entryId : void 0,
71769
- checkpointId: typeof parsed.checkpointId === "string" ? parsed.checkpointId : void 0
72805
+ entryId: typeof parsed.entryId === "string" ? parsed.entryId : void 0
71770
72806
  };
71771
72807
  }
71772
72808
  if (parsed.type === "session.fork") {
@@ -71780,6 +72816,14 @@ function parseInput(line) {
71780
72816
  cwd: typeof parsed.cwd === "string" ? parsed.cwd : void 0
71781
72817
  };
71782
72818
  }
72819
+ if (parsed.type === "sidechat.request" && typeof parsed.requestId === "string" && typeof parsed.prompt === "string") {
72820
+ return {
72821
+ type: "sidechat.request",
72822
+ sessionId: typeof parsed.sessionId === "string" ? parsed.sessionId : void 0,
72823
+ requestId: parsed.requestId,
72824
+ prompt: parsed.prompt
72825
+ };
72826
+ }
71783
72827
  if ((parsed.type === "turn.request" || parsed.type === "turn.input") && typeof parsed.turnId === "string" && typeof parsed.prompt === "string") {
71784
72828
  const streamingBehavior = parsed.streamingBehavior;
71785
72829
  if (streamingBehavior !== void 0 && streamingBehavior !== "steer" && streamingBehavior !== "followUp") {
@@ -71813,6 +72857,12 @@ function assistantTextFromMessage(message) {
71813
72857
  }
71814
72858
  return text;
71815
72859
  }
72860
+ function assistantUsageFromAgent(agent) {
72861
+ const messages = agent?.state.messages ?? [];
72862
+ const lastMessage = messages[messages.length - 1];
72863
+ if (lastMessage?.role !== "assistant") return void 0;
72864
+ return toJsonValue(lastMessage.usage);
72865
+ }
71816
72866
  function assistantErrorFromAgent(agent) {
71817
72867
  const messages = agent?.state.messages ?? [];
71818
72868
  const lastMessage = messages[messages.length - 1];
@@ -71921,7 +72971,7 @@ async function loadJsonModeExtensions(params) {
71921
72971
  );
71922
72972
  runner.setUIContext(uiContext);
71923
72973
  await restoreExtensionProviderCredentials(params.runtime.modelRegistry);
71924
- const extensionTools = convertExtensionTools(extensions);
72974
+ const extensionTools = filterEnabledTools(convertExtensionTools(extensions), params.runtime.settingsManager);
71925
72975
  availableTools = [...availableTools, ...extensionTools];
71926
72976
  return {
71927
72977
  runner,
@@ -71930,6 +72980,8 @@ async function loadJsonModeExtensions(params) {
71930
72980
  };
71931
72981
  }
71932
72982
  async function runJsonEventsMode(params) {
72983
+ const eventDetail = params.eventDetail === FULL_DETAIL ? FULL_DETAIL : COMPACT_DETAIL;
72984
+ const eventStringCap = eventDetail === FULL_DETAIL ? MAX_EVENT_STRING_LENGTH : COMPACT_EVENT_STRING_LENGTH;
71933
72985
  const sessionId = params.explicitSessionId || params.runtime?.sessionManager.getActiveSession()?.id || `pie-json-${Date.now()}`;
71934
72986
  let activeTurn = null;
71935
72987
  let runtime = params.runtime;
@@ -71940,13 +72992,28 @@ async function runJsonEventsMode(params) {
71940
72992
  let trace;
71941
72993
  let lastRetryEvent;
71942
72994
  let mockActiveEntryId;
71943
- let thinkingLevel = "off";
72995
+ let thinkingLevel = DEFAULT_THINKING_LEVEL;
72996
+ let thinkingLevelInitialized = false;
71944
72997
  let defaultRuntime;
71945
72998
  let defaultModelState;
71946
72999
  const completedSessionControlRequests = /* @__PURE__ */ new Map();
73000
+ const completedSidechatRequests = /* @__PURE__ */ new Map();
73001
+ const ensureRuntimeInFlight = /* @__PURE__ */ new Map();
73002
+ let activeSidechat = null;
73003
+ let activeSidechatPromise = null;
73004
+ let sidechatChainStopped = false;
71947
73005
  const noProgressTimeoutMs = Number(process.env.PIE_JSON_EVENTS_NO_PROGRESS_MS || 6e5);
71948
73006
  const mockResponseDelayMs = Number(process.env.PIE_TEST_MOCK_RESPONSE_DELAY_MS || 0);
73007
+ const prewarmTimeoutMs = Number(process.env.PIE_JSON_EVENTS_PREWARM_TIMEOUT_MS || 1e4);
73008
+ const sidechatTimeoutMs = Number(process.env.PIE_JSON_EVENTS_SIDECHAT_TIMEOUT_MS || 3e5);
73009
+ await prewarmRuntime();
71949
73010
  writeEvent({ type: "agent.ready", sessionId });
73011
+ function trackSidechatPromise(promise) {
73012
+ activeSidechatPromise = promise;
73013
+ void promise.finally(() => {
73014
+ if (activeSidechatPromise === promise) activeSidechatPromise = null;
73015
+ });
73016
+ }
71950
73017
  function sessionMismatchError(requestedSessionId) {
71951
73018
  return `session mismatch: JSON events process is bound to ${sessionId}, received ${requestedSessionId}`;
71952
73019
  }
@@ -72118,15 +73185,10 @@ async function runJsonEventsMode(params) {
72118
73185
  }
72119
73186
  function resolveRewindEntryId(message) {
72120
73187
  const entryId = message.entryId?.trim();
72121
- const checkpointId = message.checkpointId?.trim();
72122
- if (entryId && checkpointId && entryId !== checkpointId) {
72123
- throw invalidSessionControl("entryId and checkpointId must match when both are provided");
73188
+ if (!entryId) {
73189
+ throw invalidSessionControl("entryId is required");
72124
73190
  }
72125
- const resolved = entryId || checkpointId;
72126
- if (!resolved) {
72127
- throw invalidSessionControl("entryId or checkpointId is required");
72128
- }
72129
- return resolved;
73191
+ return entryId;
72130
73192
  }
72131
73193
  function writeSessionControlFailure(message, operation, error) {
72132
73194
  const detail = error instanceof SessionControlError ? error : new SessionControlError("session_io_error", error instanceof Error ? error.message : String(error));
@@ -72178,14 +73240,12 @@ async function runJsonEventsMode(params) {
72178
73240
  entryId
72179
73241
  });
72180
73242
  await syncRuntimeAfterRewind(controlSessionId);
72181
- mockActiveEntryId = result.headEntryId;
73243
+ mockActiveEntryId = result.activeEntryId;
72182
73244
  const event = {
72183
73245
  type: "session.rewound",
72184
73246
  sessionId: controlSessionId,
72185
73247
  requestId,
72186
73248
  entryId: result.entryId,
72187
- checkpointId: result.checkpointId,
72188
- headEntryId: result.headEntryId,
72189
73249
  activeEntryId: result.activeEntryId
72190
73250
  };
72191
73251
  completedSessionControlRequests.set(requestId, event);
@@ -72222,11 +73282,8 @@ async function runJsonEventsMode(params) {
72222
73282
  requestId,
72223
73283
  sourceSessionId: result.sourceSessionId,
72224
73284
  sourceEntryId: result.sourceEntryId,
72225
- sourceCheckpointId: result.sourceCheckpointId,
72226
73285
  targetSessionId: result.targetSessionId,
72227
- targetHeadEntryId: result.targetHeadEntryId,
72228
- targetActiveEntryId: result.targetActiveEntryId,
72229
- headEntryId: result.headEntryId
73286
+ targetActiveEntryId: result.targetActiveEntryId
72230
73287
  };
72231
73288
  completedSessionControlRequests.set(requestId, event);
72232
73289
  writeEvent(event);
@@ -72234,21 +73291,150 @@ async function runJsonEventsMode(params) {
72234
73291
  writeSessionControlFailure(message, "fork", error);
72235
73292
  }
72236
73293
  }
73294
+ function writeSidechatFailure(message, error, code) {
73295
+ writeEvent({
73296
+ type: "sidechat.failed",
73297
+ sessionId,
73298
+ requestId: message.requestId.trim(),
73299
+ error,
73300
+ code
73301
+ });
73302
+ }
73303
+ async function handleSidechat(message) {
73304
+ if (isSessionMismatch(message)) {
73305
+ writeSidechatFailure(message, sessionMismatchError(message.sessionId), "session_mismatch");
73306
+ return;
73307
+ }
73308
+ const requestId = message.requestId.trim();
73309
+ if (!requestId) {
73310
+ writeSidechatFailure(message, "requestId is required", "invalid_request");
73311
+ return;
73312
+ }
73313
+ const cached = completedSidechatRequests.get(requestId);
73314
+ if (cached) {
73315
+ writeEvent(cached);
73316
+ return;
73317
+ }
73318
+ if (activeTurn && !activeTurn.finalized) {
73319
+ writeSidechatFailure(message, "Cannot run sidechat while a turn is active", "session_busy");
73320
+ return;
73321
+ }
73322
+ const startedAt = Date.now();
73323
+ if (params.mockResponse !== void 0) {
73324
+ writeEvent({ type: "sidechat.started", sessionId, requestId });
73325
+ const event = {
73326
+ type: "sidechat.completed",
73327
+ sessionId,
73328
+ requestId,
73329
+ text: params.mockResponse,
73330
+ durationMs: Date.now() - startedAt
73331
+ };
73332
+ completedSidechatRequests.set(requestId, event);
73333
+ writeEvent(event);
73334
+ return;
73335
+ }
73336
+ writeEvent({ type: "sidechat.started", sessionId, requestId });
73337
+ const sidechatController = new AbortController();
73338
+ let abortKind;
73339
+ activeSidechat = {
73340
+ abort: () => {
73341
+ if (!abortKind) abortKind = "interrupted";
73342
+ sidechatController.abort();
73343
+ }
73344
+ };
73345
+ try {
73346
+ await ensureRuntime();
73347
+ if (!agent || !runtime) {
73348
+ throw new Error("JSON events runtime was not initialized");
73349
+ }
73350
+ if (runtime.initialModel.unconfigured) {
73351
+ writeSidechatFailure(message, runtime.initialModel.warning || "No models are configured.");
73352
+ return;
73353
+ }
73354
+ if (sidechatController.signal.aborted) {
73355
+ writeSidechatFailure(message, "sidechat aborted before dispatch", "sidechat_interrupted");
73356
+ return;
73357
+ }
73358
+ const parent = agent;
73359
+ const { Agent: AgentClass } = await import("./chunks/src-HYZFAW4V.js");
73360
+ const sideAgent = new AgentClass({
73361
+ initialState: {
73362
+ systemPrompt: parent.state.systemPrompt,
73363
+ model: parent.state.model,
73364
+ thinkingLevel: parent.state.thinkingLevel,
73365
+ tools: [],
73366
+ messages: structuredClone(parent.state.messages)
73367
+ },
73368
+ apiKey: runtime.apiKey,
73369
+ streamFn: params.streamFn
73370
+ });
73371
+ sidechatController.signal.addEventListener("abort", () => sideAgent.abort(), { once: true });
73372
+ const timer = setTimeout(() => {
73373
+ if (!abortKind) abortKind = "timeout";
73374
+ sidechatController.abort();
73375
+ }, sidechatTimeoutMs);
73376
+ timer.unref?.();
73377
+ try {
73378
+ await sideAgent.prompt(message.prompt);
73379
+ } finally {
73380
+ clearTimeout(timer);
73381
+ }
73382
+ if (abortKind) {
73383
+ const code = abortKind === "timeout" ? "sidechat_timeout" : "sidechat_interrupted";
73384
+ writeSidechatFailure(message, sideAgent.state.error || "sidechat aborted", code);
73385
+ return;
73386
+ }
73387
+ const failure = sideAgent.state.error;
73388
+ if (failure) {
73389
+ writeSidechatFailure(message, failure);
73390
+ return;
73391
+ }
73392
+ const sidechatText = assistantTextFromAgent(sideAgent);
73393
+ const capped = sidechatText === void 0 ? void 0 : truncateForEvent(sidechatText, eventStringCap);
73394
+ const event = {
73395
+ type: "sidechat.completed",
73396
+ sessionId,
73397
+ requestId,
73398
+ text: capped?.value,
73399
+ ...capped?.truncated ? { truncated: true, fullLength: capped.fullLength } : {},
73400
+ usage: assistantUsageFromAgent(sideAgent),
73401
+ durationMs: Date.now() - startedAt
73402
+ };
73403
+ completedSidechatRequests.set(requestId, event);
73404
+ writeEvent(event);
73405
+ } catch (error) {
73406
+ const detail = errorToFailureDetail(error);
73407
+ writeSidechatFailure(
73408
+ message,
73409
+ typeof detail === "string" ? detail : detail.message,
73410
+ typeof detail === "string" ? void 0 : detail.code
73411
+ );
73412
+ } finally {
73413
+ activeSidechat = null;
73414
+ }
73415
+ }
72237
73416
  function finalize(kind, detail) {
72238
73417
  const turn = activeTurn;
72239
73418
  if (!turn || turn.finalized) return;
72240
73419
  turn.finalized = true;
72241
- const headEntryId = activeHeadEntryId();
73420
+ const activeEntryId = activeHeadEntryId();
72242
73421
  const base = {
72243
73422
  sessionId: turn.sessionId,
72244
73423
  turnId: turn.turnId,
72245
- headEntryId,
72246
- activeEntryId: headEntryId
73424
+ activeEntryId
72247
73425
  };
72248
73426
  switch (kind) {
72249
- case "completed":
72250
- writeEvent({ type: "turn.completed", ...base, summary: (typeof detail === "string" ? detail : turn.summary).trim() });
73427
+ case "completed": {
73428
+ const summaryText = (typeof detail === "string" ? detail : turn.summary).trim();
73429
+ const capped = truncateForEvent(summaryText, eventStringCap);
73430
+ writeEvent({
73431
+ type: "turn.completed",
73432
+ ...base,
73433
+ summary: capped.value,
73434
+ ...capped.truncated ? { truncated: true, fullLength: capped.fullLength } : {}
73435
+ });
72251
73436
  break;
73437
+ }
72252
73438
  case "failed":
72253
73439
  writeEvent({ type: "turn.failed", ...base, ...failureDetail(detail) });
72254
73440
  break;
@@ -72261,13 +73447,12 @@ async function runJsonEventsMode(params) {
72261
73447
  }
72262
73448
  }
72263
73449
  function emitSettled(turn) {
72264
- const headEntryId = activeHeadEntryId();
73450
+ const activeEntryId = activeHeadEntryId();
72265
73451
  writeEvent({
72266
73452
  type: "turn.settled",
72267
73453
  sessionId: turn.sessionId,
72268
73454
  turnId: turn.turnId,
72269
- headEntryId,
72270
- activeEntryId: headEntryId
73455
+ activeEntryId
72271
73456
  });
72272
73457
  }
72273
73458
  function rememberTerminal(kind, detail, options) {
@@ -72290,14 +73475,20 @@ async function runJsonEventsMode(params) {
72290
73475
  break;
72291
73476
  case "tool_started":
72292
73477
  touch();
72293
- writeEvent({
72294
- type: "tool.started",
72295
- sessionId: activeTurn.sessionId,
72296
- turnId: activeTurn.turnId,
72297
- toolCallId: event.toolCallId,
72298
- name: event.toolName,
72299
- data: toJsonValue({ args: event.args })
72300
- });
73478
+ {
73479
+ const argsOversized = eventDetail === COMPACT_DETAIL && hasOversizedValue(event.args, eventStringCap);
73480
+ writeEvent({
73481
+ type: "tool.started",
73482
+ sessionId: activeTurn.sessionId,
73483
+ turnId: activeTurn.turnId,
73484
+ toolCallId: event.toolCallId,
73485
+ name: event.toolName,
73486
+ data: toJsonValue({
73487
+ args: event.args,
73488
+ ...argsOversized ? { truncated: true } : {}
73489
+ }, 0, eventStringCap)
73490
+ });
73491
+ }
72301
73492
  break;
72302
73493
  case "tool_progressed":
72303
73494
  touch();
@@ -72311,18 +73502,23 @@ async function runJsonEventsMode(params) {
72311
73502
  break;
72312
73503
  case "tool_finished":
72313
73504
  touch();
72314
- writeEvent({
72315
- type: "tool.completed",
72316
- sessionId: activeTurn.sessionId,
72317
- turnId: activeTurn.turnId,
72318
- toolCallId: event.toolCallId,
72319
- name: event.toolName,
72320
- status: event.isError ? "error" : "ok",
72321
- data: toJsonValue({
72322
- resultText: extractJsonEventsToolProgressText(event.result),
72323
- isError: event.isError
72324
- })
72325
- });
73505
+ {
73506
+ const resultText = extractJsonEventsToolProgressText(event.result);
73507
+ const capped = resultText === void 0 || resultText === null ? void 0 : truncateForEvent(resultText, eventStringCap);
73508
+ writeEvent({
73509
+ type: "tool.completed",
73510
+ sessionId: activeTurn.sessionId,
73511
+ turnId: activeTurn.turnId,
73512
+ toolCallId: event.toolCallId,
73513
+ name: event.toolName,
73514
+ status: event.isError ? "error" : "ok",
73515
+ data: toJsonValue({
73516
+ resultText: capped?.value,
73517
+ isError: event.isError,
73518
+ ...capped?.truncated ? { truncated: true, fullLength: capped.fullLength } : {}
73519
+ }, 0, eventStringCap)
73520
+ });
73521
+ }
72326
73522
  break;
72327
73523
  }
72328
73524
  }
@@ -72448,9 +73644,30 @@ async function runJsonEventsMode(params) {
72448
73644
  trace = void 0;
72449
73645
  cleanupBashOutputArtifacts();
72450
73646
  }
73647
+ async function prewarmRuntime() {
73648
+ if (params.mockResponse !== void 0) return;
73649
+ if (!params.createRuntime && !params.runtime) return;
73650
+ try {
73651
+ await new Promise((resolve7, reject) => {
73652
+ const timer = setTimeout(() => reject(new Error("prewarm timeout")), prewarmTimeoutMs);
73653
+ timer.unref?.();
73654
+ void ensureRuntime().then(resolve7, reject).finally(() => clearTimeout(timer));
73655
+ });
73656
+ } catch {
73657
+ }
73658
+ }
72451
73659
  async function ensureRuntime(requestedCwd) {
72452
73660
  if (params.mockResponse !== void 0) return;
72453
73661
  const effectiveCwd = requestedCwd ?? process.cwd();
73662
+ const inFlight = ensureRuntimeInFlight.get(effectiveCwd);
73663
+ if (inFlight) return inFlight;
73664
+ const composed = composeRuntime(effectiveCwd);
73665
+ ensureRuntimeInFlight.set(effectiveCwd, composed);
73666
+ void composed.finally(() => ensureRuntimeInFlight.delete(effectiveCwd)).catch(() => {
73667
+ });
73668
+ return composed;
73669
+ }
73670
+ async function composeRuntime(effectiveCwd) {
72454
73671
  if (params.createRuntime && (!runtime || runtime.cwd !== effectiveCwd)) {
72455
73672
  await disposeRuntime();
72456
73673
  runtime = await params.createRuntime({ cwd: effectiveCwd, sessionId });
@@ -72545,6 +73762,11 @@ async function runJsonEventsMode(params) {
72545
73762
  agent.setTools(wrapToolsWithExtensions(agent.state.tools, jsonModeExtensions.runner));
72546
73763
  controller.updateRuntimeContext({ tools: agent.state.tools });
72547
73764
  disposeSemanticSubscription = agent.subscribeSemantic(handleSemanticEvent);
73765
+ if (!thinkingLevelInitialized) {
73766
+ thinkingLevelInitialized = true;
73767
+ thinkingLevel = resolveInitialThinkingLevel({ session: runtime.sessionManager.getActiveSession(), settingsManager: runtime.settingsManager });
73768
+ agent.setThinkingLevel(thinkingLevel);
73769
+ }
72548
73770
  }
72549
73771
  function metadataSelection(metadata, key) {
72550
73772
  if (!metadata || !(key in metadata)) return void 0;
@@ -72606,6 +73828,7 @@ async function runJsonEventsMode(params) {
72606
73828
  const nextLevel = requestedThinkingLevel === null ? "off" : requestedThinkingLevel;
72607
73829
  if (!isThinkingLevel(nextLevel)) throw new Error(`Unsupported thinkingLevel: ${nextLevel}`);
72608
73830
  thinkingLevel = nextLevel;
73831
+ thinkingLevelInitialized = true;
72609
73832
  applyThinkingLevel({
72610
73833
  level: thinkingLevel,
72611
73834
  agent,
@@ -72865,6 +74088,8 @@ async function runJsonEventsMode(params) {
72865
74088
  }
72866
74089
  if (message.type === "shutdown") {
72867
74090
  shutdownActiveTurn(message.reason);
74091
+ sidechatChainStopped = true;
74092
+ activeSidechat?.abort();
72868
74093
  break;
72869
74094
  }
72870
74095
  if (message.type === "turn.interrupt") {
@@ -72879,6 +74104,24 @@ async function runJsonEventsMode(params) {
72879
74104
  await handleSessionFork(message);
72880
74105
  continue;
72881
74106
  }
74107
+ if (message.type === "sidechat.request") {
74108
+ const prior = activeSidechatPromise ?? Promise.resolve();
74109
+ trackSidechatPromise(prior.then(async () => {
74110
+ if (sidechatChainStopped) {
74111
+ const cached = completedSidechatRequests.get(message.requestId.trim());
74112
+ if (cached) {
74113
+ writeEvent(cached);
74114
+ } else {
74115
+ writeSidechatFailure(message, "sidechat discarded on shutdown", "sidechat_interrupted");
74116
+ }
74117
+ return;
74118
+ }
74119
+ await handleSidechat(message).catch((error) => {
74120
+ writeSidechatFailure(message, error instanceof Error ? error.message : String(error));
74121
+ });
74122
+ }));
74123
+ continue;
74124
+ }
72882
74125
  const shouldTrackTurn = !activeTurn || activeTurn.finalized;
72883
74126
  const turnPromise = runTurn(message).catch((error) => {
72884
74127
  writeEvent({
@@ -72897,6 +74140,7 @@ async function runJsonEventsMode(params) {
72897
74140
  }
72898
74141
  }
72899
74142
  await activeTurnPromise;
74143
+ await activeSidechatPromise;
72900
74144
  await disposeRuntime();
72901
74145
  }
72902
74146
 
@@ -72953,6 +74197,7 @@ Commands:
72953
74197
  sessions List, read, rename, rewind, and fork Pie sessions
72954
74198
  extensions List and enable/disable extensions (list/enable/disable)
72955
74199
  permissions Explain Pie tool and shell safety defaults
74200
+ capabilities Report command surface and flags for agents (add --json)
72956
74201
  help Show this help message
72957
74202
 
72958
74203
  Interactive Mode Key Bindings:
@@ -73001,86 +74246,13 @@ setupGlobalErrorHandlers(logError);
73001
74246
  migrateConfigFromAgentDir();
73002
74247
  var STABLE_CLI_COMMANDS = {
73003
74248
  chat: "chat",
74249
+ capabilities: "capabilities",
73004
74250
  doctor: "doctor",
73005
74251
  models: "models",
73006
74252
  sessions: "sessions",
73007
74253
  extensions: "extensions",
73008
74254
  permissions: "permissions"
73009
74255
  };
73010
- async function runDoctorCommand(args) {
73011
- const json = args.includes("--json");
73012
- const report = await runDoctor();
73013
- if (json) {
73014
- console.log(JSON.stringify(report, null, 2));
73015
- } else {
73016
- console.log(formatDoctorReport(report));
73017
- }
73018
- if (!report.ok) {
73019
- process.exitCode = 1;
73020
- }
73021
- }
73022
- async function runModelsCommand(args) {
73023
- const subcommand = args[0] || "help";
73024
- const parsed = parseModelsCommandArgs(args.slice(1));
73025
- if (subcommand === "init") {
73026
- const result = initModelsConfig(parsed);
73027
- if (parsed.stdout) {
73028
- process.stdout.write(result.content);
73029
- } else {
73030
- console.log(`Wrote models.json template: ${result.path}`);
73031
- }
73032
- return;
73033
- }
73034
- if (subcommand === "validate") {
73035
- const result = validateModelsConfig({ path: parsed.path });
73036
- if (parsed.json) {
73037
- const probe = result.ok && parsed.probe ? await probeModelsConfig({ path: parsed.path, probe: parsed.probe }) : void 0;
73038
- console.log(JSON.stringify({ ...result, ...probe ? { probe } : {} }, null, 2));
73039
- if (!result.ok || probe?.status === "failed") process.exitCode = 1;
73040
- return;
73041
- }
73042
- if (!result.ok) {
73043
- console.error(result.message);
73044
- process.exitCode = 1;
73045
- return;
73046
- }
73047
- console.log(result.message);
73048
- if (result.profiles.length > 0) {
73049
- console.log(`Profiles: ${result.profiles.join(", ")}`);
73050
- console.log(`Models: ${result.modelCount}`);
73051
- }
73052
- if (result.defaultModel) {
73053
- console.log(`Default: ${result.defaultModel}`);
73054
- }
73055
- if (result.modelClasses && result.modelClasses.length > 0) {
73056
- console.log(`Model classes: ${result.modelClasses.join(", ")}`);
73057
- }
73058
- if (result.webSearchModels && result.webSearchModels.length > 0) {
73059
- console.log(`Web search: ${result.webSearchModels.join(", ")}`);
73060
- }
73061
- if (result.webSearchRouteCandidates && result.webSearchRouteCandidates.length > 0) {
73062
- console.log(`Web search routes: ${result.webSearchRouteCandidates.join(", ")}`);
73063
- }
73064
- if (result.fileCapabilityModels && result.fileCapabilityModels.length > 0) {
73065
- console.log(`File capabilities: ${result.fileCapabilityModels.join(", ")}`);
73066
- }
73067
- if (result.toolModelRoutes && result.toolModelRoutes.length > 0) {
73068
- console.log(`Tool model routes: ${result.toolModelRoutes.join(", ")}`);
73069
- }
73070
- if (parsed.probe) {
73071
- const probe = await probeModelsConfig({ path: parsed.path, probe: parsed.probe });
73072
- console.log(`Probe ${probe.purpose}: ${probe.message}`);
73073
- for (const attempt of probe.attempts) {
73074
- console.log(`- ${attempt.status} ${attempt.route} sources=${attempt.sourceCount}${attempt.failureCategory ? ` failure=${attempt.failureCategory}` : ""}`);
73075
- }
73076
- if (probe.status === "failed") process.exitCode = 1;
73077
- }
73078
- return;
73079
- }
73080
- console.log(`Usage:
73081
- pie models init [--path <path>] [--force] [--stdout]
73082
- pie models validate [--path <path>] [--probe web_search] [--json]`);
73083
- }
73084
74256
  async function runWithJsonEventsConsoleIsolation(fn) {
73085
74257
  const originalLog = console.log;
73086
74258
  const originalInfo = console.info;
@@ -73099,7 +74271,21 @@ async function runWithJsonEventsConsoleIsolation(fn) {
73099
74271
  }
73100
74272
  async function createCliTestStreamFnFromEnv() {
73101
74273
  if (!process.env.PIE_TEST_MOCK_STREAM_SEQUENCE) return void 0;
73102
- return (await import("./chunks/test-stream-XCE2KXJU.js")).createTestStreamFnFromSequenceEnv();
74274
+ return (await import("./chunks/test-stream-5T7E7C25.js")).createTestStreamFnFromSequenceEnv();
74275
+ }
74276
+ function resolveJsonEventsDetailFlag() {
74277
+ for (let index = 0; index < process.argv.length; index += 1) {
74278
+ const arg = process.argv[index];
74279
+ if (arg === "--event-detail") {
74280
+ const raw = process.argv[index + 1] ?? "";
74281
+ return raw === "compact" || raw === "full" ? { value: raw } : { error: raw || "(missing)" };
74282
+ }
74283
+ if (arg.startsWith("--event-detail=")) {
74284
+ const raw = arg.slice("--event-detail=".length);
74285
+ return raw === "compact" || raw === "full" ? { value: raw } : { error: raw };
74286
+ }
74287
+ }
74288
+ return { value: "compact" };
73103
74289
  }
73104
74290
  async function startChat(initialPrompt, testCommand) {
73105
74291
  const rawOutput = process.argv.includes("--raw-output");
@@ -73111,6 +74297,12 @@ async function startChat(initialPrompt, testCommand) {
73111
74297
  const extensionPaths = parseRepeatedFlag(process.argv, "--extension-path");
73112
74298
  const extensionFlags = parseExtensionFlags(process.argv);
73113
74299
  if (jsonEvents) {
74300
+ const detailFlag = resolveJsonEventsDetailFlag();
74301
+ if ("error" in detailFlag) {
74302
+ console.error(`Invalid --event-detail value: ${detailFlag.error}. Use compact or full.`);
74303
+ process.exit(2);
74304
+ }
74305
+ const eventDetail = detailFlag.value;
73114
74306
  let developerInstructions;
73115
74307
  try {
73116
74308
  developerInstructions = resolveDeveloperInstructionsFromArgs(process.argv);
@@ -73124,7 +74316,8 @@ async function startChat(initialPrompt, testCommand) {
73124
74316
  explicitSessionId,
73125
74317
  resumeRequired,
73126
74318
  developerInstructions,
73127
- mockResponse: process.env.PIE_TEST_MOCK_RESPONSE
74319
+ mockResponse: process.env.PIE_TEST_MOCK_RESPONSE,
74320
+ eventDetail
73128
74321
  });
73129
74322
  return;
73130
74323
  }
@@ -73132,6 +74325,7 @@ async function startChat(initialPrompt, testCommand) {
73132
74325
  explicitSessionId,
73133
74326
  resumeRequired,
73134
74327
  developerInstructions,
74328
+ eventDetail,
73135
74329
  streamFn: await createCliTestStreamFnFromEnv(),
73136
74330
  createRuntime: async ({ cwd, sessionId }) => composeCliRuntime({
73137
74331
  explicitSessionId: explicitSessionId ?? sessionId,
@@ -73218,6 +74412,16 @@ async function main() {
73218
74412
  console.log(VERSION);
73219
74413
  return;
73220
74414
  }
74415
+ if (args.length === 0 && !process.stdin.isTTY) {
74416
+ console.error("pie: no arguments and stdin is not a terminal; refusing to start interactive mode.");
74417
+ showHelp();
74418
+ process.exitCode = 1;
74419
+ return;
74420
+ }
74421
+ if (args[0] === STABLE_CLI_COMMANDS.capabilities) {
74422
+ runCapabilitiesCommand(args.slice(1));
74423
+ return;
74424
+ }
73221
74425
  if (args[0] === STABLE_CLI_COMMANDS.doctor) {
73222
74426
  await runDoctorCommand(args.slice(1));
73223
74427
  return;
@@ -73240,7 +74444,7 @@ async function main() {
73240
74444
  return;
73241
74445
  }
73242
74446
  if (args[0] === STABLE_CLI_COMMANDS.permissions) {
73243
- console.log(formatPermissionsHelp());
74447
+ runPermissionsCommand(args.slice(1));
73244
74448
  return;
73245
74449
  }
73246
74450
  const parsed = parseCliInvocation(args);