@cydm/pie 2.6.1 → 2.6.2

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.
@@ -1,7 +1,7 @@
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-IKLQHWY2.js";
4
+ } from "../../../chunks/chunk-HT3OYGS7.js";
5
5
  import "../../../chunks/chunk-Y4CYXJR7.js";
6
6
  import "../../../chunks/chunk-O2DKMLNX.js";
7
7
  import "../../../chunks/chunk-XXGYUYXI.js";
@@ -7,7 +7,7 @@ import {
7
7
  isPlanModeSafeCommand,
8
8
  markCompletedPlanSteps,
9
9
  restoreExecutionState
10
- } from "../../../chunks/chunk-IKLQHWY2.js";
10
+ } from "../../../chunks/chunk-HT3OYGS7.js";
11
11
  import "../../../chunks/chunk-Y4CYXJR7.js";
12
12
  import "../../../chunks/chunk-O2DKMLNX.js";
13
13
  import {
@@ -1,11 +1,11 @@
1
1
  import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
2
2
  import {
3
3
  createCliHostCapabilities
4
- } from "../../../chunks/chunk-3F5ZP2CV.js";
4
+ } from "../../../chunks/chunk-INQMGIKU.js";
5
5
  import {
6
6
  createSharedFileSystemTools,
7
7
  createSubagentCapability
8
- } from "../../../chunks/chunk-IKLQHWY2.js";
8
+ } from "../../../chunks/chunk-HT3OYGS7.js";
9
9
  import "../../../chunks/chunk-Y4CYXJR7.js";
10
10
  import "../../../chunks/chunk-O2DKMLNX.js";
11
11
  import "../../../chunks/chunk-5353DEEC.js";
@@ -8,7 +8,7 @@ import {
8
8
  executeManageTodoList,
9
9
  executionStateToTodos,
10
10
  restoreExecutionState
11
- } from "../../../chunks/chunk-IKLQHWY2.js";
11
+ } from "../../../chunks/chunk-HT3OYGS7.js";
12
12
  import "../../../chunks/chunk-Y4CYXJR7.js";
13
13
  import "../../../chunks/chunk-O2DKMLNX.js";
14
14
  import "../../../chunks/chunk-XXGYUYXI.js";
@@ -1639,7 +1639,6 @@ ${formatSkillsForPrompt(deps.skills ?? [])}`;
1639
1639
  const subagent = createAgentInstance(deps, systemPrompt, tools, model, apiKey, plan.thinkingLevel);
1640
1640
  let assistantTurns = 0;
1641
1641
  let toolCalls = 0;
1642
- let outputTruncated = false;
1643
1642
  let rejectTimeout = () => void 0;
1644
1643
  let timeout;
1645
1644
  let unsubscribe;
@@ -1686,7 +1685,6 @@ ${formatSkillsForPrompt(deps.skills ?? [])}`;
1686
1685
  }
1687
1686
  if (event.type === "message_end" && event.message?.role === "assistant") {
1688
1687
  assistantTurns++;
1689
- if (event.message.stopReason === "length") outputTruncated = true;
1690
1688
  }
1691
1689
  });
1692
1690
  let prompt = task.task;
