perchai-cli 2.4.50 → 2.4.51

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 +138 -26
  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();
@@ -91149,7 +91150,6 @@ Final answers lead with findings, name artifacts or delivery status, and give on
91149
91150
  var MARKET_DESK_TOOL_NAMES;
91150
91151
  var init_marketDeskAccess = __esm({
91151
91152
  "features/perchTerminal/runtime/marketDesk/marketDeskAccess.ts"() {
91152
- "use strict";
91153
91153
  init_toolNames();
91154
91154
  MARKET_DESK_TOOL_NAMES = /* @__PURE__ */ new Set([
91155
91155
  TOOL_NAMES.getMarketSignal,
@@ -139518,7 +139518,6 @@ function offSandboxEvent(runId, handler) {
139518
139518
  var LOCAL_WORKSPACE_ACCESS_UNAVAILABLE;
139519
139519
  var init_bridge = __esm({
139520
139520
  "features/perchTerminal/desktop/bridge.ts"() {
139521
- "use strict";
139522
139521
  LOCAL_WORKSPACE_ACCESS_UNAVAILABLE = "Local workspace access is unavailable. Open Perch Desktop or update the app.";
139523
139522
  }
139524
139523
  });
@@ -197736,6 +197735,7 @@ function recordDispatchBatchResult(tasks, result2, ctx, opts = {}) {
197736
197735
  var lifecycleByRun, MAX_LIFECYCLE_RUNS;
197737
197736
  var init_workerLifecycle = __esm({
197738
197737
  "features/perchTerminal/runtime/workers/workerLifecycle.ts"() {
197738
+ "use strict";
197739
197739
  init_workerManifest();
197740
197740
  lifecycleByRun = /* @__PURE__ */ new Map();
197741
197741
  MAX_LIFECYCLE_RUNS = 200;
@@ -223760,7 +223760,13 @@ async function runFlockTurn(input, deps, options = {}) {
223760
223760
  turnInput: input
223761
223761
  };
223762
223762
  const modelCall = options.plannerModelCall ?? defaultFlockPlannerModelCall(llmCtx);
223763
- plan = await runLlmFlockPlanner(llmCtx, modelCall).catch(() => null);
223763
+ const plannerTimeoutMs = options.plannerTimeoutMs ?? FLOCK_PLANNER_TIMEOUT_MS;
223764
+ const first2 = await runLlmPlannerWithTimeout(llmCtx, modelCall, plannerTimeoutMs);
223765
+ plan = first2.plan;
223766
+ if (!plan && !first2.timedOut) {
223767
+ const retry = await runLlmPlannerWithTimeout(llmCtx, modelCall, plannerTimeoutMs);
223768
+ plan = retry.plan;
223769
+ }
223764
223770
  }
223765
223771
  if (!plan) {
223766
223772
  plan = (options.planFn ?? planFlock)(task, {
@@ -223991,14 +223997,25 @@ ${FLOCK_GROUNDING_CONTRACT}` : worker.objective;
223991
223997
  const workersFailed = outcomes.filter((outcome) => outcome.status === "failed").length + workersBlocked;
223992
223998
  const flockStatus = userCancelled || wallTimeHit && workersDone === 0 ? "cancelled" : workersFailed === 0 && workersDone === plan.workers.length ? "completed" : workersDone > 0 ? "partial" : "failed";
223993
223999
  const permissionHint = workersBlocked > 0 && (turnInput.permissionMode ?? "default") === "default" ? "\n\nTip: flock workers inherit default permissions \u2014 run `/permission take_the_wheel` before `/flock` so bash redirects and file writes do not stall on approval gates." : "";
224000
+ const deterministicSummary = buildFlockSummary(
224001
+ plan,
224002
+ [...outcomes, ...revisionOutcomes],
224003
+ flockStatus,
224004
+ toolCallsUsed,
224005
+ wallTimeHit
224006
+ );
224007
+ const synthesisModelCall = options.synthesisModelCall !== void 0 ? options.synthesisModelCall : options.plannerModelCall === void 0 ? defaultFlockSynthesisModelCall(input, plan.flockId) : null;
224008
+ let summaryBody = deterministicSummary;
224009
+ if (synthesisModelCall && !userCancelled && workersDone > 0) {
224010
+ const synthesized = await runFlockSynthesisWithTimeout(
224011
+ synthesisModelCall,
224012
+ buildFlockSynthesisPrompt(task, [...outcomes, ...revisionOutcomes], revisionReport),
224013
+ FLOCK_SYNTHESIS_TIMEOUT_MS
224014
+ );
224015
+ if (synthesized) summaryBody = synthesized;
224016
+ }
223994
224017
  const assistantText = [
223995
- buildFlockSummary(
223996
- plan,
223997
- [...outcomes, ...revisionOutcomes],
223998
- flockStatus,
223999
- toolCallsUsed,
224000
- wallTimeHit
224001
- ),
224018
+ summaryBody,
224002
224019
  ...buildVerificationSection(revisionReport),
224003
224020
  ...modelOverrides.applied.map(
224004
224021
  (override) => `Model override: ${override.displayName} ran on ${override.label}.`
@@ -224040,6 +224057,89 @@ ${FLOCK_GROUNDING_CONTRACT}` : worker.objective;
224040
224057
  }
224041
224058
  }
224042
224059
  }
224060
+ async function runLlmPlannerWithTimeout(ctx, modelCall, timeoutMs) {
224061
+ let timer;
224062
+ const timeout = new Promise((resolve5) => {
224063
+ timer = setTimeout(() => resolve5("timeout"), Math.max(1, timeoutMs));
224064
+ });
224065
+ try {
224066
+ const race = await Promise.race([
224067
+ runLlmFlockPlanner(ctx, modelCall).then((plan) => ({ plan })).catch(() => ({ plan: null })),
224068
+ timeout
224069
+ ]);
224070
+ if (race === "timeout") return { plan: null, timedOut: true };
224071
+ return { plan: race.plan, timedOut: false };
224072
+ } finally {
224073
+ if (timer) clearTimeout(timer);
224074
+ }
224075
+ }
224076
+ function defaultFlockSynthesisModelCall(input, flockId) {
224077
+ return async ({ systemPrompt, userPrompt }) => {
224078
+ const result2 = await runModelToolLoop({
224079
+ lane: "chat",
224080
+ founderModelSelection: input.founderModelSelection ?? null,
224081
+ systemPrompt,
224082
+ messages: [{ role: "user", content: userPrompt }],
224083
+ tools: [],
224084
+ workspaceRoot: input.activeRootPath ?? "",
224085
+ desktopConnected: input.desktopConnected,
224086
+ cliLocalTools: input.cliLocalTools === true,
224087
+ activeRootPath: input.activeRootPath,
224088
+ permissionMode: input.permissionMode,
224089
+ workspaceId: input.workspaceId,
224090
+ threadId: input.threadId,
224091
+ supabaseConfigured: input.supabaseConfigured,
224092
+ supabase: input.supabase ?? null,
224093
+ cliServerAppUrl: input.cliServerAppUrl ?? null,
224094
+ cliServerAccessToken: input.cliServerAccessToken ?? null,
224095
+ runId: flockId,
224096
+ chatMode: "ask",
224097
+ maxIterations: 1,
224098
+ onEvent: () => {
224099
+ }
224100
+ });
224101
+ return { ok: result2.ok, text: result2.text };
224102
+ };
224103
+ }
224104
+ function buildFlockSynthesisPrompt(task, outcomes, revisionReport) {
224105
+ const results = outcomes.filter((outcome) => outcome.status === "done" && outcome.result).map((outcome) => {
224106
+ const raw = outcome.result?.structuredOutput ?? outcome.result?.summary ?? "";
224107
+ const serialized = typeof raw === "string" ? raw : JSON.stringify(raw);
224108
+ return {
224109
+ role: outcome.worker.displayName,
224110
+ output: serialized.slice(0, FLOCK_SYNTHESIS_MAX_OUTPUT_PER_WORKER)
224111
+ };
224112
+ });
224113
+ const verification = revisionReport ? {
224114
+ verdict: revisionReport.recheckFindings?.verdict ?? revisionReport.findings.verdict ?? null,
224115
+ issues: (revisionReport.recheckFindings?.issues ?? revisionReport.findings.issues).slice(0, 6),
224116
+ revised: revisionReport.revised
224117
+ } : null;
224118
+ const systemPrompt = [
224119
+ "You are the coordinator that just ran a Flock: a team of sub-agents that worked in parallel on the user's task.",
224120
+ "The sub-agents have finished. Write the final answer to the user in your own voice \u2014 clear, direct, friendly, first person.",
224121
+ "Lead with what the user asked for. Present the actual results and fold in any important verification caveats.",
224122
+ "Do not mention sub-agents, workers, roles, nicknames, IDs, or orchestration mechanics unless directly useful to the user.",
224123
+ "Synthesize only from the provided results \u2014 do not invent facts and do not say you will do further work."
224124
+ ].join("\n");
224125
+ const userPrompt = JSON.stringify({ task, results, verification });
224126
+ return { systemPrompt, userPrompt };
224127
+ }
224128
+ async function runFlockSynthesisWithTimeout(call, prompt, timeoutMs) {
224129
+ let timer;
224130
+ const timeout = new Promise((resolve5) => {
224131
+ timer = setTimeout(() => resolve5("timeout"), Math.max(1, timeoutMs));
224132
+ });
224133
+ try {
224134
+ const race = await Promise.race([
224135
+ call(prompt).then((result2) => result2.ok && result2.text.trim() ? result2.text.trim() : null).catch(() => null),
224136
+ timeout
224137
+ ]);
224138
+ return race === "timeout" ? null : race;
224139
+ } finally {
224140
+ if (timer) clearTimeout(timer);
224141
+ }
224142
+ }
224043
224143
  function emitPlanEvent(emit, plan) {
224044
224144
  emit({
224045
224145
  type: "flock_plan_ready",
@@ -224272,7 +224372,7 @@ function flockWorkerStatusFromSpawnResult(result2) {
224272
224372
  function now11() {
224273
224373
  return (/* @__PURE__ */ new Date()).toISOString();
224274
224374
  }
224275
- var FLOCK_GROUNDING_CONTRACT;
224375
+ var FLOCK_PLANNER_TIMEOUT_MS, FLOCK_SYNTHESIS_TIMEOUT_MS, FLOCK_SYNTHESIS_MAX_OUTPUT_PER_WORKER, FLOCK_GROUNDING_CONTRACT;
224276
224376
  var init_runFlockTurn = __esm({
224277
224377
  "features/perchTerminal/runtime/flock/runFlockTurn.ts"() {
224278
224378
  "use strict";
@@ -224286,6 +224386,10 @@ var init_runFlockTurn = __esm({
224286
224386
  init_flockLlmPlanner();
224287
224387
  init_flockPlanner();
224288
224388
  init_flockRoles();
224389
+ init_toolLoop();
224390
+ FLOCK_PLANNER_TIMEOUT_MS = 35e3;
224391
+ FLOCK_SYNTHESIS_TIMEOUT_MS = 3e4;
224392
+ FLOCK_SYNTHESIS_MAX_OUTPUT_PER_WORKER = 2e3;
224289
224393
  FLOCK_GROUNDING_CONTRACT = [
224290
224394
  "Grounding contract: your context includes sources[] gathered by research.",
224291
224395
  "Every nontrivial factual claim in the draft must carry an inline [n] marker mapping to one of those sources, with a numbered source list at the end.",
@@ -232698,15 +232802,13 @@ function getBackgroundTaskStore() {
232698
232802
  var STORE_FILE, BackgroundTaskStore, singleton;
232699
232803
  var init_backgroundTaskRegistry = __esm({
232700
232804
  "features/perchTerminal/runtime/background/backgroundTaskRegistry.ts"() {
232805
+ "use strict";
232701
232806
  init_backgroundTaskTypes();
232702
232807
  STORE_FILE = "background-tasks.json";
232703
232808
  BackgroundTaskStore = class {
232704
- baseDir;
232705
- storePath;
232706
- outputDir;
232707
- records = /* @__PURE__ */ new Map();
232708
- loaded = false;
232709
232809
  constructor(options = {}) {
232810
+ this.records = /* @__PURE__ */ new Map();
232811
+ this.loaded = false;
232710
232812
  this.baseDir = options.baseDir ?? path11.join(os.homedir(), ".perch");
232711
232813
  this.storePath = path11.join(this.baseDir, STORE_FILE);
232712
232814
  this.outputDir = path11.join(this.baseDir, "background-output");
@@ -289977,9 +290079,17 @@ function flockEventToCliRow(event) {
289977
290079
  switch (event.type) {
289978
290080
  case "flock_run_started":
289979
290081
  return { id: null, text: "gathering the flock\u2026", tone: "accent" };
289980
- case "flock_plan_ready":
290082
+ case "flock_plan_ready": {
289981
290083
  if (!event.accepted) return null;
289982
- return { id: null, text: `flock plan \xB7 ${event.workers.length} workers`, tone: "muted" };
290084
+ const names = event.workers.map((worker) => worker.displayName);
290085
+ const shown = names.slice(0, 4).join(", ");
290086
+ const extra = names.length > 4 ? ` +${names.length - 4} more` : "";
290087
+ return {
290088
+ id: null,
290089
+ text: `flock plan \xB7 ${event.workers.length} agents \xB7 ${shown}${extra}`,
290090
+ tone: "accent"
290091
+ };
290092
+ }
289983
290093
  case "flock_worker_update":
289984
290094
  return {
289985
290095
  id: `flock-${event.flockId}-${event.flockWorkerId}`,
@@ -290475,18 +290585,18 @@ async function runReadlineInteractivePerchCli(writer, deps, options) {
290475
290585
  trimRecentMessages(state.recentMessages);
290476
290586
  recordCliUsageRunId(state, result2.runId);
290477
290587
  state.contextSnapshot = result2.contextSnapshot ?? state.contextSnapshot;
290478
- const admitted = await admitCliLearningMemory(connection, {
290588
+ void admitCliLearningMemory(connection, {
290479
290589
  prompt: turnPrompt,
290480
290590
  assistantText: result2.assistantText,
290481
290591
  status: result2.status,
290482
290592
  personaId: state.personaId,
290483
290593
  threadId: result2.threadId,
290484
290594
  runId: result2.runId
290595
+ }).then((admitted) => {
290596
+ if (admitted > 0) writeModeLine(writer, "memory", `saved ${admitted}`);
290597
+ }).catch(() => {
290485
290598
  });
290486
290599
  await persistInteractiveCliState(state);
290487
- if (admitted > 0) {
290488
- writeModeLine(writer, "memory", `saved ${admitted}`);
290489
- }
290490
290600
  };
290491
290601
  try {
290492
290602
  while (true) {
@@ -291188,17 +291298,19 @@ async function runInkInteractivePerchCli(writer, deps, options) {
291188
291298
  trimRecentMessages(state.recentMessages);
291189
291299
  recordCliUsageRunId(state, result2.runId);
291190
291300
  state.contextSnapshot = result2.contextSnapshot ?? state.contextSnapshot;
291191
- const admitted = await admitCliLearningMemory(connection, {
291301
+ void admitCliLearningMemory(connection, {
291192
291302
  prompt,
291193
291303
  assistantText,
291194
291304
  status: result2.status,
291195
291305
  personaId: state.personaId,
291196
291306
  threadId: result2.threadId,
291197
291307
  runId: result2.runId
291308
+ }).then((admitted) => {
291309
+ if (admitted > 0) {
291310
+ addItem({ label: "memory", text: `saved ${admitted}`, tone: "muted" });
291311
+ }
291312
+ }).catch(() => {
291198
291313
  });
291199
- if (admitted > 0) {
291200
- addItem({ label: "memory", text: `saved ${admitted}`, tone: "muted" });
291201
- }
291202
291314
  await persistInteractiveCliState(state);
291203
291315
  } catch (error) {
291204
291316
  addItem({ label: "stop", text: humanizeCliError(errorMessage(error)), tone: "danger" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perchai-cli",
3
- "version": "2.4.50",
3
+ "version": "2.4.51",
4
4
  "description": "Perch AI command-line interface",
5
5
  "bin": {
6
6
  "perch": "bin/perch"