@piewf/cli 5.13.1 → 5.13.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.
package/dist/src/cli.js CHANGED
@@ -8,12 +8,11 @@ import { ProjectTrustStore, SessionManager, SettingsManager, createAgentSessionF
8
8
  import { Value } from "typebox/value";
9
9
  import { doctor, doctorExitCode, formatDoctorReport } from "./doctor.js";
10
10
  import { doctorCleanup, doctorCleanupExitCode, formatDoctorCleanupReport } from "./doctor-cleanup.js";
11
- import workflowExtension, { errorText, formatWorkflowProgress, isNodeError, jsonValue, loadAgentDefinitions, registeredWorkflowFunctionSources, truncateWorkflowProgress, workflowCatalog, workflowSettingsPath } from "pi-extensible-workflows";
11
+ import workflowExtension, { errorText, formatWorkflowProgress, isNodeError, jsonValue, loadAgentDefinitions, object, registeredWorkflowFunctionSources, truncateWorkflowProgress, workflowCatalog, workflowSettingsPath } from "pi-extensible-workflows";
12
12
  import { portableEngineVersion, portablePiVersion, writePortableWorkflowBundle } from "./bundles.js";
13
13
  import { runSessionInspector, transcriptFileLines } from "./session-inspector.js";
14
14
  import { isPersistedRun, listPersistedSessionIds, listRunIds } from "pi-extensible-workflows/persistence";
15
15
  import { shareTrajectoryRun } from "pi-extensible-workflows/trajectory";
16
- function object(value) { return typeof value === "object" && value !== null && !Array.isArray(value); }
17
16
  function has(value, key) { return Object.prototype.hasOwnProperty.call(value, key); }
18
17
  function requiredArg(args, index) {
19
18
  const value = args[index];
@@ -97,7 +96,7 @@ function coerce(raw, type, schema) {
97
96
  }
98
97
  else if (type === "number") {
99
98
  value = Number(raw);
100
- if (!Number.isFinite(value))
99
+ if (!raw.trim() || !Number.isFinite(value))
101
100
  throw new Error(`Invalid number: ${raw}`);
102
101
  }
103
102
  else {
@@ -2,27 +2,20 @@ import { createHash } from "node:crypto";
2
2
  import { lstat, readdir, readFile, stat } from "node:fs/promises";
3
3
  import { homedir } from "node:os";
4
4
  import { join, resolve } from "node:path";
5
- import { validateBudget } from "pi-extensible-workflows";
6
- import { RUN_STATES } from "pi-extensible-workflows";
7
- import { errorText, isNodeError, jsonValue, validateModelAliases } from "pi-extensible-workflows";
8
- import { validateSchema } from "pi-extensible-workflows";
5
+ import { AGENT_STATES as CORE_AGENT_STATES, BUDGET_DIMENSIONS as CORE_BUDGET_DIMENSIONS, BUDGET_EVENT_TYPES as CORE_BUDGET_EVENT_TYPES, HARD_TERMINAL_RUN_STATES, RUN_STATES, errorText, isNodeError, isThinkingLevel, jsonValue, object, validateBudget, validateModelAliases, validateSchema } from "pi-extensible-workflows";
9
6
  import { acquireSessionLease, hasLiveSessionLease, projectSessionsDirectory, RunStore } from "pi-extensible-workflows/persistence";
10
- const TERMINAL_STATES = new Set(["completed", "failed", "stopped"]);
11
7
  const DAY_MS = 24 * 60 * 60 * 1000;
12
8
  const REQUIRED_RUN_FILES = ["workflow.js", "state.json", "snapshot.json", "journal.json", "ownership.json", "worktrees.json", "borrowed-worktrees.json", "system-prompts.json"];
13
9
  const OPTIONAL_RUN_FILES = new Set(["result.json", "summary.json"]);
14
10
  const RUN_DIRECTORIES = new Set(["worktrees", ".system-prompts"]);
15
11
  const RUN_FILES = new Set([...REQUIRED_RUN_FILES, ...OPTIONAL_RUN_FILES]);
16
- const AGENT_STATES = new Set(["queued", "running", "waiting_for_child", "paused", "retrying", "completed", "failed", "cancelled"]);
17
- const SCHEDULER_STATES = new Set(["queued", "running", "waiting_for_child", "paused", "retrying", "completed", "failed", "cancelled"]);
18
- const BUDGET_DIMENSIONS = new Set(["tokens", "costUsd", "durationMs", "agentLaunches"]);
19
- const BUDGET_EVENT_TYPES = new Set(["soft_crossed", "hard_overrun", "hard_exhausted", "adjustment_requested", "adjustment_approved", "adjustment_rejected"]);
20
- function object(value) { return typeof value === "object" && value !== null && !Array.isArray(value); }
12
+ // String-keyed views of the core vocabularies: persisted files are read as unknown, so membership is checked on plain strings.
13
+ const AGENT_STATES = new Set(CORE_AGENT_STATES);
14
+ const BUDGET_DIMENSIONS = new Set(CORE_BUDGET_DIMENSIONS);
15
+ const BUDGET_EVENT_TYPES = new Set(CORE_BUDGET_EVENT_TYPES);
21
16
  function isList(value) { return Array.isArray(value); }
22
- function isThinking(value) { return ["off", "minimal", "low", "medium", "high", "xhigh", "max"].some((candidate) => candidate === value); }
23
17
  function isRunState(value) { return RUN_STATES.some((candidate) => candidate === value); }
24
18
  function isAgentState(value) { return typeof value === "string" && AGENT_STATES.has(value); }
25
- function isSchedulerState(value) { return typeof value === "string" && SCHEDULER_STATES.has(value); }
26
19
  function isToolCallState(value) { return ["running", "completed", "failed"].some((candidate) => candidate === value); }
27
20
  function isActivityKind(value) { return ["reasoning", "tool", "text"].some((candidate) => candidate === value); }
28
21
  function positiveDays(value) { if (!Number.isSafeInteger(value) || value < 1 || !Number.isFinite(value * DAY_MS))
@@ -42,7 +35,7 @@ function positiveInteger(value, label) { if (typeof value !== "number" || !Numbe
42
35
  throw new Error(`${label} is invalid`); }
43
36
  function finiteNumber(value, label) { if (typeof value !== "number" || !Number.isFinite(value) || value < 0)
44
37
  throw new Error(`${label} is invalid`); }
45
- function model(value, label) { if (!object(value) || typeof value.provider !== "string" || !value.provider || typeof value.model !== "string" || !value.model || (value.thinking !== undefined && !isThinking(value.thinking)))
38
+ function model(value, label) { if (!object(value) || typeof value.provider !== "string" || !value.provider || typeof value.model !== "string" || !value.model || (value.thinking !== undefined && !isThinkingLevel(value.thinking)))
46
39
  throw new Error(`${label} is invalid`); }
47
40
  function accounting(value, label) { if (!object(value))
48
41
  throw new Error(`${label} is invalid`); for (const key of ["input", "output", "cacheRead", "cacheWrite", "cost"])
@@ -54,7 +47,7 @@ function resourceSelectors(value, label) { if (value === undefined)
54
47
  stringList(value.extensions, `${label}.extensions`); if (value.tools !== undefined)
55
48
  stringList(value.tools, `${label}.tools`); }
56
49
  function agentDefinition(value, label) { if (!object(value))
57
- throw new Error(`${label} is invalid`); optionalString(value.prompt, `${label}.prompt`); optionalString(value.description, `${label}.description`); optionalString(value.model, `${label}.model`); if (value.thinking !== undefined && !isThinking(value.thinking))
50
+ throw new Error(`${label} is invalid`); optionalString(value.prompt, `${label}.prompt`); optionalString(value.description, `${label}.description`); optionalString(value.model, `${label}.model`); if (value.thinking !== undefined && !isThinkingLevel(value.thinking))
58
51
  throw new Error(`${label}.thinking is invalid`); if (value.tools !== undefined)
59
52
  stringList(value.tools, `${label}.tools`); if (value.skills !== undefined)
60
53
  stringList(value.skills, `${label}.skills`); if (value.extensions !== undefined)
@@ -69,7 +62,7 @@ function validateScheduledOptions(value, label) { if (!object(value) || typeof v
69
62
  throw new Error(`${label} is invalid`); optionalString(value.requestedLabel, `${label}.requestedLabel`); optionalString(value.parentBreadcrumb, `${label}.parentBreadcrumb`); stringList(value.tools, `${label}.tools`); if (value.skills !== undefined)
70
63
  stringList(value.skills, `${label}.skills`); if (value.extensions !== undefined)
71
64
  stringList(value.extensions, `${label}.extensions`); if (value.contextFiles !== undefined)
72
- stringList(value.contextFiles, `${label}.contextFiles`); optionalString(value.worktreeOwner, `${label}.worktreeOwner`); optionalString(value.model, `${label}.model`); if (value.thinking !== undefined && !isThinking(value.thinking))
65
+ stringList(value.contextFiles, `${label}.contextFiles`); optionalString(value.worktreeOwner, `${label}.worktreeOwner`); optionalString(value.model, `${label}.model`); if (value.thinking !== undefined && !isThinkingLevel(value.thinking))
73
66
  throw new Error(`${label}.thinking is invalid`); optionalRole(value.role, `${label}.role`); if (value.schema !== undefined)
74
67
  validateSchema(value.schema, `${label}.schema`); if (value.retries !== undefined)
75
68
  nonNegativeInteger(value.retries, `${label}.retries`); if (value.timeoutMs !== undefined && value.timeoutMs !== null)
@@ -146,7 +139,7 @@ function validateAgent(value, label) {
146
139
  finiteNumber(value.lastEventAt, `${label}.lastEventAt`);
147
140
  }
148
141
  function validateUsage(value, label) { if (!object(value))
149
- throw new Error(`${label} is invalid`); for (const key of ["tokens", "costUsd", "durationMs", "agentLaunches"])
142
+ throw new Error(`${label} is invalid`); for (const key of BUDGET_DIMENSIONS)
150
143
  finiteNumber(value[key], `${label}.${key}`); }
151
144
  function validateBudgetEvents(value) { if (value === undefined)
152
145
  return; if (!isList(value))
@@ -306,7 +299,7 @@ async function validateRunDirectory(store) {
306
299
  }
307
300
  }
308
301
  function validateOwnershipRecord(value, label, ownershipIds) {
309
- if (!object(value) || typeof value.id !== "string" || !value.id || ownershipIds.has(value.id) || typeof value.label !== "string" || !value.label || !isSchedulerState(value.state))
302
+ if (!object(value) || typeof value.id !== "string" || !value.id || ownershipIds.has(value.id) || typeof value.label !== "string" || !value.label || !isAgentState(value.state))
310
303
  throw new Error(`${label} is invalid`);
311
304
  optionalString(value.parentId, `${label}.parentId`);
312
305
  optionalString(value.prompt, `${label}.prompt`);
@@ -443,7 +436,7 @@ async function recheckCandidate(entry, cutoffMs) {
443
436
  return "State record disappeared before deletion";
444
437
  const loaded = await entry.store.load().catch((error) => { throw new Error(`Candidate could not be reloaded: ${errorText(error)}`); });
445
438
  validateRunRecord(loaded.run);
446
- if (loaded.run.id !== entry.runId || loaded.run.state !== entry.run.state || !TERMINAL_STATES.has(loaded.run.state))
439
+ if (loaded.run.id !== entry.runId || loaded.run.state !== entry.run.state || !HARD_TERMINAL_RUN_STATES.has(loaded.run.state))
447
440
  return "Candidate state changed before deletion";
448
441
  const after = await stat(join(entry.store.directory, "state.json"));
449
442
  if (before.mtimeMs !== after.mtimeMs || after.mtimeMs !== entry.stateMtimeMs)
@@ -455,7 +448,7 @@ async function recheckCandidate(entry, cutoffMs) {
455
448
  function planSession(scan, cutoffMs) {
456
449
  if (scan.liveLease)
457
450
  return { candidates: [], skipped: scan.runs.map((entry) => runItem(entry, "skipped", "Session has a live ownership lease")) };
458
- const oldTerminal = new Set(scan.runs.filter(({ run, stateMtimeMs }) => TERMINAL_STATES.has(run.state) && stateMtimeMs < cutoffMs).map(({ runId }) => runId));
451
+ const oldTerminal = new Set(scan.runs.filter(({ run, stateMtimeMs }) => HARD_TERMINAL_RUN_STATES.has(run.state) && stateMtimeMs < cutoffMs).map(({ runId }) => runId));
459
452
  const protectedRuns = new Set();
460
453
  const visit = (runId) => { if (protectedRuns.has(runId))
461
454
  return; protectedRuns.add(runId); const entry = scan.runs.find(({ runId: current }) => current === runId); for (const dependency of entry?.dependencies ?? [])
@@ -466,7 +459,7 @@ function planSession(scan, cutoffMs) {
466
459
  visit(dependency);
467
460
  const skipped = [];
468
461
  for (const entry of scan.runs) {
469
- if (!TERMINAL_STATES.has(entry.run.state))
462
+ if (!HARD_TERMINAL_RUN_STATES.has(entry.run.state))
470
463
  skipped.push(runItem(entry, "skipped", `Run state ${entry.run.state} is active or resumable`));
471
464
  else if (entry.stateMtimeMs >= cutoffMs)
472
465
  skipped.push(runItem(entry, "skipped", "State record is not older than the cutoff"));
@@ -4,7 +4,7 @@ import { emitKeypressEvents } from "node:readline";
4
4
  import { createInterface } from "node:readline/promises";
5
5
  import { stdin, stdout } from "node:process";
6
6
  import { highlightCode, initTheme, SessionManager, truncateToVisualLines } from "@earendil-works/pi-coding-agent";
7
- import { errorText, formatBudgetStatus, inspectWorkflowScript } from "pi-extensible-workflows";
7
+ import { errorText, formatBudgetStatus, inspectWorkflowScript, parseThinking } from "pi-extensible-workflows";
8
8
  import { listPersistedSessionIds, listRunIds, RunStore } from "pi-extensible-workflows/persistence";
9
9
  function text(content) {
10
10
  if (typeof content === "string")
@@ -69,13 +69,6 @@ function mergedModels(groups) {
69
69
  totals.set(item.model, (totals.get(item.model) ?? 0) + item.cost);
70
70
  return [...totals].map(([model, cost]) => ({ model, cost })).sort((a, b) => b.cost - a.cost || a.model.localeCompare(b.model));
71
71
  }
72
- const thinkingLevels = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
73
- function isThinkingLevel(value) {
74
- return typeof value === "string" && thinkingLevels.some((level) => level === value);
75
- }
76
- function parsedThinking(value) {
77
- return isThinkingLevel(value) ? value : undefined;
78
- }
79
72
  function modelName(provider, model) {
80
73
  return typeof provider === "string" && provider && typeof model === "string" && model ? `${provider}/${model}` : undefined;
81
74
  }
@@ -93,7 +86,7 @@ function transcript(manager) {
93
86
  continue;
94
87
  }
95
88
  if (entry.type === "thinking_level_change") {
96
- thinking = parsedThinking(entry.thinkingLevel);
89
+ thinking = parseThinking(entry.thinkingLevel);
97
90
  if (thinking === undefined)
98
91
  throw new Error("Invalid thinking policy");
99
92
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piewf/cli",
3
- "version": "5.13.1",
3
+ "version": "5.13.2",
4
4
  "description": "CLI for pi-extensible-workflows",
5
5
  "homepage": "https://vekexasia.github.io/pi-extensible-workflows/",
6
6
  "repository": {
@@ -43,7 +43,7 @@
43
43
  "@earendil-works/pi-coding-agent": "0.85.0",
44
44
  "@earendil-works/pi-server": "0.85.0",
45
45
  "@earendil-works/pi-tui": "0.85.0",
46
- "pi-extensible-workflows": "5.13.1",
46
+ "pi-extensible-workflows": "5.13.2",
47
47
  "typebox": "1.3.7"
48
48
  },
49
49
  "publishConfig": {