perchai-cli 2.4.12 → 2.4.13

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 +150 -70
  2. package/package.json +1 -1
package/dist/perch.mjs CHANGED
@@ -75566,7 +75566,6 @@ 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";
75570
75569
  init_generateAPAuditPacket();
75571
75570
  init_inventoryFolder();
75572
75571
  init_loadBusinessTables();
@@ -80628,13 +80627,13 @@ function buildDesktopContextSection(input) {
80628
80627
  label: "CLI workspace",
80629
80628
  content: [
80630
80629
  "## CLI local workspace",
80631
- "Perch is running from a terminal. Local filesystem, shell, sandbox-code, and AP evidence tools are available through the CLI local bridge.",
80630
+ "Perch is running from a terminal. Local filesystem, shell, sandbox-code, and AP evidence tools are available through the CLI local runtime.",
80632
80631
  input.activeRootPath?.trim() ? `Workspace root: ${input.activeRootPath}. Treat relative paths as relative to this root.` : "Workspace root: current terminal directory.",
80633
- "Desktop-only services are not connected here: embedded browser, Google/Gmail/Calendar delivery, Desktop RAG indexing, MCP Desktop servers, and project memory writes are unavailable unless the Desktop app is running.",
80632
+ "GUI-only services are not connected here: embedded browser, Google/Gmail/Calendar delivery, Desktop RAG indexing, MCP Desktop servers, and project memory writes are unavailable unless the desktop app is running.",
80634
80633
  localReadTools.length > 0 ? `Read tools: ${localReadTools.join(", ")}.` : "Read tools: none exposed for this turn.",
80635
80634
  localWriteTools.length > 0 ? `Write/command tools: ${localWriteTools.join(", ")}. These are governed by the selected permission mode and command policy.` : "Write/command tools: none exposed for this turn."
80636
80635
  ].join("\n"),
80637
- reason: "Terminal local bridge state and CLI tool availability for this turn.",
80636
+ reason: "Terminal local runtime state and CLI tool availability for this turn.",
80638
80637
  sourcePath: input.activeRootPath,
80639
80638
  metadata: {
80640
80639
  desktopConnected: false,
@@ -80649,7 +80648,7 @@ function buildDesktopContextSection(input) {
80649
80648
  lane: "desktop",
80650
80649
  label: "Desktop workspace",
80651
80650
  content: "",
80652
- reason: "Desktop bridge is unavailable in browser mode.",
80651
+ reason: "Local workspace runtime is unavailable in browser mode.",
80653
80652
  sourcePath: input.activeRootPath,
80654
80653
  skipped: true,
80655
80654
  metadata: { desktopConnected: false }
@@ -80736,7 +80735,7 @@ function buildDesktopContextSection(input) {
80736
80735
  label: "Desktop workspace",
80737
80736
  content: [
80738
80737
  "## Desktop environment",
80739
- "Desktop bridge is connected. Local filesystem tools are available to the operator runtime.",
80738
+ "Desktop local runtime is connected. Local filesystem tools are available to the operator runtime.",
80740
80739
  input.activeRootPath?.trim() ? `Optional folder scope: ${input.activeRootPath} (you are NOT limited to it \u2014 absolute paths anywhere in the user's home work directly).` : "No folder scope is selected, and none is needed. When the user gives a file path, USE IT DIRECTLY \u2014 call readLocalFile / glob / grep / visionInspect with the absolute path (e.g. /Users/you/Desktop/shot.png). Full-access policy already allows any path in the user's home. NEVER tell the user to select or approve a folder, and NEVER refuse a file because no folder is selected.",
80741
80740
  `Visible local sources: ${totalVisibleFiles}`,
80742
80741
  input.localSourcesMeta?.refreshedAt ? `Local source snapshot refreshed: ${input.localSourcesMeta.refreshedAt}` : null,
@@ -80754,7 +80753,7 @@ function buildDesktopContextSection(input) {
80754
80753
  availableReadTools.length > 0 ? `Read tools: ${availableReadTools.join(", ")}.` : "Read tools: none exposed for this turn.",
80755
80754
  availableWriteTools.length > 0 ? `Write/command tools: ${availableWriteTools.join(", ")}. These are governed by the selected permission mode and command policy.` : "Write/command tools: none exposed for this turn."
80756
80755
  ].filter(Boolean).join("\n"),
80757
- reason: "Desktop bridge state, optional folder scope, and tool availability for this turn.",
80756
+ reason: "Local runtime state, optional folder scope, and tool availability for this turn.",
80758
80757
  sourcePath: input.activeRootPath,
80759
80758
  metadata: {
80760
80759
  desktopConnected: true,
@@ -82928,7 +82927,6 @@ function truncateHistoryLine(value, max2) {
82928
82927
  }
82929
82928
  var init_operatorTruth = __esm({
82930
82929
  "features/perchTerminal/runtime/operatorTruth.ts"() {
82931
- "use strict";
82932
82930
  }
82933
82931
  });
82934
82932
 
@@ -91830,7 +91828,7 @@ function assembleContext(input) {
91830
91828
  );
91831
91829
  if (!input.desktopConnected && input.cliLocalTools !== true) {
91832
91830
  warnings.push(
91833
- "Desktop bridge is not connected. Local workspace tools are unavailable in browser mode."
91831
+ "Local workspace runtime is not connected. Local workspace tools are unavailable in browser mode."
91834
91832
  );
91835
91833
  }
91836
91834
  return {
@@ -133572,7 +133570,7 @@ function validateToolCallPolicy(input) {
133572
133570
  return {
133573
133571
  ok: false,
133574
133572
  code: "tool_desktop_required",
133575
- message: "Local and browser automation tools require Perch AI Desktop. Filesystem access is validated per path by the current permission mode.",
133573
+ message: "This tool requires a local runtime. Filesystem access is validated per path by the current permission mode.",
133576
133574
  riskLevel: classified.riskLevel,
133577
133575
  desktopRequired: true
133578
133576
  };
@@ -136033,7 +136031,7 @@ function getNativeToolDefinitions() {
136033
136031
  type: "function",
136034
136032
  function: {
136035
136033
  name: TOOL_NAMES.configInspect,
136036
- description: "Inspect the Perch Terminal runtime configuration: execution host, Desktop bridge status, optional folder scope, permission mode, and capabilities.",
136034
+ description: "Inspect the Perch Terminal runtime configuration: execution host, local runtime status, optional folder scope, permission mode, and capabilities.",
136037
136035
  parameters: {
136038
136036
  type: "object",
136039
136037
  properties: {},
@@ -136936,7 +136934,7 @@ function notifyApprovedRootsChanged() {
136936
136934
  }
136937
136935
  async function pickFolder(request) {
136938
136936
  const bridge = getDesktopBridge();
136939
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
136937
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
136940
136938
  const result2 = await bridge.pickFolder({
136941
136939
  permissionMode: currentPermissionMode(),
136942
136940
  defaultPath: request?.defaultPath ?? null,
@@ -136949,7 +136947,7 @@ async function pickFolder(request) {
136949
136947
  }
136950
136948
  async function approveFolderAccess(path14, options) {
136951
136949
  const bridge = getDesktopBridge();
136952
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
136950
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
136953
136951
  const result2 = await bridge.approveFolderAccess({
136954
136952
  path: path14,
136955
136953
  permissionMode: currentPermissionMode(),
@@ -136963,7 +136961,7 @@ async function checkFsAccess(request) {
136963
136961
  if (!bridge) {
136964
136962
  return {
136965
136963
  decision: "block",
136966
- reason: "Desktop bridge not available",
136964
+ reason: "Local runtime not available",
136967
136965
  absolutePath: request.absolutePath,
136968
136966
  approvalPath: request.absolutePath
136969
136967
  };
@@ -136982,7 +136980,7 @@ async function checkFullDiskAccess() {
136982
136980
  }
136983
136981
  async function resolveDefaultSavePath(request) {
136984
136982
  const bridge = getDesktopBridge();
136985
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
136983
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
136986
136984
  return bridge.resolveDefaultSavePath(request);
136987
136985
  }
136988
136986
  async function getApprovedRoots() {
@@ -136992,17 +136990,17 @@ async function getApprovedRoots() {
136992
136990
  }
136993
136991
  async function removeRoot(rootId) {
136994
136992
  const bridge = getDesktopBridge();
136995
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
136993
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
136996
136994
  return bridge.removeRoot(rootId);
136997
136995
  }
136998
136996
  async function listFiles(rootId, relativePath, depth) {
136999
136997
  const bridge = getDesktopBridge();
137000
- if (!bridge) return { ok: false, rootId, basePath: "", entries: [], truncated: false, error: "Desktop bridge not available" };
136998
+ if (!bridge) return { ok: false, rootId, basePath: "", entries: [], truncated: false, error: "Local runtime not available" };
137001
136999
  return bridge.listFiles(withPermissionMode({ rootId, relativePath, depth }));
137002
137000
  }
137003
137001
  async function readFile2(rootId, relativePath) {
137004
137002
  const bridge = getDesktopBridge();
137005
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137003
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137006
137004
  return bridge.readFile(withPermissionMode({ rootId, relativePath }));
137007
137005
  }
137008
137006
  function normalizeLegacyListResult(result2, request) {
@@ -137038,7 +137036,7 @@ async function listLocalSourcesDetailed(request) {
137038
137036
  query: typeof request === "object" && request ? request.query ?? null : null,
137039
137037
  refreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
137040
137038
  maxSources: 0,
137041
- warning: "Desktop bridge not available"
137039
+ warning: "Local runtime not available"
137042
137040
  };
137043
137041
  }
137044
137042
  const raw = await bridge.listLocalSources(request);
@@ -137046,7 +137044,7 @@ async function listLocalSourcesDetailed(request) {
137046
137044
  }
137047
137045
  async function readLocalSourceFile(localSourceId) {
137048
137046
  const bridge = getDesktopBridge();
137049
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137047
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137050
137048
  return bridge.readLocalFile(localSourceId, { permissionMode: currentPermissionMode() });
137051
137049
  }
137052
137050
  async function getProjectRules(rootId) {
@@ -137056,37 +137054,37 @@ async function getProjectRules(rootId) {
137056
137054
  }
137057
137055
  async function readProjectMemory(rootId) {
137058
137056
  const bridge = getDesktopBridge();
137059
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137057
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137060
137058
  return bridge.readProjectMemory({ rootId });
137061
137059
  }
137062
137060
  async function writeProjectMemory(rootId, meta) {
137063
137061
  const bridge = getDesktopBridge();
137064
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137062
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137065
137063
  return bridge.writeProjectMemory({ rootId, meta });
137066
137064
  }
137067
137065
  async function writeMemoryFile(rootId, request) {
137068
137066
  const bridge = getDesktopBridge();
137069
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137067
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137070
137068
  return bridge.writeMemoryFile({ rootId, ...request });
137071
137069
  }
137072
137070
  async function writeRule(rootId, request) {
137073
137071
  const bridge = getDesktopBridge();
137074
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137072
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137075
137073
  return bridge.writeRule({ rootId, ...request });
137076
137074
  }
137077
137075
  async function writePerchMd(rootId, request) {
137078
137076
  const bridge = getDesktopBridge();
137079
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137077
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137080
137078
  return bridge.writePerchMd({ rootId, ...request });
137081
137079
  }
137082
137080
  async function readGlobalPerchMd() {
137083
137081
  const bridge = getDesktopBridge();
137084
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137082
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137085
137083
  return bridge.readGlobalPerchMd();
137086
137084
  }
137087
137085
  async function writeGlobalPerchMd(content) {
137088
137086
  const bridge = getDesktopBridge();
137089
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137087
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137090
137088
  return bridge.writeGlobalPerchMd({ content });
137091
137089
  }
137092
137090
  async function getMcpStatus() {
@@ -137101,12 +137099,12 @@ async function listMcpTools() {
137101
137099
  }
137102
137100
  async function callMcpTool(request) {
137103
137101
  const bridge = getDesktopBridge();
137104
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137102
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137105
137103
  return bridge.callMcpTool(request);
137106
137104
  }
137107
137105
  async function callEmbeddedBrowserTool(request) {
137108
137106
  const bridge = getDesktopBridge();
137109
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137107
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137110
137108
  if (typeof bridge.callEmbeddedBrowserTool !== "function") {
137111
137109
  return { ok: false, error: "Embedded browser bridge not available in this desktop build" };
137112
137110
  }
@@ -137114,7 +137112,7 @@ async function callEmbeddedBrowserTool(request) {
137114
137112
  }
137115
137113
  async function setEmbeddedBrowserVisible(open) {
137116
137114
  const bridge = getDesktopBridge();
137117
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137115
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137118
137116
  if (typeof bridge.setEmbeddedBrowserVisible !== "function") {
137119
137117
  return { ok: false, error: "Embedded browser bridge not available in this desktop build" };
137120
137118
  }
@@ -137137,7 +137135,7 @@ function onEmbeddedBrowserState(handler) {
137137
137135
  }
137138
137136
  async function reconnectMcp() {
137139
137137
  const bridge = getDesktopBridge();
137140
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137138
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137141
137139
  return bridge.reconnectMcp();
137142
137140
  }
137143
137141
  async function getOrStartBashTerminal(request) {
@@ -137182,37 +137180,37 @@ async function runLocalBash(request) {
137182
137180
  }
137183
137181
  async function readWorkspaceFile(request) {
137184
137182
  const bridge = getDesktopBridge();
137185
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137183
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137186
137184
  return bridge.readWorkspaceFile(withPermissionMode(request, request));
137187
137185
  }
137188
137186
  async function writeWorkspaceFile(request) {
137189
137187
  const bridge = getDesktopBridge();
137190
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137188
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137191
137189
  return bridge.writeWorkspaceFile(withPermissionMode(request, request));
137192
137190
  }
137193
137191
  async function moveLocalFile(request) {
137194
137192
  const bridge = getDesktopBridge();
137195
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137193
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137196
137194
  return bridge.moveLocalFile(withPermissionMode(request, request));
137197
137195
  }
137198
137196
  async function copyLocalFile(request) {
137199
137197
  const bridge = getDesktopBridge();
137200
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137198
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137201
137199
  return bridge.copyLocalFile(withPermissionMode(request, request));
137202
137200
  }
137203
137201
  async function createDirectory(request) {
137204
137202
  const bridge = getDesktopBridge();
137205
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137203
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137206
137204
  return bridge.createDirectory(withPermissionMode(request, request));
137207
137205
  }
137208
137206
  async function deleteLocalFile(request) {
137209
137207
  const bridge = getDesktopBridge();
137210
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137208
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137211
137209
  return bridge.deleteLocalFile(withPermissionMode(request, request));
137212
137210
  }
137213
137211
  async function printFile(request) {
137214
137212
  const bridge = getDesktopBridge();
137215
- if (!bridge) return { ok: false, error: "Desktop bridge not available" };
137213
+ if (!bridge) return { ok: false, error: "Local runtime not available" };
137216
137214
  return bridge.printFile(withPermissionMode(request, request));
137217
137215
  }
137218
137216
  async function listWorkspaceFilesGlob(request) {
@@ -137271,7 +137269,7 @@ async function runLocalAPAuditPacket(request) {
137271
137269
  if (!bridge) {
137272
137270
  return {
137273
137271
  ok: false,
137274
- error: "Desktop bridge not available",
137272
+ error: "Local runtime not available",
137275
137273
  errorCode: "desktop_bridge_unavailable",
137276
137274
  executionHost: "electron_desktop",
137277
137275
  durationMs: 0
@@ -137284,7 +137282,7 @@ async function runLocalPrepareAPEvidence(request) {
137284
137282
  if (!bridge?.runLocalPrepareAPEvidence) {
137285
137283
  return {
137286
137284
  ok: false,
137287
- error: "Desktop bridge AP evidence tool not available",
137285
+ error: "AP evidence tool not available in local runtime",
137288
137286
  errorCode: bridge ? "missing_dependency" : "desktop_bridge_unavailable",
137289
137287
  executionHost: "electron_desktop",
137290
137288
  durationMs: 0
@@ -137297,7 +137295,7 @@ async function runLocalQueryAPCases(request) {
137297
137295
  if (!bridge?.runLocalQueryAPCases) {
137298
137296
  return {
137299
137297
  ok: false,
137300
- error: "Desktop bridge AP case query tool not available",
137298
+ error: "AP case query tool not available in local runtime",
137301
137299
  errorCode: bridge ? "missing_dependency" : "desktop_bridge_unavailable",
137302
137300
  executionHost: "electron_desktop",
137303
137301
  durationMs: 0
@@ -137310,7 +137308,7 @@ async function runLocalRenderAPControlGraph(request) {
137310
137308
  if (!bridge?.runLocalRenderAPControlGraph) {
137311
137309
  return {
137312
137310
  ok: false,
137313
- error: "Desktop bridge AP control graph tool not available",
137311
+ error: "AP control graph tool not available in local runtime",
137314
137312
  errorCode: bridge ? "missing_dependency" : "desktop_bridge_unavailable",
137315
137313
  executionHost: "electron_desktop",
137316
137314
  durationMs: 0
@@ -137323,7 +137321,7 @@ async function runLocalPayrollRunArtifact(request) {
137323
137321
  if (!bridge) {
137324
137322
  return {
137325
137323
  ok: false,
137326
- error: "Desktop bridge not available",
137324
+ error: "Local runtime not available",
137327
137325
  errorCode: "desktop_bridge_unavailable",
137328
137326
  executionHost: "electron_desktop",
137329
137327
  durationMs: 0
@@ -137338,7 +137336,7 @@ function desktopGLReconcileAvailable() {
137338
137336
  async function runLocalGLReconcileArtifact(folderPath) {
137339
137337
  const bridge = getDesktopBridge();
137340
137338
  if (!bridge?.runLocalGLReconcileArtifact) {
137341
- return { ok: false, error: "Desktop bridge unavailable", errorCode: "desktop_bridge_unavailable", executionHost: "electron_desktop", durationMs: 0 };
137339
+ return { ok: false, error: "Local runtime unavailable", errorCode: "desktop_bridge_unavailable", executionHost: "electron_desktop", durationMs: 0 };
137342
137340
  }
137343
137341
  return bridge.runLocalGLReconcileArtifact({ folderPath });
137344
137342
  }
@@ -137349,7 +137347,7 @@ function desktopStatementAuditAvailable() {
137349
137347
  async function runLocalStatementAuditArtifact(folderPath) {
137350
137348
  const bridge = getDesktopBridge();
137351
137349
  if (!bridge?.runLocalStatementAuditArtifact) {
137352
- return { ok: false, error: "Desktop bridge unavailable", errorCode: "desktop_bridge_unavailable", executionHost: "electron_desktop", durationMs: 0 };
137350
+ return { ok: false, error: "Local runtime unavailable", errorCode: "desktop_bridge_unavailable", executionHost: "electron_desktop", durationMs: 0 };
137353
137351
  }
137354
137352
  return bridge.runLocalStatementAuditArtifact({ folderPath });
137355
137353
  }
@@ -137374,7 +137372,7 @@ async function runSandboxCodeJob(request) {
137374
137372
  kind: "blocked",
137375
137373
  error: bridge ? "capability_unavailable" : "not_desktop",
137376
137374
  executionHost: "electron-main",
137377
- message: bridge ? "This Perch AI Desktop bridge version does not expose runSandboxCodeJob." : "Perch AI Desktop bridge is unavailable."
137375
+ message: bridge ? "This Perch build does not expose runSandboxCodeJob." : "Local runtime is unavailable."
137378
137376
  };
137379
137377
  }
137380
137378
  return bridge.runSandboxCodeJob(request);
@@ -198170,8 +198168,8 @@ async function resolvePlaybookInputs(args) {
198170
198168
  return {
198171
198169
  ok: false,
198172
198170
  status: "blocked",
198173
- errorCode: "desktop_bridge_unavailable",
198174
- message: "Managed playbooks require the Perch AI Desktop bridge for local source discovery and file reads.",
198171
+ errorCode: "local_runtime_unavailable",
198172
+ message: "Managed playbooks require the local runtime for source discovery and file reads.",
198175
198173
  folderPath: rawFolderPath,
198176
198174
  warnings: []
198177
198175
  };
@@ -199532,7 +199530,6 @@ function containsBrowserDeliveryTask(tasks) {
199532
199530
  var BROWSER_DELIVERY_ROLE_IDS;
199533
199531
  var init_browserDeliveryLock = __esm({
199534
199532
  "features/perchTerminal/agentPlatform/browserDeliveryLock.ts"() {
199535
- "use strict";
199536
199533
  BROWSER_DELIVERY_ROLE_IDS = /* @__PURE__ */ new Set([
199537
199534
  "doc_writer",
199538
199535
  "email_sender",
@@ -199903,6 +199900,30 @@ function abortRuntimeRun(runId, reason = "Run cancelled.") {
199903
199900
  cleanupRun(record);
199904
199901
  return true;
199905
199902
  }
199903
+ function submitRuntimeSteer(input) {
199904
+ const runId = input.runId?.trim();
199905
+ if (!runId) return { ok: false, error: "No active turn to steer." };
199906
+ if (input.expectedRunId && input.expectedRunId !== runId) {
199907
+ return { ok: false, error: `Expected active turn ${input.expectedRunId}, found ${runId}.` };
199908
+ }
199909
+ const record = getRuntimeRun(runId);
199910
+ if (!record || record.status !== "running" || record.controller.signal.aborted) {
199911
+ return { ok: false, error: "No active turn to steer." };
199912
+ }
199913
+ if (input.threadId && record.threadId && input.threadId !== record.threadId) {
199914
+ return { ok: false, error: "Active turn belongs to a different thread." };
199915
+ }
199916
+ const text = input.text.trim();
199917
+ if (!text) return { ok: false, error: "Steer input cannot be empty." };
199918
+ record.steerQueue.push({
199919
+ text,
199920
+ mode: input.mode ?? "append",
199921
+ clientMessageId: input.clientMessageId ?? null,
199922
+ submittedAt: (/* @__PURE__ */ new Date()).toISOString()
199923
+ });
199924
+ record.updatedAt = nowMs();
199925
+ return { ok: true, turnId: runId };
199926
+ }
199906
199927
  function consumeRuntimeSteers(runId) {
199907
199928
  const record = getRuntimeRun(runId);
199908
199929
  if (!record || record.status !== "running" || record.controller.signal.aborted) return [];
@@ -205951,14 +205972,14 @@ async function runAPAuditPacketAdapter(args) {
205951
205972
  const folderPath = (args.folderPath ?? "").trim();
205952
205973
  if (!isDesktopAvailable()) {
205953
205974
  return blockedEnvelope({
205954
- reason: "AP audit packet requires the Perch AI Desktop bridge for local filesystem access. The browser cannot read the user's filesystem.",
205955
- errorCode: "desktop_bridge_unavailable",
205975
+ reason: "AP audit packet requires the local runtime for filesystem access. The browser cannot read the user's filesystem.",
205976
+ errorCode: "local_runtime_unavailable",
205956
205977
  folderPath: folderPath || null
205957
205978
  });
205958
205979
  }
205959
205980
  if (!desktopAPAuditAvailable()) {
205960
205981
  return blockedEnvelope({
205961
- reason: "This Perch AI Desktop bridge version does not expose runLocalAPAuditPacket. Update Perch AI Desktop to a build that includes the AP audit capability.",
205982
+ reason: "This Perch build does not expose runLocalAPAuditPacket. Update Perch CLI or rebuild the app package.",
205962
205983
  errorCode: "capability_unavailable",
205963
205984
  folderPath: folderPath || null
205964
205985
  });
@@ -206181,14 +206202,14 @@ function preflight(toolName) {
206181
206202
  if (!isDesktopAvailable()) {
206182
206203
  return blocked({
206183
206204
  toolName,
206184
- reason: "AP evidence tools require the Perch AI Desktop bridge for local filesystem access.",
206185
- errorCode: "desktop_bridge_unavailable"
206205
+ reason: "AP evidence tools require the local runtime for filesystem access.",
206206
+ errorCode: "local_runtime_unavailable"
206186
206207
  });
206187
206208
  }
206188
206209
  if (!desktopAPEvidenceAvailable()) {
206189
206210
  return blocked({
206190
206211
  toolName,
206191
- reason: "This Perch AI Desktop bridge version does not expose AP evidence tools. Update Desktop or run npm run desktop:compile.",
206212
+ reason: "This Perch build does not expose AP evidence tools. Update Perch CLI or rebuild the app package.",
206192
206213
  errorCode: "capability_unavailable"
206193
206214
  });
206194
206215
  }
@@ -206939,13 +206960,13 @@ function blocked2(reason, errorCode) {
206939
206960
  async function runSandboxAnalysisAdapter(args, opts) {
206940
206961
  if (!isDesktopAvailable()) {
206941
206962
  return blocked2(
206942
- "runSandboxAnalysis requires the Perch AI Desktop bridge. The browser cannot execute sandbox jobs against local sources.",
206943
- "desktop_bridge_unavailable"
206963
+ "runSandboxAnalysis requires the local runtime. The browser cannot execute sandbox jobs against local sources.",
206964
+ "local_runtime_unavailable"
206944
206965
  );
206945
206966
  }
206946
206967
  if (!desktopSandboxAnalysisAvailable()) {
206947
206968
  return blocked2(
206948
- "This Perch AI Desktop bridge version does not expose runSandboxAnalysisJob. Update Perch AI Desktop to a build that includes the sandbox analysis capability.",
206969
+ "This Perch build does not expose runSandboxAnalysisJob. Update Perch CLI or rebuild the app package.",
206949
206970
  "capability_unavailable"
206950
206971
  );
206951
206972
  }
@@ -210448,7 +210469,7 @@ function validateSuitePlan(plan, opts) {
210448
210469
  };
210449
210470
  }
210450
210471
  if (opts?.desktopConnected === false) {
210451
- return { valid: false, reason: "Desktop bridge required for local workflow tools." };
210472
+ return { valid: false, reason: "Local runtime required for workflow tools." };
210452
210473
  }
210453
210474
  return { valid: true };
210454
210475
  }
@@ -213140,7 +213161,7 @@ var init_config2 = __esm({
213140
213161
  ok: true,
213141
213162
  configured: false,
213142
213163
  servers: [],
213143
- note: "Desktop bridge required for MCP."
213164
+ note: "MCP servers are not connected in this runtime."
213144
213165
  };
213145
213166
  }
213146
213167
  const { getMcpStatus: getMcpStatus2 } = await Promise.resolve().then(() => (init_bridge(), bridge_exports));
@@ -213975,7 +213996,7 @@ async function executeToolCall(call, opts) {
213975
213996
  startMs,
213976
213997
  startedAt,
213977
213998
  code: "tool_desktop_required",
213978
- message: "Desktop bridge not available. Local tools require Perch AI Desktop.",
213999
+ message: "Local runtime not available. Local tools require Perch Desktop or CLI local mode.",
213979
214000
  riskLevel: policy.riskLevel,
213980
214001
  desktopRequired: true
213981
214002
  });
@@ -224069,13 +224090,13 @@ function createCliNodeLocalBridge(input) {
224069
224090
  };
224070
224091
  },
224071
224092
  getProjectRules: async () => [],
224072
- readProjectMemory: async () => ({ ok: false, error: "Project memory is desktop-only in CLI local mode." }),
224073
- writeProjectMemory: async () => ({ ok: false, error: "Project memory is desktop-only in CLI local mode." }),
224074
- writeMemoryFile: async () => ({ ok: false, error: "Project memory is desktop-only in CLI local mode." }),
224075
- writeRule: async () => ({ ok: false, error: "Project rules are desktop-only in CLI local mode." }),
224076
- writePerchMd: async () => ({ ok: false, error: "PERCH.md writes are desktop-only in CLI local mode." }),
224077
- readGlobalPerchMd: async () => ({ ok: false, error: "Global PERCH.md is desktop-only in CLI local mode." }),
224078
- writeGlobalPerchMd: async () => ({ ok: false, error: "Global PERCH.md is desktop-only in CLI local mode." }),
224093
+ readProjectMemory: async () => ({ ok: false, error: "Project memory is not available in this CLI package yet." }),
224094
+ writeProjectMemory: async () => ({ ok: false, error: "Project memory writes are not available in this CLI package yet." }),
224095
+ writeMemoryFile: async () => ({ ok: false, error: "Project memory files are not available in this CLI package yet." }),
224096
+ writeRule: async () => ({ ok: false, error: "Project rule writes are not available in this CLI package yet." }),
224097
+ writePerchMd: async () => ({ ok: false, error: "PERCH.md writes are not available in this CLI package yet." }),
224098
+ readGlobalPerchMd: async () => ({ ok: false, error: "Global PERCH.md is not available in this CLI package yet." }),
224099
+ writeGlobalPerchMd: async () => ({ ok: false, error: "Global PERCH.md writes are not available in this CLI package yet." }),
224079
224100
  getMcpStatus: async () => [],
224080
224101
  listMcpTools: async () => [],
224081
224102
  callMcpTool: async () => ({ ok: false, error: "MCP tools are not available in CLI local mode." }),
@@ -224631,6 +224652,7 @@ function buildCliTurnInput(input, resolved) {
224631
224652
  const userId = input.userId ?? null;
224632
224653
  return {
224633
224654
  trimmedInput: resolved.prompt,
224655
+ clientRunId: input.clientRunId ?? null,
224634
224656
  chatMode: input.chatMode ?? "agents",
224635
224657
  threadId: resolved.threadId,
224636
224658
  personaId: input.personaId ?? DEFAULT_PERSONA_ID,
@@ -280080,6 +280102,7 @@ async function runInkInteractivePerchCli(writer, deps, options) {
280080
280102
  const [pulse, setPulse] = React11.useState(0);
280081
280103
  const [, refresh] = React11.useState(0);
280082
280104
  const liveTextRef = React11.useRef("");
280105
+ const activeRunRef = React11.useRef(null);
280083
280106
  React11.useEffect(() => {
280084
280107
  if (!working) return void 0;
280085
280108
  const timer = setInterval(() => setPulse((value) => value + 1), 120);
@@ -280148,6 +280171,15 @@ async function runInkInteractivePerchCli(writer, deps, options) {
280148
280171
  setLiveText("");
280149
280172
  liveTextRef.current = "";
280150
280173
  const toolNamesById = /* @__PURE__ */ new Map();
280174
+ const clientRunId = createCliRunId();
280175
+ const externalController = new AbortController();
280176
+ const runtimeRun = registerRuntimeRun({
280177
+ runId: clientRunId,
280178
+ kind: "turn",
280179
+ threadId: state.threadId,
280180
+ parentSignal: externalController.signal
280181
+ });
280182
+ activeRunRef.current = { runId: clientRunId, threadId: state.threadId };
280151
280183
  try {
280152
280184
  if (!isCliModelConnectionReady(connection)) {
280153
280185
  addItem({
@@ -280173,8 +280205,10 @@ async function runInkInteractivePerchCli(writer, deps, options) {
280173
280205
  workspaceId: hostedContext.workspaceId,
280174
280206
  permanentMemories: hostedContext.permanentMemories,
280175
280207
  founderModelSelection: connection.founderModelSelection,
280208
+ clientRunId,
280176
280209
  desktopConnected: state.desktopConnected,
280177
280210
  cliLocalTools: state.cliLocalTools,
280211
+ signal: runtimeRun.controller.signal,
280178
280212
  onEvent: (event) => {
280179
280213
  switch (event.type) {
280180
280214
  case "content_delta":
@@ -280268,6 +280302,11 @@ async function runInkInteractivePerchCli(writer, deps, options) {
280268
280302
  } catch (error) {
280269
280303
  addItem({ label: "stop", text: humanizeCliError(errorMessage(error)), tone: "danger" });
280270
280304
  } finally {
280305
+ finishRuntimeRun(clientRunId, runtimeRun.controller.signal.aborted ? "cancelled" : "completed");
280306
+ externalController.abort();
280307
+ if (activeRunRef.current?.runId === clientRunId) {
280308
+ activeRunRef.current = null;
280309
+ }
280271
280310
  setWorking(false);
280272
280311
  setWorkingText("ready");
280273
280312
  setLiveText("");
@@ -280275,7 +280314,44 @@ async function runInkInteractivePerchCli(writer, deps, options) {
280275
280314
  }
280276
280315
  }, [addItem, app, reconnect, runTurn, updateToolItem, working]);
280277
280316
  Ink2.useInput((input, key) => {
280278
- if (working) return;
280317
+ if (working) {
280318
+ if (key.ctrl && input === "c") {
280319
+ const active = activeRunRef.current;
280320
+ if (active && abortRuntimeRun(active.runId, "Stopped from CLI.")) {
280321
+ addItem({ label: "stop", text: "stop requested", tone: "danger" });
280322
+ setWorkingText("stopping");
280323
+ }
280324
+ return;
280325
+ }
280326
+ if (key.return) {
280327
+ const steerText = draft.trim();
280328
+ const active = activeRunRef.current;
280329
+ if (steerText && active) {
280330
+ const result2 = submitRuntimeSteer({
280331
+ runId: active.runId,
280332
+ expectedRunId: active.runId,
280333
+ threadId: active.threadId,
280334
+ text: steerText,
280335
+ mode: "append",
280336
+ clientMessageId: `cli-steer-${Date.now()}`
280337
+ });
280338
+ if (result2.ok) {
280339
+ addItem({ label: "you", text: steerText, tone: "touch" });
280340
+ addItem({ label: "system", text: "steer queued", tone: "muted" });
280341
+ setDraft("");
280342
+ } else {
280343
+ addItem({ label: "need", text: result2.error, tone: "danger" });
280344
+ }
280345
+ }
280346
+ return;
280347
+ }
280348
+ if (key.backspace || key.delete) {
280349
+ setDraft((value) => value.slice(0, -1));
280350
+ return;
280351
+ }
280352
+ if (input && !key.escape) setDraft((value) => value + input);
280353
+ return;
280354
+ }
280279
280355
  if (key.return) {
280280
280356
  void submitPrompt(draft);
280281
280357
  return;
@@ -280422,7 +280498,7 @@ async function runInkInteractivePerchCli(writer, deps, options) {
280422
280498
  )
280423
280499
  );
280424
280500
  }),
280425
- { exitOnCtrlC: true }
280501
+ { exitOnCtrlC: false }
280426
280502
  );
280427
280503
  await instance.waitUntilExit();
280428
280504
  connection.restore();
@@ -280950,6 +281026,9 @@ function trimRecentMessages(messages) {
280950
281026
  messages.splice(0, messages.length - 30);
280951
281027
  }
280952
281028
  }
281029
+ function createCliRunId() {
281030
+ return `cli-turn-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
281031
+ }
280953
281032
  async function runAuthCommand(parsed, writer) {
280954
281033
  if (parsed.action === "logout") {
280955
281034
  await clearStoredCliAuthSession();
@@ -281220,6 +281299,7 @@ var init_perch_cli = __esm({
281220
281299
  init_cliStandaloneOAuth();
281221
281300
  init_contextMeterDisplay();
281222
281301
  init_threadSession();
281302
+ init_runRegistry();
281223
281303
  execFileAsync3 = promisify3(execFile3);
281224
281304
  DEFAULT_CLI_LOGIN_APP_URL = "https://app.perchai.app";
281225
281305
  CLI_PACKAGE_VERSION = readCliPackageVersion();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perchai-cli",
3
- "version": "2.4.12",
3
+ "version": "2.4.13",
4
4
  "description": "Perch AI command-line interface",
5
5
  "bin": {
6
6
  "perch": "bin/perch"