perchai-cli 2.4.26 → 2.4.28

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 +238 -21
  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();
@@ -76921,6 +76922,8 @@ function buildTranscriptSegments(state) {
76921
76922
  const terminalSegmentIdx = /* @__PURE__ */ new Map();
76922
76923
  const workerRunIdx = /* @__PURE__ */ new Map();
76923
76924
  const flockRunIdx = /* @__PURE__ */ new Map();
76925
+ const flockIdByWorkerId = /* @__PURE__ */ new Map();
76926
+ const flockWorkerIdByToolCallId = /* @__PURE__ */ new Map();
76924
76927
  const diagnosticIdxByKey = /* @__PURE__ */ new Map();
76925
76928
  const capabilityIdx = /* @__PURE__ */ new Map();
76926
76929
  const liveCardIdx = /* @__PURE__ */ new Map();
@@ -76944,6 +76947,32 @@ function buildTranscriptSegments(state) {
76944
76947
  if (runId) sandboxRunIdx.set(runId, idx);
76945
76948
  if (toolCallId) sandboxRunIdx.set(toolCallId, idx);
76946
76949
  }
76950
+ function updateFlockWorkerByWorkerId(workerId, updater) {
76951
+ if (!workerId) return false;
76952
+ const flockId = flockIdByWorkerId.get(workerId);
76953
+ if (!flockId) return false;
76954
+ const idx = flockRunIdx.get(flockId);
76955
+ if (idx === void 0) return true;
76956
+ const seg = segments[idx];
76957
+ if (seg?.kind !== "flock_run") return true;
76958
+ segments[idx] = {
76959
+ ...seg,
76960
+ workers: seg.workers.map(
76961
+ (worker) => worker.workerId === workerId ? updater(worker) : worker
76962
+ )
76963
+ };
76964
+ return true;
76965
+ }
76966
+ function isFlockOwnedToolEvent(event) {
76967
+ return Boolean(
76968
+ event.workerId && flockIdByWorkerId.has(event.workerId) || event.toolCallId && flockWorkerIdByToolCallId.has(event.toolCallId)
76969
+ );
76970
+ }
76971
+ function isQuietFlockDiagnostic(event) {
76972
+ if (!event.workerId || !flockIdByWorkerId.has(event.workerId)) return false;
76973
+ if (event.code === "tool_call_budget_exhausted") return false;
76974
+ return event.code === "source_read_budget_reached" || event.code === "source_analysis_max_iteration_closing_attempted" || event.code === "source_analysis_max_iteration_synthesized" || event.code === "source_analysis_max_iteration_closing_fallback" || event.code === "source_analysis_max_iteration_incomplete" || /source-read budget|source-analysis|read-only source/i.test(event.message);
76975
+ }
76947
76976
  function flushReasoning() {
76948
76977
  if (!pendingReasoning) return;
76949
76978
  segments.push({ kind: "reasoning", text: pendingReasoning, seq: seq++, timestamp: pendingReasoningTs });
@@ -77037,6 +77066,15 @@ function buildTranscriptSegments(state) {
77037
77066
  ev.toolCallId ? [ev.toolCallId] : [],
77038
77067
  ev.ts
77039
77068
  );
77069
+ if (ev.workerId && flockIdByWorkerId.has(ev.workerId)) {
77070
+ if (ev.toolCallId) flockWorkerIdByToolCallId.set(ev.toolCallId, ev.workerId);
77071
+ updateFlockWorkerByWorkerId(ev.workerId, (worker) => ({
77072
+ ...worker,
77073
+ toolCalls: (worker.toolCalls ?? 0) + 1,
77074
+ lastToolName: ev.toolName
77075
+ }));
77076
+ break;
77077
+ }
77040
77078
  if (ev.workerId) {
77041
77079
  const idx = segments.length;
77042
77080
  segments.push({
@@ -77091,6 +77129,7 @@ function buildTranscriptSegments(state) {
77091
77129
  }
77092
77130
  case "tool_call_completed": {
77093
77131
  flushReasoning();
77132
+ if (isFlockOwnedToolEvent(ev)) break;
77094
77133
  const id = ev.toolCallId ?? ev.toolName;
77095
77134
  const idx = toolCallIdx.get(id);
77096
77135
  if (idx !== void 0) {
@@ -77107,6 +77146,7 @@ function buildTranscriptSegments(state) {
77107
77146
  }
77108
77147
  case "tool_result": {
77109
77148
  flushReasoning();
77149
+ if (isFlockOwnedToolEvent(ev)) break;
77110
77150
  const id = ev.toolCallId ?? ev.toolName;
77111
77151
  if (ev.toolName === "run_sandbox_code") {
77112
77152
  updateSandboxRun(ev.toolCallId, null, (seg) => ({ ...seg, resultText: ev.text }));
@@ -77135,6 +77175,7 @@ function buildTranscriptSegments(state) {
77135
77175
  case "tool_output_delta": {
77136
77176
  flushReasoning();
77137
77177
  if (!ev.text) break;
77178
+ if (isFlockOwnedToolEvent(ev)) break;
77138
77179
  const id = ev.toolCallId ?? ev.toolName;
77139
77180
  if (ev.toolName === "run_sandbox_code") {
77140
77181
  updateSandboxRun(ev.toolCallId, null, (seg) => ({ ...seg, resultText: `${seg.resultText ?? ""}${ev.text}` }));
@@ -77543,6 +77584,7 @@ function buildTranscriptSegments(state) {
77543
77584
  case "diagnostic": {
77544
77585
  flushReasoning();
77545
77586
  if (!ev.message.trim()) break;
77587
+ if (isQuietFlockDiagnostic(ev)) break;
77546
77588
  if (ev.code === "tool_call_budget_exhausted") {
77547
77589
  let attributed = false;
77548
77590
  if (ev.workerId) {
@@ -77694,6 +77736,7 @@ function buildTranscriptSegments(state) {
77694
77736
  }
77695
77737
  case "tool_call_failed": {
77696
77738
  flushReasoning();
77739
+ if (isFlockOwnedToolEvent(ev)) break;
77697
77740
  const id = ev.toolCallId ?? ev.toolName;
77698
77741
  const idx = toolCallIdx.get(id);
77699
77742
  if (idx !== void 0) {
@@ -77808,6 +77851,12 @@ function buildTranscriptSegments(state) {
77808
77851
  }
77809
77852
  case "worker_run_started": {
77810
77853
  flushReasoning();
77854
+ if (updateFlockWorkerByWorkerId(ev.workerId, (worker) => ({
77855
+ ...worker,
77856
+ status: worker.status === "queued" ? "running" : worker.status
77857
+ }))) {
77858
+ break;
77859
+ }
77811
77860
  const idx = segments.length;
77812
77861
  segments.push({
77813
77862
  kind: "worker_run",
@@ -77824,6 +77873,12 @@ function buildTranscriptSegments(state) {
77824
77873
  }
77825
77874
  case "worker_run_completed": {
77826
77875
  flushReasoning();
77876
+ if (updateFlockWorkerByWorkerId(ev.workerId, (worker) => ({
77877
+ ...worker,
77878
+ status: ev.ok ? "done" : "failed"
77879
+ }))) {
77880
+ break;
77881
+ }
77827
77882
  const runIdx = workerRunIdx.get(ev.workerId);
77828
77883
  if (runIdx !== void 0) {
77829
77884
  const seg = segments[runIdx];
@@ -77876,6 +77931,11 @@ function buildTranscriptSegments(state) {
77876
77931
  if (idx === void 0) break;
77877
77932
  const seg = segments[idx];
77878
77933
  if (seg?.kind !== "flock_run") break;
77934
+ if (ev.accepted) {
77935
+ for (const worker of ev.workers) {
77936
+ flockIdByWorkerId.set(worker.workerId, ev.flockId);
77937
+ }
77938
+ }
77879
77939
  segments[idx] = ev.accepted ? {
77880
77940
  ...seg,
77881
77941
  status: "running",
@@ -77897,7 +77957,11 @@ function buildTranscriptSegments(state) {
77897
77957
  if (seg?.kind !== "flock_run") break;
77898
77958
  const known = seg.workers.some((worker) => worker.flockWorkerId === ev.flockWorkerId);
77899
77959
  const workers = known ? seg.workers.map(
77900
- (worker) => worker.flockWorkerId === ev.flockWorkerId ? { ...worker, status: ev.status } : worker
77960
+ (worker) => worker.flockWorkerId === ev.flockWorkerId ? {
77961
+ ...worker,
77962
+ status: ev.status,
77963
+ note: ev.detail ?? worker.note
77964
+ } : worker
77901
77965
  ) : [
77902
77966
  ...seg.workers,
77903
77967
  {
@@ -77905,7 +77969,8 @@ function buildTranscriptSegments(state) {
77905
77969
  workerId: ev.workerId,
77906
77970
  displayName: ev.displayName,
77907
77971
  nickname: ev.nickname,
77908
- status: ev.status
77972
+ status: ev.status,
77973
+ note: ev.detail
77909
77974
  }
77910
77975
  ];
77911
77976
  segments[idx] = { ...seg, workers };
@@ -78308,9 +78373,13 @@ function segmentToCompact(seg) {
78308
78373
  summary: seg.summary?.slice(0, 300),
78309
78374
  flockWorkers: seg.workers.map((worker) => ({
78310
78375
  flockWorkerId: worker.flockWorkerId,
78376
+ workerId: worker.workerId,
78311
78377
  displayName: worker.displayName,
78312
78378
  nickname: worker.nickname,
78313
- status: worker.status
78379
+ status: worker.status,
78380
+ note: worker.note,
78381
+ toolCalls: worker.toolCalls,
78382
+ lastToolName: worker.lastToolName
78314
78383
  }))
78315
78384
  };
78316
78385
  case "worker_tool_call":
@@ -91587,7 +91656,6 @@ function listFinancialPlaybooks() {
91587
91656
  var AP_AUDIT_PACKET_DEF, PLAYBOOKS;
91588
91657
  var init_registry2 = __esm({
91589
91658
  "features/perchTerminal/runtime/financialPlaybooks/registry.ts"() {
91590
- "use strict";
91591
91659
  init_managedWorkflowRegistry2();
91592
91660
  init_toolNames();
91593
91661
  AP_AUDIT_PACKET_DEF = {
@@ -220709,11 +220777,11 @@ function flockTaskGate(rawTask) {
220709
220777
  if (!task) {
220710
220778
  return { ok: false, task, reason: "No task given. Usage: /flock <task>." };
220711
220779
  }
220712
- if (/\/flock\b/i.test(task)) {
220780
+ if (/(^|[\s([{])\/flock(?:\s|$)/i.test(task)) {
220713
220781
  return {
220714
220782
  ok: false,
220715
220783
  task,
220716
- reason: "A flock cannot gather another flock \u2014 recursive /flock is not allowed."
220784
+ reason: "Subagents cannot start another /flock run from inside a /flock request."
220717
220785
  };
220718
220786
  }
220719
220787
  const wordCount = task.split(/\s+/).length;
@@ -221475,15 +221543,30 @@ function buildSpawnContext(input, flockId, signal, emit, worker) {
221475
221543
  };
221476
221544
  }
221477
221545
  function buildFlockSummary(plan, outcomes, status, toolCallsUsed, wallTimeHit) {
221478
- const header = status === "completed" ? `Flock finished: ${plan.summary}.` : status === "partial" ? `Flock finished with partial results (${plan.summary}).` : status === "cancelled" ? `Flock stopped early${wallTimeHit ? " \u2014 wall-time cap reached" : ""}.` : `Flock failed \u2014 no worker produced a usable result.`;
221546
+ const header = status === "completed" ? `Subagents finished: ${plan.summary}.` : status === "partial" ? `Subagents finished with partial results (${plan.summary}).` : status === "cancelled" ? `Subagents stopped early${wallTimeHit ? " \u2014 wall-time cap reached" : ""}.` : `Subagents failed \u2014 no worker produced a usable result.`;
221479
221547
  const lines = outcomes.map((outcome) => {
221480
221548
  const mark = outcome.status === "done" ? "done" : outcome.status === "failed" ? "failed" : outcome.status;
221481
- const detail = outcome.detail ? ` \u2014 ${clampLine(outcome.detail, 160)}` : "";
221549
+ const detailText = friendlyOutcomeDetail(outcome.detail);
221550
+ const detail = detailText ? ` \u2014 ${clampLine(detailText, 160)}` : "";
221482
221551
  return `- ${outcome.worker.displayName} (${outcome.worker.nickname}): ${mark}${detail}`;
221483
221552
  });
221484
221553
  const footer = `Used ${toolCallsUsed}/${plan.caps.maxTotalToolCalls} tool calls across ${outcomes.length} workers.`;
221485
221554
  return [header, ...lines, footer].join("\n");
221486
221555
  }
221556
+ function friendlyOutcomeDetail(detail) {
221557
+ const clean = clampLine(detail, 220);
221558
+ if (!clean) return "";
221559
+ if (/Reached maximum \d+ tool-call iterations/i.test(clean)) {
221560
+ return "finished from gathered evidence";
221561
+ }
221562
+ if (/source-read budget|read-only source|source-analysis/i.test(clean)) {
221563
+ return "used gathered evidence";
221564
+ }
221565
+ if (/tool-call budget exhausted|Tool budget reached/i.test(clean)) {
221566
+ return "tool limit reached; finished from evidence";
221567
+ }
221568
+ return clean;
221569
+ }
221487
221570
  function clampLine(text, max2) {
221488
221571
  const clean = (text ?? "").replace(/\s+/g, " ").trim();
221489
221572
  return clean.length > max2 ? `${clean.slice(0, max2 - 1)}\u2026` : clean;
@@ -230793,6 +230876,10 @@ var init_cliStandaloneOAuth = __esm({
230793
230876
  });
230794
230877
 
230795
230878
  // features/perchTerminal/runtime/contextMeterDisplay.ts
230879
+ function positiveInt3(value) {
230880
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return null;
230881
+ return Math.floor(value);
230882
+ }
230796
230883
  function formatTokenK(n) {
230797
230884
  if (n < 1e3) return `${Math.max(0, Math.round(n))}`;
230798
230885
  return `${(n / 1e3).toFixed(n < 1e4 ? 1 : 0)}k`;
@@ -230811,12 +230898,17 @@ function resolvePrimaryContextTokens(snapshot) {
230811
230898
  if (rowTokens > 0) return rowTokens;
230812
230899
  return 0;
230813
230900
  }
230814
- function resolveContextMeterDisplay(snapshot) {
230815
- const limitTokens = snapshot?.rawContextWindowTokens ?? snapshot?.effectiveLimitTokens ?? snapshot?.contextLimitTokens ?? getEffectiveContextWindow();
230901
+ function resolveContextMeterDisplay(snapshot, activeModel) {
230902
+ const activeRawLimitTokens = positiveInt3(activeModel?.contextWindow) ?? positiveInt3(activeModel?.contextWindowTokens);
230903
+ const activeEffectiveLimitTokens = activeRawLimitTokens ? getEffectiveContextWindow({
230904
+ contextWindow: activeRawLimitTokens,
230905
+ maxOutputTokens: activeModel?.maxOutputTokens ?? null
230906
+ }) : null;
230907
+ const limitTokens = activeRawLimitTokens ?? snapshot?.rawContextWindowTokens ?? activeEffectiveLimitTokens ?? snapshot?.effectiveLimitTokens ?? snapshot?.contextLimitTokens ?? getEffectiveContextWindow();
230816
230908
  const threadTokens = snapshot?.threadContextTokens ?? snapshot?.totalContextTokens ?? snapshot?.latestSendTokens ?? 0;
230817
230909
  const primaryTokens = resolvePrimaryContextTokens(snapshot);
230818
230910
  const rawLimitTokens = Math.max(1, limitTokens);
230819
- const effectiveLimitTokens = snapshot?.effectiveLimitTokens ?? snapshot?.contextLimitTokens ?? rawLimitTokens;
230911
+ const effectiveLimitTokens = activeEffectiveLimitTokens ?? snapshot?.effectiveLimitTokens ?? snapshot?.contextLimitTokens ?? rawLimitTokens;
230820
230912
  const reservedOutputTokens = Math.max(0, rawLimitTokens - effectiveLimitTokens);
230821
230913
  const committedTokens = Math.min(rawLimitTokens, primaryTokens + reservedOutputTokens);
230822
230914
  const workerUsageTokens = snapshot?.workerUsage?.reduce((sum, worker) => sum + Math.max(0, worker.totalTokens), 0) ?? 0;
@@ -230836,6 +230928,8 @@ function resolveContextMeterDisplay(snapshot) {
230836
230928
  workerTokens,
230837
230929
  jobTotalTokens: job,
230838
230930
  limitTokens,
230931
+ effectiveLimitTokens,
230932
+ reservedOutputTokens,
230839
230933
  composerLabel,
230840
230934
  hasWorkers
230841
230935
  };
@@ -285305,7 +285399,8 @@ ${HELP_TEXT}`);
285305
285399
  trimRecentMessages(nextRecentMessages);
285306
285400
  writeCliTurnResult(result3, parsed.json, writer, {
285307
285401
  personaId: parsed.personaId,
285308
- color: shouldUseCliColor()
285402
+ color: shouldUseCliColor(),
285403
+ activeContextModel: resolveCliActiveContextModel(connection)
285309
285404
  });
285310
285405
  await admitCliLearningMemory(connection, {
285311
285406
  prompt: parsed.prompt,
@@ -285658,7 +285753,8 @@ async function runReadlineInteractivePerchCli(writer, deps, options) {
285658
285753
  });
285659
285754
  writeCliTurnResult(result2, false, writer, {
285660
285755
  personaId: state.personaId,
285661
- color: shouldUseCliColor()
285756
+ color: shouldUseCliColor(),
285757
+ activeContextModel: resolveCliActiveContextModel(connection)
285662
285758
  });
285663
285759
  appendRecentMessage(state.recentMessages, "user", prompt);
285664
285760
  if (result2.assistantText.trim()) {
@@ -285844,6 +285940,8 @@ async function runInkInteractivePerchCli(writer, deps, options) {
285844
285940
  const toolNamesById = /* @__PURE__ */ new Map();
285845
285941
  const flockWorkerNames = /* @__PURE__ */ new Map();
285846
285942
  const flockLimitMeta = /* @__PURE__ */ new Map();
285943
+ const aggregateToolIds = /* @__PURE__ */ new Map();
285944
+ const aggregateToolMeta = /* @__PURE__ */ new Map();
285847
285945
  const clientRunId = createCliRunId();
285848
285946
  const externalController = new AbortController();
285849
285947
  const runtimeRun = registerRuntimeRun({
@@ -285907,11 +286005,28 @@ async function runInkInteractivePerchCli(writer, deps, options) {
285907
286005
  case "tool_call_started": {
285908
286006
  const toolId = event.toolCallId ?? `${event.toolName}-${Date.now()}`;
285909
286007
  const name = humanizeCliToolName(event.toolName);
286008
+ const aggregateKind = cliToolAggregateKind(event.toolName);
285910
286009
  toolInputsById.current.set(toolId, {
285911
286010
  toolName: event.toolName,
285912
286011
  input: event.input ?? {}
285913
286012
  });
285914
286013
  toolNamesById.set(toolId, name);
286014
+ if (aggregateKind) {
286015
+ aggregateToolIds.set(toolId, aggregateKind);
286016
+ const meta2 = getCliToolAggregateMeta(aggregateToolMeta, aggregateKind);
286017
+ meta2.started += 1;
286018
+ meta2.pending += 1;
286019
+ appendCliToolAggregateDetail(meta2, event.toolName, event.input ?? {}, "started");
286020
+ updateToolItem(cliToolAggregateItemId(aggregateKind), {
286021
+ label: "tool",
286022
+ text: renderCliToolAggregateText(aggregateKind, meta2),
286023
+ tone: "muted",
286024
+ detailLines: meta2.detailLines,
286025
+ expanded: false
286026
+ });
286027
+ setWorkingText(cliToolAggregateWorkingText(aggregateKind));
286028
+ break;
286029
+ }
285915
286030
  const fileStart = buildFileToolDisplay(event.toolName, event.input ?? {}, "running");
285916
286031
  if (fileStart) richToolIds.current.add(toolId);
285917
286032
  updateToolItem(toolId, {
@@ -285927,6 +286042,22 @@ async function runInkInteractivePerchCli(writer, deps, options) {
285927
286042
  case "tool_call_completed": {
285928
286043
  const toolId = event.toolCallId ?? `${event.toolName}-${Date.now()}`;
285929
286044
  const stored = toolInputsById.current.get(toolId);
286045
+ const aggregateKind = aggregateToolIds.get(toolId);
286046
+ if (aggregateKind) {
286047
+ const meta2 = getCliToolAggregateMeta(aggregateToolMeta, aggregateKind);
286048
+ meta2.pending = Math.max(0, meta2.pending - 1);
286049
+ meta2.done += 1;
286050
+ appendCliToolAggregateDetail(meta2, event.toolName, stored?.input ?? {}, "done");
286051
+ updateToolItem(cliToolAggregateItemId(aggregateKind), {
286052
+ label: "tool",
286053
+ text: renderCliToolAggregateText(aggregateKind, meta2),
286054
+ tone: meta2.failed > 0 ? "danger" : "success",
286055
+ detailLines: meta2.detailLines,
286056
+ expanded: false
286057
+ });
286058
+ setWorkingText("thinking");
286059
+ break;
286060
+ }
285930
286061
  const fileDone = buildFileToolDisplay(event.toolName, stored?.input ?? {}, "done");
285931
286062
  if (fileDone) {
285932
286063
  richToolIds.current.add(toolId);
@@ -285956,6 +286087,22 @@ async function runInkInteractivePerchCli(writer, deps, options) {
285956
286087
  case "tool_call_failed": {
285957
286088
  const toolId = event.toolCallId ?? `${event.toolName}-${Date.now()}`;
285958
286089
  const name = toolNamesById.get(toolId) ?? humanizeCliToolName(event.toolName);
286090
+ const stored = toolInputsById.current.get(toolId);
286091
+ const aggregateKind = aggregateToolIds.get(toolId);
286092
+ if (aggregateKind) {
286093
+ const meta2 = getCliToolAggregateMeta(aggregateToolMeta, aggregateKind);
286094
+ meta2.pending = Math.max(0, meta2.pending - 1);
286095
+ meta2.failed += 1;
286096
+ appendCliToolAggregateDetail(meta2, event.toolName, stored?.input ?? {}, "failed", event.error);
286097
+ updateToolItem(cliToolAggregateItemId(aggregateKind), {
286098
+ label: "tool",
286099
+ text: renderCliToolAggregateText(aggregateKind, meta2),
286100
+ tone: "danger",
286101
+ detailLines: meta2.detailLines,
286102
+ expanded: false
286103
+ });
286104
+ break;
286105
+ }
285959
286106
  updateToolItem(toolId, {
285960
286107
  label: "need",
285961
286108
  text: `${name} \xB7 ${event.error ?? "needs attention"}`,
@@ -286515,7 +286662,10 @@ async function runInteractiveSlashCommand(input) {
286515
286662
  `);
286516
286663
  return "continue";
286517
286664
  case "context":
286518
- input.writer.stdout(renderCliContextDetails(input.state.contextSnapshot));
286665
+ input.writer.stdout(renderCliContextDetails(
286666
+ input.state.contextSnapshot,
286667
+ resolveCliActiveContextModel(input.getConnection?.() ?? null)
286668
+ ));
286519
286669
  return "continue";
286520
286670
  case "memoryAudit": {
286521
286671
  const audit = explainLearningAdmission({
@@ -286685,6 +286835,7 @@ function renderInteractiveStatus(state, connection, session, workspaceId) {
286685
286835
  const signedIn = session === void 0 ? isCliModelConnectionReady(connection) : isStoredCliAuthSessionUsable(session);
286686
286836
  const connectionStatus = isCliModelConnectionReady(connection) ? "connected" : "locked \xB7 run /login";
286687
286837
  const color = shouldUseCliColor();
286838
+ const activeContextModel = resolveCliActiveContextModel(connection);
286688
286839
  const lines = [
286689
286840
  ["version", CLI_PACKAGE_VERSION],
286690
286841
  ["cwd", state.cwd],
@@ -286697,7 +286848,7 @@ function renderInteractiveStatus(state, connection, session, workspaceId) {
286697
286848
  ["mode", state.chatMode],
286698
286849
  ["persona", state.personaId],
286699
286850
  ["thread", state.threadId],
286700
- ["context", renderCliContextMeter(state.contextSnapshot)],
286851
+ ["context", renderCliContextMeter(state.contextSnapshot, activeContextModel)],
286701
286852
  ["saved", state.persistedThreadUpdatedAt ? new Date(state.persistedThreadUpdatedAt).toLocaleString() : "(new thread)"],
286702
286853
  ["local-tools", state.cliLocalTools ? "on" : "off"]
286703
286854
  ];
@@ -287045,21 +287196,21 @@ function isPermanentMemoryLike2(value) {
287045
287196
  const memory = value;
287046
287197
  return typeof memory.id === "string" && typeof memory.title === "string" && typeof memory.body === "string";
287047
287198
  }
287048
- function renderCliContextMeter(snapshot) {
287199
+ function renderCliContextMeter(snapshot, activeModel) {
287049
287200
  if (!snapshot) return "Context";
287050
- const meter = resolveContextMeterDisplay(snapshot);
287201
+ const meter = resolveContextMeterDisplay(snapshot, activeModel);
287051
287202
  const percent2 = snapshot.contextPercentage ?? Math.round(meter.committedTokens / Math.max(1, meter.limitTokens) * 100);
287052
287203
  const parts = [`${meter.composerLabel}`, `${Math.max(0, Math.min(100, percent2))}%`];
287053
287204
  const compacted = snapshot.compactedRowCount ?? 0;
287054
287205
  if (snapshot.compacted || compacted > 0) parts.push(`compacted ${compacted}`);
287055
287206
  return parts.join(" \xB7 ");
287056
287207
  }
287057
- function renderCliContextDetails(snapshot) {
287208
+ function renderCliContextDetails(snapshot, activeModel) {
287058
287209
  if (!snapshot) return "context no meter yet\n";
287059
- const meter = resolveContextMeterDisplay(snapshot);
287210
+ const meter = resolveContextMeterDisplay(snapshot, activeModel);
287060
287211
  const percent2 = snapshot.contextPercentage ?? Math.round(meter.committedTokens / Math.max(1, meter.limitTokens) * 100);
287061
287212
  const lines = [
287062
- ["context", renderCliContextMeter(snapshot)],
287213
+ ["context", renderCliContextMeter(snapshot, activeModel)],
287063
287214
  ["thread", `${formatCliTokenCount(meter.threadTokens)} tokens`],
287064
287215
  ["limit", `${formatCliTokenCount(meter.limitTokens)} tokens`],
287065
287216
  ["fill", `${Math.max(0, Math.min(100, percent2))}%`],
@@ -287079,6 +287230,10 @@ function humanizeCliToolName(name) {
287079
287230
  function isCliModelConnectionReady(connection) {
287080
287231
  return Boolean(connection?.authenticated && connection.founderModelSelection);
287081
287232
  }
287233
+ function resolveCliActiveContextModel(connection) {
287234
+ if (!connection?.founderModelSelection) return null;
287235
+ return resolveModelForLane(connection.founderModelSelection, "chat");
287236
+ }
287082
287237
  function renderCliAuthSummary(connection) {
287083
287238
  if (isCliModelConnectionReady(connection)) {
287084
287239
  return `signed in${connection.email ? ` as ${connection.email}` : ""}`;
@@ -287415,6 +287570,67 @@ function buildFileToolDisplay(toolName, input, phase, summary) {
287415
287570
  ]
287416
287571
  };
287417
287572
  }
287573
+ function cliToolAggregateKind(toolName) {
287574
+ const normalized = toolName.toLowerCase();
287575
+ if (normalized === "readlocalfile" || normalized === "readlocalsourcefile") return "read";
287576
+ if (normalized === "glob" || normalized === "grep" || normalized === "listlocalsources" || normalized === "statpath" || normalized === "validateworkspaceroot") return "search";
287577
+ return null;
287578
+ }
287579
+ function getCliToolAggregateMeta(store, kind) {
287580
+ const existing = store.get(kind);
287581
+ if (existing) return existing;
287582
+ const created = {
287583
+ started: 0,
287584
+ done: 0,
287585
+ failed: 0,
287586
+ pending: 0,
287587
+ detailLines: []
287588
+ };
287589
+ store.set(kind, created);
287590
+ return created;
287591
+ }
287592
+ function cliToolAggregateItemId(kind) {
287593
+ return `tool-aggregate-${kind}`;
287594
+ }
287595
+ function cliToolAggregateWorkingText(kind) {
287596
+ return kind === "read" ? "reading files" : "searching";
287597
+ }
287598
+ function renderCliToolAggregateText(kind, meta) {
287599
+ const completed = meta.done + meta.failed;
287600
+ const count = Math.max(completed, meta.started);
287601
+ const noun = kind === "read" ? `file${count === 1 ? "" : "s"}` : `path${count === 1 ? "" : "s"}`;
287602
+ const verb = kind === "read" ? "read" : "searched";
287603
+ const status = meta.pending > 0 ? `${meta.pending} running` : "done";
287604
+ const failed = meta.failed > 0 ? ` \xB7 ${meta.failed} failed` : "";
287605
+ return `${verb} ${count} ${noun} \xB7 ${status}${failed} \xB7 ctrl-e`;
287606
+ }
287607
+ function appendCliToolAggregateDetail(meta, toolName, input, status, error) {
287608
+ const label = humanizeCliToolName(toolName);
287609
+ const target = describeCliToolAggregateInput(toolName, input);
287610
+ const tail = error ? ` \xB7 ${error}` : "";
287611
+ meta.detailLines.push({
287612
+ tone: "meta",
287613
+ text: `${label}${target ? ` \xB7 ${target}` : ""} \xB7 ${status}${tail}`
287614
+ });
287615
+ if (meta.detailLines.length > INK_DETAIL_LINE_LIMIT) {
287616
+ meta.detailLines = meta.detailLines.slice(-INK_DETAIL_LINE_LIMIT);
287617
+ }
287618
+ }
287619
+ function describeCliToolAggregateInput(toolName, input) {
287620
+ const normalized = toolName.toLowerCase();
287621
+ if (normalized === "glob") {
287622
+ const pattern = stringValue8(input.pattern) ?? stringValue8(input.glob) ?? "*";
287623
+ const base = stringValue8(input.path) ?? stringValue8(input.cwd) ?? ".";
287624
+ return `${pattern} in ${shortFilePath(base)}`;
287625
+ }
287626
+ if (normalized === "grep") {
287627
+ const pattern = stringValue8(input.pattern) ?? stringValue8(input.query) ?? "";
287628
+ const base = stringValue8(input.path) ?? stringValue8(input.cwd) ?? ".";
287629
+ return `${pattern || "pattern"} in ${shortFilePath(base)}`;
287630
+ }
287631
+ const filePath = stringValue8(input.path) ?? stringValue8(input.filePath) ?? stringValue8(input.localSourceId) ?? stringValue8(input.cwd);
287632
+ return filePath ? shortFilePath(filePath) : null;
287633
+ }
287418
287634
  function describeChangeSummary(summary) {
287419
287635
  const kind = summary.changeKind ?? "changed";
287420
287636
  const added = summary.linesAdded ?? 0;
@@ -287657,7 +287873,7 @@ function writeCliTurnResult(result2, json, writer, options = {}) {
287657
287873
  color: options.color,
287658
287874
  streamLabels: true
287659
287875
  });
287660
- const contextLine = `${paint("context".padEnd(12), "muted", options.color === true)} ${renderCliContextMeter(result2.contextSnapshot)}
287876
+ const contextLine = `${paint("context".padEnd(12), "muted", options.color === true)} ${renderCliContextMeter(result2.contextSnapshot, options.activeContextModel)}
287661
287877
  `;
287662
287878
  writer.stdout(`${rendered}${rendered.endsWith("\n") ? "" : "\n"}${contextLine}`);
287663
287879
  }
@@ -287813,6 +288029,7 @@ var init_perch_cli = __esm({
287813
288029
  init_cliAuthSession();
287814
288030
  init_cliStandaloneOAuth();
287815
288031
  init_contextMeterDisplay();
288032
+ init_modelRegistry();
287816
288033
  init_threadSession();
287817
288034
  init_runRegistry();
287818
288035
  init_learningMemory();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perchai-cli",
3
- "version": "2.4.26",
3
+ "version": "2.4.28",
4
4
  "description": "Perch AI command-line interface",
5
5
  "bin": {
6
6
  "perch": "bin/perch"