@cydm/pie 2.7.10 → 2.7.12

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/README.md CHANGED
@@ -240,7 +240,7 @@ Pie uses a hierarchical slash-command menu. The current built-in commands includ
240
240
  | `pie extensions list/enable/disable` | Inspect and toggle extensions non-interactively |
241
241
  | `/sessions/compact` | Summarize history and create a compact checkpoint |
242
242
  | `/sessions/new` | Start a new session |
243
- | `/sessions/resume` | Resume a different session |
243
+ | `/sessions/resume` | Resume a session (defaults to current-directory sessions; press `g` in the selector to show the full loaded recent list) |
244
244
  | `/sessions/tree` | Navigate the session tree |
245
245
  | `/sessions/info` | Show session information |
246
246
  | `/sessions/fork` | Fork from an earlier message |
@@ -363,7 +363,7 @@ Pie exposes one default model-visible web tool: `web_research`. Use it for publi
363
363
 
364
364
  ## Permissions
365
365
 
366
- Pie's default tools operate from the current workspace. File tools are workspace-scoped, `~/.pie` is allowlisted for Pie configuration/session support, and shell commands return structured timeout/truncation/error details. The canonical shell tool is still named `bash` for compatibility; on Windows `shell:auto` uses PowerShell, and callers can explicitly request `bash`, `powershell`, or `cmd`. High-risk shell commands require confirmation outside YOLO mode. `/settings/yolo` relaxes filesystem sandbox directory restrictions and should only be used in trusted workspaces. `pie permissions` prints the current policy-derived safety summary.
366
+ Pie's default tools operate from the current workspace. File tools are workspace-scoped, `~/.pie` is allowlisted for Pie configuration/session support, and shell commands return structured timeout/truncation/error details. The canonical shell tool is still named `bash` for compatibility; on Windows `shell:auto` uses PowerShell, and callers can explicitly request `bash`, `powershell`, or `cmd`. High-risk shell commands require confirmation outside YOLO mode; the confirmation dialog also offers a time-boxed `Yolo (5 min)` option that suppresses further high-risk prompts for five minutes without enabling full YOLO mode. `/settings/yolo` relaxes filesystem sandbox directory restrictions and should only be used in trusted workspaces. `pie permissions` prints the current policy-derived safety summary.
367
367
 
368
368
  In interactive Plan mode (`/tools/plan`), workspace editing tools are unavailable. The `bash` guard accepts simple read commands and read-only pipelines such as `rg 'TODO' . | head -20`; it blocks redirects, command substitution, and write-capable subcommands. Use `plan_scratch` to write, read, or list temporary text drafts in a directory dedicated to the current session. Shell redirection to `/tmp` remains blocked in Plan mode. When the plan is ready, the agent submits the ordered steps with `plan_submit` and you choose to execute immediately (progress is tracked through `[DONE:n]` markers in responses), keep planning, or refine the plan in a prefilled editor; a plan written as plain text under a `Plan:`/`计划:` heading is still detected as a fallback. Leave Plan mode before changing workspace files.
369
369
 
@@ -1,9 +1,9 @@
1
1
  import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
2
2
  import {
3
3
  createAskUserCapability
4
- } from "../../../chunks/chunk-C4LXD56O.js";
5
- import "../../../chunks/chunk-JO6EDWHP.js";
6
- import "../../../chunks/chunk-2B6VS2NH.js";
4
+ } from "../../../chunks/chunk-AFPRX5XI.js";
5
+ import "../../../chunks/chunk-2NBUFVUQ.js";
6
+ import "../../../chunks/chunk-EN47TIBG.js";
7
7
  import "../../../chunks/chunk-XXGYUYXI.js";
8
8
  import "../../../chunks/chunk-LZQXQOPJ.js";
9
9
 
@@ -6,9 +6,9 @@ import {
6
6
  extractPlanTodoItems,
7
7
  markCompletedPlanSteps,
8
8
  restoreExecutionState
9
- } from "../../../chunks/chunk-C4LXD56O.js";
10
- import "../../../chunks/chunk-JO6EDWHP.js";
11
- import "../../../chunks/chunk-2B6VS2NH.js";
9
+ } from "../../../chunks/chunk-AFPRX5XI.js";
10
+ import "../../../chunks/chunk-2NBUFVUQ.js";
11
+ import "../../../chunks/chunk-EN47TIBG.js";
12
12
  import {
13
13
  Type
14
14
  } from "../../../chunks/chunk-XXGYUYXI.js";
@@ -121,6 +121,30 @@ function planModeExtension(ctx) {
121
121
  return items.map((item, index) => `${index + 1}. ${item.text}${item.detail ? `
122
122
  ${item.detail}` : ""}`).join("\n");
123
123
  }
