perchai-cli 2.4.28 → 2.4.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/perch.mjs +292 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@ Run `perch`, then use these commands inside the terminal chat:
|
|
|
19
19
|
|
|
20
20
|
- `/help` — show commands.
|
|
21
21
|
- `/status` — show cwd, auth, mode, persona, permission, and thread.
|
|
22
|
+
- `/usage` — show tokens and estimated cost for this session.
|
|
22
23
|
- `/cwd [dir]` — show or change the working directory.
|
|
23
24
|
- `/permission [default|auto_review|take_the_wheel|plan]` — show or change permission mode.
|
|
24
25
|
- `/permissions [default|auto_review|take_the_wheel|plan]` — alias for `/permission`.
|
package/dist/perch.mjs
CHANGED
|
@@ -75918,6 +75918,7 @@ function isTurnAbortedError(error) {
|
|
|
75918
75918
|
var TURN_STOPPED_BY_USER_MESSAGE;
|
|
75919
75919
|
var init_turnAbort = __esm({
|
|
75920
75920
|
"features/perchTerminal/runtime/turnAbort.ts"() {
|
|
75921
|
+
"use strict";
|
|
75921
75922
|
TURN_STOPPED_BY_USER_MESSAGE = "Turn stopped by user.";
|
|
75922
75923
|
}
|
|
75923
75924
|
});
|
|
@@ -86691,6 +86692,7 @@ function truncateHistoryLine(value, max2) {
|
|
|
86691
86692
|
}
|
|
86692
86693
|
var init_operatorTruth = __esm({
|
|
86693
86694
|
"features/perchTerminal/runtime/operatorTruth.ts"() {
|
|
86695
|
+
"use strict";
|
|
86694
86696
|
}
|
|
86695
86697
|
});
|
|
86696
86698
|
|
|
@@ -221593,13 +221595,282 @@ var init_runFlockTurn = __esm({
|
|
|
221593
221595
|
}
|
|
221594
221596
|
});
|
|
221595
221597
|
|
|
221598
|
+
// features/perchTerminal/runtime/usage/usageCommand.ts
|
|
221599
|
+
function parseUsageCommand(raw) {
|
|
221600
|
+
const trimmed = raw.trim();
|
|
221601
|
+
if (!/^\/usage(?:\s|$)/i.test(trimmed)) return null;
|
|
221602
|
+
const task = trimmed.slice("/usage".length).trim();
|
|
221603
|
+
return task ? { kind: "usage", task } : { kind: "usage" };
|
|
221604
|
+
}
|
|
221605
|
+
var init_usageCommand = __esm({
|
|
221606
|
+
"features/perchTerminal/runtime/usage/usageCommand.ts"() {
|
|
221607
|
+
"use strict";
|
|
221608
|
+
}
|
|
221609
|
+
});
|
|
221610
|
+
|
|
221611
|
+
// lib/perch-ai/access.ts
|
|
221612
|
+
function hasPrivilegedPerchAiRole(memberships) {
|
|
221613
|
+
return memberships.some(
|
|
221614
|
+
(membership) => membership.status === "active" && (membership.role === "admin" || membership.role === "internal")
|
|
221615
|
+
);
|
|
221616
|
+
}
|
|
221617
|
+
function canShowPerchDevTools(membershipRole) {
|
|
221618
|
+
if (!membershipRole) return false;
|
|
221619
|
+
return hasPrivilegedPerchAiRole([
|
|
221620
|
+
{
|
|
221621
|
+
role: membershipRole,
|
|
221622
|
+
status: "active"
|
|
221623
|
+
}
|
|
221624
|
+
]);
|
|
221625
|
+
}
|
|
221626
|
+
var init_access = __esm({
|
|
221627
|
+
"lib/perch-ai/access.ts"() {
|
|
221628
|
+
}
|
|
221629
|
+
});
|
|
221630
|
+
|
|
221631
|
+
// features/perchTerminal/runtime/usage/usageSummary.ts
|
|
221632
|
+
function collectUsageRunIds(input) {
|
|
221633
|
+
const runIds = /* @__PURE__ */ new Set();
|
|
221634
|
+
for (const runId of input.usageRunIds ?? []) {
|
|
221635
|
+
const cleaned = runId.trim();
|
|
221636
|
+
if (cleaned) runIds.add(cleaned);
|
|
221637
|
+
}
|
|
221638
|
+
for (const message of input.recentMessages ?? []) {
|
|
221639
|
+
if (message.kind !== "text" || message.role !== "assistant") continue;
|
|
221640
|
+
const stateRunId = message.operatorState?.runId?.trim();
|
|
221641
|
+
if (stateRunId) runIds.add(stateRunId);
|
|
221642
|
+
for (const event of message.operatorState?.events ?? []) {
|
|
221643
|
+
if (event.type === "turn_started" && event.runId.trim()) runIds.add(event.runId.trim());
|
|
221644
|
+
}
|
|
221645
|
+
for (const event of message.transcriptEvents ?? []) {
|
|
221646
|
+
if (event.type === "turn_started" && event.runId.trim()) runIds.add(event.runId.trim());
|
|
221647
|
+
}
|
|
221648
|
+
}
|
|
221649
|
+
return Array.from(runIds).slice(-250);
|
|
221650
|
+
}
|
|
221651
|
+
function summarizeInferenceUsageRows(rows, options = {}) {
|
|
221652
|
+
const promptTokens = sumRows(rows, "prompt_tokens");
|
|
221653
|
+
const completionTokens = sumRows(rows, "completion_tokens");
|
|
221654
|
+
const cachedTokens = sumRows(rows, "cached_tokens");
|
|
221655
|
+
const totalTokens = sumRows(rows, "total_tokens");
|
|
221656
|
+
const estimatedCostUsd = rows.reduce((sum, row) => sum + readNumber(row.estimated_cost_usd), 0);
|
|
221657
|
+
const created = rows.map((row) => row.created_at).filter((value) => typeof value === "string" && value.length > 0).sort();
|
|
221658
|
+
const summary = {
|
|
221659
|
+
ok: true,
|
|
221660
|
+
calls: rows.length,
|
|
221661
|
+
promptTokens,
|
|
221662
|
+
completionTokens,
|
|
221663
|
+
cachedTokens,
|
|
221664
|
+
totalTokens,
|
|
221665
|
+
estimatedCostUsd,
|
|
221666
|
+
runCount: new Set(options.runIds?.length ? options.runIds : rows.map((row) => row.run_id).filter(Boolean)).size,
|
|
221667
|
+
since: created[0] ?? null,
|
|
221668
|
+
through: created[created.length - 1] ?? null,
|
|
221669
|
+
showModelDetails: Boolean(options.showModelDetails)
|
|
221670
|
+
};
|
|
221671
|
+
if (summary.showModelDetails) {
|
|
221672
|
+
summary.modelBreakdown = buildModelBreakdown(rows);
|
|
221673
|
+
}
|
|
221674
|
+
return summary;
|
|
221675
|
+
}
|
|
221676
|
+
function formatUsageSummaryText(summary) {
|
|
221677
|
+
const lines = [
|
|
221678
|
+
"Usage this session",
|
|
221679
|
+
`- Calls: ${formatInteger(summary.calls)}`,
|
|
221680
|
+
`- Tokens: ${formatInteger(summary.totalTokens)} total (${formatInteger(summary.promptTokens)} input \xB7 ${formatInteger(summary.completionTokens)} output${summary.cachedTokens ? ` \xB7 ${formatInteger(summary.cachedTokens)} cached` : ""})`,
|
|
221681
|
+
`- Estimated cost: ${formatUsd(summary.estimatedCostUsd)}`
|
|
221682
|
+
];
|
|
221683
|
+
if (summary.calls === 0) {
|
|
221684
|
+
lines.push("- Nothing has been recorded for this session yet.");
|
|
221685
|
+
}
|
|
221686
|
+
if (summary.showModelDetails && summary.modelBreakdown?.length) {
|
|
221687
|
+
lines.push("", "Internal detail:");
|
|
221688
|
+
for (const row of summary.modelBreakdown.slice(0, 8)) {
|
|
221689
|
+
lines.push(
|
|
221690
|
+
`- ${row.label}: ${formatInteger(row.totalTokens)} tokens \xB7 ${formatUsd(row.estimatedCostUsd)} \xB7 ${formatInteger(row.calls)} calls`
|
|
221691
|
+
);
|
|
221692
|
+
}
|
|
221693
|
+
}
|
|
221694
|
+
return lines.join("\n");
|
|
221695
|
+
}
|
|
221696
|
+
function buildModelBreakdown(rows) {
|
|
221697
|
+
const byModel = /* @__PURE__ */ new Map();
|
|
221698
|
+
for (const row of rows) {
|
|
221699
|
+
const label = [row.model_option_id, row.provider, row.model].filter((value) => typeof value === "string" && value.trim()).join(" \xB7 ") || "unknown model";
|
|
221700
|
+
const current = byModel.get(label) ?? {
|
|
221701
|
+
label,
|
|
221702
|
+
calls: 0,
|
|
221703
|
+
promptTokens: 0,
|
|
221704
|
+
completionTokens: 0,
|
|
221705
|
+
cachedTokens: 0,
|
|
221706
|
+
totalTokens: 0,
|
|
221707
|
+
estimatedCostUsd: 0
|
|
221708
|
+
};
|
|
221709
|
+
current.calls += 1;
|
|
221710
|
+
current.promptTokens += readNumber(row.prompt_tokens);
|
|
221711
|
+
current.completionTokens += readNumber(row.completion_tokens);
|
|
221712
|
+
current.cachedTokens += readNumber(row.cached_tokens);
|
|
221713
|
+
current.totalTokens += readNumber(row.total_tokens);
|
|
221714
|
+
current.estimatedCostUsd += readNumber(row.estimated_cost_usd);
|
|
221715
|
+
byModel.set(label, current);
|
|
221716
|
+
}
|
|
221717
|
+
return Array.from(byModel.values()).sort((a, b2) => b2.estimatedCostUsd - a.estimatedCostUsd);
|
|
221718
|
+
}
|
|
221719
|
+
function sumRows(rows, key) {
|
|
221720
|
+
return rows.reduce((sum, row) => sum + readNumber(row[key]), 0);
|
|
221721
|
+
}
|
|
221722
|
+
function readNumber(value) {
|
|
221723
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
221724
|
+
if (typeof value === "string") {
|
|
221725
|
+
const parsed = Number(value);
|
|
221726
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
221727
|
+
}
|
|
221728
|
+
return 0;
|
|
221729
|
+
}
|
|
221730
|
+
function formatInteger(value) {
|
|
221731
|
+
return Math.max(0, Math.round(value)).toLocaleString("en-US");
|
|
221732
|
+
}
|
|
221733
|
+
function formatUsd(value) {
|
|
221734
|
+
if (!Number.isFinite(value) || value <= 0) return "$0.00";
|
|
221735
|
+
return value < 0.01 ? `$${value.toFixed(4)}` : `$${value.toFixed(2)}`;
|
|
221736
|
+
}
|
|
221737
|
+
var init_usageSummary = __esm({
|
|
221738
|
+
"features/perchTerminal/runtime/usage/usageSummary.ts"() {
|
|
221739
|
+
"use strict";
|
|
221740
|
+
}
|
|
221741
|
+
});
|
|
221742
|
+
|
|
221743
|
+
// features/perchTerminal/runtime/usage/runUsageTurn.ts
|
|
221744
|
+
async function runUsageTurn(input, deps) {
|
|
221745
|
+
const startedAt = Date.now();
|
|
221746
|
+
const runId = input.clientRunId ?? `usage-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
221747
|
+
const now14 = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
221748
|
+
const events = [];
|
|
221749
|
+
const emit = (event) => {
|
|
221750
|
+
events.push(event);
|
|
221751
|
+
deps.onEvent?.(event);
|
|
221752
|
+
};
|
|
221753
|
+
emit({ type: "turn_started", runId, ts: now14(), isSystemNotification: false });
|
|
221754
|
+
let userMessageId = null;
|
|
221755
|
+
if (input.workspaceId && input.skipUserMessagePersistence !== true) {
|
|
221756
|
+
userMessageId = await deps.persistUserMessage({
|
|
221757
|
+
workspaceId: input.workspaceId,
|
|
221758
|
+
threadId: input.threadId,
|
|
221759
|
+
role: "user",
|
|
221760
|
+
mode: input.chatMode,
|
|
221761
|
+
content: input.trimmedInput
|
|
221762
|
+
});
|
|
221763
|
+
if (userMessageId) {
|
|
221764
|
+
emit({ type: "user_message_persisted", messageId: userMessageId, ts: now14() });
|
|
221765
|
+
}
|
|
221766
|
+
}
|
|
221767
|
+
const runIds = collectUsageRunIds({
|
|
221768
|
+
usageRunIds: input.usageRunIds,
|
|
221769
|
+
recentMessages: input.recentMessages
|
|
221770
|
+
});
|
|
221771
|
+
const showModelDetails = canShowPerchDevTools(input.sessionContext.membershipRole);
|
|
221772
|
+
const summary = await resolveUsageSummary(input, runIds, showModelDetails);
|
|
221773
|
+
const assistantText = formatUsageSummaryText(summary);
|
|
221774
|
+
let assistantMessageId = null;
|
|
221775
|
+
if (input.workspaceId) {
|
|
221776
|
+
assistantMessageId = await deps.persistAssistantMessage({
|
|
221777
|
+
workspaceId: input.workspaceId,
|
|
221778
|
+
threadId: input.threadId,
|
|
221779
|
+
role: "assistant",
|
|
221780
|
+
mode: input.chatMode,
|
|
221781
|
+
content: assistantText,
|
|
221782
|
+
contentJson: {
|
|
221783
|
+
slashCommand: "usage",
|
|
221784
|
+
turnSummary: assistantText,
|
|
221785
|
+
usageSummary: summary,
|
|
221786
|
+
transcriptEvents: events
|
|
221787
|
+
}
|
|
221788
|
+
});
|
|
221789
|
+
}
|
|
221790
|
+
emit({
|
|
221791
|
+
type: "assistant_response",
|
|
221792
|
+
text: assistantText,
|
|
221793
|
+
messageId: assistantMessageId ?? runId,
|
|
221794
|
+
ts: now14()
|
|
221795
|
+
});
|
|
221796
|
+
const durationMs = Date.now() - startedAt;
|
|
221797
|
+
emit({ type: "turn_completed", runId, durationMs, status: "completed", ts: now14() });
|
|
221798
|
+
return {
|
|
221799
|
+
status: "completed",
|
|
221800
|
+
assistantText,
|
|
221801
|
+
messageId: assistantMessageId ?? runId,
|
|
221802
|
+
runId,
|
|
221803
|
+
durationMs
|
|
221804
|
+
};
|
|
221805
|
+
}
|
|
221806
|
+
async function resolveUsageSummary(input, runIds, showModelDetails) {
|
|
221807
|
+
if (runIds.length === 0) {
|
|
221808
|
+
return summarizeInferenceUsageRows([], { showModelDetails, runIds });
|
|
221809
|
+
}
|
|
221810
|
+
if (input.supabase && input.userId && input.workspaceId) {
|
|
221811
|
+
const rows = await fetchUsageRows(input.supabase, {
|
|
221812
|
+
userId: input.userId,
|
|
221813
|
+
workspaceId: input.workspaceId,
|
|
221814
|
+
runIds
|
|
221815
|
+
});
|
|
221816
|
+
return summarizeInferenceUsageRows(rows, { showModelDetails, runIds });
|
|
221817
|
+
}
|
|
221818
|
+
const viaServer = await fetchUsageSummaryFromServer(input, runIds);
|
|
221819
|
+
if (viaServer) return viaServer;
|
|
221820
|
+
return summarizeInferenceUsageRows([], { showModelDetails, runIds });
|
|
221821
|
+
}
|
|
221822
|
+
async function fetchUsageRows(supabase, input) {
|
|
221823
|
+
const { data, error } = await supabase.from("perch_ai_inference_usage").select("*").eq("user_id", input.userId).eq("workspace_id", input.workspaceId).in("run_id", input.runIds).order("created_at", { ascending: true }).limit(1e3);
|
|
221824
|
+
if (error) throw error;
|
|
221825
|
+
return data ?? [];
|
|
221826
|
+
}
|
|
221827
|
+
async function fetchUsageSummaryFromServer(input, runIds) {
|
|
221828
|
+
const appUrl = input.cliServerAppUrl?.trim();
|
|
221829
|
+
const token = input.cliServerAccessToken?.trim();
|
|
221830
|
+
if (!appUrl || !token || typeof fetch !== "function") return null;
|
|
221831
|
+
const controller = new AbortController();
|
|
221832
|
+
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
221833
|
+
try {
|
|
221834
|
+
const response = await fetch(`${appUrl.replace(/\/+$/, "")}/api/perch-terminal/usage`, {
|
|
221835
|
+
method: "POST",
|
|
221836
|
+
headers: {
|
|
221837
|
+
Accept: "application/json",
|
|
221838
|
+
"Content-Type": "application/json",
|
|
221839
|
+
Authorization: `Bearer ${token}`
|
|
221840
|
+
},
|
|
221841
|
+
body: JSON.stringify({ runIds }),
|
|
221842
|
+
signal: controller.signal
|
|
221843
|
+
});
|
|
221844
|
+
if (!response.ok) return null;
|
|
221845
|
+
const payload = await response.json();
|
|
221846
|
+
return payload.ok ? payload.summary : null;
|
|
221847
|
+
} catch {
|
|
221848
|
+
return null;
|
|
221849
|
+
} finally {
|
|
221850
|
+
clearTimeout(timeout);
|
|
221851
|
+
}
|
|
221852
|
+
}
|
|
221853
|
+
var init_runUsageTurn = __esm({
|
|
221854
|
+
"features/perchTerminal/runtime/usage/runUsageTurn.ts"() {
|
|
221855
|
+
"use strict";
|
|
221856
|
+
init_access();
|
|
221857
|
+
init_usageSummary();
|
|
221858
|
+
}
|
|
221859
|
+
});
|
|
221860
|
+
|
|
221596
221861
|
// features/perchTerminal/runtime/commands/sharedSlashCommands.ts
|
|
221597
221862
|
function parseSharedSlashCommand(raw) {
|
|
221598
|
-
return parseFlockCommand(raw);
|
|
221863
|
+
return parseUsageCommand(raw) ?? parseFlockCommand(raw);
|
|
221864
|
+
}
|
|
221865
|
+
function sharedSlashCommandRunsAsTurn(command) {
|
|
221866
|
+
if (!command) return false;
|
|
221867
|
+
if (command.kind === "usage") return true;
|
|
221868
|
+
return Boolean(command.task);
|
|
221599
221869
|
}
|
|
221600
221870
|
function resolveTurnRunnerForPrompt(raw) {
|
|
221601
221871
|
const parsed = parseSharedSlashCommand(raw);
|
|
221602
221872
|
if (!parsed) return null;
|
|
221873
|
+
if (parsed.kind === "usage") return runUsageTurn;
|
|
221603
221874
|
return runFlockTurn;
|
|
221604
221875
|
}
|
|
221605
221876
|
var init_sharedSlashCommands = __esm({
|
|
@@ -221607,6 +221878,8 @@ var init_sharedSlashCommands = __esm({
|
|
|
221607
221878
|
"use strict";
|
|
221608
221879
|
init_flockCommand();
|
|
221609
221880
|
init_runFlockTurn();
|
|
221881
|
+
init_usageCommand();
|
|
221882
|
+
init_runUsageTurn();
|
|
221610
221883
|
}
|
|
221611
221884
|
});
|
|
221612
221885
|
|
|
@@ -230206,6 +230479,7 @@ function buildCliTurnInput(input, resolved) {
|
|
|
230206
230479
|
userId,
|
|
230207
230480
|
selectedSourceId: input.selectedSourceId ?? null,
|
|
230208
230481
|
recentMessages: input.recentMessages ?? [],
|
|
230482
|
+
usageRunIds: input.usageRunIds ?? [],
|
|
230209
230483
|
sessionContext: emptyPerchTerminalSessionContext({
|
|
230210
230484
|
userId,
|
|
230211
230485
|
workspaceId
|
|
@@ -285705,7 +285979,7 @@ async function runReadlineInteractivePerchCli(writer, deps, options) {
|
|
|
285705
285979
|
if (!prompt) continue;
|
|
285706
285980
|
if (prompt === "/exit" || prompt === "exit" || prompt === "quit") break;
|
|
285707
285981
|
const sharedCommand = parseSharedSlashCommand(prompt);
|
|
285708
|
-
const runsAsSharedTurn =
|
|
285982
|
+
const runsAsSharedTurn = sharedSlashCommandRunsAsTurn(sharedCommand);
|
|
285709
285983
|
if (prompt.startsWith("/") && !runsAsSharedTurn) {
|
|
285710
285984
|
try {
|
|
285711
285985
|
const commandResult = await runInteractiveSlashCommand({
|
|
@@ -285743,6 +286017,7 @@ async function runReadlineInteractivePerchCli(writer, deps, options) {
|
|
|
285743
286017
|
permissionMode: state.permissionMode,
|
|
285744
286018
|
threadId: state.threadId,
|
|
285745
286019
|
recentMessages: state.recentMessages,
|
|
286020
|
+
usageRunIds: state.usageRunIds,
|
|
285746
286021
|
userId: hostedContext.userId,
|
|
285747
286022
|
workspaceId: hostedContext.workspaceId,
|
|
285748
286023
|
permanentMemories: hostedContext.permanentMemories,
|
|
@@ -285761,6 +286036,7 @@ async function runReadlineInteractivePerchCli(writer, deps, options) {
|
|
|
285761
286036
|
appendRecentMessage(state.recentMessages, "assistant", result2.assistantText.trim());
|
|
285762
286037
|
}
|
|
285763
286038
|
trimRecentMessages(state.recentMessages);
|
|
286039
|
+
recordCliUsageRunId(state, result2.runId);
|
|
285764
286040
|
state.contextSnapshot = result2.contextSnapshot ?? state.contextSnapshot;
|
|
285765
286041
|
const admitted = await admitCliLearningMemory(connection, {
|
|
285766
286042
|
prompt,
|
|
@@ -285899,7 +286175,7 @@ async function runInkInteractivePerchCli(writer, deps, options) {
|
|
|
285899
286175
|
return;
|
|
285900
286176
|
}
|
|
285901
286177
|
const sharedTurnCommand = parseSharedSlashCommand(prompt);
|
|
285902
|
-
if (prompt.startsWith("/") && !sharedTurnCommand
|
|
286178
|
+
if (prompt.startsWith("/") && !sharedSlashCommandRunsAsTurn(sharedTurnCommand)) {
|
|
285903
286179
|
const commandWriter = {
|
|
285904
286180
|
stdout: (text) => {
|
|
285905
286181
|
const clean = text.trim();
|
|
@@ -285973,6 +286249,7 @@ async function runInkInteractivePerchCli(writer, deps, options) {
|
|
|
285973
286249
|
permissionMode: state.permissionMode,
|
|
285974
286250
|
threadId: state.threadId,
|
|
285975
286251
|
recentMessages: state.recentMessages,
|
|
286252
|
+
usageRunIds: state.usageRunIds,
|
|
285976
286253
|
userId: hostedContext.userId,
|
|
285977
286254
|
workspaceId: hostedContext.workspaceId,
|
|
285978
286255
|
permanentMemories: hostedContext.permanentMemories,
|
|
@@ -286349,6 +286626,7 @@ async function runInkInteractivePerchCli(writer, deps, options) {
|
|
|
286349
286626
|
appendRecentMessage(state.recentMessages, "user", prompt);
|
|
286350
286627
|
if (assistantText) appendRecentMessage(state.recentMessages, "assistant", assistantText);
|
|
286351
286628
|
trimRecentMessages(state.recentMessages);
|
|
286629
|
+
recordCliUsageRunId(state, result2.runId);
|
|
286352
286630
|
state.contextSnapshot = result2.contextSnapshot ?? state.contextSnapshot;
|
|
286353
286631
|
const admitted = await admitCliLearningMemory(connection, {
|
|
286354
286632
|
prompt,
|
|
@@ -286740,6 +287018,7 @@ async function runInteractiveSlashCommand(input) {
|
|
|
286740
287018
|
}
|
|
286741
287019
|
input.state.threadId = parsed.value === "new" ? `cli-${Date.now()}` : parsed.value;
|
|
286742
287020
|
input.state.recentMessages = [];
|
|
287021
|
+
input.state.usageRunIds = [];
|
|
286743
287022
|
input.state.contextSnapshot = null;
|
|
286744
287023
|
input.state.persistedThreadUpdatedAt = null;
|
|
286745
287024
|
await hydrateInteractiveCliState(input.state);
|
|
@@ -286747,6 +287026,7 @@ async function runInteractiveSlashCommand(input) {
|
|
|
286747
287026
|
return "continue";
|
|
286748
287027
|
case "clear":
|
|
286749
287028
|
input.state.recentMessages = [];
|
|
287029
|
+
input.state.usageRunIds = [];
|
|
286750
287030
|
input.state.contextSnapshot = null;
|
|
286751
287031
|
input.state.persistedThreadUpdatedAt = null;
|
|
286752
287032
|
clearThreadSession(input.state.threadId);
|
|
@@ -286997,6 +287277,7 @@ function createInteractiveCliState(options) {
|
|
|
286997
287277
|
cliLocalTools: options.cliLocalTools ?? true,
|
|
286998
287278
|
appUrl: resolveCliAppUrl(options.appUrl ?? null, null),
|
|
286999
287279
|
recentMessages: [],
|
|
287280
|
+
usageRunIds: [],
|
|
287000
287281
|
contextSnapshot: null,
|
|
287001
287282
|
persistedThreadUpdatedAt: null
|
|
287002
287283
|
};
|
|
@@ -287013,6 +287294,7 @@ async function syncInteractiveCliThreadScope(state, connection, options = {}) {
|
|
|
287013
287294
|
if (!options.force && state.threadScopeKey === nextScopeKey) return;
|
|
287014
287295
|
state.threadScopeKey = nextScopeKey;
|
|
287015
287296
|
state.recentMessages = [];
|
|
287297
|
+
state.usageRunIds = [];
|
|
287016
287298
|
state.contextSnapshot = null;
|
|
287017
287299
|
state.persistedThreadUpdatedAt = null;
|
|
287018
287300
|
clearThreadSession(state.threadId);
|
|
@@ -287751,6 +288033,11 @@ function trimRecentMessages(messages) {
|
|
|
287751
288033
|
messages.splice(0, messages.length - 30);
|
|
287752
288034
|
}
|
|
287753
288035
|
}
|
|
288036
|
+
function recordCliUsageRunId(state, runId) {
|
|
288037
|
+
const cleaned = runId?.trim();
|
|
288038
|
+
if (!cleaned) return;
|
|
288039
|
+
state.usageRunIds = [.../* @__PURE__ */ new Set([...state.usageRunIds, cleaned])].slice(-250);
|
|
288040
|
+
}
|
|
287754
288041
|
function createCliRunId() {
|
|
287755
288042
|
return `cli-turn-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
287756
288043
|
}
|
|
@@ -288092,6 +288379,7 @@ Commands:
|
|
|
288092
288379
|
/help Show this list.
|
|
288093
288380
|
/status Show cwd, auth, mode, persona, permission, and thread.
|
|
288094
288381
|
/skills [name] List Perch core skills, or show one skill body.
|
|
288382
|
+
/usage Show tokens and estimated cost for this session.
|
|
288095
288383
|
/cwd [dir] Show or change the working directory.
|
|
288096
288384
|
/permission [mode] Show or set default, auto_review, take_the_wheel, or plan.
|
|
288097
288385
|
/permissions [mode] Alias for /permission.
|
|
@@ -288151,6 +288439,7 @@ Commands:
|
|
|
288151
288439
|
];
|
|
288152
288440
|
PERCH_SPLASH_COMMANDS = [
|
|
288153
288441
|
["/status", "show auth, route, tools, and thread"],
|
|
288442
|
+
["/usage", "show session tokens and estimated cost"],
|
|
288154
288443
|
["/skills", "show reusable operator skills"],
|
|
288155
288444
|
["/persona", "swap saffron or quill"],
|
|
288156
288445
|
["/permission", "change autonomy for the next turns"],
|