@@ -1722,6 +1720,7 @@ ${formatSkillsForPrompt(deps.skills ?? [])}`;
1722
1720
  throw new Error(`Subagent stream interrupted (SSE stream aborted before the report was produced, stopReason=${terminalStopReason ?? "none"}${collectedResponse ? ", partial transcript discarded" : ", no text response"})${detail}`);
1723
1721
  }
1724
1722
  let response = collectedResponse;
1723
+ const outputTruncated = terminalStopReason === "length";
1725
1724
  if (outputTruncated) {
1726
1725
  task.confidenceSignals.push({ type: "output_truncated", message: "final response hit the provider's output token limit" });
1727
1726
  response += "\n\n[Incomplete report: the subagent's final response was cut off by the provider's output token limit (stopReason=length). The items above may be a partial list \u2014 rerun the task with a narrower scope, or ask for the remaining items.]";
@@ -7866,7 +7865,7 @@ var AskUserMultiParamsSchema = Type.Object({
7866
7865
  }))
7867
7866
  });
7868
7867
  function buildUnsupportedResult(error) {
7869
- const message = error instanceof InteractionUnavailableError ? error.message : error instanceof Error ? error.message : String(error);
7868
+ const message = error instanceof InteractionUnavailableError ? `${error.message}. This host has no interactive UI registered (headless or print run) \u2014 do not retry ask_user tools here; proceed without asking, or state the question as plain text.` : error instanceof Error ? error.message : String(error);
7870
7869
  return {
7871
7870
  content: [{ type: "text", text: `Interaction unavailable: ${message}` }],
7872
7871
  details: { unsupported: true, error: message },
@@ -8082,6 +8081,13 @@ function createAskUserCapability() {
8082
8081
  parameters: AskUserMultiParamsSchema,
8083
8082
  async execute(toolCallIdOrArgs, maybeArgs) {
8084
8083
  try {
8084
+ if (!hasInteractionHandler()) {
8085
+ return {
8086
+ content: [{ type: "text", text: "Interaction unavailable: this host has no interactive UI registered (headless or print run). Do not retry ask_user_multi here \u2014 proceed without asking, or state the question as plain text." }],
8087
+ details: { unsupported: true, error: "no interaction handler" },
8088
+ isError: true
8089
+ };
8090
+ }
8085
8091
  const args = typeof toolCallIdOrArgs === "string" && maybeArgs !== void 0 ? maybeArgs : toolCallIdOrArgs;
8086
8092
  const normalized = normalizeAskUserArgs(args);
8087
8093
  if ("error" in normalized) {
@@ -15367,9 +15373,18 @@ var webResearchSchema = Type.Object({
15367
15373
  description: "Maximum credible sources to fetch and cite. Defaults to 3."
15368
15374
  }))
15369
15375
  });
15376
+ function httpStatusFromFailureText(text) {
15377
+ const match2 = /\bHTTP\s?(\d{3})\b/i.exec(text);
15378
+ return match2 ? Number(match2[1]) : void 0;
15379
+ }
15370
15380
  var DEFAULT_WEB_RESEARCH_SEARCH_ATTEMPT_TIMEOUT_MS = 45e3;
15371
- var DEFAULT_WEB_RESEARCH_FETCH_ATTEMPT_TIMEOUT_MS = 3e4;
15381
+ var DEFAULT_WEB_RESEARCH_FETCH_ATTEMPT_TIMEOUT_MS = 45e3;
15372
15382
  var DEFAULT_WEB_RESEARCH_BROWSER_ATTEMPT_TIMEOUT_MS = 45e3;
15383
+ function isTransientFetchFailure(category, text) {
15384
+ if (category === "timeout" || category === "provider_error") return true;
15385
+ if (category === "http_error") return /\bHTTP\s?(5\d\d|429)\b/i.test(text);
15386
+ return false;
15387
+ }
15373
15388
  function isRecord5(value) {
15374
15389
  return !!value && typeof value === "object" && !Array.isArray(value);
15375
15390
  }
@@ -16107,36 +16122,48 @@ function createSharedWebResearchTool(deps) {
16107
16122
  return true;
16108
16123
  });
16109
16124
  const fetchScoredSource = async (scored) => {
16110
- const startedAt = Date.now();
16111
16125
  onUpdate?.({
16112
16126
  content: [{ type: "text", text: `Fetching source: ${scored.source.url}` }],
16113
16127
  details: { query, actualQueries: [...new Set(actualQueries)], sources: dedupedSources, fetchedSources, providerAttempts, attempts, qualityScore: qualityScore(fetchedSources, scoredSources), warnings, browserRequiredSources }
16114
16128
  });
16115
- const fetchResult = await runWithDeadline(
16116
- (childSignal) => webFetchTool.execute("web_research_fetch", { url: scored.source.url }, childSignal),
16117
- fetchAttemptTimeoutMs,
16118
- `web_research fetch attempt timed out after ${Math.ceil(fetchAttemptTimeoutMs / 1e3)} seconds`,
16119
- signal
16120
- ).catch((error) => ({
16121
- content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }],
16122
- details: {
16123
- url: scored.source.url,
16124
- sourceUrl: scored.source.url,
16125
- durationMs: Date.now() - startedAt,
16126
- failureCategory: "timeout"
16127
- },
16128
- isError: true
16129
- }));
16129
+ const fetchOnce = async () => {
16130
+ const startedAt = Date.now();
16131
+ const result = await runWithDeadline(
16132
+ (childSignal) => webFetchTool.execute("web_research_fetch", { url: scored.source.url }, childSignal),
16133
+ fetchAttemptTimeoutMs,
16134
+ `web_research fetch attempt timed out after ${Math.ceil(fetchAttemptTimeoutMs / 1e3)} seconds`,
16135
+ signal
16136
+ ).catch((error) => ({
16137
+ content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }],
16138
+ details: {
16139
+ url: scored.source.url,
16140
+ sourceUrl: scored.source.url,
16141
+ durationMs: Date.now() - startedAt,
16142
+ failureCategory: "timeout"
16143
+ },
16144
+ isError: true
16145
+ }));
16146
+ return { result, startedAt, endedAt: Date.now() };
16147
+ };
16148
+ let attempt = await fetchOnce();
16149
+ let attemptTotalMs = attempt.endedAt - attempt.startedAt;
16150
+ if (attempt.result.isError && isTransientFetchFailure(contentDetails(attempt.result).failureCategory, textFromResult(attempt.result))) {
16151
+ attempts.push({ phase: "fetch", url: scored.source.url, status: "failed", failureCategory: contentDetails(attempt.result).failureCategory, failureStatus: httpStatusFromFailureText(textFromResult(attempt.result)), durationMs: attemptTotalMs });
16152
+ attempt = await fetchOnce();
16153
+ attemptTotalMs += attempt.endedAt - attempt.startedAt;
16154
+ }
16155
+ const fetchResult = attempt.result;
16130
16156
  const details2 = contentDetails(fetchResult);
16131
16157
  const text = textFromResult(fetchResult);
16132
- fetchDurationMs += Date.now() - startedAt;
16133
- sourceReadDurations.push(Date.now() - startedAt);
16158
+ fetchDurationMs += attemptTotalMs;
16159
+ sourceReadDurations.push(attemptTotalMs);
16134
16160
  attempts.push({
16135
16161
  phase: "fetch",
16136
16162
  url: scored.source.url,
16137
16163
  status: fetchResult.isError ? "failed" : "completed",
16138
16164
  failureCategory: details2.failureCategory,
16139
- durationMs: Date.now() - startedAt
16165
+ ...fetchResult.isError ? { failureStatus: httpStatusFromFailureText(text) } : {},
16166
+ durationMs: attemptTotalMs
16140
16167
  });
16141
16168
  if (details2.requiresBrowser) {
16142
16169
  const browserEntry = {
@@ -16243,9 +16270,12 @@ function createSharedWebResearchTool(deps) {
16243
16270
  }
16244
16271
  };
16245
16272
  if (fetchedSources.length === 0) {
16273
+ const fetchFailures = details.attempts.filter((attempt) => attempt.phase === "fetch" && attempt.status === "failed").slice(0, 6);
16274
+ const failureSummary = fetchFailures.length ? `
16275
+ Fetch failures: ${fetchFailures.map((attempt) => `${attempt.url} (${attempt.failureCategory || "fetch_failed"}${attempt.failureStatus !== void 0 ? ` ${attempt.failureStatus}` : ""})`).join("; ")}` : "";
16246
16276
  const reason = browserRequiredSources.length ? "web_research found candidate sources, but they require browser-rendered reading and no host browser reader completed." : "web_research could not fetch any credible source for this query.";
16247
16277
  return {
16248
- content: [{ type: "text", text: `${reason}
16278
+ content: [{ type: "text", text: `${reason}${failureSummary}
16249
16279
 
16250
16280
  Queries tried: ${details.actualQueries.join("; ") || query}` }],