124
+ async function planSelect(prompt, options, leftShortcut) {
125
+ const response = await ctx.interactions.request({
126
+ type: "select_one",
127
+ id: `plan-mode:${Date.now()}`,
128
+ prompt,
129
+ options,
130
+ // No timeoutMs: plan approval must wait for the user indefinitely,
131
+ // matching the pre-protocol ctx.ui.select behavior.
132
+ ...leftShortcut ? { leftShortcut } : {}
133
+ });
134
+ return response.type === "select_one" ? response.selection : void 0;
135
+ }
136
+ function planNotify(message, level = "info") {
137
+ void ctx.interactions.request({ type: "notify", message, level, markdown: true }).catch(() => void 0);
138
+ }
139
+ async function planRefine(items) {
140
+ const response = await ctx.interactions.request({
141
+ type: "multiline_input",
142
+ id: `plan-mode:refine:${Date.now()}`,
143
+ prompt: "Refine the plan (edit the plan or add feedback):",
144
+ prefill: formatPlanText(items)
145
+ });
146
+ return response.type === "multiline_input" ? response.value : void 0;
147
+ }
124
148
  function renderPlanMarkdown(goal, items) {
125
149
  const heading = goal ? `## \u{1F4CB} ${goal} \u2014 ${items.length} steps` : `## \u{1F4CB} Plan \u2014 ${items.length} steps`;
126
150
  const steps = items.map((item, index) => {
@@ -208,15 +232,15 @@ Execute the steps in order, starting with step 1. After completing a step, inclu
208
232
  lastOfferedPlanKey = JSON.stringify(items);
209
233
  const rendered = items.map((item) => `${item.step}. \u2610 ${item.text}${item.detail ? `
210
234
  ${item.detail}` : ""}`).join("\n");
211
- if (!ctx.hasUI) {
235
+ if (!ctx.interactions.isAvailable()) {
212
236
  return reply(`Plan submitted (${items.length} steps) in headless mode; interactive approval is unavailable, so nothing will be executed:
213
237
 
214
238
  ${rendered}`);
215
239
  }
216
240
  const goal = typeof args.goal === "string" && args.goal.trim() ? args.goal.trim() : void 0;
217
- ctx.ui.notify(renderPlanMarkdown(goal, items), "info", { markdown: true });
241
+ planNotify(renderPlanMarkdown(goal, items));
218
242
  const yoloOption = typeof ctx.setYoloMode === "function" && !ctx.yoloMode ? ["Execute in YOLO mode (auto-approve everything)"] : [];
219
- const choice = await ctx.ui.select(`\u{1F4CB} Plan (${items.length} steps) \u2014 what next?`, [
243
+ const choice = await planSelect(`\u{1F4CB} Plan (${items.length} steps) \u2014 what next?`, [
220
244
  "Execute the plan (track progress)",
221
245
  ...yoloOption,
222
246
  "Stay in plan mode",
@@ -230,7 +254,7 @@ ${rendered}`);
230
254
  return reply(startPlanExecution(items));
231
255
  }
232
256
  if (choice === "Refine the plan") {
233
- const refinement = await ctx.ui.editor("Refine the plan (edit the plan or add feedback):", formatPlanText(items));
257
+ const refinement = await planRefine(items);
234
258
  if (refinement?.trim()) {
235
259
  return reply(`The user refined the plan:
236
260
 
@@ -298,7 +322,8 @@ ${completedList}`, "info", { markdown: true });
298
322
  }
299
323
  return;
300
324
  }
301
- if (!planModeEnabled || !ctx.hasUI) return;
325
+ if (!planModeEnabled) return;
326
+ if (!ctx.interactions.isAvailable()) return;
302
327
  if (approvalOfferedThisTurn) return;
303
328
  const messages = event.messages || [];
304
329
  const lastAssistant = [...messages].reverse().find(
@@ -316,10 +341,10 @@ ${completedList}`, "info", { markdown: true });
316
341
  approvalOfferedThisTurn = true;
317
342
  const todoListText = extracted.map((item, index) => `${index + 1}. \u2610 ${item.text}${item.detail ? `
318
343
  - \u21B3 ${item.detail}` : ""}`).join("\n");
319
- ctx.ui.notify(`## \u{1F4CB} Plan \u2014 ${extracted.length} steps
344
+ planNotify(`## \u{1F4CB} Plan \u2014 ${extracted.length} steps
320
345
 
321
- ${todoListText}`, "info", { markdown: true });
322
- const choice = await ctx.ui.select(`\u{1F4CB} Plan (${extracted.length} steps) \u2014 what next?`, [
346
+ ${todoListText}`);
347
+ const choice = await planSelect(`\u{1F4CB} Plan (${extracted.length} steps) \u2014 what next?`, [
323
348
  "Execute the plan (track progress)",
324
349
  "Stay in plan mode",
325
350
  "Refine the plan"
@@ -329,7 +354,7 @@ ${todoListText}`, "info", { markdown: true });
329
354
  return;
330
355
  }
331
356
  if (choice === "Refine the plan") {
332
- const refinement = await ctx.ui.editor("Refine the plan (edit the plan or add feedback):", formatPlanText(extracted));
357
+ const refinement = await planRefine(extracted);
333
358
  if (refinement?.trim()) {
334
359
  ctx.sendUserMessage(refinement.trim());
335
360
  }
@@ -1,18 +1,20 @@
1
1
  import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
2
2
  import {
3
+ classifyCliFailure,
3
4
  createCliHostCapabilities,
5
+ createCliRetryPolicy,
4
6
  formatTransportLifecycle,
5
7
  getConfigDir,
6
8
  logTransportLifecycle
7
- } from "../../../chunks/chunk-5GUPRFNP.js";
9
+ } from "../../../chunks/chunk-YI3CSQGD.js";
8
10
  import {
9
11
  SUBAGENT_SESSION_RECORD_VERSION,
10
12
  createPolicyEnforcedTools,
11
13
  createSharedFileSystemTools,
12
14
  createSubagentCapability
13
- } from "../../../chunks/chunk-C4LXD56O.js";
14
- import "../../../chunks/chunk-JO6EDWHP.js";
15
- import "../../../chunks/chunk-2B6VS2NH.js";
15
+ } from "../../../chunks/chunk-AFPRX5XI.js";
16
+ import "../../../chunks/chunk-2NBUFVUQ.js";
17
+ import "../../../chunks/chunk-EN47TIBG.js";
16
18
  import "../../../chunks/chunk-XXGYUYXI.js";
17
19
  import "../../../chunks/chunk-LZQXQOPJ.js";
18
20
 
@@ -398,6 +400,10 @@ function subagentExtension(ctx) {
398
400
  },
399
401
  skills,
400
402
  yoloMode: ctx.yoloMode,
403
+ recovery: {
404
+ policy: createCliRetryPolicy(),
405
+ classifyFailure: (context) => classifyCliFailure(context)
406
+ },
401
407
  getParentTools: () => ctx.getAllTools(),
402
408
  log: (message) => ctx.log(message),
403
409
  notify: ctx.hasUI ? (message, type) => ctx.ui.notify(message, type === "success" ? "info" : type) : void 0,
@@ -8,9 +8,9 @@ import {
8
8
  executeManageTodoList,
9
9
  executionStateToTodos,
10
10
  restoreExecutionState
11
- } from "../../../chunks/chunk-C4LXD56O.js";
12
- import "../../../chunks/chunk-JO6EDWHP.js";
13
- import "../../../chunks/chunk-2B6VS2NH.js";
11
+ } from "../../../chunks/chunk-AFPRX5XI.js";
12
+ import "../../../chunks/chunk-2NBUFVUQ.js";
13
+ import "../../../chunks/chunk-EN47TIBG.js";
14
14
  import "../../../chunks/chunk-XXGYUYXI.js";
15
15
  import "../../../chunks/chunk-LZQXQOPJ.js";
16
16
 
@@ -4,7 +4,7 @@ import {
4
4
  disposeModelTransports,
5
5
  streamSimple,
6
6
  validateToolArguments
7
- } from "./chunk-2B6VS2NH.js";
7
+ } from "./chunk-EN47TIBG.js";
8
8
 
9
9
  // ../../packages/agent-core/src/agent-loop.ts
10
10
  function createLoopErrorMessage(error, config, signal) {
@@ -182,6 +182,11 @@ async function runLoop(currentContext, newMessages, config, signal, stream, stre
182
182
  }
183
183
  }
184
184
  stream.push({ type: "turn_end", message, toolResults });
185
+ if (hasMoreToolCalls && config.beforeToolContinuation) {
186
+ const replacement = await config.beforeToolContinuation(signal);
187
+ if (signal?.aborted) throw new Error("Request was aborted");
188
+ if (replacement) currentContext.messages = replacement.slice();
189
+ }
185
190
  if (steeringAfterTools && steeringAfterTools.length > 0) {
186
191
  pendingMessages = steeringAfterTools;
187
192
  steeringAfterTools = null;
@@ -444,7 +449,22 @@ function isUserContentBlock(value) {
444
449
  return false;
445
450
  }
446
451
  const type = value.type;
447
- return type === "text" || type === "image" || type === "audio" || type === "video" || type === "fileRef";
452
+ return type === "text" || type === "image" || type === "audio" || type === "video" || type === "fileRef" || type === "hostContext";
453
+ }
454
+ async function awaitToolContinuation(handler, signal) {
455
+ let onAbort;
456
+ const cancelled = new Promise((_resolve, reject) => {
457
+ onAbort = () => reject(new Error("Request was aborted"));
458
+ signal.addEventListener("abort", onAbort, { once: true });
459
+ });
460
+ try {
461
+ await Promise.race([Promise.resolve().then(() => {
462
+ if (signal.aborted) throw new Error("Request was aborted");
463
+ return handler(signal);
464
+ }), cancelled]);
465
+ } finally {
466
+ signal.removeEventListener("abort", onAbort);
467
+ }
448
468
  }
449
469
  var Agent = class {
450
470
  _state;
@@ -467,6 +487,7 @@ var Agent = class {
467
487
  _thinkingBudgets;
468
488
  _maxRetryDelayMs;
469
489
  hooks;
490
+ beforeToolContinuation;
470
491
  /** Stable identity for this agent instance, bound onto every run signal
471
492
  * handed to tools (signals themselves are per-run; see tool-caller-identity.ts). */
472
493
  toolCallerIdentity = createToolCallerIdentity();
@@ -559,6 +580,15 @@ var Agent = class {
559
580
  setHooks(hooks) {
560
581
  this.hooks = hooks;
561
582
  }
583
+ /** Single session-policy owner. Runs after the complete tool turn has been
584
+ * published to observers; replaceMessages here also updates the active loop.
585
+ * Removal does not detach a callback already captured by an active run. */
586
+ setBeforeToolContinuation(handler) {
587
+ this.beforeToolContinuation = handler;
588
+ return () => {
589
+ if (this.beforeToolContinuation === handler) this.beforeToolContinuation = void 0;
590
+ };
591
+ }
562
592
  replaceMessages(ms) {
563
593
  this.releaseTransport();
564
594
  this._state.messages = ms.slice();
@@ -648,6 +678,7 @@ var Agent = class {
648
678
  this._state.error = void 0;
649
679
  this.steeringQueue = [];
650
680
  this.followUpQueue = [];
681
+ this.resolveRunningPrompt?.();
651
682
  this.runningPrompt = void 0;
652
683
  this.resolveRunningPrompt = void 0;
653
684
  this.updateStatusSnapshot({
@@ -670,6 +701,7 @@ var Agent = class {
670
701
  this._state.error = void 0;
671
702
  this.steeringQueue = [];
672
703
  this.followUpQueue = [];
704
+ this.resolveRunningPrompt?.();
673
705
  this.runningPrompt = void 0;
674
706
  this.resolveRunningPrompt = void 0;
675
707
  this.abortController = void 0;
@@ -784,6 +816,8 @@ var Agent = class {
784
816
  tools: this._state.tools
785
817
  };
786
818
  let skipInitialSteeringPoll = options?.skipInitialSteeringPoll === true;
819
+ const beforeToolContinuation = this.beforeToolContinuation;
820
+ let acknowledgeToolTurn;
787
821
  const config = {
788
822
  model,
789
823
  reasoning,
@@ -794,6 +828,16 @@ var Agent = class {
794
828
  maxRetryDelayMs: this._maxRetryDelayMs,
795
829
  convertToLlm: this.convertToLlm,
796
830
  transformContext: this.transformContext,
831
+ beforeToolContinuation: beforeToolContinuation ? async () => {
832
+ await new Promise((resolve) => {
833
+ acknowledgeToolTurn = resolve;
834
+ });
835
+ acknowledgeToolTurn = void 0;
836
+ if (!ownsRun() || runController.signal.aborted) throw new Error("Request was aborted");
837
+ await awaitToolContinuation(beforeToolContinuation, runController.signal);
838
+ if (!ownsRun() || runController.signal.aborted) throw new Error("Request was aborted");
839
+ return this._state.messages.slice();
840
+ } : void 0,
797
841
  getApiKey: this.getApiKey,
798
842
  hooks: this.hooks,
799
843
  getSteeringMessages: async () => {
@@ -855,6 +899,7 @@ var Agent = class {
855
899
  break;
856
900
  }
857
901
  this.emit(event, ownsRun);
902
+ if (event.type === "turn_end") acknowledgeToolTurn?.();
858
903
  }
859
904
  if (ownsRun() && partial && partial.role === "assistant" && partial.content?.length > 0) {
860
905
  const onlyEmpty = !partial.content.some(
@@ -889,6 +934,7 @@ var Agent = class {
889
934
  this._state.error = err?.message || String(err);
890
935
  this.emit({ type: "agent_end", messages: [errorMsg] }, ownsRun);
891
936
  } finally {
937
+ acknowledgeToolTurn?.();
892
938
  if (ownsRun()) {
893
939
  this._state.isStreaming = false;
894
940
  this._state.streamMessage = null;