perchai-cli 2.4.36 → 2.4.38

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/perch.mjs +95 -28
  2. package/package.json +1 -1
package/dist/perch.mjs CHANGED
@@ -75566,6 +75566,7 @@ var init_payroll = __esm({
75566
75566
  // lib/perchBusinessTools/index.ts
75567
75567
  var init_perchBusinessTools = __esm({
75568
75568
  "lib/perchBusinessTools/index.ts"() {
75569
+ "use strict";
75569
75570
  init_generateAPAuditPacket();
75570
75571
  init_inventoryFolder();
75571
75572
  init_loadBusinessTables();
@@ -83126,7 +83127,6 @@ function listFinancialRoleIds() {
83126
83127
  var FINANCIAL_ROLE_REGISTRY, evidenceScoutManifest;
83127
83128
  var init_financialRoles = __esm({
83128
83129
  "features/perchTerminal/agentPlatform/financialRoles.ts"() {
83129
- "use strict";
83130
83130
  FINANCIAL_ROLE_REGISTRY = /* @__PURE__ */ new Map();
83131
83131
  evidenceScoutManifest = {
83132
83132
  workerId: "evidence_scout",
@@ -205378,7 +205378,7 @@ function clampInt(value, min2, max2, fallback) {
205378
205378
  const num = typeof value === "number" && Number.isFinite(value) ? Math.floor(value) : fallback;
205379
205379
  return Math.min(Math.max(num, min2), max2);
205380
205380
  }
205381
- var ADHOC_FORBIDDEN_TOOLS, ADHOC_WRITE_TOOLS, ADHOC_MAX_ITERATIONS, MAX_NAME_CHARS, MAX_CONTRACT_CHARS, DEFAULT_OUTPUT_CONTRACT;
205381
+ var ADHOC_FORBIDDEN_TOOLS, ADHOC_WRITE_TOOLS, ADHOC_MAX_ITERATIONS, ADHOC_MAX_TOOL_CALLS, MAX_NAME_CHARS, MAX_CONTRACT_CHARS, DEFAULT_OUTPUT_CONTRACT;
205382
205382
  var init_adhocManifest = __esm({
205383
205383
  "features/perchTerminal/runtime/workers/adhocManifest.ts"() {
205384
205384
  "use strict";
@@ -205407,6 +205407,7 @@ var init_adhocManifest = __esm({
205407
205407
  TOOL_NAMES.createDirectory
205408
205408
  ]);
205409
205409
  ADHOC_MAX_ITERATIONS = 10;
205410
+ ADHOC_MAX_TOOL_CALLS = 40;
205410
205411
  MAX_NAME_CHARS = 40;
205411
205412
  MAX_CONTRACT_CHARS = 240;
205412
205413
  DEFAULT_OUTPUT_CONTRACT = "Return a short structured summary of what was found or done.";
@@ -205495,6 +205496,14 @@ var init_spawnWorker = __esm({
205495
205496
  handler: async (args, ctx) => {
205496
205497
  let workerId = String(args.workerId ?? "");
205497
205498
  const objective = String(args.objective ?? "");
205499
+ if (!ctx.onEvent) {
205500
+ return {
205501
+ ok: false,
205502
+ workerId,
205503
+ summary: "spawn_worker requires an event sink from the parent tool loop.",
205504
+ errorCode: "worker_event_sink_missing"
205505
+ };
205506
+ }
205498
205507
  let adhocManifestId = null;
205499
205508
  let adhocNickname = null;
205500
205509
  const customSpec = parseAdhocSpec(args.custom);
@@ -205509,11 +205518,13 @@ var init_spawnWorker = __esm({
205509
205518
  if (workerId && customSpec && !resolveWorkerManifest(workerId)) {
205510
205519
  workerId = "";
205511
205520
  }
205521
+ let adhocDisplayTitle;
205512
205522
  if (!workerId && customSpec) {
205513
205523
  const { manifest, droppedTools } = buildAdhocWorkerManifest(customSpec);
205514
205524
  registerWorkerManifest(manifest);
205515
205525
  adhocManifestId = manifest.workerId;
205516
205526
  adhocNickname = flockNicknameFor(manifest.workerId, 0);
205527
+ adhocDisplayTitle = `${manifest.name} \xB7 ${adhocNickname}`;
205517
205528
  workerId = manifest.workerId;
205518
205529
  if (droppedTools.length > 0 && ctx.onEvent) {
205519
205530
  ctx.onEvent({
@@ -205534,15 +205545,7 @@ var init_spawnWorker = __esm({
205534
205545
  }
205535
205546
  const context = typeof args.context === "string" || typeof args.context === "object" && args.context !== null && !Array.isArray(args.context) ? args.context : void 0;
205536
205547
  const lane = typeof args.lane === "string" ? args.lane : void 0;
205537
- const maxIterations = typeof args.maxIterations === "number" ? args.maxIterations : void 0;
205538
- if (!ctx.onEvent) {
205539
- return {
205540
- ok: false,
205541
- workerId,
205542
- summary: "spawn_worker requires an event sink from the parent tool loop.",
205543
- errorCode: "worker_event_sink_missing"
205544
- };
205545
- }
205548
+ const maxIterations = typeof args.maxIterations === "number" ? adhocManifestId ? Math.min(args.maxIterations, ADHOC_MAX_ITERATIONS) : args.maxIterations : void 0;
205546
205549
  const enrichedContext = await threadPriorSpecialistContext({
205547
205550
  threadId: ctx.threadId,
205548
205551
  roleId: workerId,
@@ -205564,8 +205567,19 @@ Proceed with the available context. If a missing fact is essential, ask the user
205564
205567
  ].filter(Boolean).join("\n\n");
205565
205568
  try {
205566
205569
  const result2 = await spawnWorker(
205567
- { workerId, objective: workerObjective, context: enrichedContext, lane, maxIterations },
205568
205570
  {
205571
+ workerId,
205572
+ objective: workerObjective,
205573
+ context: enrichedContext,
205574
+ lane,
205575
+ maxIterations,
205576
+ // Ad-hoc workers get a hard real-tool-call ceiling (observed live:
205577
+ // an unbounded 66-call run). Registered workers keep their existing
205578
+ // behavior — suites and managed workflows own their own budgets.
205579
+ maxToolCalls: adhocManifestId ? ADHOC_MAX_TOOL_CALLS : void 0
205580
+ },
205581
+ {
205582
+ displayTitle: adhocDisplayTitle,
205569
205583
  workspaceRoot: effectiveWorkspaceRoot(ctx),
205570
205584
  desktopConnected: ctx.desktopConnected,
205571
205585
  // CLI surface: children inherit the local bridge and server auth,
@@ -220237,12 +220251,13 @@ async function spawnWorker(args, ctx) {
220237
220251
  errorCode: "worker_not_found"
220238
220252
  };
220239
220253
  }
220254
+ const eventTitle = ctx.displayTitle?.trim() || manifest.name;
220240
220255
  const runDiscriminator = ctx.runId ?? Date.now().toString(36);
220241
220256
  const childThreadId = args.childThreadId ?? (ctx.threadId ? `${ctx.threadId}::worker::${args.workerId}::${runDiscriminator}` : null);
220242
220257
  const workerRun = ctx.threadId ? await registerWorkerRun({
220243
220258
  threadId: ctx.threadId,
220244
220259
  workerId: args.workerId,
220245
- title: manifest.name,
220260
+ title: eventTitle,
220246
220261
  objective: args.objective,
220247
220262
  context: args.context,
220248
220263
  parentToolCallId: ctx.parentToolCallId ?? null,
@@ -220265,14 +220280,14 @@ async function spawnWorker(args, ctx) {
220265
220280
  childThreadId,
220266
220281
  workerRunId: runtimeWorkerRunId,
220267
220282
  workerId: args.workerId,
220268
- title: manifest.name
220283
+ title: eventTitle
220269
220284
  });
220270
220285
  }
220271
220286
  ctx.onEvent({
220272
220287
  type: "worker_run_started",
220273
220288
  workerId: args.workerId,
220274
220289
  workerRunId: runtimeWorkerRunId,
220275
- title: manifest.name,
220290
+ title: eventTitle,
220276
220291
  objective: args.objective,
220277
220292
  contextSummary: summarizeWorkerContextForTranscript(args.context),
220278
220293
  childThreadId,
@@ -220479,18 +220494,24 @@ ${contextBlock}
220479
220494
  attachOperatorScreenshots: isBrowserDeliveryRole(args.workerId)
220480
220495
  };
220481
220496
  const result2 = await runModelToolLoop(loopInput);
220497
+ const approvalBlocked = result2.stopReason === "approval_required" ? result2.pendingApproval ?? null : null;
220482
220498
  const recoveredStructuredOutput = recoverWorkerStructuredOutput(args.workerId, args.objective, args.context, result2.toolCalls);
220483
220499
  const structuredOutput = parseStructuredRecordFromModelText(result2.text) ?? recoveredStructuredOutput;
220484
220500
  const missingRequiredOutput = result2.ok && Boolean(manifest.outputSchema) && !structuredOutput;
220485
220501
  const verifierPartialOrFail = (args.workerId === "source_verifier" || args.workerId === "legal_citation_verifier") && structuredOutput !== null && typeof structuredOutput === "object" && structuredOutput.verificationStatus !== "pass";
220486
220502
  const hasUsableOutput = Boolean(structuredOutput) || Boolean(recoveredStructuredOutput) || (result2.text?.trim().length ?? 0) > 50;
220487
- const workerOk = (result2.ok || Boolean(recoveredStructuredOutput)) && !missingRequiredOutput;
220488
- const usabilityStatus = resolveWorkerUsability({
220503
+ const workerOk = !approvalBlocked && (result2.ok || Boolean(recoveredStructuredOutput)) && !missingRequiredOutput;
220504
+ const usabilityStatus = approvalBlocked ? "needs_user_input" : resolveWorkerUsability({
220489
220505
  workerOk,
220490
220506
  hasUsableOutput,
220491
220507
  verifierPartialOrFail
220492
220508
  });
220493
220509
  const workerWarnings = [];
220510
+ if (approvalBlocked) {
220511
+ workerWarnings.push(
220512
+ "Worker stopped at an approval gate \u2014 the gated action did not run."
220513
+ );
220514
+ }
220494
220515
  if (verifierPartialOrFail && hasUsableOutput) {
220495
220516
  workerWarnings.push(`Verification returned "${structuredOutput?.verificationStatus ?? "unknown"}" \u2014 output usable with caveat.`);
220496
220517
  }
@@ -220502,7 +220523,7 @@ ${contextBlock}
220502
220523
  }
220503
220524
  const recoveredSummary = recoveredStructuredOutput ? summarizeRecoveredWorkerOutput(args.workerId, recoveredStructuredOutput) : null;
220504
220525
  const summary = truncateSummary(
220505
- workerOk ? !result2.ok && recoveredSummary ? recoveredSummary : result2.text || recoveredSummary || (recoveredStructuredOutput ? JSON.stringify(recoveredStructuredOutput) : "") : missingRequiredOutput ? result2.text || "Worker stopped without returning the required structured JSON output." : result2.text || result2.error || "Worker failed before producing output."
220526
+ approvalBlocked ? `Blocked at an approval gate: ${approvalBlocked.toolName ?? "a gated tool"} requires approval in the current permission mode${approvalBlocked.reason ? ` (${approvalBlocked.reason})` : ""}. The action did not run. Approve it interactively, or re-run under /permission take_the_wheel.` : workerOk ? !result2.ok && recoveredSummary ? recoveredSummary : result2.text || recoveredSummary || (recoveredStructuredOutput ? JSON.stringify(recoveredStructuredOutput) : "") : missingRequiredOutput ? result2.text || "Worker stopped without returning the required structured JSON output." : result2.text || result2.error || "Worker failed before producing output."
220506
220527
  );
220507
220528
  const durationMs = Date.now() - startMs;
220508
220529
  let contextPacket = null;
@@ -220539,7 +220560,7 @@ ${contextBlock}
220539
220560
  type: "worker_run_completed",
220540
220561
  workerId: args.workerId,
220541
220562
  workerRunId: runtimeWorkerRunId,
220542
- title: manifest.name,
220563
+ title: eventTitle,
220543
220564
  childThreadId,
220544
220565
  persistedThreadId,
220545
220566
  parentToolCallId: ctx.parentToolCallId ?? null,
@@ -220573,7 +220594,7 @@ ${contextBlock}
220573
220594
  missingRequiredOutput,
220574
220595
  verifierPartialOrFail,
220575
220596
  error: result2.error
220576
- }) : void 0,
220597
+ }) : approvalBlocked ? "worker_approval_required" : void 0,
220577
220598
  workerOffThreadTokens: workerTokenEstimate,
220578
220599
  contextPacket
220579
220600
  }, { supabase: ctx.supabase ?? null });
@@ -220597,7 +220618,7 @@ ${contextBlock}
220597
220618
  missingRequiredOutput,
220598
220619
  verifierPartialOrFail,
220599
220620
  error: result2.error
220600
- }) : void 0,
220621
+ }) : approvalBlocked ? "worker_approval_required" : void 0,
220601
220622
  warnings: workerWarnings.length > 0 ? workerWarnings : void 0
220602
220623
  };
220603
220624
  } catch (error) {
@@ -220608,7 +220629,7 @@ ${contextBlock}
220608
220629
  type: "worker_run_completed",
220609
220630
  workerId: args.workerId,
220610
220631
  workerRunId: runtimeWorkerRunId,
220611
- title: manifest.name,
220632
+ title: eventTitle,
220612
220633
  childThreadId,
220613
220634
  parentToolCallId: ctx.parentToolCallId ?? null,
220614
220635
  ok: false,
@@ -287177,7 +287198,8 @@ __export(perch_cli_exports, {
287177
287198
  flockNicknameAccentColor: () => flockNicknameAccentColor,
287178
287199
  parseInteractiveSlashCommand: () => parseInteractiveSlashCommand,
287179
287200
  parsePerchCli: () => parsePerchCli,
287180
- runPerchCli: () => runPerchCli
287201
+ runPerchCli: () => runPerchCli,
287202
+ workerEventToCliRow: () => workerEventToCliRow
287181
287203
  });
287182
287204
  import fs14 from "node:fs";
287183
287205
  import { createHash as createHash4 } from "node:crypto";
@@ -287210,6 +287232,26 @@ function flockEventToCliRow(event) {
287210
287232
  return null;
287211
287233
  }
287212
287234
  }
287235
+ function workerEventToCliRow(event) {
287236
+ switch (event.type) {
287237
+ case "worker_run_started":
287238
+ return {
287239
+ id: `worker-${event.workerRunId ?? event.workerId}`,
287240
+ text: `${event.title} \xB7 running`,
287241
+ tone: "muted"
287242
+ };
287243
+ case "worker_run_completed": {
287244
+ const status = event.ok ? event.usabilityStatus === "needs_user_input" ? "blocked" : "done" : "failed";
287245
+ return {
287246
+ id: `worker-${event.workerRunId ?? event.workerId}`,
287247
+ text: `${event.title} \xB7 ${FLOCK_STATUS_LABELS[status] ?? status}`,
287248
+ tone: status === "done" ? "success" : status === "failed" || status === "blocked" ? "danger" : "muted"
287249
+ };
287250
+ }
287251
+ default:
287252
+ return null;
287253
+ }
287254
+ }
287213
287255
  async function runPerchCli(argv, writer = defaultWriter(), deps = {}) {
287214
287256
  const parsed = parsePerchCli(argv);
287215
287257
  if (!parsed.ok) {
@@ -287811,6 +287853,7 @@ async function runInkInteractivePerchCli(writer, deps, options) {
287811
287853
  liveTextRef.current = "";
287812
287854
  const toolNamesById = /* @__PURE__ */ new Map();
287813
287855
  const flockWorkerNames = /* @__PURE__ */ new Map();
287856
+ const workerTitles = /* @__PURE__ */ new Map();
287814
287857
  const flockLimitMeta = /* @__PURE__ */ new Map();
287815
287858
  const aggregateToolIds = /* @__PURE__ */ new Map();
287816
287859
  const aggregateToolMeta = /* @__PURE__ */ new Map();
@@ -287876,6 +287919,7 @@ async function runInkInteractivePerchCli(writer, deps, options) {
287876
287919
  setWorkingText("recovering route");
287877
287920
  break;
287878
287921
  case "tool_call_started": {
287922
+ if (event.workerId) break;
287879
287923
  const toolId = event.toolCallId ?? `${event.toolName}-${Date.now()}`;
287880
287924
  const name = humanizeCliToolName(event.toolName);
287881
287925
  const aggregateKind = cliToolAggregateKind(event.toolName);
@@ -287913,6 +287957,7 @@ async function runInkInteractivePerchCli(writer, deps, options) {
287913
287957
  break;
287914
287958
  }
287915
287959
  case "tool_call_completed": {
287960
+ if (event.workerId) break;
287916
287961
  const toolId = event.toolCallId ?? `${event.toolName}-${Date.now()}`;
287917
287962
  const stored = toolInputsById.current.get(toolId);
287918
287963
  const aggregateKind = aggregateToolIds.get(toolId);
@@ -287958,6 +288003,7 @@ async function runInkInteractivePerchCli(writer, deps, options) {
287958
288003
  break;
287959
288004
  }
287960
288005
  case "tool_call_failed": {
288006
+ if (event.workerId) break;
287961
288007
  const toolId = event.toolCallId ?? `${event.toolName}-${Date.now()}`;
287962
288008
  const name = toolNamesById.get(toolId) ?? humanizeCliToolName(event.toolName);
287963
288009
  const stored = toolInputsById.current.get(toolId);
@@ -288162,7 +288208,7 @@ async function runInkInteractivePerchCli(writer, deps, options) {
288162
288208
  meta2.skipped += event.skippedCount ?? 0;
288163
288209
  for (const toolName of event.toolNames ?? []) meta2.tools.add(toolName);
288164
288210
  flockLimitMeta.set(limitKey, meta2);
288165
- const who = event.workerId ? flockWorkerNames.get(event.workerId) ?? "A worker" : "The model";
288211
+ const who = event.workerId ? workerTitles.get(event.workerId) ?? flockWorkerNames.get(event.workerId) ?? "A worker" : "The model";
288166
288212
  updateToolItem(limitKey, {
288167
288213
  label: "flock",
288168
288214
  text: `${who} hit its tool limit and is finishing from gathered evidence.`,
@@ -288178,6 +288224,25 @@ async function runInkInteractivePerchCli(writer, deps, options) {
288178
288224
  });
288179
288225
  break;
288180
288226
  }
288227
+ case "worker_run_started":
288228
+ case "worker_run_completed": {
288229
+ if (event.type === "worker_run_started") {
288230
+ workerTitles.set(event.workerId, event.title);
288231
+ }
288232
+ const row = workerEventToCliRow(event);
288233
+ if (!row) break;
288234
+ if (row.id) {
288235
+ updateToolItem(row.id, { label: "worker", text: row.text, tone: row.tone });
288236
+ } else {
288237
+ addItem({ label: "worker", text: row.text, tone: row.tone });
288238
+ }
288239
+ if (event.type === "worker_run_started") {
288240
+ setWorkingText(event.title.split(" \xB7 ")[0] ?? event.title);
288241
+ } else {
288242
+ setWorkingText("thinking");
288243
+ }
288244
+ break;
288245
+ }
288181
288246
  case "flock_run_started":
288182
288247
  case "flock_plan_ready":
288183
288248
  case "flock_worker_update":
@@ -288352,7 +288417,7 @@ async function runInkInteractivePerchCli(writer, deps, options) {
288352
288417
  const renderFlockRow = (key, line, tone, showLabel) => {
288353
288418
  const dotParts = line.split(" \xB7 ");
288354
288419
  const limitMatch = dotParts.length < 2 ? line.match(/^(.+?) (hit its tool limit .*)$/) : null;
288355
- if (dotParts.length < 2 && !limitMatch) {
288420
+ if (dotParts.length === 2 && WORKER_STATUS_WORDS.has(dotParts[1] ?? "") || dotParts.length < 2 && !limitMatch) {
288356
288421
  return renderTranscriptRow(key, "flock", line, tone, showLabel);
288357
288422
  }
288358
288423
  if (limitMatch) {
@@ -288399,7 +288464,7 @@ async function runInkInteractivePerchCli(writer, deps, options) {
288399
288464
  React11.createElement(Ink2.Box, { width: INK_LABEL_WIDTH, flexShrink: 0 }),
288400
288465
  React11.createElement(Ink2.Text, { color: CLI_BRAND.divider }, INK_DIVIDER)
288401
288466
  ) : null,
288402
- lines.map((line, lineIndex) => item.label === "flock" ? renderFlockRow(`${item.id}-${lineIndex}`, line, item.tone, lineIndex === 0) : renderTranscriptRow(
288467
+ lines.map((line, lineIndex) => item.label === "flock" || item.label === "worker" ? renderFlockRow(`${item.id}-${lineIndex}`, line, item.tone, lineIndex === 0) : renderTranscriptRow(
288403
288468
  `${item.id}-${lineIndex}`,
288404
288469
  item.label,
288405
288470
  line,
@@ -289902,7 +289967,7 @@ function defaultWriter() {
289902
289967
  stderr: (text) => process.stderr.write(text)
289903
289968
  };
289904
289969
  }
289905
- var execFileAsync3, DEFAULT_CLI_LOGIN_APP_URL, CLI_PACKAGE_VERSION, CLI_BRAND, ANSI2, HELP_TEXT, INTERACTIVE_HELP_TEXT, FLOCK_STATUS_LABELS, INK_LABEL_WIDTH, INK_ROW_LIMIT, INK_DETAIL_LINE_LIMIT, INK_DIVIDER, PERCH_MOTION_FRAMES, PERCH_SPLASH_WIDTH, PERCH_SPLASH_SCENE, PERCH_SPLASH_COMMANDS, FLOCK_NICKNAME_ACCENTS;
289970
+ var execFileAsync3, DEFAULT_CLI_LOGIN_APP_URL, CLI_PACKAGE_VERSION, CLI_BRAND, ANSI2, HELP_TEXT, INTERACTIVE_HELP_TEXT, FLOCK_STATUS_LABELS, WORKER_STATUS_WORDS, INK_LABEL_WIDTH, INK_ROW_LIMIT, INK_DETAIL_LINE_LIMIT, INK_DIVIDER, PERCH_MOTION_FRAMES, PERCH_SPLASH_WIDTH, PERCH_SPLASH_SCENE, PERCH_SPLASH_COMMANDS, FLOCK_NICKNAME_ACCENTS;
289906
289971
  var init_perch_cli = __esm({
289907
289972
  "scripts/perch-cli.ts"() {
289908
289973
  "use strict";
@@ -289997,8 +290062,10 @@ Commands:
289997
290062
  done: "done",
289998
290063
  failed: "failed",
289999
290064
  cancelled: "stopped",
290000
- skipped: "skipped"
290065
+ skipped: "skipped",
290066
+ blocked: "blocked"
290001
290067
  };
290068
+ WORKER_STATUS_WORDS = new Set(Object.values(FLOCK_STATUS_LABELS));
290002
290069
  INK_LABEL_WIDTH = 12;
290003
290070
  INK_ROW_LIMIT = 100;
290004
290071
  INK_DETAIL_LINE_LIMIT = 80;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perchai-cli",
3
- "version": "2.4.36",
3
+ "version": "2.4.38",
4
4
  "description": "Perch AI command-line interface",
5
5
  "bin": {
6
6
  "perch": "bin/perch"