@cydm/pie 1.0.33 → 1.0.35

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.
Files changed (2) hide show
  1. package/dist/cli.js +145 -24
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -43962,7 +43962,7 @@ import * as path29 from "path";
43962
43962
 
43963
43963
  // src/cli-args.ts
43964
43964
  var FLAGS_WITH_VALUES = /* @__PURE__ */ new Set(["--session-id", "--extension-path"]);
43965
- var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["--raw-output", "--json-output", "--json-events"]);
43965
+ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["--raw-output", "--json-output", "--json-events", "--resume-required"]);
43966
43966
  var HELP_FLAGS = /* @__PURE__ */ new Set(["help", "--help", "-h"]);
43967
43967
  var COMMANDS = /* @__PURE__ */ new Set(["chat", "help"]);
43968
43968
  function parseCliInvocation(args) {
@@ -44014,6 +44014,16 @@ function parseCliInvocation(args) {
44014
44014
  initialPrompt: positionals.join(" ").trim() || void 0
44015
44015
  };
44016
44016
  }
44017
+ function parseRepeatedFlag(args, flag) {
44018
+ const values = [];
44019
+ for (let i = 0; i < args.length; i += 1) {
44020
+ if (args[i] === flag && args[i + 1]) {
44021
+ values.push(args[i + 1]);
44022
+ i += 1;
44023
+ }
44024
+ }
44025
+ return values;
44026
+ }
44017
44027
 
44018
44028
  // src/debug/file-logger.ts
44019
44029
  import * as fs from "fs";