16251
16281
  details,
@@ -8,7 +8,7 @@ import {
8
8
  createSharedWebSearchTool,
9
9
  interpretShellExit,
10
10
  requestInteraction
11
- } from "./chunk-IKLQHWY2.js";
11
+ } from "./chunk-HT3OYGS7.js";
12
12
  import {
13
13
  getBuiltinDir,
14
14
  getConfigDir
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  createCliWebResearchHealthStore,
8
8
  formatSize,
9
9
  truncateHeadTail
10
- } from "./chunks/chunk-3F5ZP2CV.js";
10
+ } from "./chunks/chunk-INQMGIKU.js";
11
11
  import {
12
12
  AGENTS_CONTEXT_FILE_NAME,
13
13
  AgentSessionController,
@@ -60,7 +60,7 @@ import {
60
60
  setExtensionEnabledInSettings,
61
61
  shouldPreserveExecutionStateForUserText,
62
62
  supersedeExecutionState
63
- } from "./chunks/chunk-IKLQHWY2.js";
63
+ } from "./chunks/chunk-HT3OYGS7.js";
64
64
  import "./chunks/chunk-Y4CYXJR7.js";
65
65
  import {
66
66
  Deref,
@@ -53581,17 +53581,40 @@ function filterEnabledTools(tools, settingsManager) {
53581
53581
  const disabled = getDisabledToolNames(settingsManager);
53582
53582
  return disabled.size > 0 ? tools.filter((tool) => !disabled.has(tool.name)) : [...tools];
53583
53583
  }
53584
+ function mergeSnapshotDelta(atLoad, current, onDisk) {
53585
+ const merged = { ...onDisk };
53586
+ const load = atLoad;
53587
+ const cur = current;
53588
+ const allKeys = /* @__PURE__ */ new Set([...Object.keys(load), ...Object.keys(cur)]);
53589
+ for (const key of allKeys) {
53590
+ const before = JSON.stringify(load[key]);
53591
+ const after = JSON.stringify(cur[key]);
53592
+ if (before === after) continue;
53593
+ if (after === void 0) delete merged[key];
53594
+ else merged[key] = cur[key];
53595
+ }
53596
+ return merged;
53597
+ }
53584
53598
  var SettingsManager = class {
53585
53599
  settingsPath;
53586
53600
  legacyConfigPath;
53587
53601
  settings;
53588
53602
  legacyConfig;
53603
+ /** Last state this instance successfully persisted (starts at the load
53604
+ * snapshot). The delta vs THIS — not vs the load snapshot — is what a
53605
+ * write may persist: a load-time baseline misses in-process
53606
+ * write-after-write (e.g. disable then re-enable must prune the key, not
53607
+ * 'revert to load state = untouched'). */
53608
+ settingsLastWritten;
53609
+ legacyConfigLastWritten;
53589
53610
  constructor() {
53590
53611
  const configDir = getConfigDir();
53591
53612
  this.settingsPath = getSettingsPath();
53592
53613
  this.legacyConfigPath = `${configDir}/config.json`;
53593
53614
  this.settings = this.loadSettingsFile();
53615
+ this.settingsLastWritten = structuredClone(this.settings);
53594
53616
  this.legacyConfig = this.loadLegacyConfigFile();
53617
+ this.legacyConfigLastWritten = structuredClone(this.legacyConfig);
53595
53618
  }
53596
53619
  readJson(filePath) {
53597
53620
  if (!fs4.existsSync(filePath)) return null;
@@ -53612,7 +53635,38 @@ var SettingsManager = class {
53612
53635
  }
53613
53636
  writeSettings() {
53614
53637
  this.ensureDir();
53615
- fs4.writeFileSync(this.settingsPath, JSON.stringify(this.settings, null, 2));
53638
+ const written = this.writeMergedOrFull(() => this.settingsPath, () => this.settingsLastWritten, () => this.settings, (value) => this.writeSettingsValue(value));
53639
+ this.settings = structuredClone(written);
53640
+ this.settingsLastWritten = structuredClone(written);
53641
+ }
53642
+ /** Merged write for an explicit candidate state (setters that build a
53643
+ * nextSettings object): writes FIRST (a failed write leaves this
53644
+ * instance's cached state untouched), then returns the value actually
53645
+ * written — callers assign THAT (not their candidate, which lacks keys
53646
+ * preserved from disk) so state and lastWritten never diverge. */
53647
+ writeSettingsWith(next) {
53648
+ const written = this.writeMergedOrFull(() => this.settingsPath, () => this.settingsLastWritten, () => next, (value) => this.writeSettingsValue(value));
53649
+ this.settingsLastWritten = structuredClone(written);
53650
+ return written;
53651
+ }
53652
+ /** Shared write path: merge our delta onto the current on-disk object and
53653
+ * return the value actually written. When the file EXISTS but does not
53654
+ * parse (another process mid-write over a non-atomic writeFileSync),
53655
+ * merging onto {} would silently delete every unrelated key — fall back
53656
+ * to the full candidate write instead. */
53657
+ writeMergedOrFull(path34, lastWritten, current, write) {
53658
+ let onDisk = null;
53659
+ let corrupt = false;
53660
+ if (fs4.existsSync(path34())) {
53661
+ try {
53662
+ onDisk = JSON.parse(fs4.readFileSync(path34(), "utf-8"));
53663
+ } catch {
53664
+ corrupt = true;
53665
+ }
53666
+ }
53667
+ const value = corrupt ? current() : mergeSnapshotDelta(lastWritten(), current(), onDisk ?? {});
53668
+ write(value);
53669
+ return value;
53616
53670
  }
53617
53671
  writeSettingsValue(settings) {
53618
53672
  this.ensureDir();
@@ -53620,7 +53674,10 @@ var SettingsManager = class {
53620
53674
  }
53621
53675
  writeLegacyConfig() {
53622
53676
  this.ensureDir();
53623
- fs4.writeFileSync(this.legacyConfigPath, JSON.stringify(this.legacyConfig, null, 2));
53677
+ this.writeMergedOrFull(() => this.legacyConfigPath, () => this.legacyConfigLastWritten, () => this.legacyConfig, (value) => {
53678
+ fs4.writeFileSync(this.legacyConfigPath, JSON.stringify(value, null, 2));
53679
+ this.legacyConfigLastWritten = structuredClone(value);
53680
+ });
53624
53681
  }
53625
53682
  getLegacyApiKey() {
53626
53683
  return this.legacyConfig.apiKey;
@@ -53741,8 +53798,7 @@ var SettingsManager = class {
53741
53798
  extensions: this.settings.extensions ? { ...this.settings.extensions } : void 0
53742
53799
  };
53743
53800
  setExtensionEnabledInSettings(nextSettings, identity, enabled);
53744
- this.writeSettingsValue(nextSettings);
53745
- this.settings = nextSettings;
53801
+ this.settings = this.writeSettingsWith(nextSettings);
53746
53802
  }
53747
53803
  /** Raw skill enablement map. */
53748
53804
  getSkillSettings() {
@@ -53756,8 +53812,7 @@ var SettingsManager = class {
53756
53812
  setSkillEnabled(name, enabled) {
53757
53813
  const nextSettings = { ...this.settings };
53758
53814
  setEnablementInMap(nextSettings, "skills", name, enabled);
53759
- this.writeSettingsValue(nextSettings);
53760
- this.settings = nextSettings;
53815
+ this.settings = this.writeSettingsWith(nextSettings);
53761
53816
  }
53762
53817
  /** Disabled runtime tool names (may contain names no longer installed). */
53763
53818
  getDisabledTools() {
@@ -53779,8 +53834,7 @@ var SettingsManager = class {
53779
53834
  if (Object.keys(nextTools).length === 0) {
53780
53835
  delete nextSettings.tools;
53781
53836
  }
53782
- this.writeSettingsValue(nextSettings);
53783
- this.settings = nextSettings;
53837
+ this.settings = this.writeSettingsWith(nextSettings);
53784
53838
  }
53785
53839
  };
53786
53840
 
@@ -63783,11 +63837,15 @@ function sanitizeNotificationText(text) {
63783
63837
  function buildDesktopNotificationSequences(title, body) {
63784
63838
  const sanitizedTitle = sanitizeNotificationText(title);
63785
63839
  const sanitizedBody = sanitizeNotificationText(body);
63786
- return [
63787
- `\x1B]9;${sanitizedTitle}: ${sanitizedBody}\x07`,
63788
- `\x1B]777;notify;${sanitizedTitle};${sanitizedBody}\x07`,
63789
- `\x1B]99;i=1:d=0;${sanitizedBody}\x07`
63790
- ];
63840
+ const program = process.env.TERM_PROGRAM ?? "";
63841
+ const term = process.env.TERM ?? "";
63842
+ if (/kitty/i.test(program) || /^xterm-kitty/.test(term)) {
63843
+ return [`\x1B]99;i=1:d=0;${sanitizedBody}\x07`];
63844
+ }
63845
+ if (/rxvt|terminology/i.test(program) || /^rxvt-unicode/.test(term)) {
63846
+ return [`\x1B]777;notify;${sanitizedTitle};${sanitizedBody}\x07`];
63847
+ }
63848
+ return [`\x1B]9;${sanitizedTitle}: ${sanitizedBody}\x07`];
63791
63849
  }
63792
63850
 
63793
63851
  // src/extensions/runner.ts
@@ -78442,6 +78500,7 @@ function createInteractiveAgentSessionController(host) {
78442
78500
  }
78443
78501
  let retryElapsedTicker;
78444
78502
  let retryElapsedStartedAt = 0;
78503
+ let pendingSettleKind;
78445
78504
  const stopRetryElapsedTicker = () => {
78446
78505
  if (retryElapsedTicker !== void 0) {
78447
78506
  clearInterval(retryElapsedTicker);
@@ -78585,10 +78644,14 @@ function createInteractiveAgentSessionController(host) {
78585
78644
  host.appendWarningSummary(outcome.terminalSummary);
78586
78645
  }
78587
78646
  host.sessionTrace.completeTurn(event.message, false);
78588
- ringTurnSettledBell(host);
78647
+ pendingSettleKind = "completed";
78589
78648
  host._semanticTurnIndex++;
78590
78649
  } else if (event.type === "turn_settled") {
78591
78650
  host.setControllerBusy(false);
78651
+ if (pendingSettleKind === "completed") {
78652
+ ringTurnSettledBell(host);
78653
+ }
78654
+ pendingSettleKind = void 0;
78592
78655
  } else if (event.type === "turn_failed_terminal") {
78593
78656
  host.clearContinuationTimeout("turn_failed_terminal");
78594
78657
  if (event.message) {
@@ -78624,6 +78687,7 @@ function createInteractiveAgentSessionController(host) {
78624
78687
  }
78625
78688
  host.showError(event.failure.message);
78626
78689
  host.ringAttentionBell?.("turn-failed");
78690
+ pendingSettleKind = "failed";
78627
78691
  host.setControllerBusy(false);
78628
78692
  } else if (event.type === "turn_cancelled") {
78629
78693
  const outcome = host.executionState.abort();
@@ -78640,6 +78704,7 @@ function createInteractiveAgentSessionController(host) {
78640
78704
  if (outcome.terminalSummary) {
78641
78705
  host.appendWarningSummary(outcome.terminalSummary);
78642
78706
  }
78707
+ pendingSettleKind = "cancelled";
78643
78708
  host.setControllerBusy(false);
78644
78709
  } else if (event.type === "runtime_guard_advisory") {
78645
78710
  host.noteRuntimeGuardAdvisory(event);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cydm/pie",
3
- "version": "2.6.1",
3
+ "version": "2.6.2",
4
4
  "description": "Pie AI Agent CLI",
5
5
  "type": "module",
6
6
  "bin": {