perchai-cli 2.4.49 → 2.4.51

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 +798 -651
  2. package/package.json +1 -1
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
  });
@@ -76223,6 +76224,7 @@ function getToolDisplayName(toolName) {
76223
76224
  var NON_MODULE_TOOL_OWNERS, TOOL_RISK, TOOL_DISPLAY_NAMES;
76224
76225
  var init_catalog = __esm({
76225
76226
  "features/perchTerminal/runtime/toolSystem/catalog.ts"() {
76227
+ "use strict";
76226
76228
  init_toolNames();
76227
76229
  NON_MODULE_TOOL_OWNERS = {
76228
76230
  [TOOL_NAMES.listSources]: "lane",
@@ -91827,6 +91829,7 @@ function listFinancialPlaybooks() {
91827
91829
  var AP_AUDIT_PACKET_DEF, PLAYBOOKS;
91828
91830
  var init_registry2 = __esm({
91829
91831
  "features/perchTerminal/runtime/financialPlaybooks/registry.ts"() {
91832
+ "use strict";
91830
91833
  init_managedWorkflowRegistry2();
91831
91834
  init_toolNames();
91832
91835
  AP_AUDIT_PACKET_DEF = {
@@ -119507,6 +119510,21 @@ var init_browserSession = __esm({
119507
119510
  }
119508
119511
  });
119509
119512
 
119513
+ // features/perchTerminal/runtime/cliAuthRefreshRegistry.ts
119514
+ function setCliModelProxyAuthRefresher(fn) {
119515
+ refresher = fn;
119516
+ }
119517
+ function refreshCliModelProxyAuth() {
119518
+ return refresher ? refresher() : Promise.resolve(null);
119519
+ }
119520
+ var refresher;
119521
+ var init_cliAuthRefreshRegistry = __esm({
119522
+ "features/perchTerminal/runtime/cliAuthRefreshRegistry.ts"() {
119523
+ "use strict";
119524
+ refresher = null;
119525
+ }
119526
+ });
119527
+
119510
119528
  // features/perchTerminal/runtime/roostUserSelectionClient.ts
119511
119529
  function coerceSelection(raw) {
119512
119530
  const choice = normalizeRoostModelChoice(raw?.choice);
@@ -119546,507 +119564,6 @@ var init_roostUserSelectionClient = __esm({
119546
119564
  }
119547
119565
  });
119548
119566
 
119549
- // features/perchTerminal/runtime/cliHost/cliAuthSession.ts
119550
- var cliAuthSession_exports = {};
119551
- __export(cliAuthSession_exports, {
119552
- clearStoredCliAuthSession: () => clearStoredCliAuthSession,
119553
- isStoredCliAuthSessionUsable: () => isStoredCliAuthSessionUsable,
119554
- parseStoredCliAuthSession: () => parseStoredCliAuthSession,
119555
- readStoredCliAuthSession: () => readStoredCliAuthSession,
119556
- writeStoredCliAuthSession: () => writeStoredCliAuthSession
119557
- });
119558
- import { execFile } from "node:child_process";
119559
- import fs9 from "node:fs/promises";
119560
- import os from "node:os";
119561
- import path10 from "node:path";
119562
- import { promisify } from "node:util";
119563
- async function readStoredCliAuthSession() {
119564
- const raw = shouldUseFallbackAuthFile() ? await readFallbackSecret().catch(() => null) : process.platform === "darwin" ? await readKeychainSecret().catch(() => null) : await readFallbackSecret().catch(() => null);
119565
- return parseStoredCliAuthSession(raw);
119566
- }
119567
- async function writeStoredCliAuthSession(session) {
119568
- const raw = JSON.stringify(session);
119569
- if (process.platform === "darwin" && !shouldUseFallbackAuthFile()) {
119570
- await execFileAsync("security", [
119571
- "add-generic-password",
119572
- "-U",
119573
- "-s",
119574
- KEYCHAIN_SERVICE,
119575
- "-a",
119576
- KEYCHAIN_ACCOUNT,
119577
- "-w",
119578
- raw
119579
- ]);
119580
- return;
119581
- }
119582
- await writeFallbackSecret(raw);
119583
- }
119584
- async function clearStoredCliAuthSession() {
119585
- if (process.platform === "darwin" && !shouldUseFallbackAuthFile()) {
119586
- await execFileAsync("security", [
119587
- "delete-generic-password",
119588
- "-s",
119589
- KEYCHAIN_SERVICE,
119590
- "-a",
119591
- KEYCHAIN_ACCOUNT
119592
- ]).catch(() => void 0);
119593
- return;
119594
- }
119595
- await fs9.rm(fallbackSessionPath(), { force: true }).catch(() => void 0);
119596
- }
119597
- function shouldUseFallbackAuthFile() {
119598
- return Boolean(process.env.PERCH_CLI_AUTH_DIR?.trim());
119599
- }
119600
- function isStoredCliAuthSessionUsable(session, nowSeconds = Math.floor(Date.now() / 1e3)) {
119601
- if (!session?.accessToken?.trim()) return false;
119602
- if (!session.appUrl?.trim()) return false;
119603
- if (!session.expiresAt) return true;
119604
- return session.expiresAt > nowSeconds + 90;
119605
- }
119606
- function parseStoredCliAuthSession(raw) {
119607
- if (!raw?.trim()) return null;
119608
- try {
119609
- const parsed = JSON.parse(raw);
119610
- if (parsed.version !== 1) return null;
119611
- if (!parsed.appUrl || !parsed.accessToken || !parsed.updatedAt) return null;
119612
- return {
119613
- version: 1,
119614
- appUrl: parsed.appUrl,
119615
- accessToken: parsed.accessToken,
119616
- refreshToken: parsed.refreshToken ?? null,
119617
- expiresAt: typeof parsed.expiresAt === "number" ? parsed.expiresAt : null,
119618
- userId: parsed.userId ?? null,
119619
- email: parsed.email ?? null,
119620
- updatedAt: parsed.updatedAt
119621
- };
119622
- } catch {
119623
- return null;
119624
- }
119625
- }
119626
- async function readKeychainSecret() {
119627
- const { stdout } = await execFileAsync("security", [
119628
- "find-generic-password",
119629
- "-s",
119630
- KEYCHAIN_SERVICE,
119631
- "-a",
119632
- KEYCHAIN_ACCOUNT,
119633
- "-w"
119634
- ]);
119635
- return stdout.trim() || null;
119636
- }
119637
- function fallbackSessionPath() {
119638
- const base = process.env.PERCH_CLI_AUTH_DIR?.trim() || path10.join(os.homedir(), ".perch");
119639
- return path10.join(base, "cli-auth-session.json");
119640
- }
119641
- async function readFallbackSecret() {
119642
- return await fs9.readFile(fallbackSessionPath(), "utf8");
119643
- }
119644
- async function writeFallbackSecret(raw) {
119645
- const filePath = fallbackSessionPath();
119646
- await fs9.mkdir(path10.dirname(filePath), { recursive: true, mode: 448 });
119647
- await fs9.writeFile(filePath, raw, { mode: 384 });
119648
- }
119649
- var execFileAsync, KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT;
119650
- var init_cliAuthSession = __esm({
119651
- "features/perchTerminal/runtime/cliHost/cliAuthSession.ts"() {
119652
- "use strict";
119653
- execFileAsync = promisify(execFile);
119654
- KEYCHAIN_SERVICE = "app.perchai.cli-auth";
119655
- KEYCHAIN_ACCOUNT = "default";
119656
- }
119657
- });
119658
-
119659
- // features/perchTerminal/runtime/publicModelDefault.ts
119660
- function parsePublicModelDefaultPayload(value) {
119661
- const raw = value && typeof value === "object" ? value : {};
119662
- const selection = sanitizeFounderModelSelection(
119663
- raw.selection ?? DEFAULT_FOUNDER_MODEL_SELECTION
119664
- );
119665
- return {
119666
- selection,
119667
- updatedAt: typeof raw.updatedAt === "string" ? raw.updatedAt : null,
119668
- updatedBy: typeof raw.updatedBy === "string" ? raw.updatedBy : null
119669
- };
119670
- }
119671
- var init_publicModelDefault = __esm({
119672
- "features/perchTerminal/runtime/publicModelDefault.ts"() {
119673
- "use strict";
119674
- init_modelRegistry();
119675
- }
119676
- });
119677
-
119678
- // features/perchTerminal/runtime/cliHost/modelConnection.ts
119679
- async function connectCliModelProxy(input = {}) {
119680
- const storedSession = input.storedSession === void 0 ? await readStoredCliAuthSession() : input.storedSession;
119681
- const usableStoredSession = await ensureFreshCliAuthSession({
119682
- session: storedSession,
119683
- fetchImpl: input.fetchImpl
119684
- });
119685
- const appUrl = resolveCliAppUrl(input.appUrl, usableStoredSession?.appUrl ?? null);
119686
- if (!appUrl) return noCliModelConnection();
119687
- if (!usableStoredSession?.accessToken) return noCliModelConnection(appUrl);
119688
- const fetchImpl = input.fetchImpl ?? globalThis.fetch;
119689
- if (typeof fetchImpl !== "function") return noCliModelConnection(appUrl);
119690
- const selection = await fetchPublicModelDefaultSelection(
119691
- appUrl,
119692
- fetchImpl,
119693
- usableStoredSession?.accessToken ?? null
119694
- );
119695
- if (!selection) return noCliModelConnection(appUrl);
119696
- const priorProxy = process.env[MODEL_PROXY_ENV];
119697
- const priorToken = process.env[MODEL_PROXY_TOKEN_ENV];
119698
- process.env[MODEL_PROXY_ENV] = appUrl;
119699
- if (usableStoredSession?.accessToken) {
119700
- process.env[MODEL_PROXY_TOKEN_ENV] = usableStoredSession.accessToken;
119701
- }
119702
- return {
119703
- appUrl,
119704
- authenticated: !!usableStoredSession?.accessToken,
119705
- userId: usableStoredSession?.userId ?? null,
119706
- email: usableStoredSession?.email ?? null,
119707
- founderModelSelection: selection,
119708
- restore: () => {
119709
- if (priorProxy === void 0) {
119710
- delete process.env[MODEL_PROXY_ENV];
119711
- } else {
119712
- process.env[MODEL_PROXY_ENV] = priorProxy;
119713
- }
119714
- if (priorToken === void 0) {
119715
- delete process.env[MODEL_PROXY_TOKEN_ENV];
119716
- } else {
119717
- process.env[MODEL_PROXY_TOKEN_ENV] = priorToken;
119718
- }
119719
- }
119720
- };
119721
- }
119722
- function resolveCliAppUrl(explicit, stored) {
119723
- const raw = explicit?.trim() || process.env[CLI_APP_URL_ENV]?.trim() || process.env[FALLBACK_APP_URL_ENV]?.trim() || stored?.trim() || DEFAULT_CLI_APP_URL;
119724
- if (!raw) return null;
119725
- const withProtocol = /^https?:\/\//i.test(raw) ? raw : `http://${raw}`;
119726
- try {
119727
- const url = new URL(withProtocol);
119728
- url.pathname = url.pathname.replace(/\/+$/, "");
119729
- url.search = "";
119730
- url.hash = "";
119731
- return url.toString().replace(/\/+$/, "");
119732
- } catch {
119733
- return null;
119734
- }
119735
- }
119736
- async function fetchPublicModelDefaultSelection(appUrl, fetchImpl, accessToken) {
119737
- const url = `${appUrl}/api/perch-terminal/public-model-default`;
119738
- const controller = new AbortController();
119739
- const timeout = setTimeout(() => controller.abort(), 3500);
119740
- try {
119741
- const response = await fetchImpl(url, {
119742
- method: "GET",
119743
- headers: {
119744
- Accept: "application/json",
119745
- ...accessToken ? { Authorization: `Bearer ${accessToken}` } : {}
119746
- },
119747
- signal: controller.signal
119748
- });
119749
- if (!response.ok) return null;
119750
- const body = await response.json();
119751
- if (!body || typeof body !== "object") return null;
119752
- const record = body;
119753
- if (record.ok !== true) return null;
119754
- return parsePublicModelDefaultPayload({ selection: record.selection }).selection;
119755
- } catch {
119756
- return null;
119757
- } finally {
119758
- clearTimeout(timeout);
119759
- }
119760
- }
119761
- function noCliModelConnection(appUrl = null) {
119762
- return {
119763
- appUrl,
119764
- authenticated: false,
119765
- userId: null,
119766
- email: null,
119767
- founderModelSelection: null,
119768
- restore: () => {
119769
- }
119770
- };
119771
- }
119772
- var MODEL_PROXY_ENV, MODEL_PROXY_TOKEN_ENV, CLI_APP_URL_ENV, FALLBACK_APP_URL_ENV, DEFAULT_CLI_APP_URL;
119773
- var init_modelConnection = __esm({
119774
- "features/perchTerminal/runtime/cliHost/modelConnection.ts"() {
119775
- "use strict";
119776
- init_publicModelDefault();
119777
- init_cliAuthSession();
119778
- init_cliAuthRefresh();
119779
- MODEL_PROXY_ENV = "PERCH_MODEL_CALL_PROXY_URL";
119780
- MODEL_PROXY_TOKEN_ENV = "PERCH_MODEL_CALL_PROXY_TOKEN";
119781
- CLI_APP_URL_ENV = "PERCH_CLI_APP_URL";
119782
- FALLBACK_APP_URL_ENV = "PERCH_APP_URL";
119783
- DEFAULT_CLI_APP_URL = "https://app.perchai.app";
119784
- }
119785
- });
119786
-
119787
- // features/perchTerminal/runtime/cliHost/cliStandaloneOAuth.ts
119788
- import { execFile as execFile2 } from "node:child_process";
119789
- import http from "node:http";
119790
- import { promisify as promisify2 } from "node:util";
119791
- async function runCliStandaloneOAuthLogin(input) {
119792
- const appUrl = resolveCliAppUrl(input.appUrl, null);
119793
- if (!appUrl) {
119794
- return { ok: false, code: "invalid_app_url", error: "Invalid Perch app URL." };
119795
- }
119796
- const fetchImpl = input.fetchImpl ?? globalThis.fetch;
119797
- if (typeof fetchImpl !== "function") {
119798
- return { ok: false, code: "fetch_unavailable", error: "This Node runtime does not expose fetch." };
119799
- }
119800
- const config = await fetchCliAuthConfig(appUrl, fetchImpl);
119801
- if (!config.ok || !config.supabaseUrl || !config.supabaseAnonKey) {
119802
- return {
119803
- ok: false,
119804
- code: "auth_config_unavailable",
119805
- error: "The Perch app did not return CLI auth configuration."
119806
- };
119807
- }
119808
- const provider = selectOAuthProvider(config.providers ?? [], input.provider);
119809
- if (!provider) {
119810
- return {
119811
- ok: false,
119812
- code: "oauth_provider_unavailable",
119813
- error: "No supported CLI OAuth provider is enabled for this Perch app."
119814
- };
119815
- }
119816
- const callback = await createOAuthCallbackServer({
119817
- host: config.redirectHost?.trim() || "127.0.0.1",
119818
- timeoutMs: input.timeoutMs ?? DEFAULT_LOGIN_TIMEOUT_MS
119819
- });
119820
- try {
119821
- const memoryStorage = createMemoryAuthStorage();
119822
- const supabase = createClient(config.supabaseUrl, config.supabaseAnonKey, {
119823
- auth: {
119824
- flowType: "pkce",
119825
- autoRefreshToken: false,
119826
- detectSessionInUrl: false,
119827
- persistSession: true,
119828
- storage: memoryStorage
119829
- }
119830
- });
119831
- const { data, error } = await supabase.auth.signInWithOAuth({
119832
- provider,
119833
- options: {
119834
- redirectTo: callback.redirectTo,
119835
- skipBrowserRedirect: true,
119836
- data: { signup_source: "cli" }
119837
- }
119838
- });
119839
- if (error || !data.url) {
119840
- return {
119841
- ok: false,
119842
- code: "oauth_start_failed",
119843
- error: error?.message ?? "Supabase did not return an OAuth URL."
119844
- };
119845
- }
119846
- await (input.openExternal ?? openExternal)(data.url);
119847
- const callbackResult = await callback.waitForCode();
119848
- if (!callbackResult.ok) {
119849
- return callbackResult;
119850
- }
119851
- const exchanged = await supabase.auth.exchangeCodeForSession(callbackResult.code);
119852
- if (exchanged.error || !exchanged.data.session) {
119853
- return {
119854
- ok: false,
119855
- code: "oauth_exchange_failed",
119856
- error: exchanged.error?.message ?? "Supabase did not return a CLI session."
119857
- };
119858
- }
119859
- const session = storedSessionFromSupabaseSession({
119860
- appUrl,
119861
- accessToken: exchanged.data.session.access_token,
119862
- refreshToken: exchanged.data.session.refresh_token,
119863
- expiresAt: exchanged.data.session.expires_at ?? null,
119864
- userId: exchanged.data.session.user.id,
119865
- email: exchanged.data.session.user.email ?? null
119866
- });
119867
- await writeStoredCliAuthSession(session);
119868
- return { ok: true, session };
119869
- } finally {
119870
- await callback.close();
119871
- }
119872
- }
119873
- async function fetchCliAuthConfig(appUrl, fetchImpl = globalThis.fetch) {
119874
- const response = await fetchImpl(`${appUrl}/api/perch-terminal/cli-auth/config`, {
119875
- method: "GET",
119876
- headers: { Accept: "application/json" }
119877
- });
119878
- if (!response.ok) return { ok: false };
119879
- return await response.json();
119880
- }
119881
- function selectOAuthProvider(providers, preferred) {
119882
- const available = new Set(providers);
119883
- if (preferred && available.has(preferred)) return preferred;
119884
- if (available.has("google")) return "google";
119885
- if (available.has("github")) return "github";
119886
- return null;
119887
- }
119888
- function storedSessionFromSupabaseSession(input) {
119889
- return {
119890
- version: 1,
119891
- appUrl: input.appUrl,
119892
- accessToken: input.accessToken,
119893
- refreshToken: input.refreshToken ?? null,
119894
- expiresAt: input.expiresAt ?? null,
119895
- userId: input.userId ?? null,
119896
- email: input.email ?? null,
119897
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
119898
- };
119899
- }
119900
- function createMemoryAuthStorage() {
119901
- const values2 = /* @__PURE__ */ new Map();
119902
- return {
119903
- getItem: (key) => values2.get(key) ?? null,
119904
- setItem: (key, value) => {
119905
- values2.set(key, value);
119906
- },
119907
- removeItem: (key) => {
119908
- values2.delete(key);
119909
- }
119910
- };
119911
- }
119912
- async function createOAuthCallbackServer(input) {
119913
- let resolveResult = null;
119914
- const resultPromise = new Promise((resolve5) => {
119915
- resolveResult = resolve5;
119916
- });
119917
- const server = http.createServer((request, response) => {
119918
- const requestUrl = new URL(request.url ?? "/", `http://${input.host}`);
119919
- if (requestUrl.pathname !== CALLBACK_PATH) {
119920
- response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
119921
- response.end("Not found");
119922
- return;
119923
- }
119924
- const code = requestUrl.searchParams.get("code");
119925
- const error = requestUrl.searchParams.get("error_description") ?? requestUrl.searchParams.get("error");
119926
- if (!code) {
119927
- response.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
119928
- response.end(renderCallbackHtml(false));
119929
- resolveResult?.({
119930
- ok: false,
119931
- code: "oauth_callback_missing_code",
119932
- error: error ?? "OAuth callback did not include a code."
119933
- });
119934
- resolveResult = null;
119935
- return;
119936
- }
119937
- response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
119938
- response.end(renderCallbackHtml(true));
119939
- resolveResult?.({ ok: true, code });
119940
- resolveResult = null;
119941
- });
119942
- await new Promise((resolve5, reject2) => {
119943
- server.once("error", reject2);
119944
- server.listen(0, input.host, () => {
119945
- server.off("error", reject2);
119946
- resolve5();
119947
- });
119948
- });
119949
- const address = server.address();
119950
- const redirectTo = `http://${input.host}:${address.port}${CALLBACK_PATH}`;
119951
- const timeout = setTimeout(() => {
119952
- resolveResult?.({
119953
- ok: false,
119954
- code: "oauth_timeout",
119955
- error: "Timed out waiting for browser sign-in to complete."
119956
- });
119957
- resolveResult = null;
119958
- }, input.timeoutMs);
119959
- return {
119960
- redirectTo,
119961
- waitForCode: async () => resultPromise,
119962
- close: async () => {
119963
- clearTimeout(timeout);
119964
- await new Promise((resolve5) => server.close(() => resolve5()));
119965
- }
119966
- };
119967
- }
119968
- function renderCallbackHtml(ok) {
119969
- const title = ok ? "Perch CLI signed in" : "Perch CLI sign-in failed";
119970
- const message = ok ? "You can close this tab and return to your terminal." : "Return to your terminal and try again.";
119971
- return `<!doctype html><html><head><meta charset="utf-8"><title>${title}</title></head><body style="font-family:system-ui;padding:32px;background:#100d0b;color:#fff8f0"><h1>${title}</h1><p>${message}</p></body></html>`;
119972
- }
119973
- async function openExternal(url) {
119974
- if (process.platform === "darwin") {
119975
- await execFileAsync2("open", [url]);
119976
- } else if (process.platform === "win32") {
119977
- await execFileAsync2("cmd", ["/c", "start", "", url]);
119978
- } else {
119979
- await execFileAsync2("xdg-open", [url]);
119980
- }
119981
- }
119982
- var execFileAsync2, DEFAULT_LOGIN_TIMEOUT_MS, CALLBACK_PATH;
119983
- var init_cliStandaloneOAuth = __esm({
119984
- "features/perchTerminal/runtime/cliHost/cliStandaloneOAuth.ts"() {
119985
- "use strict";
119986
- init_dist4();
119987
- init_modelConnection();
119988
- init_cliAuthSession();
119989
- execFileAsync2 = promisify2(execFile2);
119990
- DEFAULT_LOGIN_TIMEOUT_MS = 12e4;
119991
- CALLBACK_PATH = "/callback";
119992
- }
119993
- });
119994
-
119995
- // features/perchTerminal/runtime/cliHost/cliAuthRefresh.ts
119996
- var cliAuthRefresh_exports = {};
119997
- __export(cliAuthRefresh_exports, {
119998
- ensureFreshCliAuthSession: () => ensureFreshCliAuthSession
119999
- });
120000
- async function ensureFreshCliAuthSession(input) {
120001
- const session = input.session;
120002
- if (!session?.accessToken?.trim()) return null;
120003
- const nowSeconds = Math.floor(Date.now() / 1e3);
120004
- const stillUsable = !session.expiresAt || session.expiresAt > nowSeconds + 90;
120005
- if (stillUsable && !input.forceRefresh) return session;
120006
- if (!session.refreshToken?.trim()) return null;
120007
- const fetchImpl = input.fetchImpl ?? globalThis.fetch;
120008
- if (typeof fetchImpl !== "function") return null;
120009
- const appUrl = session.appUrl?.trim();
120010
- if (!appUrl) return null;
120011
- const config = await fetchCliAuthConfig(appUrl, fetchImpl).catch(() => ({ ok: false }));
120012
- if (!config.ok || !config.supabaseUrl || !config.supabaseAnonKey) return null;
120013
- const supabase = createClient(config.supabaseUrl, config.supabaseAnonKey, {
120014
- auth: {
120015
- autoRefreshToken: false,
120016
- detectSessionInUrl: false,
120017
- persistSession: false
120018
- }
120019
- });
120020
- try {
120021
- const { data, error } = await supabase.auth.refreshSession({
120022
- refresh_token: session.refreshToken
120023
- });
120024
- if (error || !data.session) return null;
120025
- const refreshed = {
120026
- version: 1,
120027
- appUrl: session.appUrl,
120028
- accessToken: data.session.access_token,
120029
- refreshToken: data.session.refresh_token ?? session.refreshToken,
120030
- expiresAt: data.session.expires_at ?? null,
120031
- userId: data.session.user?.id ?? session.userId ?? null,
120032
- email: data.session.user?.email ?? session.email ?? null,
120033
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
120034
- };
120035
- await writeStoredCliAuthSession(refreshed);
120036
- return refreshed;
120037
- } catch {
120038
- return null;
120039
- }
120040
- }
120041
- var init_cliAuthRefresh = __esm({
120042
- "features/perchTerminal/runtime/cliHost/cliAuthRefresh.ts"() {
120043
- "use strict";
120044
- init_dist4();
120045
- init_cliStandaloneOAuth();
120046
- init_cliAuthSession();
120047
- }
120048
- });
120049
-
120050
119567
  // features/perchTerminal/runtime/modelRouter.ts
120051
119568
  function roostUserSelectionBodyFields() {
120052
119569
  const selection = getEffectiveRoostUserSelection();
@@ -120344,17 +119861,7 @@ async function syncModelProxyAuthAfter401() {
120344
119861
  await syncBrowserAuthSession();
120345
119862
  return null;
120346
119863
  }
120347
- const { readStoredCliAuthSession: readStoredCliAuthSession2 } = await Promise.resolve().then(() => (init_cliAuthSession(), cliAuthSession_exports));
120348
- const { ensureFreshCliAuthSession: ensureFreshCliAuthSession2 } = await Promise.resolve().then(() => (init_cliAuthRefresh(), cliAuthRefresh_exports));
120349
- const fresh = await ensureFreshCliAuthSession2({
120350
- session: await readStoredCliAuthSession2(),
120351
- forceRefresh: true
120352
- });
120353
- if (!fresh?.accessToken?.trim()) return null;
120354
- if (typeof process !== "undefined") {
120355
- process.env[MODEL_CALL_PROXY_TOKEN_ENV] = fresh.accessToken;
120356
- }
120357
- return fresh.accessToken;
119864
+ return refreshCliModelProxyAuth();
120358
119865
  }
120359
119866
  function withModelProxyAuthHeader(init, token) {
120360
119867
  if (!token?.trim()) return init;
@@ -120515,6 +120022,7 @@ var init_modelRouter = __esm({
120515
120022
  init_contextMeter();
120516
120023
  init_inferenceUsageRecorder();
120517
120024
  init_browserSession();
120025
+ init_cliAuthRefreshRegistry();
120518
120026
  init_roostUserSelectionClient();
120519
120027
  init_providerModelStep();
120520
120028
  init_toolCallNormalizer();
@@ -214765,8 +214273,8 @@ var init_barSeries = __esm({
214765
214273
  });
214766
214274
 
214767
214275
  // lib/perchMarketDesk/data/fixtureStore.ts
214768
- import fs10 from "node:fs";
214769
- import path11 from "node:path";
214276
+ import fs9 from "node:fs";
214277
+ import path10 from "node:path";
214770
214278
  function createFixtureStore(input) {
214771
214279
  const instruments = [...input.instruments].sort((a, b2) => a.ticker.localeCompare(b2.ticker));
214772
214280
  const seriesByTicker = /* @__PURE__ */ new Map();
@@ -214813,22 +214321,22 @@ function createFixtureStore(input) {
214813
214321
  };
214814
214322
  }
214815
214323
  function loadFixtureStoreFromDir(dir) {
214816
- const instrumentsPath = path11.join(dir, "instruments.json");
214817
- const instruments = JSON.parse(fs10.readFileSync(instrumentsPath, "utf8"));
214324
+ const instrumentsPath = path10.join(dir, "instruments.json");
214325
+ const instruments = JSON.parse(fs9.readFileSync(instrumentsPath, "utf8"));
214818
214326
  const bars = [];
214819
- const barsDir = path11.join(dir, "bars");
214820
- for (const file of fs10.readdirSync(barsDir)) {
214327
+ const barsDir = path10.join(dir, "bars");
214328
+ for (const file of fs9.readdirSync(barsDir)) {
214821
214329
  if (!file.endsWith(".json")) continue;
214822
- const ticker = path11.basename(file, ".json");
214823
- const rows = JSON.parse(fs10.readFileSync(path11.join(barsDir, file), "utf8"));
214330
+ const ticker = path10.basename(file, ".json");
214331
+ const rows = JSON.parse(fs9.readFileSync(path10.join(barsDir, file), "utf8"));
214824
214332
  for (const row of rows) bars.push({ ...row, ticker: row.ticker ?? ticker });
214825
214333
  }
214826
214334
  let fundamentals;
214827
- const fundamentalsPath = path11.join(dir, "fundamentals.json");
214828
- if (fs10.existsSync(fundamentalsPath)) {
214829
- fundamentals = JSON.parse(fs10.readFileSync(fundamentalsPath, "utf8"));
214335
+ const fundamentalsPath = path10.join(dir, "fundamentals.json");
214336
+ if (fs9.existsSync(fundamentalsPath)) {
214337
+ fundamentals = JSON.parse(fs9.readFileSync(fundamentalsPath, "utf8"));
214830
214338
  }
214831
- return createFixtureStore({ instruments, bars, fundamentals, label: `dir:${path11.basename(dir)}` });
214339
+ return createFixtureStore({ instruments, bars, fundamentals, label: `dir:${path10.basename(dir)}` });
214832
214340
  }
214833
214341
  var init_fixtureStore = __esm({
214834
214342
  "lib/perchMarketDesk/data/fixtureStore.ts"() {
@@ -224252,7 +223760,13 @@ async function runFlockTurn(input, deps, options = {}) {
224252
223760
  turnInput: input
224253
223761
  };
224254
223762
  const modelCall = options.plannerModelCall ?? defaultFlockPlannerModelCall(llmCtx);
224255
- plan = await runLlmFlockPlanner(llmCtx, modelCall).catch(() => null);
223763
+ const plannerTimeoutMs = options.plannerTimeoutMs ?? FLOCK_PLANNER_TIMEOUT_MS;
223764
+ const first2 = await runLlmPlannerWithTimeout(llmCtx, modelCall, plannerTimeoutMs);
223765
+ plan = first2.plan;
223766
+ if (!plan && !first2.timedOut) {
223767
+ const retry = await runLlmPlannerWithTimeout(llmCtx, modelCall, plannerTimeoutMs);
223768
+ plan = retry.plan;
223769
+ }
224256
223770
  }
224257
223771
  if (!plan) {
224258
223772
  plan = (options.planFn ?? planFlock)(task, {
@@ -224356,7 +223870,7 @@ async function runFlockTurn(input, deps, options = {}) {
224356
223870
  toolCallsReserved += reservedToolCalls;
224357
223871
  emitWorkerUpdate(emit, plan, worker, "running");
224358
223872
  try {
224359
- turnInput = await refreshFlockCliAuth(turnInput);
223873
+ turnInput = deps.refreshTurnInput ? await deps.refreshTurnInput(turnInput) : turnInput;
224360
223874
  const context = contextOverride ?? buildWorkerContext(worker, sharedContext, outputByFlockWorkerId, plan);
224361
223875
  const objective = worker.role === "worker" && contextContainsSources(context) ? `${worker.objective}
224362
223876
  ${FLOCK_GROUNDING_CONTRACT}` : worker.objective;
@@ -224483,14 +223997,25 @@ ${FLOCK_GROUNDING_CONTRACT}` : worker.objective;
224483
223997
  const workersFailed = outcomes.filter((outcome) => outcome.status === "failed").length + workersBlocked;
224484
223998
  const flockStatus = userCancelled || wallTimeHit && workersDone === 0 ? "cancelled" : workersFailed === 0 && workersDone === plan.workers.length ? "completed" : workersDone > 0 ? "partial" : "failed";
224485
223999
  const permissionHint = workersBlocked > 0 && (turnInput.permissionMode ?? "default") === "default" ? "\n\nTip: flock workers inherit default permissions \u2014 run `/permission take_the_wheel` before `/flock` so bash redirects and file writes do not stall on approval gates." : "";
224000
+ const deterministicSummary = buildFlockSummary(
224001
+ plan,
224002
+ [...outcomes, ...revisionOutcomes],
224003
+ flockStatus,
224004
+ toolCallsUsed,
224005
+ wallTimeHit
224006
+ );
224007
+ const synthesisModelCall = options.synthesisModelCall !== void 0 ? options.synthesisModelCall : options.plannerModelCall === void 0 ? defaultFlockSynthesisModelCall(input, plan.flockId) : null;
224008
+ let summaryBody = deterministicSummary;
224009
+ if (synthesisModelCall && !userCancelled && workersDone > 0) {
224010
+ const synthesized = await runFlockSynthesisWithTimeout(
224011
+ synthesisModelCall,
224012
+ buildFlockSynthesisPrompt(task, [...outcomes, ...revisionOutcomes], revisionReport),
224013
+ FLOCK_SYNTHESIS_TIMEOUT_MS
224014
+ );
224015
+ if (synthesized) summaryBody = synthesized;
224016
+ }
224486
224017
  const assistantText = [
224487
- buildFlockSummary(
224488
- plan,
224489
- [...outcomes, ...revisionOutcomes],
224490
- flockStatus,
224491
- toolCallsUsed,
224492
- wallTimeHit
224493
- ),
224018
+ summaryBody,
224494
224019
  ...buildVerificationSection(revisionReport),
224495
224020
  ...modelOverrides.applied.map(
224496
224021
  (override) => `Model override: ${override.displayName} ran on ${override.label}.`
@@ -224532,6 +224057,89 @@ ${FLOCK_GROUNDING_CONTRACT}` : worker.objective;
224532
224057
  }
224533
224058
  }
224534
224059
  }
224060
+ async function runLlmPlannerWithTimeout(ctx, modelCall, timeoutMs) {
224061
+ let timer;
224062
+ const timeout = new Promise((resolve5) => {
224063
+ timer = setTimeout(() => resolve5("timeout"), Math.max(1, timeoutMs));
224064
+ });
224065
+ try {
224066
+ const race = await Promise.race([
224067
+ runLlmFlockPlanner(ctx, modelCall).then((plan) => ({ plan })).catch(() => ({ plan: null })),
224068
+ timeout
224069
+ ]);
224070
+ if (race === "timeout") return { plan: null, timedOut: true };
224071
+ return { plan: race.plan, timedOut: false };
224072
+ } finally {
224073
+ if (timer) clearTimeout(timer);
224074
+ }
224075
+ }
224076
+ function defaultFlockSynthesisModelCall(input, flockId) {
224077
+ return async ({ systemPrompt, userPrompt }) => {
224078
+ const result2 = await runModelToolLoop({
224079
+ lane: "chat",
224080
+ founderModelSelection: input.founderModelSelection ?? null,
224081
+ systemPrompt,
224082
+ messages: [{ role: "user", content: userPrompt }],
224083
+ tools: [],
224084
+ workspaceRoot: input.activeRootPath ?? "",
224085
+ desktopConnected: input.desktopConnected,
224086
+ cliLocalTools: input.cliLocalTools === true,
224087
+ activeRootPath: input.activeRootPath,
224088
+ permissionMode: input.permissionMode,
224089
+ workspaceId: input.workspaceId,
224090
+ threadId: input.threadId,
224091
+ supabaseConfigured: input.supabaseConfigured,
224092
+ supabase: input.supabase ?? null,
224093
+ cliServerAppUrl: input.cliServerAppUrl ?? null,
224094
+ cliServerAccessToken: input.cliServerAccessToken ?? null,
224095
+ runId: flockId,
224096
+ chatMode: "ask",
224097
+ maxIterations: 1,
224098
+ onEvent: () => {
224099
+ }
224100
+ });
224101
+ return { ok: result2.ok, text: result2.text };
224102
+ };
224103
+ }
224104
+ function buildFlockSynthesisPrompt(task, outcomes, revisionReport) {
224105
+ const results = outcomes.filter((outcome) => outcome.status === "done" && outcome.result).map((outcome) => {
224106
+ const raw = outcome.result?.structuredOutput ?? outcome.result?.summary ?? "";
224107
+ const serialized = typeof raw === "string" ? raw : JSON.stringify(raw);
224108
+ return {
224109
+ role: outcome.worker.displayName,
224110
+ output: serialized.slice(0, FLOCK_SYNTHESIS_MAX_OUTPUT_PER_WORKER)
224111
+ };
224112
+ });
224113
+ const verification = revisionReport ? {
224114
+ verdict: revisionReport.recheckFindings?.verdict ?? revisionReport.findings.verdict ?? null,
224115
+ issues: (revisionReport.recheckFindings?.issues ?? revisionReport.findings.issues).slice(0, 6),
224116
+ revised: revisionReport.revised
224117
+ } : null;
224118
+ const systemPrompt = [
224119
+ "You are the coordinator that just ran a Flock: a team of sub-agents that worked in parallel on the user's task.",
224120
+ "The sub-agents have finished. Write the final answer to the user in your own voice \u2014 clear, direct, friendly, first person.",
224121
+ "Lead with what the user asked for. Present the actual results and fold in any important verification caveats.",
224122
+ "Do not mention sub-agents, workers, roles, nicknames, IDs, or orchestration mechanics unless directly useful to the user.",
224123
+ "Synthesize only from the provided results \u2014 do not invent facts and do not say you will do further work."
224124
+ ].join("\n");
224125
+ const userPrompt = JSON.stringify({ task, results, verification });
224126
+ return { systemPrompt, userPrompt };
224127
+ }
224128
+ async function runFlockSynthesisWithTimeout(call, prompt, timeoutMs) {
224129
+ let timer;
224130
+ const timeout = new Promise((resolve5) => {
224131
+ timer = setTimeout(() => resolve5("timeout"), Math.max(1, timeoutMs));
224132
+ });
224133
+ try {
224134
+ const race = await Promise.race([
224135
+ call(prompt).then((result2) => result2.ok && result2.text.trim() ? result2.text.trim() : null).catch(() => null),
224136
+ timeout
224137
+ ]);
224138
+ return race === "timeout" ? null : race;
224139
+ } finally {
224140
+ if (timer) clearTimeout(timer);
224141
+ }
224142
+ }
224535
224143
  function emitPlanEvent(emit, plan) {
224536
224144
  emit({
224537
224145
  type: "flock_plan_ready",
@@ -224761,36 +224369,16 @@ function flockWorkerStatusFromSpawnResult(result2) {
224761
224369
  if (result2.usabilityStatus === "needs_user_input") return "blocked";
224762
224370
  return "done";
224763
224371
  }
224764
- async function refreshFlockCliAuth(input) {
224765
- if (input.cliLocalTools !== true) return input;
224766
- const stored = await readStoredCliAuthSession();
224767
- if (!stored?.accessToken?.trim()) return input;
224768
- const turnToken = input.cliServerAccessToken?.trim();
224769
- if (turnToken && turnToken !== stored.accessToken) return input;
224770
- const fresh = await ensureFreshCliAuthSession({ session: stored });
224771
- if (!fresh?.accessToken?.trim()) return input;
224772
- if (fresh.accessToken === input.cliServerAccessToken) return input;
224773
- if (typeof process !== "undefined") {
224774
- process.env.PERCH_MODEL_CALL_PROXY_TOKEN = fresh.accessToken;
224775
- }
224776
- return {
224777
- ...input,
224778
- cliServerAccessToken: fresh.accessToken,
224779
- marketDeskProxyAccessToken: input.marketDeskProxyAccessToken ?? fresh.accessToken
224780
- };
224781
- }
224782
224372
  function now11() {
224783
224373
  return (/* @__PURE__ */ new Date()).toISOString();
224784
224374
  }
224785
- var FLOCK_GROUNDING_CONTRACT;
224375
+ var FLOCK_PLANNER_TIMEOUT_MS, FLOCK_SYNTHESIS_TIMEOUT_MS, FLOCK_SYNTHESIS_MAX_OUTPUT_PER_WORKER, FLOCK_GROUNDING_CONTRACT;
224786
224376
  var init_runFlockTurn = __esm({
224787
224377
  "features/perchTerminal/runtime/flock/runFlockTurn.ts"() {
224788
224378
  "use strict";
224789
224379
  init_operatorStream();
224790
224380
  init_runRegistry();
224791
224381
  init_spawnWorker2();
224792
- init_cliAuthSession();
224793
- init_cliAuthRefresh();
224794
224382
  init_registry();
224795
224383
  init_flockCommand();
224796
224384
  init_flockLimits();
@@ -224798,6 +224386,10 @@ var init_runFlockTurn = __esm({
224798
224386
  init_flockLlmPlanner();
224799
224387
  init_flockPlanner();
224800
224388
  init_flockRoles();
224389
+ init_toolLoop();
224390
+ FLOCK_PLANNER_TIMEOUT_MS = 35e3;
224391
+ FLOCK_SYNTHESIS_TIMEOUT_MS = 3e4;
224392
+ FLOCK_SYNTHESIS_MAX_OUTPUT_PER_WORKER = 2e3;
224801
224393
  FLOCK_GROUNDING_CONTRACT = [
224802
224394
  "Grounding contract: your context includes sources[] gathered by research.",
224803
224395
  "Every nontrivial factual claim in the draft must carry an inline [n] marker mapping to one of those sources, with a numbered source list at the end.",
@@ -233104,9 +232696,9 @@ var init_backgroundTaskTypes = __esm({
233104
232696
  });
233105
232697
 
233106
232698
  // features/perchTerminal/runtime/background/backgroundTaskRegistry.ts
233107
- import fs11 from "node:fs";
233108
- import os2 from "node:os";
233109
- import path12 from "node:path";
232699
+ import fs10 from "node:fs";
232700
+ import os from "node:os";
232701
+ import path11 from "node:path";
233110
232702
  function isTerminalStatus(status) {
233111
232703
  return status !== "running";
233112
232704
  }
@@ -233163,19 +232755,19 @@ function runtimeMs(record) {
233163
232755
  function readOutputPreview(outputPath, previewBytes = BACKGROUND_TASK_BOUNDS.previewBytes) {
233164
232756
  if (!outputPath) return { preview: "", truncated: false };
233165
232757
  try {
233166
- const stat2 = fs11.statSync(outputPath);
233167
- const fd = fs11.openSync(outputPath, "r");
232758
+ const stat2 = fs10.statSync(outputPath);
232759
+ const fd = fs10.openSync(outputPath, "r");
233168
232760
  try {
233169
232761
  if (stat2.size <= previewBytes) {
233170
232762
  const buf2 = Buffer.alloc(stat2.size);
233171
- fs11.readSync(fd, buf2, 0, stat2.size, 0);
232763
+ fs10.readSync(fd, buf2, 0, stat2.size, 0);
233172
232764
  return { preview: buf2.toString("utf8"), truncated: false };
233173
232765
  }
233174
232766
  const buf = Buffer.alloc(previewBytes);
233175
- fs11.readSync(fd, buf, 0, previewBytes, stat2.size - previewBytes);
232767
+ fs10.readSync(fd, buf, 0, previewBytes, stat2.size - previewBytes);
233176
232768
  return { preview: buf.toString("utf8"), truncated: true };
233177
232769
  } finally {
233178
- fs11.closeSync(fd);
232770
+ fs10.closeSync(fd);
233179
232771
  }
233180
232772
  } catch {
233181
232773
  return { preview: "", truncated: false };
@@ -233210,28 +232802,26 @@ function getBackgroundTaskStore() {
233210
232802
  var STORE_FILE, BackgroundTaskStore, singleton;
233211
232803
  var init_backgroundTaskRegistry = __esm({
233212
232804
  "features/perchTerminal/runtime/background/backgroundTaskRegistry.ts"() {
232805
+ "use strict";
233213
232806
  init_backgroundTaskTypes();
233214
232807
  STORE_FILE = "background-tasks.json";
233215
232808
  BackgroundTaskStore = class {
233216
- baseDir;
233217
- storePath;
233218
- outputDir;
233219
- records = /* @__PURE__ */ new Map();
233220
- loaded = false;
233221
232809
  constructor(options = {}) {
233222
- this.baseDir = options.baseDir ?? path12.join(os2.homedir(), ".perch");
233223
- this.storePath = path12.join(this.baseDir, STORE_FILE);
233224
- this.outputDir = path12.join(this.baseDir, "background-output");
232810
+ this.records = /* @__PURE__ */ new Map();
232811
+ this.loaded = false;
232812
+ this.baseDir = options.baseDir ?? path11.join(os.homedir(), ".perch");
232813
+ this.storePath = path11.join(this.baseDir, STORE_FILE);
232814
+ this.outputDir = path11.join(this.baseDir, "background-output");
233225
232815
  }
233226
232816
  ensureDirs() {
233227
- fs11.mkdirSync(this.baseDir, { recursive: true });
233228
- fs11.mkdirSync(this.outputDir, { recursive: true });
232817
+ fs10.mkdirSync(this.baseDir, { recursive: true });
232818
+ fs10.mkdirSync(this.outputDir, { recursive: true });
233229
232819
  }
233230
232820
  load() {
233231
232821
  if (this.loaded) return;
233232
232822
  this.loaded = true;
233233
232823
  try {
233234
- const raw = fs11.readFileSync(this.storePath, "utf8");
232824
+ const raw = fs10.readFileSync(this.storePath, "utf8");
233235
232825
  const parsed = JSON.parse(raw);
233236
232826
  const tasks = Array.isArray(parsed.tasks) ? parsed.tasks : [];
233237
232827
  this.records = new Map(tasks.map((t) => [t.taskId, t]));
@@ -233243,11 +232833,11 @@ var init_backgroundTaskRegistry = __esm({
233243
232833
  this.ensureDirs();
233244
232834
  const tasks = Array.from(this.records.values());
233245
232835
  const tmp = `${this.storePath}.tmp`;
233246
- fs11.writeFileSync(tmp, JSON.stringify({ version: 1, tasks }, null, 2), "utf8");
233247
- fs11.renameSync(tmp, this.storePath);
232836
+ fs10.writeFileSync(tmp, JSON.stringify({ version: 1, tasks }, null, 2), "utf8");
232837
+ fs10.renameSync(tmp, this.storePath);
233248
232838
  }
233249
232839
  outputPathFor(taskId) {
233250
- return path12.join(this.outputDir, `${taskId}.log`);
232840
+ return path11.join(this.outputDir, `${taskId}.log`);
233251
232841
  }
233252
232842
  list() {
233253
232843
  this.load();
@@ -233409,14 +232999,14 @@ var init_backgroundWakeController = __esm({
233409
232999
 
233410
233000
  // features/perchTerminal/runtime/background/runBackgroundCommand.ts
233411
233001
  import { spawn as spawn2 } from "node:child_process";
233412
- import fs12 from "node:fs";
233413
- import path13 from "node:path";
233002
+ import fs11 from "node:fs";
233003
+ import path12 from "node:path";
233414
233004
  function newTaskId() {
233415
233005
  return `bg-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
233416
233006
  }
233417
233007
  function createCappedWriter(filePath, maxBytes) {
233418
- fs12.mkdirSync(path13.dirname(filePath), { recursive: true });
233419
- const stream = fs12.createWriteStream(filePath, { flags: "w" });
233008
+ fs11.mkdirSync(path12.dirname(filePath), { recursive: true });
233009
+ const stream = fs11.createWriteStream(filePath, { flags: "w" });
233420
233010
  let written = 0;
233421
233011
  let truncated = false;
233422
233012
  return {
@@ -233589,10 +233179,10 @@ var init_runBackgroundCommand = __esm({
233589
233179
 
233590
233180
  // features/perchTerminal/runtime/cliHost/nodeLocalBridge.ts
233591
233181
  import { spawn as spawn3 } from "node:child_process";
233592
- import fs13 from "node:fs";
233182
+ import fs12 from "node:fs";
233593
233183
  import fsp from "node:fs/promises";
233594
- import os3 from "node:os";
233595
- import path14 from "node:path";
233184
+ import os2 from "node:os";
233185
+ import path13 from "node:path";
233596
233186
  function installCliNodeLocalBridge(input) {
233597
233187
  const globalRef = globalThis;
233598
233188
  const previous = globalRef.__PERCH_TEST_DESKTOP_BRIDGE__;
@@ -233606,7 +233196,7 @@ function installCliNodeLocalBridge(input) {
233606
233196
  };
233607
233197
  }
233608
233198
  function readCliProjectMemoryState(workspaceRoot) {
233609
- const root2 = path14.resolve(expandHome4(workspaceRoot));
233199
+ const root2 = path13.resolve(expandHome4(workspaceRoot));
233610
233200
  if (!isCliProjectMemoryAvailable(root2)) {
233611
233201
  return {
233612
233202
  available: false,
@@ -233621,7 +233211,7 @@ function readCliProjectMemoryState(workspaceRoot) {
233621
233211
  };
233622
233212
  }
233623
233213
  function createCliNodeLocalBridge(input) {
233624
- const workspaceRoot = path14.resolve(input.workspaceRoot);
233214
+ const workspaceRoot = path13.resolve(input.workspaceRoot);
233625
233215
  const now23 = () => (/* @__PURE__ */ new Date()).toISOString();
233626
233216
  const sandboxHandlers = /* @__PURE__ */ new Map();
233627
233217
  const emitSandboxEvent = (event) => {
@@ -233635,7 +233225,7 @@ function createCliNodeLocalBridge(input) {
233635
233225
  cwd: workspaceRoot,
233636
233226
  projectRoot: workspaceRoot,
233637
233227
  shellPath: process.env.SHELL || "/bin/zsh",
233638
- shellName: path14.basename(process.env.SHELL || "zsh"),
233228
+ shellName: path13.basename(process.env.SHELL || "zsh"),
233639
233229
  path: process.env.PATH || "",
233640
233230
  status: "running",
233641
233231
  backend: "spawn-fallback",
@@ -233662,7 +233252,7 @@ function createCliNodeLocalBridge(input) {
233662
233252
  capabilities: ["local-files", "local-shell"]
233663
233253
  }),
233664
233254
  checkFsAccess: async (request) => {
233665
- const absolutePath = path14.resolve(expandHome4(request.absolutePath));
233255
+ const absolutePath = path13.resolve(expandHome4(request.absolutePath));
233666
233256
  return {
233667
233257
  decision: "allow",
233668
233258
  reason: "Allowed by Perch CLI local filesystem access.",
@@ -233680,7 +233270,7 @@ function createCliNodeLocalBridge(input) {
233680
233270
  ok: true,
233681
233271
  directory: workspaceRoot,
233682
233272
  fileName: request.fileName,
233683
- absolutePath: path14.join(workspaceRoot, request.fileName),
233273
+ absolutePath: path13.join(workspaceRoot, request.fileName),
233684
233274
  source: "approved_root"
233685
233275
  }),
233686
233276
  validateWorkspaceRoot: async () => ({
@@ -233769,7 +233359,7 @@ function createCliNodeLocalBridge(input) {
233769
233359
  writeWorkspaceFile: async (request) => {
233770
233360
  const baseRoot = resolveRequestRoot(workspaceRoot, request.workspaceRoot);
233771
233361
  const target = resolveWritePath(baseRoot, request.relativePath);
233772
- await fsp.mkdir(path14.dirname(target), { recursive: true });
233362
+ await fsp.mkdir(path13.dirname(target), { recursive: true });
233773
233363
  const flag = request.overwrite === true ? "w" : "wx";
233774
233364
  const payload = request.encoding === "base64" && typeof request.contentBase64 === "string" ? Buffer.from(request.contentBase64, "base64") : request.content ?? "";
233775
233365
  try {
@@ -233789,14 +233379,14 @@ function createCliNodeLocalBridge(input) {
233789
233379
  moveLocalFile: async (request) => {
233790
233380
  const src = resolveReadPath(workspaceRoot, request.src);
233791
233381
  const dest = resolveWritePath(workspaceRoot, request.dest);
233792
- await fsp.mkdir(path14.dirname(dest), { recursive: true });
233382
+ await fsp.mkdir(path13.dirname(dest), { recursive: true });
233793
233383
  await fsp.rename(src, dest);
233794
233384
  return { ok: true, fromPath: src, toPath: dest };
233795
233385
  },
233796
233386
  copyLocalFile: async (request) => {
233797
233387
  const src = resolveReadPath(workspaceRoot, request.src);
233798
233388
  const dest = resolveWritePath(workspaceRoot, request.dest);
233799
- await fsp.mkdir(path14.dirname(dest), { recursive: true });
233389
+ await fsp.mkdir(path13.dirname(dest), { recursive: true });
233800
233390
  await fsp.copyFile(src, dest);
233801
233391
  return { ok: true, fromPath: src, toPath: dest };
233802
233392
  },
@@ -233821,7 +233411,7 @@ function createCliNodeLocalBridge(input) {
233821
233411
  const pattern = request.pattern?.trim() || "**/*";
233822
233412
  const regex2 = globToRegex(pattern);
233823
233413
  const files = await collectFiles(root2, { maxVisits: Math.max(maxResults * 20, 1e3) });
233824
- const matches = files.map((file) => path14.relative(root2, file).replace(/\\/g, "/")).filter((relative2) => regex2.test(relative2)).slice(0, maxResults);
233414
+ const matches = files.map((file) => path13.relative(root2, file).replace(/\\/g, "/")).filter((relative2) => regex2.test(relative2)).slice(0, maxResults);
233825
233415
  return {
233826
233416
  ok: true,
233827
233417
  matches,
@@ -233844,7 +233434,7 @@ function createCliNodeLocalBridge(input) {
233844
233434
  const files = await collectFiles(root2, { maxVisits: 5e3 });
233845
233435
  const matches = [];
233846
233436
  for (const file of files) {
233847
- const relative2 = path14.relative(root2, file).replace(/\\/g, "/");
233437
+ const relative2 = path13.relative(root2, file).replace(/\\/g, "/");
233848
233438
  if (includeRegex && !includeRegex.test(relative2)) continue;
233849
233439
  const text = await readTextFileIfReasonable(file);
233850
233440
  if (text === null) continue;
@@ -233883,7 +233473,7 @@ function createCliNodeLocalBridge(input) {
233883
233473
  const stat2 = await safeStat(target);
233884
233474
  return {
233885
233475
  ok: true,
233886
- relativePath: path14.isAbsolute(request.relativePath) || request.relativePath.startsWith("~") ? target : path14.relative(baseRoot, target) || ".",
233476
+ relativePath: path13.isAbsolute(request.relativePath) || request.relativePath.startsWith("~") ? target : path13.relative(baseRoot, target) || ".",
233887
233477
  exists: Boolean(stat2),
233888
233478
  isFile: stat2?.isFile() ?? false,
233889
233479
  isDirectory: stat2?.isDirectory() ?? false,
@@ -233917,7 +233507,7 @@ function createCliNodeLocalBridge(input) {
233917
233507
  return {
233918
233508
  ok: true,
233919
233509
  data: buffer.toString("base64"),
233920
- fileName: path14.relative(workspaceRoot, target) || path14.basename(target),
233510
+ fileName: path13.relative(workspaceRoot, target) || path13.basename(target),
233921
233511
  mimeType: inferMimeType(target),
233922
233512
  sizeBytes: stat2.size,
233923
233513
  encoding: "base64"
@@ -234013,7 +233603,7 @@ async function runCliPrepareAPEvidence(workspaceRoot, request) {
234013
233603
  topCases: (artifact.cases ?? []).slice(0, 12),
234014
233604
  duplicateCases: result2.payload.duplicateCases,
234015
233605
  controlGraph: result2.payload.controlGraph,
234016
- relativeOutputDir: typeof artifact.outputDir === "string" ? path14.basename(artifact.outputDir) : null,
233606
+ relativeOutputDir: typeof artifact.outputDir === "string" ? path13.basename(artifact.outputDir) : null,
234017
233607
  approvedRootMatch: true
234018
233608
  },
234019
233609
  executionHost: "electron_desktop",
@@ -234102,7 +233692,7 @@ async function prepareCliSandboxInputs(workspaceRoot, request) {
234102
233692
  const jobId = request.jobId?.trim() || `cli-sandbox-${Date.now()}`;
234103
233693
  const maxFiles = Math.max(1, Math.min(request.maxFiles ?? 20, 100));
234104
233694
  const maxBytesPerFile = Math.max(1, Math.min(request.maxBytesPerFile ?? 20 * 1024 * 1024, 50 * 1024 * 1024));
234105
- const tempDir = await fsp.mkdtemp(path14.join(os3.tmpdir(), `${jobId}-`));
233695
+ const tempDir = await fsp.mkdtemp(path13.join(os2.tmpdir(), `${jobId}-`));
234106
233696
  const entries = [];
234107
233697
  const skipped = [];
234108
233698
  for (const localSourceId of request.localSourceIds.slice(0, maxFiles)) {
@@ -234116,13 +233706,13 @@ async function prepareCliSandboxInputs(workspaceRoot, request) {
234116
233706
  skipped.push({ localSourceId, reason: `File is too large: ${stat2.size} bytes.` });
234117
233707
  continue;
234118
233708
  }
234119
- const relativePath = safeSandboxRelativePath(path14.isAbsolute(localSourceId) || localSourceId.startsWith("~") ? path14.basename(sourcePath) : path14.relative(workspaceRoot, sourcePath));
234120
- const tempPath = path14.join(tempDir, relativePath);
234121
- await fsp.mkdir(path14.dirname(tempPath), { recursive: true });
233709
+ const relativePath = safeSandboxRelativePath(path13.isAbsolute(localSourceId) || localSourceId.startsWith("~") ? path13.basename(sourcePath) : path13.relative(workspaceRoot, sourcePath));
233710
+ const tempPath = path13.join(tempDir, relativePath);
233711
+ await fsp.mkdir(path13.dirname(tempPath), { recursive: true });
234122
233712
  await fsp.copyFile(sourcePath, tempPath);
234123
233713
  entries.push({
234124
233714
  localSourceId,
234125
- fileName: path14.basename(sourcePath),
233715
+ fileName: path13.basename(sourcePath),
234126
233716
  relativePath,
234127
233717
  tempPath,
234128
233718
  sizeBytes: stat2.size,
@@ -234160,11 +233750,11 @@ async function runCliSandboxCodeJob(workspaceRoot, request, onEvent) {
234160
233750
  getApprovedRoot: (rootId) => rootId === CLI_ROOT_ID ? { id: CLI_ROOT_ID, path: workspaceRoot, approvedAt: (/* @__PURE__ */ new Date()).toISOString() } : null,
234161
233751
  isPathSafe: (_rootPath, targetPath) => !targetPath.split(/[\\/]+/).includes(".."),
234162
233752
  isInsideApprovedRoot: (filePath) => {
234163
- const absolute = path14.resolve(expandHome4(filePath));
233753
+ const absolute = path13.resolve(expandHome4(filePath));
234164
233754
  if (isInside(workspaceRoot, absolute)) {
234165
233755
  return { id: CLI_ROOT_ID, path: workspaceRoot, approvedAt: (/* @__PURE__ */ new Date()).toISOString() };
234166
233756
  }
234167
- return { id: "cli-absolute-root", path: path14.dirname(absolute), approvedAt: (/* @__PURE__ */ new Date()).toISOString() };
233757
+ return { id: "cli-absolute-root", path: path13.dirname(absolute), approvedAt: (/* @__PURE__ */ new Date()).toISOString() };
234168
233758
  },
234169
233759
  shouldIgnore: (fileName) => IGNORED_DIRS.has(fileName),
234170
233760
  guessMimeType: inferMimeType,
@@ -234207,7 +233797,7 @@ function safeSandboxRelativePath(value) {
234207
233797
  return clean || "input";
234208
233798
  }
234209
233799
  function guessCliFileType(filePath) {
234210
- const ext = path14.extname(filePath).toLowerCase();
233800
+ const ext = path13.extname(filePath).toLowerCase();
234211
233801
  if ([".xlsx", ".xls"].includes(ext)) return "spreadsheet";
234212
233802
  if (ext === ".csv" || ext === ".tsv") return "csv";
234213
233803
  if (ext === ".pdf") return "pdf";
@@ -234226,7 +233816,7 @@ async function readCliProjectMemoryBridge(workspaceRoot) {
234226
233816
  return { ok: true, meta: state.meta };
234227
233817
  }
234228
233818
  async function writeCliProjectMemory(workspaceRoot, patch) {
234229
- const root2 = path14.resolve(expandHome4(workspaceRoot));
233819
+ const root2 = path13.resolve(expandHome4(workspaceRoot));
234230
233820
  if (!isCliProjectMemoryAvailable(root2)) {
234231
233821
  return {
234232
233822
  ok: false,
@@ -234242,12 +233832,12 @@ async function writeCliProjectMemory(workspaceRoot, patch) {
234242
233832
  lastOpenedThreadId: patch.lastOpenedThreadId !== void 0 ? patch.lastOpenedThreadId : current.lastOpenedThreadId,
234243
233833
  memorySummary: patch.memorySummary !== void 0 ? patch.memorySummary : current.memorySummary
234244
233834
  };
234245
- await fsp.mkdir(path14.join(root2, PERCH_DIR), { recursive: true });
233835
+ await fsp.mkdir(path13.join(root2, PERCH_DIR), { recursive: true });
234246
233836
  await atomicWriteCliUtf8(getCliProjectFilePath(root2), JSON.stringify(next, null, 2));
234247
233837
  return { ok: true, meta: enrichCliProjectMeta(root2, next) };
234248
233838
  }
234249
233839
  async function writeCliMemoryFile(workspaceRoot, request) {
234250
- const root2 = path14.resolve(expandHome4(workspaceRoot));
233840
+ const root2 = path13.resolve(expandHome4(workspaceRoot));
234251
233841
  if (!isCliProjectMemoryAvailable(root2)) {
234252
233842
  return {
234253
233843
  ok: false,
@@ -234259,8 +233849,8 @@ async function writeCliMemoryFile(workspaceRoot, request) {
234259
233849
  }
234260
233850
  const fileName = normalizeCliMemoryFileName(request.fileName);
234261
233851
  if (!fileName) return { ok: false, error: "Invalid memory file name." };
234262
- const relativePath = path14.join(PERCH_DIR, MEMORY_DIR, fileName);
234263
- const fullPath = path14.join(root2, relativePath);
233852
+ const relativePath = path13.join(PERCH_DIR, MEMORY_DIR, fileName);
233853
+ const fullPath = path13.join(root2, relativePath);
234264
233854
  const existing = await fsp.readFile(fullPath, "utf8").catch(() => "");
234265
233855
  let nextContent;
234266
233856
  try {
@@ -234273,7 +233863,7 @@ async function writeCliMemoryFile(workspaceRoot, request) {
234273
233863
  }
234274
233864
  const capError = assertCliMemoryWithinCap(nextContent);
234275
233865
  if (capError) return { ok: false, error: capError };
234276
- await fsp.mkdir(path14.dirname(fullPath), { recursive: true });
233866
+ await fsp.mkdir(path13.dirname(fullPath), { recursive: true });
234277
233867
  await atomicWriteCliUtf8(fullPath, nextContent);
234278
233868
  const updatedFile = readCliTextMemoryFile(root2, relativePath, MAX_MEMORY_BYTES);
234279
233869
  return {
@@ -234299,17 +233889,17 @@ function defaultCliProjectMeta() {
234299
233889
  }
234300
233890
  function isCliProjectMemoryAvailable(root2) {
234301
233891
  try {
234302
- return fs13.statSync(path14.join(root2, PERCH_DIR)).isDirectory();
233892
+ return fs12.statSync(path13.join(root2, PERCH_DIR)).isDirectory();
234303
233893
  } catch {
234304
233894
  return false;
234305
233895
  }
234306
233896
  }
234307
233897
  function getCliProjectFilePath(root2) {
234308
- return path14.join(root2, PERCH_DIR, PROJECT_FILE);
233898
+ return path13.join(root2, PERCH_DIR, PROJECT_FILE);
234309
233899
  }
234310
233900
  function loadCliStoredMeta(root2) {
234311
233901
  try {
234312
- const raw = fs13.readFileSync(getCliProjectFilePath(root2), "utf8");
233902
+ const raw = fs12.readFileSync(getCliProjectFilePath(root2), "utf8");
234313
233903
  return { ...defaultCliProjectMeta(), ...JSON.parse(raw) };
234314
233904
  } catch {
234315
233905
  return defaultCliProjectMeta();
@@ -234317,7 +233907,7 @@ function loadCliStoredMeta(root2) {
234317
233907
  }
234318
233908
  function enrichCliProjectMeta(root2, base) {
234319
233909
  const perchMd = readCliFirstExisting(root2, [
234320
- path14.join(PERCH_DIR, PERCH_INDEX_FILE),
233910
+ path13.join(PERCH_DIR, PERCH_INDEX_FILE),
234321
233911
  PERCH_INDEX_FILE
234322
233912
  ], MAX_TEXT_BYTES);
234323
233913
  const rules = readCliRules(root2);
@@ -234428,10 +234018,10 @@ function readCliFirstExisting(root2, relativePaths, maxBytes) {
234428
234018
  return null;
234429
234019
  }
234430
234020
  function readCliRules(root2) {
234431
- const dirPath = path14.join(root2, PERCH_DIR, RULES_DIR);
234021
+ const dirPath = path13.join(root2, PERCH_DIR, RULES_DIR);
234432
234022
  try {
234433
- return fs13.readdirSync(dirPath).filter((name) => [".md", ".txt"].includes(path14.extname(name).toLowerCase())).slice(0, 40).map((name) => readCliTextMemoryFile(root2, path14.join(PERCH_DIR, RULES_DIR, name), MAX_TEXT_BYTES)).filter((file) => file.found).map((file) => ({
234434
- fileName: path14.basename(file.relativePath),
234023
+ return fs12.readdirSync(dirPath).filter((name) => [".md", ".txt"].includes(path13.extname(name).toLowerCase())).slice(0, 40).map((name) => readCliTextMemoryFile(root2, path13.join(PERCH_DIR, RULES_DIR, name), MAX_TEXT_BYTES)).filter((file) => file.found).map((file) => ({
234024
+ fileName: path13.basename(file.relativePath),
234435
234025
  relativePath: file.relativePath,
234436
234026
  content: file.content,
234437
234027
  sizeBytes: file.sizeBytes,
@@ -234442,15 +234032,15 @@ function readCliRules(root2) {
234442
234032
  }
234443
234033
  }
234444
234034
  function readCliMemoryFiles(root2) {
234445
- const memoryDir = path14.join(root2, PERCH_DIR, MEMORY_DIR);
234035
+ const memoryDir = path13.join(root2, PERCH_DIR, MEMORY_DIR);
234446
234036
  const discovered = /* @__PURE__ */ new Set();
234447
234037
  const memoryFiles = [];
234448
234038
  try {
234449
- for (const name of fs13.readdirSync(memoryDir).slice(0, MAX_MEMORY_FILES)) {
234450
- const ext = path14.extname(name).toLowerCase();
234039
+ for (const name of fs12.readdirSync(memoryDir).slice(0, MAX_MEMORY_FILES)) {
234040
+ const ext = path13.extname(name).toLowerCase();
234451
234041
  if (ext !== ".md" && ext !== ".txt") continue;
234452
234042
  discovered.add(name);
234453
- memoryFiles.push(readCliTextMemoryFile(root2, path14.join(PERCH_DIR, MEMORY_DIR, name), MAX_MEMORY_BYTES));
234043
+ memoryFiles.push(readCliTextMemoryFile(root2, path13.join(PERCH_DIR, MEMORY_DIR, name), MAX_MEMORY_BYTES));
234454
234044
  }
234455
234045
  } catch {
234456
234046
  }
@@ -234458,20 +234048,20 @@ function readCliMemoryFiles(root2) {
234458
234048
  memoryFiles,
234459
234049
  missingMemoryFiles: EXPECTED_MEMORY_FILES.filter((name) => !discovered.has(name)).map((name) => ({
234460
234050
  fileName: name,
234461
- relativePath: path14.join(PERCH_DIR, MEMORY_DIR, name),
234051
+ relativePath: path13.join(PERCH_DIR, MEMORY_DIR, name),
234462
234052
  reason: "expected project memory file was not found"
234463
234053
  }))
234464
234054
  };
234465
234055
  }
234466
234056
  function readCliTextMemoryFile(root2, relativePath, maxBytes) {
234467
- const fullPath = path14.join(root2, relativePath);
234468
- const fileName = path14.basename(relativePath);
234057
+ const fullPath = path13.join(root2, relativePath);
234058
+ const fileName = path13.basename(relativePath);
234469
234059
  try {
234470
- const stat2 = fs13.statSync(fullPath);
234060
+ const stat2 = fs12.statSync(fullPath);
234471
234061
  if (!stat2.isFile()) {
234472
234062
  return { fileName, relativePath, content: "", found: false, error: "not a regular file" };
234473
234063
  }
234474
- const content = fs13.readFileSync(fullPath, "utf8");
234064
+ const content = fs12.readFileSync(fullPath, "utf8");
234475
234065
  if (stat2.size > maxBytes) {
234476
234066
  return {
234477
234067
  fileName,
@@ -234593,7 +234183,7 @@ function terminateProcessGroup(child, signal) {
234593
234183
  }
234594
234184
  function resolveReadPath(root2, inputPath) {
234595
234185
  const expanded = expandHome4(inputPath || ".");
234596
- return path14.resolve(path14.isAbsolute(expanded) ? expanded : path14.join(root2, expanded));
234186
+ return path13.resolve(path13.isAbsolute(expanded) ? expanded : path13.join(root2, expanded));
234597
234187
  }
234598
234188
  function resolveRequestRoot(defaultRoot, requestRoot) {
234599
234189
  const trimmed = requestRoot?.trim();
@@ -234601,25 +234191,25 @@ function resolveRequestRoot(defaultRoot, requestRoot) {
234601
234191
  }
234602
234192
  function resolveWritePath(root2, inputPath) {
234603
234193
  const expanded = expandHome4(inputPath || ".");
234604
- return path14.resolve(path14.isAbsolute(expanded) ? expanded : path14.join(root2, expanded));
234194
+ return path13.resolve(path13.isAbsolute(expanded) ? expanded : path13.join(root2, expanded));
234605
234195
  }
234606
234196
  function displayPath(root2, target, requestedPath) {
234607
234197
  const expanded = expandHome4(requestedPath || ".");
234608
- if (path14.isAbsolute(expanded) || requestedPath.startsWith("~")) return target;
234609
- return path14.relative(root2, target) || path14.basename(target);
234198
+ if (path13.isAbsolute(expanded) || requestedPath.startsWith("~")) return target;
234199
+ return path13.relative(root2, target) || path13.basename(target);
234610
234200
  }
234611
234201
  function resolveLocalSourceId(root2, localSourceId) {
234612
234202
  const raw = localSourceId.includes("::") ? localSourceId.split("::").slice(1).join("::") : localSourceId;
234613
234203
  return resolveReadPath(root2, raw);
234614
234204
  }
234615
234205
  function expandHome4(inputPath) {
234616
- if (inputPath === "~") return os3.homedir();
234617
- if (inputPath.startsWith("~/")) return path14.join(os3.homedir(), inputPath.slice(2));
234206
+ if (inputPath === "~") return os2.homedir();
234207
+ if (inputPath.startsWith("~/")) return path13.join(os2.homedir(), inputPath.slice(2));
234618
234208
  return inputPath;
234619
234209
  }
234620
234210
  function isInside(root2, candidate) {
234621
- const relative2 = path14.relative(path14.resolve(root2), path14.resolve(candidate));
234622
- return relative2 === "" || !relative2.startsWith("..") && !path14.isAbsolute(relative2);
234211
+ const relative2 = path13.relative(path13.resolve(root2), path13.resolve(candidate));
234212
+ return relative2 === "" || !relative2.startsWith("..") && !path13.isAbsolute(relative2);
234623
234213
  }
234624
234214
  async function safeStat(target) {
234625
234215
  try {
@@ -234636,7 +234226,7 @@ async function collectFiles(root2, options) {
234636
234226
  for (const entry of entries) {
234637
234227
  if (result2.length >= options.maxVisits) return;
234638
234228
  if (entry.name.startsWith(".") && entry.name !== ".env") continue;
234639
- const absolute = path14.join(dir, entry.name);
234229
+ const absolute = path13.join(dir, entry.name);
234640
234230
  if (entry.isDirectory()) {
234641
234231
  if (!IGNORED_DIRS.has(entry.name)) await walk2(absolute);
234642
234232
  } else if (entry.isFile()) {
@@ -234692,14 +234282,14 @@ function sanitizeMaxResults(value) {
234692
234282
  return Math.max(1, Math.min(typeof value === "number" ? Math.floor(value) : DEFAULT_MAX_RESULTS, 1e3));
234693
234283
  }
234694
234284
  function localSourceEntry(root2, file, absoluteId = false) {
234695
- const stat2 = fs13.statSync(file);
234696
- const relativePath = path14.relative(root2, file);
234697
- const extension2 = path14.extname(file).toLowerCase();
234285
+ const stat2 = fs12.statSync(file);
234286
+ const relativePath = path13.relative(root2, file);
234287
+ const extension2 = path13.extname(file).toLowerCase();
234698
234288
  return {
234699
234289
  localSourceId: absoluteId ? file : `${CLI_ROOT_ID}::${relativePath}`,
234700
234290
  rootId: absoluteId ? "cli-absolute-root" : CLI_ROOT_ID,
234701
234291
  relativePath,
234702
- fileName: path14.basename(file),
234292
+ fileName: path13.basename(file),
234703
234293
  extension: extension2,
234704
234294
  sizeBytes: stat2.size,
234705
234295
  modifiedAt: stat2.mtime.toISOString(),
@@ -234708,7 +234298,7 @@ function localSourceEntry(root2, file, absoluteId = false) {
234708
234298
  };
234709
234299
  }
234710
234300
  function inferMimeType(file) {
234711
- const ext = path14.extname(file).toLowerCase();
234301
+ const ext = path13.extname(file).toLowerCase();
234712
234302
  if (ext === ".pdf") return "application/pdf";
234713
234303
  if (ext === ".json") return "application/json";
234714
234304
  if (ext === ".csv") return "text/csv";
@@ -234768,6 +234358,550 @@ var init_nodeLocalBridge = __esm({
234768
234358
  }
234769
234359
  });
234770
234360
 
234361
+ // features/perchTerminal/runtime/cliHost/cliAuthSession.ts
234362
+ import { execFile } from "node:child_process";
234363
+ import fs13 from "node:fs/promises";
234364
+ import os3 from "node:os";
234365
+ import path14 from "node:path";
234366
+ import { promisify } from "node:util";
234367
+ async function readStoredCliAuthSession() {
234368
+ const raw = shouldUseFallbackAuthFile() ? await readFallbackSecret().catch(() => null) : process.platform === "darwin" ? await readKeychainSecret().catch(() => null) : await readFallbackSecret().catch(() => null);
234369
+ return parseStoredCliAuthSession(raw);
234370
+ }
234371
+ async function writeStoredCliAuthSession(session) {
234372
+ const raw = JSON.stringify(session);
234373
+ if (process.platform === "darwin" && !shouldUseFallbackAuthFile()) {
234374
+ await execFileAsync("security", [
234375
+ "add-generic-password",
234376
+ "-U",
234377
+ "-s",
234378
+ KEYCHAIN_SERVICE,
234379
+ "-a",
234380
+ KEYCHAIN_ACCOUNT,
234381
+ "-w",
234382
+ raw
234383
+ ]);
234384
+ return;
234385
+ }
234386
+ await writeFallbackSecret(raw);
234387
+ }
234388
+ async function clearStoredCliAuthSession() {
234389
+ if (process.platform === "darwin" && !shouldUseFallbackAuthFile()) {
234390
+ await execFileAsync("security", [
234391
+ "delete-generic-password",
234392
+ "-s",
234393
+ KEYCHAIN_SERVICE,
234394
+ "-a",
234395
+ KEYCHAIN_ACCOUNT
234396
+ ]).catch(() => void 0);
234397
+ return;
234398
+ }
234399
+ await fs13.rm(fallbackSessionPath(), { force: true }).catch(() => void 0);
234400
+ }
234401
+ function shouldUseFallbackAuthFile() {
234402
+ return Boolean(process.env.PERCH_CLI_AUTH_DIR?.trim());
234403
+ }
234404
+ function isStoredCliAuthSessionUsable(session, nowSeconds = Math.floor(Date.now() / 1e3)) {
234405
+ if (!session?.accessToken?.trim()) return false;
234406
+ if (!session.appUrl?.trim()) return false;
234407
+ if (!session.expiresAt) return true;
234408
+ return session.expiresAt > nowSeconds + 90;
234409
+ }
234410
+ function parseStoredCliAuthSession(raw) {
234411
+ if (!raw?.trim()) return null;
234412
+ try {
234413
+ const parsed = JSON.parse(raw);
234414
+ if (parsed.version !== 1) return null;
234415
+ if (!parsed.appUrl || !parsed.accessToken || !parsed.updatedAt) return null;
234416
+ return {
234417
+ version: 1,
234418
+ appUrl: parsed.appUrl,
234419
+ accessToken: parsed.accessToken,
234420
+ refreshToken: parsed.refreshToken ?? null,
234421
+ expiresAt: typeof parsed.expiresAt === "number" ? parsed.expiresAt : null,
234422
+ userId: parsed.userId ?? null,
234423
+ email: parsed.email ?? null,
234424
+ updatedAt: parsed.updatedAt
234425
+ };
234426
+ } catch {
234427
+ return null;
234428
+ }
234429
+ }
234430
+ async function readKeychainSecret() {
234431
+ const { stdout } = await execFileAsync("security", [
234432
+ "find-generic-password",
234433
+ "-s",
234434
+ KEYCHAIN_SERVICE,
234435
+ "-a",
234436
+ KEYCHAIN_ACCOUNT,
234437
+ "-w"
234438
+ ]);
234439
+ return stdout.trim() || null;
234440
+ }
234441
+ function fallbackSessionPath() {
234442
+ const base = process.env.PERCH_CLI_AUTH_DIR?.trim() || path14.join(os3.homedir(), ".perch");
234443
+ return path14.join(base, "cli-auth-session.json");
234444
+ }
234445
+ async function readFallbackSecret() {
234446
+ return await fs13.readFile(fallbackSessionPath(), "utf8");
234447
+ }
234448
+ async function writeFallbackSecret(raw) {
234449
+ const filePath = fallbackSessionPath();
234450
+ await fs13.mkdir(path14.dirname(filePath), { recursive: true, mode: 448 });
234451
+ await fs13.writeFile(filePath, raw, { mode: 384 });
234452
+ }
234453
+ var execFileAsync, KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT;
234454
+ var init_cliAuthSession = __esm({
234455
+ "features/perchTerminal/runtime/cliHost/cliAuthSession.ts"() {
234456
+ "use strict";
234457
+ execFileAsync = promisify(execFile);
234458
+ KEYCHAIN_SERVICE = "app.perchai.cli-auth";
234459
+ KEYCHAIN_ACCOUNT = "default";
234460
+ }
234461
+ });
234462
+
234463
+ // features/perchTerminal/runtime/publicModelDefault.ts
234464
+ function parsePublicModelDefaultPayload(value) {
234465
+ const raw = value && typeof value === "object" ? value : {};
234466
+ const selection = sanitizeFounderModelSelection(
234467
+ raw.selection ?? DEFAULT_FOUNDER_MODEL_SELECTION
234468
+ );
234469
+ return {
234470
+ selection,
234471
+ updatedAt: typeof raw.updatedAt === "string" ? raw.updatedAt : null,
234472
+ updatedBy: typeof raw.updatedBy === "string" ? raw.updatedBy : null
234473
+ };
234474
+ }
234475
+ var init_publicModelDefault = __esm({
234476
+ "features/perchTerminal/runtime/publicModelDefault.ts"() {
234477
+ "use strict";
234478
+ init_modelRegistry();
234479
+ }
234480
+ });
234481
+
234482
+ // features/perchTerminal/runtime/cliHost/installCliModelProxyAuthRefresh.ts
234483
+ function installCliModelProxyAuthRefresh() {
234484
+ setCliModelProxyAuthRefresher(async () => {
234485
+ const fresh = await ensureFreshCliAuthSession({
234486
+ session: await readStoredCliAuthSession(),
234487
+ forceRefresh: true
234488
+ });
234489
+ if (!fresh?.accessToken?.trim()) return null;
234490
+ if (typeof process !== "undefined") {
234491
+ process.env[MODEL_CALL_PROXY_TOKEN_ENV2] = fresh.accessToken;
234492
+ }
234493
+ return fresh.accessToken;
234494
+ });
234495
+ return () => setCliModelProxyAuthRefresher(null);
234496
+ }
234497
+ var MODEL_CALL_PROXY_TOKEN_ENV2;
234498
+ var init_installCliModelProxyAuthRefresh = __esm({
234499
+ "features/perchTerminal/runtime/cliHost/installCliModelProxyAuthRefresh.ts"() {
234500
+ "use strict";
234501
+ init_cliAuthRefreshRegistry();
234502
+ init_cliAuthRefresh();
234503
+ init_cliAuthSession();
234504
+ MODEL_CALL_PROXY_TOKEN_ENV2 = "PERCH_MODEL_CALL_PROXY_TOKEN";
234505
+ }
234506
+ });
234507
+
234508
+ // features/perchTerminal/runtime/cliHost/modelConnection.ts
234509
+ async function connectCliModelProxy(input = {}) {
234510
+ installCliModelProxyAuthRefresh();
234511
+ const storedSession = input.storedSession === void 0 ? await readStoredCliAuthSession() : input.storedSession;
234512
+ const usableStoredSession = await ensureFreshCliAuthSession({
234513
+ session: storedSession,
234514
+ fetchImpl: input.fetchImpl
234515
+ });
234516
+ const appUrl = resolveCliAppUrl(input.appUrl, usableStoredSession?.appUrl ?? null);
234517
+ if (!appUrl) return noCliModelConnection();
234518
+ if (!usableStoredSession?.accessToken) return noCliModelConnection(appUrl);
234519
+ const fetchImpl = input.fetchImpl ?? globalThis.fetch;
234520
+ if (typeof fetchImpl !== "function") return noCliModelConnection(appUrl);
234521
+ const selection = await fetchPublicModelDefaultSelection(
234522
+ appUrl,
234523
+ fetchImpl,
234524
+ usableStoredSession?.accessToken ?? null
234525
+ );
234526
+ if (!selection) return noCliModelConnection(appUrl);
234527
+ const priorProxy = process.env[MODEL_PROXY_ENV];
234528
+ const priorToken = process.env[MODEL_PROXY_TOKEN_ENV];
234529
+ process.env[MODEL_PROXY_ENV] = appUrl;
234530
+ if (usableStoredSession?.accessToken) {
234531
+ process.env[MODEL_PROXY_TOKEN_ENV] = usableStoredSession.accessToken;
234532
+ }
234533
+ return {
234534
+ appUrl,
234535
+ authenticated: !!usableStoredSession?.accessToken,
234536
+ userId: usableStoredSession?.userId ?? null,
234537
+ email: usableStoredSession?.email ?? null,
234538
+ founderModelSelection: selection,
234539
+ restore: () => {
234540
+ if (priorProxy === void 0) {
234541
+ delete process.env[MODEL_PROXY_ENV];
234542
+ } else {
234543
+ process.env[MODEL_PROXY_ENV] = priorProxy;
234544
+ }
234545
+ if (priorToken === void 0) {
234546
+ delete process.env[MODEL_PROXY_TOKEN_ENV];
234547
+ } else {
234548
+ process.env[MODEL_PROXY_TOKEN_ENV] = priorToken;
234549
+ }
234550
+ }
234551
+ };
234552
+ }
234553
+ function resolveCliAppUrl(explicit, stored) {
234554
+ const raw = explicit?.trim() || process.env[CLI_APP_URL_ENV]?.trim() || process.env[FALLBACK_APP_URL_ENV]?.trim() || stored?.trim() || DEFAULT_CLI_APP_URL;
234555
+ if (!raw) return null;
234556
+ const withProtocol = /^https?:\/\//i.test(raw) ? raw : `http://${raw}`;
234557
+ try {
234558
+ const url = new URL(withProtocol);
234559
+ url.pathname = url.pathname.replace(/\/+$/, "");
234560
+ url.search = "";
234561
+ url.hash = "";
234562
+ return url.toString().replace(/\/+$/, "");
234563
+ } catch {
234564
+ return null;
234565
+ }
234566
+ }
234567
+ async function fetchPublicModelDefaultSelection(appUrl, fetchImpl, accessToken) {
234568
+ const url = `${appUrl}/api/perch-terminal/public-model-default`;
234569
+ const controller = new AbortController();
234570
+ const timeout = setTimeout(() => controller.abort(), 3500);
234571
+ try {
234572
+ const response = await fetchImpl(url, {
234573
+ method: "GET",
234574
+ headers: {
234575
+ Accept: "application/json",
234576
+ ...accessToken ? { Authorization: `Bearer ${accessToken}` } : {}
234577
+ },
234578
+ signal: controller.signal
234579
+ });
234580
+ if (!response.ok) return null;
234581
+ const body = await response.json();
234582
+ if (!body || typeof body !== "object") return null;
234583
+ const record = body;
234584
+ if (record.ok !== true) return null;
234585
+ return parsePublicModelDefaultPayload({ selection: record.selection }).selection;
234586
+ } catch {
234587
+ return null;
234588
+ } finally {
234589
+ clearTimeout(timeout);
234590
+ }
234591
+ }
234592
+ function noCliModelConnection(appUrl = null) {
234593
+ return {
234594
+ appUrl,
234595
+ authenticated: false,
234596
+ userId: null,
234597
+ email: null,
234598
+ founderModelSelection: null,
234599
+ restore: () => {
234600
+ }
234601
+ };
234602
+ }
234603
+ var MODEL_PROXY_ENV, MODEL_PROXY_TOKEN_ENV, CLI_APP_URL_ENV, FALLBACK_APP_URL_ENV, DEFAULT_CLI_APP_URL;
234604
+ var init_modelConnection = __esm({
234605
+ "features/perchTerminal/runtime/cliHost/modelConnection.ts"() {
234606
+ "use strict";
234607
+ init_publicModelDefault();
234608
+ init_cliAuthSession();
234609
+ init_cliAuthRefresh();
234610
+ init_installCliModelProxyAuthRefresh();
234611
+ MODEL_PROXY_ENV = "PERCH_MODEL_CALL_PROXY_URL";
234612
+ MODEL_PROXY_TOKEN_ENV = "PERCH_MODEL_CALL_PROXY_TOKEN";
234613
+ CLI_APP_URL_ENV = "PERCH_CLI_APP_URL";
234614
+ FALLBACK_APP_URL_ENV = "PERCH_APP_URL";
234615
+ DEFAULT_CLI_APP_URL = "https://app.perchai.app";
234616
+ }
234617
+ });
234618
+
234619
+ // features/perchTerminal/runtime/cliHost/cliStandaloneOAuth.ts
234620
+ import { execFile as execFile2 } from "node:child_process";
234621
+ import http from "node:http";
234622
+ import { promisify as promisify2 } from "node:util";
234623
+ async function runCliStandaloneOAuthLogin(input) {
234624
+ const appUrl = resolveCliAppUrl(input.appUrl, null);
234625
+ if (!appUrl) {
234626
+ return { ok: false, code: "invalid_app_url", error: "Invalid Perch app URL." };
234627
+ }
234628
+ const fetchImpl = input.fetchImpl ?? globalThis.fetch;
234629
+ if (typeof fetchImpl !== "function") {
234630
+ return { ok: false, code: "fetch_unavailable", error: "This Node runtime does not expose fetch." };
234631
+ }
234632
+ const config = await fetchCliAuthConfig(appUrl, fetchImpl);
234633
+ if (!config.ok || !config.supabaseUrl || !config.supabaseAnonKey) {
234634
+ return {
234635
+ ok: false,
234636
+ code: "auth_config_unavailable",
234637
+ error: "The Perch app did not return CLI auth configuration."
234638
+ };
234639
+ }
234640
+ const provider = selectOAuthProvider(config.providers ?? [], input.provider);
234641
+ if (!provider) {
234642
+ return {
234643
+ ok: false,
234644
+ code: "oauth_provider_unavailable",
234645
+ error: "No supported CLI OAuth provider is enabled for this Perch app."
234646
+ };
234647
+ }
234648
+ const callback = await createOAuthCallbackServer({
234649
+ host: config.redirectHost?.trim() || "127.0.0.1",
234650
+ timeoutMs: input.timeoutMs ?? DEFAULT_LOGIN_TIMEOUT_MS
234651
+ });
234652
+ try {
234653
+ const memoryStorage = createMemoryAuthStorage();
234654
+ const supabase = createClient(config.supabaseUrl, config.supabaseAnonKey, {
234655
+ auth: {
234656
+ flowType: "pkce",
234657
+ autoRefreshToken: false,
234658
+ detectSessionInUrl: false,
234659
+ persistSession: true,
234660
+ storage: memoryStorage
234661
+ }
234662
+ });
234663
+ const { data, error } = await supabase.auth.signInWithOAuth({
234664
+ provider,
234665
+ options: {
234666
+ redirectTo: callback.redirectTo,
234667
+ skipBrowserRedirect: true,
234668
+ data: { signup_source: "cli" }
234669
+ }
234670
+ });
234671
+ if (error || !data.url) {
234672
+ return {
234673
+ ok: false,
234674
+ code: "oauth_start_failed",
234675
+ error: error?.message ?? "Supabase did not return an OAuth URL."
234676
+ };
234677
+ }
234678
+ await (input.openExternal ?? openExternal)(data.url);
234679
+ const callbackResult = await callback.waitForCode();
234680
+ if (!callbackResult.ok) {
234681
+ return callbackResult;
234682
+ }
234683
+ const exchanged = await supabase.auth.exchangeCodeForSession(callbackResult.code);
234684
+ if (exchanged.error || !exchanged.data.session) {
234685
+ return {
234686
+ ok: false,
234687
+ code: "oauth_exchange_failed",
234688
+ error: exchanged.error?.message ?? "Supabase did not return a CLI session."
234689
+ };
234690
+ }
234691
+ const session = storedSessionFromSupabaseSession({
234692
+ appUrl,
234693
+ accessToken: exchanged.data.session.access_token,
234694
+ refreshToken: exchanged.data.session.refresh_token,
234695
+ expiresAt: exchanged.data.session.expires_at ?? null,
234696
+ userId: exchanged.data.session.user.id,
234697
+ email: exchanged.data.session.user.email ?? null
234698
+ });
234699
+ await writeStoredCliAuthSession(session);
234700
+ return { ok: true, session };
234701
+ } finally {
234702
+ await callback.close();
234703
+ }
234704
+ }
234705
+ async function fetchCliAuthConfig(appUrl, fetchImpl = globalThis.fetch) {
234706
+ const response = await fetchImpl(`${appUrl}/api/perch-terminal/cli-auth/config`, {
234707
+ method: "GET",
234708
+ headers: { Accept: "application/json" }
234709
+ });
234710
+ if (!response.ok) return { ok: false };
234711
+ return await response.json();
234712
+ }
234713
+ function selectOAuthProvider(providers, preferred) {
234714
+ const available = new Set(providers);
234715
+ if (preferred && available.has(preferred)) return preferred;
234716
+ if (available.has("google")) return "google";
234717
+ if (available.has("github")) return "github";
234718
+ return null;
234719
+ }
234720
+ function storedSessionFromSupabaseSession(input) {
234721
+ return {
234722
+ version: 1,
234723
+ appUrl: input.appUrl,
234724
+ accessToken: input.accessToken,
234725
+ refreshToken: input.refreshToken ?? null,
234726
+ expiresAt: input.expiresAt ?? null,
234727
+ userId: input.userId ?? null,
234728
+ email: input.email ?? null,
234729
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
234730
+ };
234731
+ }
234732
+ function createMemoryAuthStorage() {
234733
+ const values2 = /* @__PURE__ */ new Map();
234734
+ return {
234735
+ getItem: (key) => values2.get(key) ?? null,
234736
+ setItem: (key, value) => {
234737
+ values2.set(key, value);
234738
+ },
234739
+ removeItem: (key) => {
234740
+ values2.delete(key);
234741
+ }
234742
+ };
234743
+ }
234744
+ async function createOAuthCallbackServer(input) {
234745
+ let resolveResult = null;
234746
+ const resultPromise = new Promise((resolve5) => {
234747
+ resolveResult = resolve5;
234748
+ });
234749
+ const server = http.createServer((request, response) => {
234750
+ const requestUrl = new URL(request.url ?? "/", `http://${input.host}`);
234751
+ if (requestUrl.pathname !== CALLBACK_PATH) {
234752
+ response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
234753
+ response.end("Not found");
234754
+ return;
234755
+ }
234756
+ const code = requestUrl.searchParams.get("code");
234757
+ const error = requestUrl.searchParams.get("error_description") ?? requestUrl.searchParams.get("error");
234758
+ if (!code) {
234759
+ response.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
234760
+ response.end(renderCallbackHtml(false));
234761
+ resolveResult?.({
234762
+ ok: false,
234763
+ code: "oauth_callback_missing_code",
234764
+ error: error ?? "OAuth callback did not include a code."
234765
+ });
234766
+ resolveResult = null;
234767
+ return;
234768
+ }
234769
+ response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
234770
+ response.end(renderCallbackHtml(true));
234771
+ resolveResult?.({ ok: true, code });
234772
+ resolveResult = null;
234773
+ });
234774
+ await new Promise((resolve5, reject2) => {
234775
+ server.once("error", reject2);
234776
+ server.listen(0, input.host, () => {
234777
+ server.off("error", reject2);
234778
+ resolve5();
234779
+ });
234780
+ });
234781
+ const address = server.address();
234782
+ const redirectTo = `http://${input.host}:${address.port}${CALLBACK_PATH}`;
234783
+ const timeout = setTimeout(() => {
234784
+ resolveResult?.({
234785
+ ok: false,
234786
+ code: "oauth_timeout",
234787
+ error: "Timed out waiting for browser sign-in to complete."
234788
+ });
234789
+ resolveResult = null;
234790
+ }, input.timeoutMs);
234791
+ return {
234792
+ redirectTo,
234793
+ waitForCode: async () => resultPromise,
234794
+ close: async () => {
234795
+ clearTimeout(timeout);
234796
+ await new Promise((resolve5) => server.close(() => resolve5()));
234797
+ }
234798
+ };
234799
+ }
234800
+ function renderCallbackHtml(ok) {
234801
+ const title = ok ? "Perch CLI signed in" : "Perch CLI sign-in failed";
234802
+ const message = ok ? "You can close this tab and return to your terminal." : "Return to your terminal and try again.";
234803
+ return `<!doctype html><html><head><meta charset="utf-8"><title>${title}</title></head><body style="font-family:system-ui;padding:32px;background:#100d0b;color:#fff8f0"><h1>${title}</h1><p>${message}</p></body></html>`;
234804
+ }
234805
+ async function openExternal(url) {
234806
+ if (process.platform === "darwin") {
234807
+ await execFileAsync2("open", [url]);
234808
+ } else if (process.platform === "win32") {
234809
+ await execFileAsync2("cmd", ["/c", "start", "", url]);
234810
+ } else {
234811
+ await execFileAsync2("xdg-open", [url]);
234812
+ }
234813
+ }
234814
+ var execFileAsync2, DEFAULT_LOGIN_TIMEOUT_MS, CALLBACK_PATH;
234815
+ var init_cliStandaloneOAuth = __esm({
234816
+ "features/perchTerminal/runtime/cliHost/cliStandaloneOAuth.ts"() {
234817
+ "use strict";
234818
+ init_dist4();
234819
+ init_modelConnection();
234820
+ init_cliAuthSession();
234821
+ execFileAsync2 = promisify2(execFile2);
234822
+ DEFAULT_LOGIN_TIMEOUT_MS = 12e4;
234823
+ CALLBACK_PATH = "/callback";
234824
+ }
234825
+ });
234826
+
234827
+ // features/perchTerminal/runtime/cliHost/cliAuthRefresh.ts
234828
+ async function ensureFreshCliAuthSession(input) {
234829
+ const session = input.session;
234830
+ if (!session?.accessToken?.trim()) return null;
234831
+ const nowSeconds = Math.floor(Date.now() / 1e3);
234832
+ const stillUsable = !session.expiresAt || session.expiresAt > nowSeconds + 90;
234833
+ if (stillUsable && !input.forceRefresh) return session;
234834
+ if (!session.refreshToken?.trim()) return null;
234835
+ const fetchImpl = input.fetchImpl ?? globalThis.fetch;
234836
+ if (typeof fetchImpl !== "function") return null;
234837
+ const appUrl = session.appUrl?.trim();
234838
+ if (!appUrl) return null;
234839
+ const config = await fetchCliAuthConfig(appUrl, fetchImpl).catch(() => ({ ok: false }));
234840
+ if (!config.ok || !config.supabaseUrl || !config.supabaseAnonKey) return null;
234841
+ const supabase = createClient(config.supabaseUrl, config.supabaseAnonKey, {
234842
+ auth: {
234843
+ autoRefreshToken: false,
234844
+ detectSessionInUrl: false,
234845
+ persistSession: false
234846
+ }
234847
+ });
234848
+ try {
234849
+ const { data, error } = await supabase.auth.refreshSession({
234850
+ refresh_token: session.refreshToken
234851
+ });
234852
+ if (error || !data.session) return null;
234853
+ const refreshed = {
234854
+ version: 1,
234855
+ appUrl: session.appUrl,
234856
+ accessToken: data.session.access_token,
234857
+ refreshToken: data.session.refresh_token ?? session.refreshToken,
234858
+ expiresAt: data.session.expires_at ?? null,
234859
+ userId: data.session.user?.id ?? session.userId ?? null,
234860
+ email: data.session.user?.email ?? session.email ?? null,
234861
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
234862
+ };
234863
+ await writeStoredCliAuthSession(refreshed);
234864
+ return refreshed;
234865
+ } catch {
234866
+ return null;
234867
+ }
234868
+ }
234869
+ var init_cliAuthRefresh = __esm({
234870
+ "features/perchTerminal/runtime/cliHost/cliAuthRefresh.ts"() {
234871
+ "use strict";
234872
+ init_dist4();
234873
+ init_cliStandaloneOAuth();
234874
+ init_cliAuthSession();
234875
+ }
234876
+ });
234877
+
234878
+ // features/perchTerminal/runtime/cliHost/refreshCliTurnAuth.ts
234879
+ async function refreshCliTurnAuth(input) {
234880
+ if (input.cliLocalTools !== true) return input;
234881
+ const stored = await readStoredCliAuthSession();
234882
+ if (!stored?.accessToken?.trim()) return input;
234883
+ const turnToken = input.cliServerAccessToken?.trim();
234884
+ if (turnToken && turnToken !== stored.accessToken) return input;
234885
+ const fresh = await ensureFreshCliAuthSession({ session: stored });
234886
+ if (!fresh?.accessToken?.trim()) return input;
234887
+ if (fresh.accessToken === input.cliServerAccessToken) return input;
234888
+ if (typeof process !== "undefined") {
234889
+ process.env.PERCH_MODEL_CALL_PROXY_TOKEN = fresh.accessToken;
234890
+ }
234891
+ return {
234892
+ ...input,
234893
+ cliServerAccessToken: fresh.accessToken,
234894
+ marketDeskProxyAccessToken: input.marketDeskProxyAccessToken ?? fresh.accessToken
234895
+ };
234896
+ }
234897
+ var init_refreshCliTurnAuth = __esm({
234898
+ "features/perchTerminal/runtime/cliHost/refreshCliTurnAuth.ts"() {
234899
+ "use strict";
234900
+ init_cliAuthSession();
234901
+ init_cliAuthRefresh();
234902
+ }
234903
+ });
234904
+
234771
234905
  // features/perchTerminal/runtime/cliHost/runCliTurn.ts
234772
234906
  import path15 from "node:path";
234773
234907
  async function runPerchCliTurn(input, options = {}) {
@@ -234790,7 +234924,8 @@ async function runPerchCliTurn(input, options = {}) {
234790
234924
  onEvent: input.onEvent,
234791
234925
  onContextSnapshot: (snapshot) => {
234792
234926
  latestContextSnapshot = snapshot;
234793
- }
234927
+ },
234928
+ refreshTurnInput: input.cliLocalTools !== false ? refreshCliTurnAuth : void 0
234794
234929
  });
234795
234930
  const restoreLocalBridge = input.cliLocalTools === false ? null : installCliNodeLocalBridge({ workspaceRoot: cwd2 });
234796
234931
  try {
@@ -234877,6 +235012,7 @@ function buildCliTurnDeps(input) {
234877
235012
  input.onEvent?.(event);
234878
235013
  },
234879
235014
  onContextAssembled: input.onContextSnapshot,
235015
+ ...input.refreshTurnInput ? { refreshTurnInput: input.refreshTurnInput } : {},
234880
235016
  persistUserMessage: async () => null,
234881
235017
  persistAssistantMessage: async () => null,
234882
235018
  createWorkflowRun: async () => null,
@@ -234932,6 +235068,7 @@ var init_runCliTurn = __esm({
234932
235068
  init_runOperatorTurn();
234933
235069
  init_nodeLocalBridge();
234934
235070
  init_nodeLocalBridge();
235071
+ init_refreshCliTurnAuth();
234935
235072
  }
234936
235073
  });
234937
235074
 
@@ -289942,9 +290079,17 @@ function flockEventToCliRow(event) {
289942
290079
  switch (event.type) {
289943
290080
  case "flock_run_started":
289944
290081
  return { id: null, text: "gathering the flock\u2026", tone: "accent" };
289945
- case "flock_plan_ready":
290082
+ case "flock_plan_ready": {
289946
290083
  if (!event.accepted) return null;
289947
- return { id: null, text: `flock plan \xB7 ${event.workers.length} workers`, tone: "muted" };
290084
+ const names = event.workers.map((worker) => worker.displayName);
290085
+ const shown = names.slice(0, 4).join(", ");
290086
+ const extra = names.length > 4 ? ` +${names.length - 4} more` : "";
290087
+ return {
290088
+ id: null,
290089
+ text: `flock plan \xB7 ${event.workers.length} agents \xB7 ${shown}${extra}`,
290090
+ tone: "accent"
290091
+ };
290092
+ }
289948
290093
  case "flock_worker_update":
289949
290094
  return {
289950
290095
  id: `flock-${event.flockId}-${event.flockWorkerId}`,
@@ -290440,18 +290585,18 @@ async function runReadlineInteractivePerchCli(writer, deps, options) {
290440
290585
  trimRecentMessages(state.recentMessages);
290441
290586
  recordCliUsageRunId(state, result2.runId);
290442
290587
  state.contextSnapshot = result2.contextSnapshot ?? state.contextSnapshot;
290443
- const admitted = await admitCliLearningMemory(connection, {
290588
+ void admitCliLearningMemory(connection, {
290444
290589
  prompt: turnPrompt,
290445
290590
  assistantText: result2.assistantText,
290446
290591
  status: result2.status,
290447
290592
  personaId: state.personaId,
290448
290593
  threadId: result2.threadId,
290449
290594
  runId: result2.runId
290595
+ }).then((admitted) => {
290596
+ if (admitted > 0) writeModeLine(writer, "memory", `saved ${admitted}`);
290597
+ }).catch(() => {
290450
290598
  });
290451
290599
  await persistInteractiveCliState(state);
290452
- if (admitted > 0) {
290453
- writeModeLine(writer, "memory", `saved ${admitted}`);
290454
- }
290455
290600
  };
290456
290601
  try {
290457
290602
  while (true) {
@@ -291153,17 +291298,19 @@ async function runInkInteractivePerchCli(writer, deps, options) {
291153
291298
  trimRecentMessages(state.recentMessages);
291154
291299
  recordCliUsageRunId(state, result2.runId);
291155
291300
  state.contextSnapshot = result2.contextSnapshot ?? state.contextSnapshot;
291156
- const admitted = await admitCliLearningMemory(connection, {
291301
+ void admitCliLearningMemory(connection, {
291157
291302
  prompt,
291158
291303
  assistantText,
291159
291304
  status: result2.status,
291160
291305
  personaId: state.personaId,
291161
291306
  threadId: result2.threadId,
291162
291307
  runId: result2.runId
291308
+ }).then((admitted) => {
291309
+ if (admitted > 0) {
291310
+ addItem({ label: "memory", text: `saved ${admitted}`, tone: "muted" });
291311
+ }
291312
+ }).catch(() => {
291163
291313
  });
291164
- if (admitted > 0) {
291165
- addItem({ label: "memory", text: `saved ${admitted}`, tone: "muted" });
291166
- }
291167
291314
  await persistInteractiveCliState(state);
291168
291315
  } catch (error) {
291169
291316
  addItem({ label: "stop", text: humanizeCliError(errorMessage(error)), tone: "danger" });