perchai-cli 2.4.14 → 2.4.16
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/dist/perch.mjs +70 -30
- package/package.json +4 -2
package/dist/perch.mjs
CHANGED
|
@@ -83131,7 +83131,6 @@ function truncateHistoryLine(value, max2) {
|
|
|
83131
83131
|
}
|
|
83132
83132
|
var init_operatorTruth = __esm({
|
|
83133
83133
|
"features/perchTerminal/runtime/operatorTruth.ts"() {
|
|
83134
|
-
"use strict";
|
|
83135
83134
|
}
|
|
83136
83135
|
});
|
|
83137
83136
|
|
|
@@ -134243,7 +134242,7 @@ function validateArgs(name, args) {
|
|
|
134243
134242
|
return "run_sandbox_code.command must be a non-empty string when provided.";
|
|
134244
134243
|
if (args.language !== void 0 && args.language !== "python" && args.language !== "node")
|
|
134245
134244
|
return 'run_sandbox_code.language must be either "python" or "node" when provided.';
|
|
134246
|
-
if (args.code !== void 0 && !hasCode)
|
|
134245
|
+
if (args.code !== void 0 && !hasCode && !hasCommand)
|
|
134247
134246
|
return "run_sandbox_code.code must be a non-empty string when provided.";
|
|
134248
134247
|
}
|
|
134249
134248
|
if (args.label !== void 0 && typeof args.label !== "string")
|
|
@@ -199728,7 +199727,6 @@ function containsBrowserDeliveryTask(tasks) {
|
|
|
199728
199727
|
var BROWSER_DELIVERY_ROLE_IDS;
|
|
199729
199728
|
var init_browserDeliveryLock = __esm({
|
|
199730
199729
|
"features/perchTerminal/agentPlatform/browserDeliveryLock.ts"() {
|
|
199731
|
-
"use strict";
|
|
199732
199730
|
BROWSER_DELIVERY_ROLE_IDS = /* @__PURE__ */ new Set([
|
|
199733
199731
|
"doc_writer",
|
|
199734
199732
|
"email_sender",
|
|
@@ -213392,11 +213390,14 @@ var init_config2 = __esm({
|
|
|
213392
213390
|
|
|
213393
213391
|
// features/perchTerminal/knowledge/knowledgeApiClient.ts
|
|
213394
213392
|
async function searchKnowledgeViaApi(input, options = {}) {
|
|
213395
|
-
const route = options.route ?? "/api/perch-terminal/knowledge/search";
|
|
213393
|
+
const route = resolveKnowledgeRoute(options.route ?? "/api/perch-terminal/knowledge/search");
|
|
213396
213394
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
213395
|
+
const headers = { "content-type": "application/json" };
|
|
213396
|
+
const token = process.env.PERCH_MODEL_CALL_PROXY_TOKEN?.trim();
|
|
213397
|
+
if (token) headers.authorization = `Bearer ${token}`;
|
|
213397
213398
|
const response = await fetchImpl(route, {
|
|
213398
213399
|
method: "POST",
|
|
213399
|
-
headers
|
|
213400
|
+
headers,
|
|
213400
213401
|
body: JSON.stringify(input)
|
|
213401
213402
|
});
|
|
213402
213403
|
const payload = await response.json().catch(() => ({}));
|
|
@@ -213408,6 +213409,13 @@ async function searchKnowledgeViaApi(input, options = {}) {
|
|
|
213408
213409
|
}
|
|
213409
213410
|
return payload;
|
|
213410
213411
|
}
|
|
213412
|
+
function resolveKnowledgeRoute(route) {
|
|
213413
|
+
if (!route.startsWith("/")) return route;
|
|
213414
|
+
if (typeof window !== "undefined") return route;
|
|
213415
|
+
const appUrl = process.env.PERCH_MODEL_CALL_PROXY_URL?.trim() || process.env.PERCH_CLI_APP_URL?.trim() || process.env.PERCH_APP_URL?.trim();
|
|
213416
|
+
if (!appUrl) return route;
|
|
213417
|
+
return new URL(route, appUrl).toString();
|
|
213418
|
+
}
|
|
213411
213419
|
var KnowledgeApiClientError;
|
|
213412
213420
|
var init_knowledgeApiClient = __esm({
|
|
213413
213421
|
"features/perchTerminal/knowledge/knowledgeApiClient.ts"() {
|
|
@@ -214630,13 +214638,22 @@ async function computeLiveSignals(store, strategyId, asOf) {
|
|
|
214630
214638
|
}
|
|
214631
214639
|
return out;
|
|
214632
214640
|
}
|
|
214641
|
+
function ledgerFields(result2) {
|
|
214642
|
+
return result2.error ? { ledgerRecorded: result2.recorded, ledgerError: result2.error } : { ledgerRecorded: result2.recorded };
|
|
214643
|
+
}
|
|
214633
214644
|
async function recordLedger(ctx, row) {
|
|
214634
|
-
if (!ctx.supabase)
|
|
214645
|
+
if (!ctx.supabase) {
|
|
214646
|
+
return { recorded: false, error: "supabase_unavailable" };
|
|
214647
|
+
}
|
|
214635
214648
|
try {
|
|
214636
214649
|
const { error } = await ctx.supabase.from("perch_ai_market_backtest_runs").insert(row);
|
|
214637
|
-
return
|
|
214638
|
-
|
|
214639
|
-
|
|
214650
|
+
if (error) return { recorded: false, error: error.message };
|
|
214651
|
+
return { recorded: true };
|
|
214652
|
+
} catch (error) {
|
|
214653
|
+
return {
|
|
214654
|
+
recorded: false,
|
|
214655
|
+
error: error instanceof Error ? error.message : String(error)
|
|
214656
|
+
};
|
|
214640
214657
|
}
|
|
214641
214658
|
}
|
|
214642
214659
|
var NOT_ADVICE, getMarketSignalTool, queryMarketSignalLogTool, explainMarketSignalTool, listMarketStrategiesTool, MAX_PERIOD_DAYS, ISO_DATE, runMarketBacktestTool, getMarketTrackRecordTool, marketDeskTools;
|
|
@@ -214900,7 +214917,8 @@ var init_marketDesk = __esm({
|
|
|
214900
214917
|
holdout_acknowledged: acknowledgeHoldout,
|
|
214901
214918
|
initiator: "tool",
|
|
214902
214919
|
workspace_id: ctx.workspaceId ?? null,
|
|
214903
|
-
thread_id: ctx.threadId ?? null
|
|
214920
|
+
thread_id: ctx.threadId ?? null,
|
|
214921
|
+
user_id: ctx.userId ?? null
|
|
214904
214922
|
};
|
|
214905
214923
|
const wf = args.walkForward && typeof args.walkForward === "object" ? args.walkForward : void 0;
|
|
214906
214924
|
try {
|
|
@@ -214923,10 +214941,10 @@ var init_marketDesk = __esm({
|
|
|
214923
214941
|
paramGrid
|
|
214924
214942
|
});
|
|
214925
214943
|
if (!result3.ok) {
|
|
214926
|
-
await recordLedger(ctx, { ...ledgerBase, status: "failed", error: result3.error, holdout_overlap: false });
|
|
214927
|
-
return { ok: false, errorCode: "walk_forward_failed", message: result3.error };
|
|
214944
|
+
const ledger3 = await recordLedger(ctx, { ...ledgerBase, status: "failed", error: result3.error, holdout_overlap: false });
|
|
214945
|
+
return { ok: false, errorCode: "walk_forward_failed", message: result3.error, ...ledgerFields(ledger3) };
|
|
214928
214946
|
}
|
|
214929
|
-
const
|
|
214947
|
+
const ledger2 = await recordLedger(ctx, {
|
|
214930
214948
|
...ledgerBase,
|
|
214931
214949
|
status: "completed",
|
|
214932
214950
|
holdout_overlap: to >= resolveHoldoutStart(),
|
|
@@ -214951,17 +214969,17 @@ var init_marketDesk = __esm({
|
|
|
214951
214969
|
testMaxDrawdown: round(f.testMetrics.maxDrawdown)
|
|
214952
214970
|
})),
|
|
214953
214971
|
oosMetrics: compactMetrics(result3.oosMetrics),
|
|
214954
|
-
|
|
214972
|
+
...ledgerFields(ledger2),
|
|
214955
214973
|
note: "Out-of-sample (oosMetrics) is the only number that counts; train-window performance is selection, not evidence.",
|
|
214956
214974
|
disclaimer: NOT_ADVICE
|
|
214957
214975
|
};
|
|
214958
214976
|
}
|
|
214959
214977
|
const result2 = await runBacktest(store, backtestCfg);
|
|
214960
214978
|
if (!result2.ok) {
|
|
214961
|
-
await recordLedger(ctx, { ...ledgerBase, status: "failed", error: result2.error, holdout_overlap: false });
|
|
214962
|
-
return { ok: false, errorCode: "backtest_failed", message: result2.error };
|
|
214979
|
+
const ledger2 = await recordLedger(ctx, { ...ledgerBase, status: "failed", error: result2.error, holdout_overlap: false });
|
|
214980
|
+
return { ok: false, errorCode: "backtest_failed", message: result2.error, ...ledgerFields(ledger2) };
|
|
214963
214981
|
}
|
|
214964
|
-
const
|
|
214982
|
+
const ledger = await recordLedger(ctx, {
|
|
214965
214983
|
...ledgerBase,
|
|
214966
214984
|
status: "completed",
|
|
214967
214985
|
holdout_overlap: result2.holdout.overlapped,
|
|
@@ -214990,13 +215008,13 @@ var init_marketDesk = __esm({
|
|
|
214990
215008
|
signalCounts: result2.signalCounts,
|
|
214991
215009
|
equityCurve: downsample(result2.equityCurve, 60).map((p) => ({ date: p.date, equity: round(p.equity, 4) })),
|
|
214992
215010
|
trades: { closed: result2.trades.filter((t) => t.netReturn !== null).length, open: result2.trades.filter((t) => t.exitDate === null && t.netReturn === null).length },
|
|
214993
|
-
|
|
215011
|
+
...ledgerFields(ledger),
|
|
214994
215012
|
note: "Hypothetical backtest with modeled fees/slippage; past simulated performance does not predict future results. Every run (including this one) is recorded in the experiment ledger.",
|
|
214995
215013
|
disclaimer: NOT_ADVICE
|
|
214996
215014
|
};
|
|
214997
215015
|
} catch (error) {
|
|
214998
215016
|
if (error instanceof HoldoutViolationError) {
|
|
214999
|
-
await recordLedger(ctx, {
|
|
215017
|
+
const ledger2 = await recordLedger(ctx, {
|
|
215000
215018
|
...ledgerBase,
|
|
215001
215019
|
status: "failed",
|
|
215002
215020
|
error: "holdout_violation",
|
|
@@ -215007,12 +215025,13 @@ var init_marketDesk = __esm({
|
|
|
215007
215025
|
errorCode: "holdout_violation",
|
|
215008
215026
|
message: error.message,
|
|
215009
215027
|
holdoutStart: error.holdoutStart,
|
|
215010
|
-
hint: "The holdout window is reserved for final evaluation. Re-run with acknowledgeHoldout: true ONLY if the user explicitly wants to spend a holdout look; the run gets flagged in the ledger either way."
|
|
215028
|
+
hint: "The holdout window is reserved for final evaluation. Re-run with acknowledgeHoldout: true ONLY if the user explicitly wants to spend a holdout look; the run gets flagged in the ledger either way.",
|
|
215029
|
+
...ledgerFields(ledger2)
|
|
215011
215030
|
};
|
|
215012
215031
|
}
|
|
215013
215032
|
const message = error instanceof Error ? error.message : String(error);
|
|
215014
|
-
await recordLedger(ctx, { ...ledgerBase, status: "failed", error: message, holdout_overlap: false });
|
|
215015
|
-
return { ok: false, errorCode: "backtest_error", message };
|
|
215033
|
+
const ledger = await recordLedger(ctx, { ...ledgerBase, status: "failed", error: message, holdout_overlap: false });
|
|
215034
|
+
return { ok: false, errorCode: "backtest_error", message, ...ledgerFields(ledger) };
|
|
215016
215035
|
}
|
|
215017
215036
|
}
|
|
215018
215037
|
};
|
|
@@ -215190,11 +215209,22 @@ var init_registry5 = __esm({
|
|
|
215190
215209
|
async function executeRegisteredTool(name, args, ctx) {
|
|
215191
215210
|
const mod = getRegisteredTool(name);
|
|
215192
215211
|
if (!mod) return { handled: false };
|
|
215212
|
+
if (MARKET_DESK_TOOL_NAMES.has(name) && !isMarketDeskEnabled()) {
|
|
215213
|
+
return {
|
|
215214
|
+
handled: true,
|
|
215215
|
+
result: {
|
|
215216
|
+
ok: false,
|
|
215217
|
+
errorCode: "market_desk_disabled",
|
|
215218
|
+
message: "Market Desk tools are disabled. Set PERCH_MARKET_DESK=1 to enable them."
|
|
215219
|
+
}
|
|
215220
|
+
};
|
|
215221
|
+
}
|
|
215193
215222
|
return { handled: true, result: await mod.handler(args, ctx) };
|
|
215194
215223
|
}
|
|
215195
215224
|
var init_executeTool = __esm({
|
|
215196
215225
|
"features/perchTerminal/runtime/toolSystem/executeTool.ts"() {
|
|
215197
215226
|
"use strict";
|
|
215227
|
+
init_marketDeskAccess();
|
|
215198
215228
|
init_registry5();
|
|
215199
215229
|
}
|
|
215200
215230
|
});
|
|
@@ -219674,17 +219704,18 @@ async function runLiveAgentsLoop(input) {
|
|
|
219674
219704
|
}
|
|
219675
219705
|
const mode = effectiveChatMode;
|
|
219676
219706
|
const loopTools = tools;
|
|
219677
|
-
const
|
|
219707
|
+
const sandboxExecutionRequired = sandboxExecutionRequiredByUserText(turn.trimmedInput, loopTools);
|
|
219708
|
+
const loopSystemPromptBase = appendLiveTurnContract(context.systemPrompt, {
|
|
219678
219709
|
chatMode: mode,
|
|
219679
219710
|
toolsAvailable: loopTools.length > 0,
|
|
219680
219711
|
userObjective: turn.trimmedInput,
|
|
219681
219712
|
planGateRejected
|
|
219682
219713
|
});
|
|
219714
|
+
const loopSystemPrompt = sandboxExecutionRequired ? appendSandboxExecutionRequiredContract(loopSystemPromptBase) : loopSystemPromptBase;
|
|
219683
219715
|
const threadSession = turn.threadId ? getThreadSessionFromMemory(turn.threadId) : null;
|
|
219684
219716
|
const contextCompaction = threadSession?.contextCompaction ?? null;
|
|
219685
219717
|
const approvedToolCall = approvedToolCallFromState(workflowState);
|
|
219686
219718
|
const runLoop = input.loopRunner ?? runModelToolLoop;
|
|
219687
|
-
const sandboxExecutionRequired = sandboxExecutionRequiredByUserText(turn.trimmedInput, loopTools);
|
|
219688
219719
|
const loopInput = {
|
|
219689
219720
|
signal: deps.signal ?? void 0,
|
|
219690
219721
|
lane: loopLane,
|
|
@@ -219715,7 +219746,7 @@ async function runLiveAgentsLoop(input) {
|
|
|
219715
219746
|
forcedInitialToolCall: approvedToolCall ?? turn.forcedInitialToolCall ?? null,
|
|
219716
219747
|
forcedInitialToolCallApproved: Boolean(approvedToolCall),
|
|
219717
219748
|
stopAfterForcedInitialToolCall: Boolean(approvedToolCall),
|
|
219718
|
-
forceToolUse: turn.forceToolUse === true
|
|
219749
|
+
forceToolUse: turn.forceToolUse === true,
|
|
219719
219750
|
mcpTools: turn.mcpTools ?? [],
|
|
219720
219751
|
maxIterations: turnHasBrowserOperatorTools(loopTools) ? 24 : turn.coordinatorSessionActive ? 20 : void 0,
|
|
219721
219752
|
attachOperatorScreenshots: turnHasBrowserOperatorTools(loopTools),
|
|
@@ -219812,10 +219843,11 @@ async function runLiveAgentsLoop(input) {
|
|
|
219812
219843
|
if (sandboxExecutionRequired && loopResult.ok && !hasRunSandboxCodeToolCall(loopResult)) {
|
|
219813
219844
|
loopResult = await runLoop({
|
|
219814
219845
|
...loopInput,
|
|
219815
|
-
forceToolUse:
|
|
219816
|
-
systemPrompt:
|
|
219817
|
-
|
|
219818
|
-
|
|
219846
|
+
forceToolUse: false,
|
|
219847
|
+
systemPrompt: appendSandboxExecutionRequiredContract(
|
|
219848
|
+
loopInput.systemPrompt,
|
|
219849
|
+
true
|
|
219850
|
+
)
|
|
219819
219851
|
});
|
|
219820
219852
|
}
|
|
219821
219853
|
if (sandboxExecutionRequired && loopResult.ok && !hasRunSandboxCodeToolCall(loopResult)) {
|
|
@@ -219946,6 +219978,12 @@ function sandboxExecutionRequiredByUserText(userText, tools) {
|
|
|
219946
219978
|
if (!mentionsSandbox) return false;
|
|
219947
219979
|
return /\b(use|run|execute|call|primary|required|mandatory|must|final numbers|must come from|do not stop)\b/.test(normalized) || /sandbox.{0,80}(primary|required|mandatory|must|execute|run|numbers|code)/.test(normalized) || /(primary|required|mandatory|must|execute|run|numbers|code).{0,80}sandbox/.test(normalized);
|
|
219948
219980
|
}
|
|
219981
|
+
function appendSandboxExecutionRequiredContract(systemPrompt, retry = false) {
|
|
219982
|
+
const prefix = retry ? "The previous attempt did not call run_sandbox_code." : "Sandbox execution is explicitly required by the user.";
|
|
219983
|
+
return `${systemPrompt.trim()}
|
|
219984
|
+
|
|
219985
|
+
${prefix} This turn is incomplete until you call run_sandbox_code. Use the command field when possible; pass source file paths in sources and read copied files from input/. The sandbox tool result already captures stdout/stderr and produced files; do not try to read output/report.json from the host filesystem after the run. Do not mention internal model, provider, or lane details in the user-facing answer. Do not finish with prose before a sandbox run.`;
|
|
219986
|
+
}
|
|
219949
219987
|
function hasRunSandboxCodeToolCall(loopResult) {
|
|
219950
219988
|
return loopResult.toolCalls.some((call) => call.toolName === TOOL_NAMES.runSandboxCode);
|
|
219951
219989
|
}
|
|
@@ -222782,7 +222820,9 @@ function maybeWarnCategoryDrift(input) {
|
|
|
222782
222820
|
const drift = sum - input.headlineTokens;
|
|
222783
222821
|
const allowed = Math.max(256, Math.ceil(input.wireOverheadTokens ?? 0) + 64);
|
|
222784
222822
|
if (Math.abs(drift) <= allowed) return;
|
|
222785
|
-
if (typeof process
|
|
222823
|
+
if (typeof process === "undefined" || process.env.PERCH_CONTEXT_METER_DEBUG !== "1") {
|
|
222824
|
+
return;
|
|
222825
|
+
}
|
|
222786
222826
|
console.warn(
|
|
222787
222827
|
`[context-meter] Category total drift in ${input.source}: categories=${sum}, headline=${input.headlineTokens}, drift=${drift}`
|
|
222788
222828
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "perchai-cli",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.16",
|
|
4
4
|
"description": "Perch AI command-line interface",
|
|
5
5
|
"bin": {
|
|
6
6
|
"perch": "bin/perch"
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
"LICENSE"
|
|
13
13
|
],
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@
|
|
15
|
+
"@mozilla/readability": "^0.6.0",
|
|
16
|
+
"@napi-rs/canvas": "^0.1.100",
|
|
17
|
+
"jsdom": "^29.1.1"
|
|
16
18
|
},
|
|
17
19
|
"engines": {
|
|
18
20
|
"node": ">=20"
|