@@ -54844,7 +54854,8 @@ var DEFAULT_APP_KEYBINDINGS = {
54844
54854
  toggleThinking: "ctrl+t",
54845
54855
  toggleSessionNamedFilter: "ctrl+n",
54846
54856
  externalEditor: "ctrl+g",
54847
- followUp: ["alt+enter", "ctrl+shift+enter"],
54857
+ steer: "alt+enter",
54858
+ followUp: [],
54848
54859
  dequeue: ["alt+up", "ctrl+shift+up"],
54849
54860
  pasteImage: "ctrl+v",
54850
54861
  newSession: [],
@@ -54869,6 +54880,7 @@ var APP_ACTIONS = [
54869
54880
  "toggleThinking",
54870
54881
  "toggleSessionNamedFilter",
54871
54882
  "externalEditor",
54883
+ "steer",
54872
54884
  "followUp",
54873
54885
  "dequeue",
54874
54886
  "pasteImage",
@@ -63849,7 +63861,7 @@ function handleHotkeysCommand(host) {
63849
63861
  theme.fg("muted", "Editor shortcuts:"),
63850
63862
  " Enter Submit message",
63851
63863
  " Shift+Enter New line",
63852
- ` ${theme.fg("dim", kb.getKeys("followUp").join(" / "))} ${theme.fg("muted", "Queue follow-up message")}`,
63864
+ ` ${theme.fg("dim", kb.getKeys("steer").join(" / "))} ${theme.fg("muted", "Queue steering message for the active turn")}`,
63853
63865
  ` ${theme.fg("dim", kb.getKeys("dequeue").join(" / "))} ${theme.fg("muted", "Restore queued messages")}`,
63854
63866
  " / Show slash commands",
63855
63867
  " Esc Cancel autocomplete",
@@ -66772,6 +66784,10 @@ function setupInteractiveKeybindings(host) {
66772
66784
  host.dequeueMessages();
66773
66785
  return { consume: true };
66774
66786
  }
66787
+ if (host.keybindings.matches(data, "steer")) {
66788
+ host.handleSteer();
66789
+ return { consume: true };
66790
+ }
66775
66791
  if (host.keybindings.matches(data, "followUp")) {
66776
66792
  host.handleFollowUp();
66777
66793
  return { consume: true };
@@ -66872,6 +66888,19 @@ function handleFollowUp(host) {
66872
66888
  void host.editor.onSubmit(text);
66873
66889
  }
66874
66890
  }
66891
+ function handleSteer(host) {
66892
+ const text = host.editor.getText().trim();
66893
+ if (!text) return;
66894
+ if (isCommandLikeInput(text)) {
66895
+ host.editor.onSubmit?.(text);
66896
+ return;
66897
+ }
66898
+ if (host.submitLock || (host.agentSessionController?.hasQueuedMessages ?? false)) {
66899
+ queueMessage(host, text, "steer");
66900
+ } else if (host.editor.onSubmit) {
66901
+ void host.editor.onSubmit(text);
66902
+ }
66903
+ }
66875
66904
  function truncate(text, maxLength) {
66876
66905
  if (text.length <= maxLength) return text;
66877
66906
  return text.slice(0, maxLength) + "...";
@@ -69558,6 +69587,9 @@ var InteractiveMode = class {
69558
69587
  handleFollowUp() {
69559
69588
  handleFollowUp(this);
69560
69589
  }
69590
+ handleSteer() {
69591
+ handleSteer(this);
69592
+ }
69561
69593
  restoreEditor() {
69562
69594
  return restoreEditor(this.uiHelperHost);
69563
69595
  }
@@ -69572,6 +69604,17 @@ var CliMissingApiKeyError = class extends Error {
69572
69604
  this.name = "CliMissingApiKeyError";
69573
69605
  }
69574
69606
  };
69607
+ var CliSessionResumeError = class extends Error {
69608
+ constructor(sessionId, code, message = code === "session_not_found" ? `Session not found: ${sessionId}` : `Session could not be recovered: ${sessionId}`) {
69609
+ super(message);
69610
+ this.sessionId = sessionId;
69611
+ this.code = code;
69612
+ this.name = "CliSessionResumeError";
69613
+ }
69614
+ sessionId;
69615
+ code;
69616
+ recoverable = true;
69617
+ };
69575
69618
  function getCliRuntimePaths() {
69576
69619
  const configDir = getConfigDir();
69577
69620
  const sessionsDir = getSessionsDir();
@@ -69644,10 +69687,17 @@ async function composeCliRuntime(params) {
69644
69687
  const cwd = params.cwd ?? process.cwd();
69645
69688
  const sessionManager = createSessionManager({ sessionsDir: paths.sessionsDir });
69646
69689
  if (params.explicitSessionId) {
69690
+ const sessionFileExists = params.requireExistingSession ? await sessionManager.hasSession(params.explicitSessionId) : false;
69647
69691
  const loaded = await sessionManager.loadSession(params.explicitSessionId, {
69648
69692
  warnIfMissing: params.warnIfExplicitSessionMissing
69649
69693
  });
69650
69694
  if (!loaded) {
69695
+ if (params.requireExistingSession) {
69696
+ throw new CliSessionResumeError(
69697
+ params.explicitSessionId,
69698
+ sessionFileExists ? "session_unrecoverable" : "session_not_found"
69699
+ );
69700
+ }
69651
69701
  await sessionManager.createSession(void 0, void 0, params.explicitSessionId);
69652
69702
  } else {
69653
69703
  const restored = restoreSessionModelState({
@@ -70174,12 +70224,17 @@ function parseInput(line) {
70174
70224
  reason: typeof parsed.reason === "string" ? parsed.reason : void 0
70175
70225
  };
70176
70226
  }
70177
- if (parsed.type === "turn.request" && typeof parsed.turnId === "string" && typeof parsed.prompt === "string") {
70227
+ if ((parsed.type === "turn.request" || parsed.type === "turn.input") && typeof parsed.turnId === "string" && typeof parsed.prompt === "string") {
70228
+ const streamingBehavior = parsed.streamingBehavior;
70229
+ if (streamingBehavior !== void 0 && streamingBehavior !== "steer" && streamingBehavior !== "followUp") {
70230
+ return null;
70231
+ }
70178
70232
  return {
70179
70233
  type: "turn.request",
70180
70234
  sessionId: typeof parsed.sessionId === "string" ? parsed.sessionId : void 0,
70181
70235
  turnId: parsed.turnId,
70182
70236
  prompt: parsed.prompt,
70237
+ streamingBehavior,
70183
70238
  cwd: typeof parsed.cwd === "string" ? parsed.cwd : void 0,
70184
70239
  metadata: isRecord4(parsed.metadata) ? parsed.metadata : void 0
70185
70240
  };
@@ -70343,6 +70398,13 @@ async function runJsonEventsMode(params) {
70343
70398
  function delay(ms) {
70344
70399
  return new Promise((resolve5) => setTimeout(resolve5, Math.max(0, ms)));
70345
70400
  }
70401
+ async function waitForActiveController(timeoutMs = 1e3) {
70402
+ const deadline = Date.now() + timeoutMs;
70403
+ while (!controller && activeTurn && !activeTurn.finalized && Date.now() < deadline) {
70404
+ await delay(5);
70405
+ }
70406
+ return controller;
70407
+ }
70346
70408
  function touch() {
70347
70409
  if (activeTurn) {
70348
70410
  activeTurn.lastActivityAt = Date.now();
@@ -70453,6 +70515,25 @@ async function runJsonEventsMode(params) {
70453
70515
  display(chunk);
70454
70516
  }
70455
70517
  }
70518
+ function failureDetail(detail) {
70519
+ if (!detail) return { error: "turn failed" };
70520
+ if (typeof detail === "string") return { error: detail };
70521
+ return {
70522
+ error: detail.message,
70523
+ code: detail.code,
70524
+ recoverable: detail.recoverable
70525
+ };
70526
+ }
70527
+ function errorToFailureDetail(error) {
70528
+ if (error instanceof CliSessionResumeError) {
70529
+ return {
70530
+ message: error.message,
70531
+ code: error.code,
70532
+ recoverable: error.recoverable
70533
+ };
70534
+ }
70535
+ return error instanceof Error ? error.message : String(error);
70536
+ }
70456
70537
  function finalize(kind, detail) {
70457
70538
  const turn = activeTurn;
70458
70539
  if (!turn || turn.finalized) return;
@@ -70466,10 +70547,10 @@ async function runJsonEventsMode(params) {
70466
70547
  };
70467
70548
  switch (kind) {
70468
70549
  case "completed":
70469
- writeEvent({ type: "turn.completed", ...base, summary: (detail ?? turn.summary).trim() });
70550
+ writeEvent({ type: "turn.completed", ...base, summary: (typeof detail === "string" ? detail : turn.summary).trim() });
70470
70551
  break;
70471
70552
  case "failed":
70472
- writeEvent({ type: "turn.failed", ...base, error: detail || "turn failed" });
70553
+ writeEvent({ type: "turn.failed", ...base, ...failureDetail(detail) });
70473
70554
  break;
70474
70555
  case "cancelled":
70475
70556
  writeEvent({ type: "turn.cancelled", ...base, reason: detail || "interrupted" });
@@ -70844,12 +70925,7 @@ async function runJsonEventsMode(params) {
70844
70925
  return;
70845
70926
  }
70846
70927
  if (activeTurn && !activeTurn.finalized) {
70847
- writeEvent({
70848
- type: "turn.failed",
70849
- sessionId,
70850
- turnId: message.turnId,
70851
- error: "another turn is already running"
70852
- });
70928
+ await queueActiveTurnInput(message);
70853
70929
  return;
70854
70930
  }
70855
70931
  activeTurn = {
@@ -70897,7 +70973,7 @@ async function runJsonEventsMode(params) {
70897
70973
  try {
70898
70974
  await ensureRuntime(message.cwd);
70899
70975
  } catch (error) {
70900
- finalize("failed", error instanceof Error ? error.message : String(error));
70976
+ finalize("failed", errorToFailureDetail(error));
70901
70977
  if (activeTurn === turn) emitSettled(turn);
70902
70978
  if (activeTurn === turn) activeTurn = null;
70903
70979
  return;
@@ -70974,6 +71050,55 @@ async function runJsonEventsMode(params) {
70974
71050
  if (activeTurn === turn) activeTurn = null;
70975
71051
  }
70976
71052
  }
71053
+ async function queueActiveTurnInput(message) {
71054
+ if (!message.streamingBehavior) {
71055
+ writeEvent({
71056
+ type: "turn.failed",
71057
+ sessionId,
71058
+ turnId: message.turnId,
71059
+ error: "another turn is already running; specify streamingBehavior ('steer' or 'followUp') to queue the message"
71060
+ });
71061
+ return;
71062
+ }
71063
+ const activeController = await waitForActiveController();
71064
+ if (!activeController) {
71065
+ writeEvent({
71066
+ type: "turn.failed",
71067
+ sessionId,
71068
+ turnId: message.turnId,
71069
+ error: "JSON events runtime is not ready to queue a streaming turn"
71070
+ });
71071
+ return;
71072
+ }
71073
+ try {
71074
+ applyRuntimeSelection(message);
71075
+ trace?.notePendingUserText(message.prompt);
71076
+ trace?.noteRuntimeEvent("json_events_turn_queued", {
71077
+ mode: message.streamingBehavior,
71078
+ turnId: message.turnId
71079
+ });
71080
+ await activeController.prompt(message.prompt, {
71081
+ source: "json-events",
71082
+ streamingBehavior: message.streamingBehavior,
71083
+ displayText: message.prompt,
71084
+ deliverWhileStreaming: message.streamingBehavior === "steer"
71085
+ });
71086
+ writeEvent({
71087
+ type: "turn.queued",
71088
+ sessionId,
71089
+ turnId: message.turnId,
71090
+ mode: message.streamingBehavior,
71091
+ activeTurnId: activeTurn?.turnId
71092
+ });
71093
+ } catch (error) {
71094
+ writeEvent({
71095
+ type: "turn.failed",
71096
+ sessionId,
71097
+ turnId: message.turnId,
71098
+ error: error instanceof Error ? error.message : String(error)
71099
+ });
71100
+ }
71101
+ }
70977
71102
  async function interrupt(message) {
70978
71103
  if (isSessionMismatch(message)) {
70979
71104
  writeEvent({
@@ -71098,6 +71223,7 @@ Commands:
71098
71223
  Interactive Mode Key Bindings:
71099
71224
  Enter Submit message
71100
71225
  \\ + Enter New line (fallback)
71226
+ Alt+Enter Queue steering message for the active turn
71101
71227
  Ctrl+C Exit
71102
71228
  Ctrl+D Exit
71103
71229
  Ctrl+O Expand/collapse tool output
@@ -71115,6 +71241,7 @@ Print Mode Options:
71115
71241
  --json-output Print structured JSON in non-interactive mode
71116
71242
  --json-events Run JSONL protocol mode over stdin/stdout
71117
71243
  --session-id Reuse or create a specific session in non-interactive mode
71244
+ --resume-required In JSON-events mode, fail if --session-id cannot be resumed
71118
71245
  --extension-path Add an extra extension search directory (repeatable)
71119
71246
  --no-builtin-extensions Skip built-in extensions (keep user/project ones)
71120
71247
  --no-extensions Skip all discovered extensions (keeps --extension-path)
@@ -71194,16 +71321,6 @@ async function runModelsCommand(args) {
71194
71321
  pie models init [--path <path>] [--force] [--stdout]
71195
71322
  pie models validate [--path <path>] [--probe web_search] [--json]`);
71196
71323
  }
71197
- function parseRepeatedFlag(args, flag) {
71198
- const values = [];
71199
- for (let i = 0; i < args.length; i += 1) {
71200
- if (args[i] === flag && args[i + 1]) {
71201
- values.push(args[i + 1]);
71202
- i += 1;
71203
- }
71204
- }
71205
- return values;
71206
- }
71207
71324
  async function runWithJsonEventsConsoleIsolation(fn) {
71208
71325
  const originalLog = console.log;
71209
71326
  const originalInfo = console.info;
@@ -71228,6 +71345,7 @@ async function startChat(initialPrompt, testCommand) {
71228
71345
  const rawOutput = process.argv.includes("--raw-output");
71229
71346
  const jsonOutput = process.argv.includes("--json-output");
71230
71347
  const jsonEvents = process.argv.includes("--json-events");
71348
+ const resumeRequired = process.argv.includes("--resume-required");
71231
71349
  const sessionIdFlagIndex = process.argv.indexOf("--session-id");
71232
71350
  const explicitSessionId = sessionIdFlagIndex >= 0 ? process.argv[sessionIdFlagIndex + 1] : void 0;
71233
71351
  const extensionPaths = parseRepeatedFlag(process.argv, "--extension-path");
@@ -71237,19 +71355,22 @@ async function startChat(initialPrompt, testCommand) {
71237
71355
  if (process.env.PIE_TEST_MOCK_RESPONSE !== void 0) {
71238
71356
  await runJsonEventsMode({
71239
71357
  explicitSessionId,
71358
+ resumeRequired,
71240
71359
  mockResponse: process.env.PIE_TEST_MOCK_RESPONSE
71241
71360
  });
71242
71361
  return;
71243
71362
  }
71244
71363
  await runJsonEventsMode({
71245
71364
  explicitSessionId,
71365
+ resumeRequired,
71246
71366
  streamFn: await createCliTestStreamFnFromEnv(),
71247
71367
  createRuntime: async ({ cwd, sessionId }) => composeCliRuntime({
71248
71368
  explicitSessionId: explicitSessionId ?? sessionId,
71249
71369
  extensionPaths,
71250
71370
  extensionFlags,
71251
71371
  cwd,
71252
- warnIfExplicitSessionMissing: false
71372
+ warnIfExplicitSessionMissing: false,
71373
+ requireExistingSession: resumeRequired && !!explicitSessionId
71253
71374
  })
71254
71375
  });
71255
71376
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cydm/pie",
3
- "version": "1.0.33",
3
+ "version": "1.0.35",
4
4
  "description": "Pie AI Agent CLI",
5
5
  "type": "module",
6
6
  "bin": {