perchai-cli 2.4.28 → 2.4.30
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 +326 -12
- 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
|
@@ -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();
|
|
@@ -83126,6 +83125,7 @@ function listFinancialRoleIds() {
|
|
|
83126
83125
|
var FINANCIAL_ROLE_REGISTRY, evidenceScoutManifest;
|
|
83127
83126
|
var init_financialRoles = __esm({
|
|
83128
83127
|
"features/perchTerminal/agentPlatform/financialRoles.ts"() {
|
|
83128
|
+
"use strict";
|
|
83129
83129
|
FINANCIAL_ROLE_REGISTRY = /* @__PURE__ */ new Map();
|
|
83130
83130
|
evidenceScoutManifest = {
|
|
83131
83131
|
workerId: "evidence_scout",
|
|
@@ -86691,6 +86691,7 @@ function truncateHistoryLine(value, max2) {
|
|
|
86691
86691
|
}
|
|
86692
86692
|
var init_operatorTruth = __esm({
|
|
86693
86693
|
"features/perchTerminal/runtime/operatorTruth.ts"() {
|
|
86694
|
+
"use strict";
|
|
86694
86695
|
}
|
|
86695
86696
|
});
|
|
86696
86697
|
|
|
@@ -118883,8 +118884,12 @@ var init_reasoningExtractor = __esm({
|
|
|
118883
118884
|
|
|
118884
118885
|
// features/perchTerminal/runtime/modelRouter.ts
|
|
118885
118886
|
async function callModelRouter(_config, request, opts) {
|
|
118887
|
+
const explicitProxyUrl = resolveModelCallProxyUrl(opts?.cliServerAppUrl ?? null);
|
|
118886
118888
|
if (typeof window !== "undefined") {
|
|
118887
|
-
return callModelRouterViaServer(request, opts);
|
|
118889
|
+
return callModelRouterViaServer(request, opts, explicitProxyUrl ?? void 0);
|
|
118890
|
+
}
|
|
118891
|
+
if (explicitProxyUrl) {
|
|
118892
|
+
return callModelRouterViaServer(request, opts, explicitProxyUrl);
|
|
118888
118893
|
}
|
|
118889
118894
|
const nodeProxyUrl = resolveNodeModelCallProxyUrl();
|
|
118890
118895
|
if (nodeProxyUrl) {
|
|
@@ -119037,7 +119042,7 @@ async function callModelRouterViaServer(request, opts, endpoint = "/api/perch-te
|
|
|
119037
119042
|
headers: {
|
|
119038
119043
|
"Content-Type": "application/json",
|
|
119039
119044
|
...hasStreamingHandler ? { Accept: "text/event-stream" } : {},
|
|
119040
|
-
...
|
|
119045
|
+
...modelProxyAuthHeader(opts?.cliServerAccessToken ?? null)
|
|
119041
119046
|
},
|
|
119042
119047
|
body,
|
|
119043
119048
|
signal: _signal
|
|
@@ -119201,7 +119206,10 @@ function waitForModelProxyRetry(attempt, signal) {
|
|
|
119201
119206
|
}
|
|
119202
119207
|
function resolveNodeModelCallProxyUrl() {
|
|
119203
119208
|
if (typeof process === "undefined") return null;
|
|
119204
|
-
|
|
119209
|
+
return resolveModelCallProxyUrl(process.env[MODEL_CALL_PROXY_ENV]?.trim() ?? null);
|
|
119210
|
+
}
|
|
119211
|
+
function resolveModelCallProxyUrl(rawInput) {
|
|
119212
|
+
const raw = rawInput?.trim();
|
|
119205
119213
|
if (!raw) return null;
|
|
119206
119214
|
if (raw.endsWith("/api/perch-terminal/model-call")) return raw;
|
|
119207
119215
|
try {
|
|
@@ -119214,9 +119222,8 @@ function resolveNodeModelCallProxyUrl() {
|
|
|
119214
119222
|
return null;
|
|
119215
119223
|
}
|
|
119216
119224
|
}
|
|
119217
|
-
function
|
|
119218
|
-
|
|
119219
|
-
const token = process.env[MODEL_CALL_PROXY_TOKEN_ENV]?.trim();
|
|
119225
|
+
function modelProxyAuthHeader(explicitToken) {
|
|
119226
|
+
const token = explicitToken?.trim() || (typeof process !== "undefined" ? process.env[MODEL_CALL_PROXY_TOKEN_ENV]?.trim() : "");
|
|
119220
119227
|
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
119221
119228
|
}
|
|
119222
119229
|
function getRouterAvailabilityReport() {
|
|
@@ -217486,7 +217493,9 @@ async function runModelToolLoop(input) {
|
|
|
217486
217493
|
runId,
|
|
217487
217494
|
lane: activeLane,
|
|
217488
217495
|
source: "tool_loop"
|
|
217489
|
-
})
|
|
217496
|
+
}),
|
|
217497
|
+
cliServerAppUrl: input.cliServerAppUrl ?? null,
|
|
217498
|
+
cliServerAccessToken: input.cliServerAccessToken ?? null
|
|
217490
217499
|
}
|
|
217491
217500
|
);
|
|
217492
217501
|
result2 = routerResult;
|
|
@@ -217663,6 +217672,8 @@ async function runModelToolLoop(input) {
|
|
|
217663
217672
|
runId,
|
|
217664
217673
|
userId: input.userId,
|
|
217665
217674
|
workspaceId: input.workspaceId,
|
|
217675
|
+
cliServerAppUrl: input.cliServerAppUrl ?? null,
|
|
217676
|
+
cliServerAccessToken: input.cliServerAccessToken ?? null,
|
|
217666
217677
|
signal,
|
|
217667
217678
|
onEvent
|
|
217668
217679
|
});
|
|
@@ -217711,6 +217722,8 @@ async function runModelToolLoop(input) {
|
|
|
217711
217722
|
runId,
|
|
217712
217723
|
userId: input.userId,
|
|
217713
217724
|
workspaceId: input.workspaceId,
|
|
217725
|
+
cliServerAppUrl: input.cliServerAppUrl ?? null,
|
|
217726
|
+
cliServerAccessToken: input.cliServerAccessToken ?? null,
|
|
217714
217727
|
signal,
|
|
217715
217728
|
onEvent
|
|
217716
217729
|
});
|
|
@@ -218241,6 +218254,8 @@ async function runModelToolLoop(input) {
|
|
|
218241
218254
|
runId,
|
|
218242
218255
|
userId: input.userId,
|
|
218243
218256
|
workspaceId: input.workspaceId,
|
|
218257
|
+
cliServerAppUrl: input.cliServerAppUrl ?? null,
|
|
218258
|
+
cliServerAccessToken: input.cliServerAccessToken ?? null,
|
|
218244
218259
|
signal,
|
|
218245
218260
|
onEvent
|
|
218246
218261
|
});
|
|
@@ -218313,7 +218328,9 @@ async function requestClosingResponse(input) {
|
|
|
218313
218328
|
runId: input.runId,
|
|
218314
218329
|
lane: input.lane,
|
|
218315
218330
|
source: "tool_loop_closing"
|
|
218316
|
-
})
|
|
218331
|
+
}),
|
|
218332
|
+
cliServerAppUrl: input.cliServerAppUrl ?? null,
|
|
218333
|
+
cliServerAccessToken: input.cliServerAccessToken ?? null
|
|
218317
218334
|
}
|
|
218318
218335
|
);
|
|
218319
218336
|
if (!routerResult.ok) return null;
|
|
@@ -218716,6 +218733,8 @@ async function finalizeChatAnswerAfterReadSuppression(input) {
|
|
|
218716
218733
|
runId: input.runId,
|
|
218717
218734
|
userId: input.userId,
|
|
218718
218735
|
workspaceId: input.workspaceId,
|
|
218736
|
+
cliServerAppUrl: input.cliServerAppUrl ?? null,
|
|
218737
|
+
cliServerAccessToken: input.cliServerAccessToken ?? null,
|
|
218719
218738
|
signal: input.signal,
|
|
218720
218739
|
onEvent: input.onEvent
|
|
218721
218740
|
}) : null;
|
|
@@ -219611,6 +219630,10 @@ ${contextBlock}
|
|
|
219611
219630
|
selectedSourceId: ctx.selectedSourceId,
|
|
219612
219631
|
supabaseConfigured: ctx.supabaseConfigured,
|
|
219613
219632
|
supabase: ctx.supabase,
|
|
219633
|
+
cliServerAppUrl: ctx.cliServerAppUrl,
|
|
219634
|
+
cliServerAccessToken: ctx.cliServerAccessToken,
|
|
219635
|
+
marketDeskProxyAppUrl: ctx.marketDeskProxyAppUrl,
|
|
219636
|
+
marketDeskProxyAccessToken: ctx.marketDeskProxyAccessToken,
|
|
219614
219637
|
runId: runtimeWorkerRunId,
|
|
219615
219638
|
chatMode: "agents",
|
|
219616
219639
|
forceToolUse: DELIVERY_WORKER_IDS.has(args.workerId),
|
|
@@ -221530,6 +221553,10 @@ function buildSpawnContext(input, flockId, signal, emit, worker) {
|
|
|
221530
221553
|
selectedSourceId: input.selectedSourceId,
|
|
221531
221554
|
supabaseConfigured: input.supabaseConfigured,
|
|
221532
221555
|
supabase: input.supabase ?? null,
|
|
221556
|
+
cliServerAppUrl: input.cliServerAppUrl ?? null,
|
|
221557
|
+
cliServerAccessToken: input.cliServerAccessToken ?? null,
|
|
221558
|
+
marketDeskProxyAppUrl: input.marketDeskProxyAppUrl ?? null,
|
|
221559
|
+
marketDeskProxyAccessToken: input.marketDeskProxyAccessToken ?? null,
|
|
221533
221560
|
runId: flockId,
|
|
221534
221561
|
// Default: the currently selected Perch model path. Only an explicit
|
|
221535
221562
|
// "use <model> as <role>" request in the task pins this worker elsewhere.
|
|
@@ -221593,13 +221620,282 @@ var init_runFlockTurn = __esm({
|
|
|
221593
221620
|
}
|
|
221594
221621
|
});
|
|
221595
221622
|
|
|
221623
|
+
// features/perchTerminal/runtime/usage/usageCommand.ts
|
|
221624
|
+
function parseUsageCommand(raw) {
|
|
221625
|
+
const trimmed = raw.trim();
|
|
221626
|
+
if (!/^\/usage(?:\s|$)/i.test(trimmed)) return null;
|
|
221627
|
+
const task = trimmed.slice("/usage".length).trim();
|
|
221628
|
+
return task ? { kind: "usage", task } : { kind: "usage" };
|
|
221629
|
+
}
|
|
221630
|
+
var init_usageCommand = __esm({
|
|
221631
|
+
"features/perchTerminal/runtime/usage/usageCommand.ts"() {
|
|
221632
|
+
"use strict";
|
|
221633
|
+
}
|
|
221634
|
+
});
|
|
221635
|
+
|
|
221636
|
+
// lib/perch-ai/access.ts
|
|
221637
|
+
function hasPrivilegedPerchAiRole(memberships) {
|
|
221638
|
+
return memberships.some(
|
|
221639
|
+
(membership) => membership.status === "active" && (membership.role === "admin" || membership.role === "internal")
|
|
221640
|
+
);
|
|
221641
|
+
}
|
|
221642
|
+
function canShowPerchDevTools(membershipRole) {
|
|
221643
|
+
if (!membershipRole) return false;
|
|
221644
|
+
return hasPrivilegedPerchAiRole([
|
|
221645
|
+
{
|
|
221646
|
+
role: membershipRole,
|
|
221647
|
+
status: "active"
|
|
221648
|
+
}
|
|
221649
|
+
]);
|
|
221650
|
+
}
|
|
221651
|
+
var init_access = __esm({
|
|
221652
|
+
"lib/perch-ai/access.ts"() {
|
|
221653
|
+
}
|
|
221654
|
+
});
|
|
221655
|
+
|
|
221656
|
+
// features/perchTerminal/runtime/usage/usageSummary.ts
|
|
221657
|
+
function collectUsageRunIds(input) {
|
|
221658
|
+
const runIds = /* @__PURE__ */ new Set();
|
|
221659
|
+
for (const runId of input.usageRunIds ?? []) {
|
|
221660
|
+
const cleaned = runId.trim();
|
|
221661
|
+
if (cleaned) runIds.add(cleaned);
|
|
221662
|
+
}
|
|
221663
|
+
for (const message of input.recentMessages ?? []) {
|
|
221664
|
+
if (message.kind !== "text" || message.role !== "assistant") continue;
|
|
221665
|
+
const stateRunId = message.operatorState?.runId?.trim();
|
|
221666
|
+
if (stateRunId) runIds.add(stateRunId);
|
|
221667
|
+
for (const event of message.operatorState?.events ?? []) {
|
|
221668
|
+
if (event.type === "turn_started" && event.runId.trim()) runIds.add(event.runId.trim());
|
|
221669
|
+
}
|
|
221670
|
+
for (const event of message.transcriptEvents ?? []) {
|
|
221671
|
+
if (event.type === "turn_started" && event.runId.trim()) runIds.add(event.runId.trim());
|
|
221672
|
+
}
|
|
221673
|
+
}
|
|
221674
|
+
return Array.from(runIds).slice(-250);
|
|
221675
|
+
}
|
|
221676
|
+
function summarizeInferenceUsageRows(rows, options = {}) {
|
|
221677
|
+
const promptTokens = sumRows(rows, "prompt_tokens");
|
|
221678
|
+
const completionTokens = sumRows(rows, "completion_tokens");
|
|
221679
|
+
const cachedTokens = sumRows(rows, "cached_tokens");
|
|
221680
|
+
const totalTokens = sumRows(rows, "total_tokens");
|
|
221681
|
+
const estimatedCostUsd = rows.reduce((sum, row) => sum + readNumber(row.estimated_cost_usd), 0);
|
|
221682
|
+
const created = rows.map((row) => row.created_at).filter((value) => typeof value === "string" && value.length > 0).sort();
|
|
221683
|
+
const summary = {
|
|
221684
|
+
ok: true,
|
|
221685
|
+
calls: rows.length,
|
|
221686
|
+
promptTokens,
|
|
221687
|
+
completionTokens,
|
|
221688
|
+
cachedTokens,
|
|
221689
|
+
totalTokens,
|
|
221690
|
+
estimatedCostUsd,
|
|
221691
|
+
runCount: new Set(options.runIds?.length ? options.runIds : rows.map((row) => row.run_id).filter(Boolean)).size,
|
|
221692
|
+
since: created[0] ?? null,
|
|
221693
|
+
through: created[created.length - 1] ?? null,
|
|
221694
|
+
showModelDetails: Boolean(options.showModelDetails)
|
|
221695
|
+
};
|
|
221696
|
+
if (summary.showModelDetails) {
|
|
221697
|
+
summary.modelBreakdown = buildModelBreakdown(rows);
|
|
221698
|
+
}
|
|
221699
|
+
return summary;
|
|
221700
|
+
}
|
|
221701
|
+
function formatUsageSummaryText(summary) {
|
|
221702
|
+
const lines = [
|
|
221703
|
+
"Usage this session",
|
|
221704
|
+
`- Calls: ${formatInteger(summary.calls)}`,
|
|
221705
|
+
`- Tokens: ${formatInteger(summary.totalTokens)} total (${formatInteger(summary.promptTokens)} input \xB7 ${formatInteger(summary.completionTokens)} output${summary.cachedTokens ? ` \xB7 ${formatInteger(summary.cachedTokens)} cached` : ""})`,
|
|
221706
|
+
`- Estimated cost: ${formatUsd(summary.estimatedCostUsd)}`
|
|
221707
|
+
];
|
|
221708
|
+
if (summary.calls === 0) {
|
|
221709
|
+
lines.push("- Nothing has been recorded for this session yet.");
|
|
221710
|
+
}
|
|
221711
|
+
if (summary.showModelDetails && summary.modelBreakdown?.length) {
|
|
221712
|
+
lines.push("", "Internal detail:");
|
|
221713
|
+
for (const row of summary.modelBreakdown.slice(0, 8)) {
|
|
221714
|
+
lines.push(
|
|
221715
|
+
`- ${row.label}: ${formatInteger(row.totalTokens)} tokens \xB7 ${formatUsd(row.estimatedCostUsd)} \xB7 ${formatInteger(row.calls)} calls`
|
|
221716
|
+
);
|
|
221717
|
+
}
|
|
221718
|
+
}
|
|
221719
|
+
return lines.join("\n");
|
|
221720
|
+
}
|
|
221721
|
+
function buildModelBreakdown(rows) {
|
|
221722
|
+
const byModel = /* @__PURE__ */ new Map();
|
|
221723
|
+
for (const row of rows) {
|
|
221724
|
+
const label = [row.model_option_id, row.provider, row.model].filter((value) => typeof value === "string" && value.trim()).join(" \xB7 ") || "unknown model";
|
|
221725
|
+
const current = byModel.get(label) ?? {
|
|
221726
|
+
label,
|
|
221727
|
+
calls: 0,
|
|
221728
|
+
promptTokens: 0,
|
|
221729
|
+
completionTokens: 0,
|
|
221730
|
+
cachedTokens: 0,
|
|
221731
|
+
totalTokens: 0,
|
|
221732
|
+
estimatedCostUsd: 0
|
|
221733
|
+
};
|
|
221734
|
+
current.calls += 1;
|
|
221735
|
+
current.promptTokens += readNumber(row.prompt_tokens);
|
|
221736
|
+
current.completionTokens += readNumber(row.completion_tokens);
|
|
221737
|
+
current.cachedTokens += readNumber(row.cached_tokens);
|
|
221738
|
+
current.totalTokens += readNumber(row.total_tokens);
|
|
221739
|
+
current.estimatedCostUsd += readNumber(row.estimated_cost_usd);
|
|
221740
|
+
byModel.set(label, current);
|
|
221741
|
+
}
|
|
221742
|
+
return Array.from(byModel.values()).sort((a, b2) => b2.estimatedCostUsd - a.estimatedCostUsd);
|
|
221743
|
+
}
|
|
221744
|
+
function sumRows(rows, key) {
|
|
221745
|
+
return rows.reduce((sum, row) => sum + readNumber(row[key]), 0);
|
|
221746
|
+
}
|
|
221747
|
+
function readNumber(value) {
|
|
221748
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
221749
|
+
if (typeof value === "string") {
|
|
221750
|
+
const parsed = Number(value);
|
|
221751
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
221752
|
+
}
|
|
221753
|
+
return 0;
|
|
221754
|
+
}
|
|
221755
|
+
function formatInteger(value) {
|
|
221756
|
+
return Math.max(0, Math.round(value)).toLocaleString("en-US");
|
|
221757
|
+
}
|
|
221758
|
+
function formatUsd(value) {
|
|
221759
|
+
if (!Number.isFinite(value) || value <= 0) return "$0.00";
|
|
221760
|
+
return value < 0.01 ? `$${value.toFixed(4)}` : `$${value.toFixed(2)}`;
|
|
221761
|
+
}
|
|
221762
|
+
var init_usageSummary = __esm({
|
|
221763
|
+
"features/perchTerminal/runtime/usage/usageSummary.ts"() {
|
|
221764
|
+
"use strict";
|
|
221765
|
+
}
|
|
221766
|
+
});
|
|
221767
|
+
|
|
221768
|
+
// features/perchTerminal/runtime/usage/runUsageTurn.ts
|
|
221769
|
+
async function runUsageTurn(input, deps) {
|
|
221770
|
+
const startedAt = Date.now();
|
|
221771
|
+
const runId = input.clientRunId ?? `usage-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
221772
|
+
const now14 = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
221773
|
+
const events = [];
|
|
221774
|
+
const emit = (event) => {
|
|
221775
|
+
events.push(event);
|
|
221776
|
+
deps.onEvent?.(event);
|
|
221777
|
+
};
|
|
221778
|
+
emit({ type: "turn_started", runId, ts: now14(), isSystemNotification: false });
|
|
221779
|
+
let userMessageId = null;
|
|
221780
|
+
if (input.workspaceId && input.skipUserMessagePersistence !== true) {
|
|
221781
|
+
userMessageId = await deps.persistUserMessage({
|
|
221782
|
+
workspaceId: input.workspaceId,
|
|
221783
|
+
threadId: input.threadId,
|
|
221784
|
+
role: "user",
|
|
221785
|
+
mode: input.chatMode,
|
|
221786
|
+
content: input.trimmedInput
|
|
221787
|
+
});
|
|
221788
|
+
if (userMessageId) {
|
|
221789
|
+
emit({ type: "user_message_persisted", messageId: userMessageId, ts: now14() });
|
|
221790
|
+
}
|
|
221791
|
+
}
|
|
221792
|
+
const runIds = collectUsageRunIds({
|
|
221793
|
+
usageRunIds: input.usageRunIds,
|
|
221794
|
+
recentMessages: input.recentMessages
|
|
221795
|
+
});
|
|
221796
|
+
const showModelDetails = canShowPerchDevTools(input.sessionContext.membershipRole);
|
|
221797
|
+
const summary = await resolveUsageSummary(input, runIds, showModelDetails);
|
|
221798
|
+
const assistantText = formatUsageSummaryText(summary);
|
|
221799
|
+
let assistantMessageId = null;
|
|
221800
|
+
if (input.workspaceId) {
|
|
221801
|
+
assistantMessageId = await deps.persistAssistantMessage({
|
|
221802
|
+
workspaceId: input.workspaceId,
|
|
221803
|
+
threadId: input.threadId,
|
|
221804
|
+
role: "assistant",
|
|
221805
|
+
mode: input.chatMode,
|
|
221806
|
+
content: assistantText,
|
|
221807
|
+
contentJson: {
|
|
221808
|
+
slashCommand: "usage",
|
|
221809
|
+
turnSummary: assistantText,
|
|
221810
|
+
usageSummary: summary,
|
|
221811
|
+
transcriptEvents: events
|
|
221812
|
+
}
|
|
221813
|
+
});
|
|
221814
|
+
}
|
|
221815
|
+
emit({
|
|
221816
|
+
type: "assistant_response",
|
|
221817
|
+
text: assistantText,
|
|
221818
|
+
messageId: assistantMessageId ?? runId,
|
|
221819
|
+
ts: now14()
|
|
221820
|
+
});
|
|
221821
|
+
const durationMs = Date.now() - startedAt;
|
|
221822
|
+
emit({ type: "turn_completed", runId, durationMs, status: "completed", ts: now14() });
|
|
221823
|
+
return {
|
|
221824
|
+
status: "completed",
|
|
221825
|
+
assistantText,
|
|
221826
|
+
messageId: assistantMessageId ?? runId,
|
|
221827
|
+
runId,
|
|
221828
|
+
durationMs
|
|
221829
|
+
};
|
|
221830
|
+
}
|
|
221831
|
+
async function resolveUsageSummary(input, runIds, showModelDetails) {
|
|
221832
|
+
if (runIds.length === 0) {
|
|
221833
|
+
return summarizeInferenceUsageRows([], { showModelDetails, runIds });
|
|
221834
|
+
}
|
|
221835
|
+
if (input.supabase && input.userId && input.workspaceId) {
|
|
221836
|
+
const rows = await fetchUsageRows(input.supabase, {
|
|
221837
|
+
userId: input.userId,
|
|
221838
|
+
workspaceId: input.workspaceId,
|
|
221839
|
+
runIds
|
|
221840
|
+
});
|
|
221841
|
+
return summarizeInferenceUsageRows(rows, { showModelDetails, runIds });
|
|
221842
|
+
}
|
|
221843
|
+
const viaServer = await fetchUsageSummaryFromServer(input, runIds);
|
|
221844
|
+
if (viaServer) return viaServer;
|
|
221845
|
+
return summarizeInferenceUsageRows([], { showModelDetails, runIds });
|
|
221846
|
+
}
|
|
221847
|
+
async function fetchUsageRows(supabase, input) {
|
|
221848
|
+
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);
|
|
221849
|
+
if (error) throw error;
|
|
221850
|
+
return data ?? [];
|
|
221851
|
+
}
|
|
221852
|
+
async function fetchUsageSummaryFromServer(input, runIds) {
|
|
221853
|
+
const appUrl = input.cliServerAppUrl?.trim();
|
|
221854
|
+
const token = input.cliServerAccessToken?.trim();
|
|
221855
|
+
if (!appUrl || !token || typeof fetch !== "function") return null;
|
|
221856
|
+
const controller = new AbortController();
|
|
221857
|
+
const timeout = setTimeout(() => controller.abort(), 5e3);
|
|
221858
|
+
try {
|
|
221859
|
+
const response = await fetch(`${appUrl.replace(/\/+$/, "")}/api/perch-terminal/usage`, {
|
|
221860
|
+
method: "POST",
|
|
221861
|
+
headers: {
|
|
221862
|
+
Accept: "application/json",
|
|
221863
|
+
"Content-Type": "application/json",
|
|
221864
|
+
Authorization: `Bearer ${token}`
|
|
221865
|
+
},
|
|
221866
|
+
body: JSON.stringify({ runIds }),
|
|
221867
|
+
signal: controller.signal
|
|
221868
|
+
});
|
|
221869
|
+
if (!response.ok) return null;
|
|
221870
|
+
const payload = await response.json();
|
|
221871
|
+
return payload.ok ? payload.summary : null;
|
|
221872
|
+
} catch {
|
|
221873
|
+
return null;
|
|
221874
|
+
} finally {
|
|
221875
|
+
clearTimeout(timeout);
|
|
221876
|
+
}
|
|
221877
|
+
}
|
|
221878
|
+
var init_runUsageTurn = __esm({
|
|
221879
|
+
"features/perchTerminal/runtime/usage/runUsageTurn.ts"() {
|
|
221880
|
+
"use strict";
|
|
221881
|
+
init_access();
|
|
221882
|
+
init_usageSummary();
|
|
221883
|
+
}
|
|
221884
|
+
});
|
|
221885
|
+
|
|
221596
221886
|
// features/perchTerminal/runtime/commands/sharedSlashCommands.ts
|
|
221597
221887
|
function parseSharedSlashCommand(raw) {
|
|
221598
|
-
return parseFlockCommand(raw);
|
|
221888
|
+
return parseUsageCommand(raw) ?? parseFlockCommand(raw);
|
|
221889
|
+
}
|
|
221890
|
+
function sharedSlashCommandRunsAsTurn(command) {
|
|
221891
|
+
if (!command) return false;
|
|
221892
|
+
if (command.kind === "usage") return true;
|
|
221893
|
+
return Boolean(command.task);
|
|
221599
221894
|
}
|
|
221600
221895
|
function resolveTurnRunnerForPrompt(raw) {
|
|
221601
221896
|
const parsed = parseSharedSlashCommand(raw);
|
|
221602
221897
|
if (!parsed) return null;
|
|
221898
|
+
if (parsed.kind === "usage") return runUsageTurn;
|
|
221603
221899
|
return runFlockTurn;
|
|
221604
221900
|
}
|
|
221605
221901
|
var init_sharedSlashCommands = __esm({
|
|
@@ -221607,6 +221903,8 @@ var init_sharedSlashCommands = __esm({
|
|
|
221607
221903
|
"use strict";
|
|
221608
221904
|
init_flockCommand();
|
|
221609
221905
|
init_runFlockTurn();
|
|
221906
|
+
init_usageCommand();
|
|
221907
|
+
init_runUsageTurn();
|
|
221610
221908
|
}
|
|
221611
221909
|
});
|
|
221612
221910
|
|
|
@@ -230206,6 +230504,7 @@ function buildCliTurnInput(input, resolved) {
|
|
|
230206
230504
|
userId,
|
|
230207
230505
|
selectedSourceId: input.selectedSourceId ?? null,
|
|
230208
230506
|
recentMessages: input.recentMessages ?? [],
|
|
230507
|
+
usageRunIds: input.usageRunIds ?? [],
|
|
230209
230508
|
sessionContext: emptyPerchTerminalSessionContext({
|
|
230210
230509
|
userId,
|
|
230211
230510
|
workspaceId
|
|
@@ -285705,7 +286004,7 @@ async function runReadlineInteractivePerchCli(writer, deps, options) {
|
|
|
285705
286004
|
if (!prompt) continue;
|
|
285706
286005
|
if (prompt === "/exit" || prompt === "exit" || prompt === "quit") break;
|
|
285707
286006
|
const sharedCommand = parseSharedSlashCommand(prompt);
|
|
285708
|
-
const runsAsSharedTurn =
|
|
286007
|
+
const runsAsSharedTurn = sharedSlashCommandRunsAsTurn(sharedCommand);
|
|
285709
286008
|
if (prompt.startsWith("/") && !runsAsSharedTurn) {
|
|
285710
286009
|
try {
|
|
285711
286010
|
const commandResult = await runInteractiveSlashCommand({
|
|
@@ -285743,6 +286042,7 @@ async function runReadlineInteractivePerchCli(writer, deps, options) {
|
|
|
285743
286042
|
permissionMode: state.permissionMode,
|
|
285744
286043
|
threadId: state.threadId,
|
|
285745
286044
|
recentMessages: state.recentMessages,
|
|
286045
|
+
usageRunIds: state.usageRunIds,
|
|
285746
286046
|
userId: hostedContext.userId,
|
|
285747
286047
|
workspaceId: hostedContext.workspaceId,
|
|
285748
286048
|
permanentMemories: hostedContext.permanentMemories,
|
|
@@ -285761,6 +286061,7 @@ async function runReadlineInteractivePerchCli(writer, deps, options) {
|
|
|
285761
286061
|
appendRecentMessage(state.recentMessages, "assistant", result2.assistantText.trim());
|
|
285762
286062
|
}
|
|
285763
286063
|
trimRecentMessages(state.recentMessages);
|
|
286064
|
+
recordCliUsageRunId(state, result2.runId);
|
|
285764
286065
|
state.contextSnapshot = result2.contextSnapshot ?? state.contextSnapshot;
|
|
285765
286066
|
const admitted = await admitCliLearningMemory(connection, {
|
|
285766
286067
|
prompt,
|
|
@@ -285899,7 +286200,7 @@ async function runInkInteractivePerchCli(writer, deps, options) {
|
|
|
285899
286200
|
return;
|
|
285900
286201
|
}
|
|
285901
286202
|
const sharedTurnCommand = parseSharedSlashCommand(prompt);
|
|
285902
|
-
if (prompt.startsWith("/") && !sharedTurnCommand
|
|
286203
|
+
if (prompt.startsWith("/") && !sharedSlashCommandRunsAsTurn(sharedTurnCommand)) {
|
|
285903
286204
|
const commandWriter = {
|
|
285904
286205
|
stdout: (text) => {
|
|
285905
286206
|
const clean = text.trim();
|
|
@@ -285973,6 +286274,7 @@ async function runInkInteractivePerchCli(writer, deps, options) {
|
|
|
285973
286274
|
permissionMode: state.permissionMode,
|
|
285974
286275
|
threadId: state.threadId,
|
|
285975
286276
|
recentMessages: state.recentMessages,
|
|
286277
|
+
usageRunIds: state.usageRunIds,
|
|
285976
286278
|
userId: hostedContext.userId,
|
|
285977
286279
|
workspaceId: hostedContext.workspaceId,
|
|
285978
286280
|
permanentMemories: hostedContext.permanentMemories,
|
|
@@ -286349,6 +286651,7 @@ async function runInkInteractivePerchCli(writer, deps, options) {
|
|
|
286349
286651
|
appendRecentMessage(state.recentMessages, "user", prompt);
|
|
286350
286652
|
if (assistantText) appendRecentMessage(state.recentMessages, "assistant", assistantText);
|
|
286351
286653
|
trimRecentMessages(state.recentMessages);
|
|
286654
|
+
recordCliUsageRunId(state, result2.runId);
|
|
286352
286655
|
state.contextSnapshot = result2.contextSnapshot ?? state.contextSnapshot;
|
|
286353
286656
|
const admitted = await admitCliLearningMemory(connection, {
|
|
286354
286657
|
prompt,
|
|
@@ -286740,6 +287043,7 @@ async function runInteractiveSlashCommand(input) {
|
|
|
286740
287043
|
}
|
|
286741
287044
|
input.state.threadId = parsed.value === "new" ? `cli-${Date.now()}` : parsed.value;
|
|
286742
287045
|
input.state.recentMessages = [];
|
|
287046
|
+
input.state.usageRunIds = [];
|
|
286743
287047
|
input.state.contextSnapshot = null;
|
|
286744
287048
|
input.state.persistedThreadUpdatedAt = null;
|
|
286745
287049
|
await hydrateInteractiveCliState(input.state);
|
|
@@ -286747,6 +287051,7 @@ async function runInteractiveSlashCommand(input) {
|
|
|
286747
287051
|
return "continue";
|
|
286748
287052
|
case "clear":
|
|
286749
287053
|
input.state.recentMessages = [];
|
|
287054
|
+
input.state.usageRunIds = [];
|
|
286750
287055
|
input.state.contextSnapshot = null;
|
|
286751
287056
|
input.state.persistedThreadUpdatedAt = null;
|
|
286752
287057
|
clearThreadSession(input.state.threadId);
|
|
@@ -286997,6 +287302,7 @@ function createInteractiveCliState(options) {
|
|
|
286997
287302
|
cliLocalTools: options.cliLocalTools ?? true,
|
|
286998
287303
|
appUrl: resolveCliAppUrl(options.appUrl ?? null, null),
|
|
286999
287304
|
recentMessages: [],
|
|
287305
|
+
usageRunIds: [],
|
|
287000
287306
|
contextSnapshot: null,
|
|
287001
287307
|
persistedThreadUpdatedAt: null
|
|
287002
287308
|
};
|
|
@@ -287013,6 +287319,7 @@ async function syncInteractiveCliThreadScope(state, connection, options = {}) {
|
|
|
287013
287319
|
if (!options.force && state.threadScopeKey === nextScopeKey) return;
|
|
287014
287320
|
state.threadScopeKey = nextScopeKey;
|
|
287015
287321
|
state.recentMessages = [];
|
|
287322
|
+
state.usageRunIds = [];
|
|
287016
287323
|
state.contextSnapshot = null;
|
|
287017
287324
|
state.persistedThreadUpdatedAt = null;
|
|
287018
287325
|
clearThreadSession(state.threadId);
|
|
@@ -287751,6 +288058,11 @@ function trimRecentMessages(messages) {
|
|
|
287751
288058
|
messages.splice(0, messages.length - 30);
|
|
287752
288059
|
}
|
|
287753
288060
|
}
|
|
288061
|
+
function recordCliUsageRunId(state, runId) {
|
|
288062
|
+
const cleaned = runId?.trim();
|
|
288063
|
+
if (!cleaned) return;
|
|
288064
|
+
state.usageRunIds = [.../* @__PURE__ */ new Set([...state.usageRunIds, cleaned])].slice(-250);
|
|
288065
|
+
}
|
|
287754
288066
|
function createCliRunId() {
|
|
287755
288067
|
return `cli-turn-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
287756
288068
|
}
|
|
@@ -288092,6 +288404,7 @@ Commands:
|
|
|
288092
288404
|
/help Show this list.
|
|
288093
288405
|
/status Show cwd, auth, mode, persona, permission, and thread.
|
|
288094
288406
|
/skills [name] List Perch core skills, or show one skill body.
|
|
288407
|
+
/usage Show tokens and estimated cost for this session.
|
|
288095
288408
|
/cwd [dir] Show or change the working directory.
|
|
288096
288409
|
/permission [mode] Show or set default, auto_review, take_the_wheel, or plan.
|
|
288097
288410
|
/permissions [mode] Alias for /permission.
|
|
@@ -288151,6 +288464,7 @@ Commands:
|
|
|
288151
288464
|
];
|
|
288152
288465
|
PERCH_SPLASH_COMMANDS = [
|
|
288153
288466
|
["/status", "show auth, route, tools, and thread"],
|
|
288467
|
+
["/usage", "show session tokens and estimated cost"],
|
|
288154
288468
|
["/skills", "show reusable operator skills"],
|
|
288155
288469
|
["/persona", "swap saffron or quill"],
|
|
288156
288470
|
["/permission", "change autonomy for the next turns"],
|