perchai-cli 2.4.18 → 2.4.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/perch.mjs +851 -132
- package/package.json +1 -1
package/dist/perch.mjs
CHANGED
|
@@ -75900,7 +75900,6 @@ function isTurnAbortedError(error) {
|
|
|
75900
75900
|
var TURN_STOPPED_BY_USER_MESSAGE;
|
|
75901
75901
|
var init_turnAbort = __esm({
|
|
75902
75902
|
"features/perchTerminal/runtime/turnAbort.ts"() {
|
|
75903
|
-
"use strict";
|
|
75904
75903
|
TURN_STOPPED_BY_USER_MESSAGE = "Turn stopped by user.";
|
|
75905
75904
|
}
|
|
75906
75905
|
});
|
|
@@ -80933,12 +80932,14 @@ function buildDesktopContextSection(input) {
|
|
|
80933
80932
|
"grep",
|
|
80934
80933
|
"statPath",
|
|
80935
80934
|
"readLocalFile",
|
|
80935
|
+
"readProjectMemory",
|
|
80936
80936
|
"listLocalSources",
|
|
80937
80937
|
"readLocalSourceFile"
|
|
80938
80938
|
].filter((name) => enabledToolSet2.has(name));
|
|
80939
80939
|
const localWriteTools = [
|
|
80940
80940
|
"writeLocalFile",
|
|
80941
80941
|
"editLocalFile",
|
|
80942
|
+
"saveToMemory",
|
|
80942
80943
|
"bash",
|
|
80943
80944
|
"runBashTerminalCommand",
|
|
80944
80945
|
"generateAPAuditPacket",
|
|
@@ -80950,16 +80951,16 @@ function buildDesktopContextSection(input) {
|
|
|
80950
80951
|
return {
|
|
80951
80952
|
id: "desktop-context",
|
|
80952
80953
|
lane: "desktop",
|
|
80953
|
-
label: "
|
|
80954
|
+
label: "Terminal workspace",
|
|
80954
80955
|
content: [
|
|
80955
|
-
"##
|
|
80956
|
-
"Perch is running from a terminal.
|
|
80956
|
+
"## Terminal workspace",
|
|
80957
|
+
"Perch is running from a terminal. Files, shell, sandbox-code, and AP evidence tools are available for the current workspace.",
|
|
80957
80958
|
input.activeRootPath?.trim() ? `Workspace root: ${input.activeRootPath}. Treat relative paths as relative to this root.` : "Workspace root: current terminal directory.",
|
|
80958
|
-
"
|
|
80959
|
+
"App-only services are not connected here: embedded browser, Google/Gmail/Calendar delivery, attached-folder semantic source indexing, and connected desktop integrations. CLI memory can still use signed-in durable memory or local .perch project memory when available.",
|
|
80959
80960
|
localReadTools.length > 0 ? `Read tools: ${localReadTools.join(", ")}.` : "Read tools: none exposed for this turn.",
|
|
80960
80961
|
localWriteTools.length > 0 ? `Write/command tools: ${localWriteTools.join(", ")}. These are governed by the selected permission mode and command policy.` : "Write/command tools: none exposed for this turn."
|
|
80961
80962
|
].join("\n"),
|
|
80962
|
-
reason: "Terminal
|
|
80963
|
+
reason: "Terminal workspace and tool availability for this turn.",
|
|
80963
80964
|
sourcePath: input.activeRootPath,
|
|
80964
80965
|
metadata: {
|
|
80965
80966
|
desktopConnected: false,
|
|
@@ -81018,8 +81019,14 @@ function buildDesktopContextSection(input) {
|
|
|
81018
81019
|
(file) => `- ${file.relativePath} (${file.matchReason})`
|
|
81019
81020
|
) : [];
|
|
81020
81021
|
const hasVisionSupport = input.selectedModelVisionSupport === true;
|
|
81021
|
-
const
|
|
81022
|
-
|
|
81022
|
+
const hasIndexedSourceSearch = Boolean(
|
|
81023
|
+
input.folderIndexSummary && (input.folderIndexSummary.sourceChunkCount > 0 || input.folderIndexSummary.retrievalChunkCount > 0)
|
|
81024
|
+
);
|
|
81025
|
+
const hasVectorSearch = Boolean(
|
|
81026
|
+
input.folderIndexSummary && (input.folderIndexSummary.embeddedChunkCount > 0 || input.folderIndexSummary.vectorReadyFiles > 0)
|
|
81027
|
+
);
|
|
81028
|
+
const searchHints = hasIndexedSourceSearch ? [
|
|
81029
|
+
hasVectorSearch ? "Prefer retrieveContext for questions that span multiple files (cross-file evidence, duplicates, totals)." : "Keyword/source search is available through retrieveContext; semantic/vector retrieval is pending until embedded chunks are created.",
|
|
81023
81030
|
"Use glob for named files like *Screenshot* or exact extensions like *.png.",
|
|
81024
81031
|
"Use statPath or glob before claiming a file does not exist.",
|
|
81025
81032
|
hasVisionSupport ? "For screenshots/images/PDF pages, open the file with readLocalSourceFile/readLocalFile and read the attached visual preview directly." : "For screenshots/images/PDF pages, open the file with readLocalSourceFile/readLocalFile; if visual details matter and no usable text was extracted, call visionInspect."
|
|
@@ -81028,8 +81035,16 @@ function buildDesktopContextSection(input) {
|
|
|
81028
81035
|
"Use statPath or glob before claiming a file does not exist.",
|
|
81029
81036
|
hasVisionSupport ? "For screenshots/images/PDF pages, open the file with readLocalSourceFile/readLocalFile and read the attached visual preview directly." : "For screenshots/images/PDF pages, open the file with readLocalSourceFile/readLocalFile; if visual details matter and no usable text was extracted, call visionInspect."
|
|
81030
81037
|
];
|
|
81031
|
-
const folderIndexLine = input.folderIndexSummary ?
|
|
81032
|
-
|
|
81038
|
+
const folderIndexLine = input.folderIndexSummary ? [
|
|
81039
|
+
`Folder index: ${input.folderIndexSummary.indexedFiles}/${input.folderIndexSummary.totalFiles} indexed`,
|
|
81040
|
+
`${input.folderIndexSummary.registeredSources} registered source(s)`,
|
|
81041
|
+
`${input.folderIndexSummary.sourceChunkCount} text/source chunk(s)`,
|
|
81042
|
+
`${input.folderIndexSummary.retrievalChunkCount} retrieval chunk(s)`,
|
|
81043
|
+
`${input.folderIndexSummary.embeddedChunkCount} embedded retrieval chunk(s)`,
|
|
81044
|
+
`${input.folderIndexSummary.vectorReadyFiles} vector-ready file(s)`,
|
|
81045
|
+
`${input.folderIndexSummary.filesNeedingOcr} need OCR`
|
|
81046
|
+
].join(" \xB7 ") + "." : "Folder index: not loaded for this turn.";
|
|
81047
|
+
const retrievalGuidance = input.folderIndexSummary && input.folderIndexSummary.vectorReadyFiles > 20 ? `This folder has ${input.folderIndexSummary.vectorReadyFiles} files with embedded retrieval chunks for semantic/vector search. Use retrieveContext for cross-file queries instead of serial readLocalSourceFile calls. The recent-files list below is a sample \u2014 not the full corpus.` : input.folderIndexSummary && input.folderIndexSummary.sourceChunkCount > 0 && input.folderIndexSummary.embeddedChunkCount === 0 ? "This folder has keyword/source chunks, but no embedded retrieval chunks yet. Use retrieveContext for keyword-backed source search; semantic/vector retrieval is pending." : null;
|
|
81033
81048
|
const mcpToolNames = enabledToolSet.has("mcp__") ? [] : Array.from(enabledToolSet).filter((tool) => tool.startsWith("mcp__"));
|
|
81034
81049
|
const mcpServerNames = Array.from(
|
|
81035
81050
|
new Set(mcpToolNames.map((tool) => tool.split("__")[1]).filter(Boolean))
|
|
@@ -81062,7 +81077,7 @@ function buildDesktopContextSection(input) {
|
|
|
81062
81077
|
content: [
|
|
81063
81078
|
"## Desktop environment",
|
|
81064
81079
|
"Local workspace access is connected. Local filesystem tools are available.",
|
|
81065
|
-
input.activeRootPath?.trim() ? `
|
|
81080
|
+
input.activeRootPath?.trim() ? `Workspace folder: ${input.activeRootPath} (optional; absolute paths anywhere in the user's home work directly).` : "No workspace folder is selected, and none is needed. When the user gives a file path, use it directly with readLocalFile / glob / grep / visionInspect and the absolute path (for example /Users/you/Desktop/shot.png).",
|
|
81066
81081
|
`Visible local sources: ${totalVisibleFiles}`,
|
|
81067
81082
|
input.localSourcesMeta?.refreshedAt ? `Local source snapshot refreshed: ${input.localSourcesMeta.refreshedAt}` : null,
|
|
81068
81083
|
input.localSourcesMeta?.truncated ? `Local source snapshot is truncated at ${input.localSourcesMeta.maxSources} files. ${input.localSourcesMeta.warning ?? "Use glob with a pattern for exact discovery."}` : null,
|
|
@@ -81079,7 +81094,7 @@ function buildDesktopContextSection(input) {
|
|
|
81079
81094
|
availableReadTools.length > 0 ? `Read tools: ${availableReadTools.join(", ")}.` : "Read tools: none exposed for this turn.",
|
|
81080
81095
|
availableWriteTools.length > 0 ? `Write/command tools: ${availableWriteTools.join(", ")}. These are governed by the selected permission mode and command policy.` : "Write/command tools: none exposed for this turn."
|
|
81081
81096
|
].filter(Boolean).join("\n"),
|
|
81082
|
-
reason: "Local workspace access, optional folder
|
|
81097
|
+
reason: "Local workspace access, optional workspace folder, and tool availability for this turn.",
|
|
81083
81098
|
sourcePath: input.activeRootPath,
|
|
81084
81099
|
metadata: {
|
|
81085
81100
|
desktopConnected: true,
|
|
@@ -85236,7 +85251,7 @@ function buildProjectMemorySections(meta) {
|
|
|
85236
85251
|
lane: "project_memory",
|
|
85237
85252
|
label: "PERCH.md",
|
|
85238
85253
|
content: ["## PERCH.md", meta.perchMd.content].join("\n"),
|
|
85239
|
-
reason: "Project operator memory loaded from the selected
|
|
85254
|
+
reason: "Project operator memory loaded from the selected workspace folder.",
|
|
85240
85255
|
sourcePath: meta.perchMd.relativePath,
|
|
85241
85256
|
metadata: {
|
|
85242
85257
|
relativePath: meta.perchMd.relativePath,
|
|
@@ -85273,7 +85288,7 @@ function buildProjectMemorySections(meta) {
|
|
|
85273
85288
|
lane: "project_memory",
|
|
85274
85289
|
label: `Rule: ${rule.fileName}`,
|
|
85275
85290
|
content: [`## Project rule: ${rule.fileName}`, rule.content].join("\n"),
|
|
85276
|
-
reason: "Rules-style project instruction loaded from the selected
|
|
85291
|
+
reason: "Rules-style project instruction loaded from the selected workspace folder.",
|
|
85277
85292
|
sourcePath: rule.relativePath,
|
|
85278
85293
|
metadata: {
|
|
85279
85294
|
relativePath: rule.relativePath,
|
|
@@ -85303,7 +85318,7 @@ function buildProjectMemorySections(meta) {
|
|
|
85303
85318
|
content: [`## Project memory: ${memory.fileName}`, memory.content].join(
|
|
85304
85319
|
"\n"
|
|
85305
85320
|
),
|
|
85306
|
-
reason: "Typed project memory loaded from the selected
|
|
85321
|
+
reason: "Typed project memory loaded from the selected workspace folder.",
|
|
85307
85322
|
sourcePath: memory.relativePath,
|
|
85308
85323
|
metadata: {
|
|
85309
85324
|
relativePath: memory.relativePath,
|
|
@@ -85414,7 +85429,7 @@ Tool strengths:
|
|
|
85414
85429
|
- For PO overages, compare each invoice/payment against the specific PO number referenced by that invoice, not only vendor-level PO coverage. If duplicate PO rows share a PO number, compare against each candidate approved amount and flag any row-level overage or ambiguous approval.
|
|
85415
85430
|
- Model-written code should emit useful output first. The sandbox workspace has stdlib Python, input_manifest.json, sandbox_runtime.json, and perch_helpers. pandas/pdfplumber may not be installed, so use stdlib CSV parsing and perch_helpers.extract_invoice_fields() for invoice PDFs unless sandbox_runtime.json says package installs are enabled and an install is truly needed. Host paths like /Users/... are not readable inside the sandbox unless passed as sources and copied under input/. If output/report.json is present, it can be used as structured evidence; if not, use stdout/stderr and produced files honestly. If the sandbox run errors, say that plainly and iterate.
|
|
85416
85431
|
- Cite sources for every reported figure. If a figure can't be traced to a document, mark it [UNSOURCED] rather than omitting it
|
|
85417
|
-
- For a Mac folder: use glob and grep with the path; use listLocalSources/readLocalSourceFile only when a folder
|
|
85432
|
+
- For a Mac folder: use glob and grep with the path; use listLocalSources/readLocalSourceFile only when a workspace folder is already selected
|
|
85418
85433
|
- Do not run a tool just because a keyword matches; reason from the user's current request and the existing thread state
|
|
85419
85434
|
- Do not redo an audit, document, or email draft that is already done unless the user asks for a revision
|
|
85420
85435
|
- Summarize tool outputs; don't stream raw JSON to the user
|
|
@@ -92429,7 +92444,7 @@ function buildContextSummary(input, rows) {
|
|
|
92429
92444
|
`mode=${input.chatMode}`,
|
|
92430
92445
|
`messages=${input.recentMessages.length}`,
|
|
92431
92446
|
`source=${input.selectedSourceId ?? "none"}`,
|
|
92432
|
-
`desktop=${input.desktopConnected ? "connected" : input.cliLocalTools === true ? "
|
|
92447
|
+
`desktop=${input.desktopConnected ? "connected" : input.cliLocalTools === true ? "terminal" : "browser"}`,
|
|
92433
92448
|
`sent=${sentCount}`,
|
|
92434
92449
|
`compacted=${compactedCount}`,
|
|
92435
92450
|
`not_found=${notFoundCount}`,
|
|
@@ -92788,9 +92803,15 @@ function createEmbeddingProviderFromEnv(env4 = readEnv()) {
|
|
|
92788
92803
|
};
|
|
92789
92804
|
}
|
|
92790
92805
|
function createBrowserEmbeddingProvider(route = "/api/perch-terminal/embeddings") {
|
|
92806
|
+
let resolvedModel = "server-configured";
|
|
92807
|
+
let resolvedDimensions = DEFAULT_RETRIEVAL_EMBEDDING_DIMENSIONS;
|
|
92791
92808
|
return {
|
|
92792
|
-
model
|
|
92793
|
-
|
|
92809
|
+
get model() {
|
|
92810
|
+
return resolvedModel;
|
|
92811
|
+
},
|
|
92812
|
+
get dimensions() {
|
|
92813
|
+
return resolvedDimensions;
|
|
92814
|
+
},
|
|
92794
92815
|
async embed(text) {
|
|
92795
92816
|
return (await this.embedBatch([text]))[0];
|
|
92796
92817
|
},
|
|
@@ -92813,6 +92834,12 @@ function createBrowserEmbeddingProvider(route = "/api/perch-terminal/embeddings"
|
|
|
92813
92834
|
if (!Array.isArray(payload.embeddings)) {
|
|
92814
92835
|
throw new EmbeddingProviderError("Embedding route returned no embeddings.");
|
|
92815
92836
|
}
|
|
92837
|
+
if (typeof payload.model === "string" && payload.model.trim()) {
|
|
92838
|
+
resolvedModel = payload.model;
|
|
92839
|
+
}
|
|
92840
|
+
if (typeof payload.dimensions === "number" && Number.isFinite(payload.dimensions)) {
|
|
92841
|
+
resolvedDimensions = payload.dimensions;
|
|
92842
|
+
}
|
|
92816
92843
|
return payload.embeddings;
|
|
92817
92844
|
}
|
|
92818
92845
|
};
|
|
@@ -92887,7 +92914,7 @@ var init_embeddingProvider = __esm({
|
|
|
92887
92914
|
|
|
92888
92915
|
// features/perchTerminal/persistence/permanentMemoryPersistence.ts
|
|
92889
92916
|
function createPermanentMemoryPersistence(supabase, options = {}) {
|
|
92890
|
-
const embeddingProvider = options.embeddingProvider ??
|
|
92917
|
+
const embeddingProvider = options.embeddingProvider ?? createDefaultPermanentMemoryEmbeddingProvider();
|
|
92891
92918
|
const selectColumns = "id, user_id, workspace_id, project_id, scope, type, title, description, body, why, how_to_apply, source_kind, source_thread_id, source_message_id, source_run_id, confidence, created_at, updated_at, last_confirmed_at, stale_after_days, archived_at, tags, metadata";
|
|
92892
92919
|
const api2 = {
|
|
92893
92920
|
async listPermanentMemories({
|
|
@@ -93147,6 +93174,21 @@ function createPermanentMemoryPersistence(supabase, options = {}) {
|
|
|
93147
93174
|
};
|
|
93148
93175
|
return api2;
|
|
93149
93176
|
}
|
|
93177
|
+
function createDefaultPermanentMemoryEmbeddingProvider() {
|
|
93178
|
+
if (typeof window !== "undefined") return createBrowserEmbeddingProvider();
|
|
93179
|
+
const status = getEmbeddingProviderStatus();
|
|
93180
|
+
let provider = null;
|
|
93181
|
+
const load = () => {
|
|
93182
|
+
provider ??= createEmbeddingProviderFromEnv();
|
|
93183
|
+
return provider;
|
|
93184
|
+
};
|
|
93185
|
+
return {
|
|
93186
|
+
model: status.configured ? status.model : DEFAULT_RETRIEVAL_EMBEDDING_MODEL,
|
|
93187
|
+
dimensions: status.configured ? status.dimensions : DEFAULT_RETRIEVAL_EMBEDDING_DIMENSIONS,
|
|
93188
|
+
embed: async (text) => load().embed(text),
|
|
93189
|
+
embedBatch: async (texts) => load().embedBatch(texts)
|
|
93190
|
+
};
|
|
93191
|
+
}
|
|
93150
93192
|
async function findSimilarPermanentMemoryForInput(input) {
|
|
93151
93193
|
const queryEmbedding = await input.embeddingProvider.embed(canonicalMemoryEmbeddingText(input.input));
|
|
93152
93194
|
const { data, error } = await input.supabase.rpc("perch_ai_match_permanent_memories", {
|
|
@@ -117027,7 +117069,7 @@ function createSourcePersistence(supabase) {
|
|
|
117027
117069
|
return count ?? 0;
|
|
117028
117070
|
},
|
|
117029
117071
|
async countEmbeddedRetrievalChunks(workspaceId) {
|
|
117030
|
-
const { count, error } = await supabase.from("perch_ai_retrieval_chunks").select("*", { count: "exact", head: true }).eq("workspace_id", workspaceId).eq("is_stale", false).not("embedding", "is", null);
|
|
117072
|
+
const { count, error } = await supabase.from("perch_ai_retrieval_chunks").select("*", { count: "exact", head: true }).eq("workspace_id", workspaceId).eq("is_stale", false).not("embedding", "is", null).not("embedding_model", "is", null).not("embedded_at", "is", null);
|
|
117031
117073
|
if (error) throw error;
|
|
117032
117074
|
return count ?? 0;
|
|
117033
117075
|
},
|
|
@@ -117133,18 +117175,30 @@ function createSourcePersistence(supabase) {
|
|
|
117133
117175
|
return data ?? [];
|
|
117134
117176
|
},
|
|
117135
117177
|
async listIndexedSourceIds(workspaceId) {
|
|
117136
|
-
const [chunks, retrieval, memory] = await Promise.all([
|
|
117178
|
+
const [chunks, retrieval, embeddedRetrieval, memory] = await Promise.all([
|
|
117137
117179
|
supabase.from("perch_ai_source_chunks").select("source_id").eq("workspace_id", workspaceId),
|
|
117138
117180
|
supabase.from("perch_ai_retrieval_chunks").select("source_id").eq("workspace_id", workspaceId).eq("is_stale", false),
|
|
117181
|
+
supabase.from("perch_ai_retrieval_chunks").select("source_id").eq("workspace_id", workspaceId).eq("is_stale", false).not("embedding", "is", null).not("embedding_model", "is", null).not("embedded_at", "is", null),
|
|
117139
117182
|
supabase.from("perch_ai_source_memory").select("source_id").eq("workspace_id", workspaceId)
|
|
117140
117183
|
]);
|
|
117141
117184
|
if (chunks.error) throw chunks.error;
|
|
117142
117185
|
if (retrieval.error) throw retrieval.error;
|
|
117186
|
+
if (embeddedRetrieval.error) throw embeddedRetrieval.error;
|
|
117143
117187
|
if (memory.error) throw memory.error;
|
|
117188
|
+
const sourceChunkSourceIds = sourceIdsFromRows(chunks.data);
|
|
117189
|
+
const retrievalChunkSourceIds = sourceIdsFromRows(retrieval.data);
|
|
117190
|
+
const embeddedRetrievalSourceIds = sourceIdsFromRows(embeddedRetrieval.data);
|
|
117144
117191
|
return {
|
|
117145
|
-
sourceChunkIds: new Set(
|
|
117146
|
-
retrievalChunkIds: new Set(
|
|
117147
|
-
|
|
117192
|
+
sourceChunkIds: new Set(sourceChunkSourceIds),
|
|
117193
|
+
retrievalChunkIds: new Set(retrievalChunkSourceIds),
|
|
117194
|
+
embeddedRetrievalChunkIds: new Set(embeddedRetrievalSourceIds),
|
|
117195
|
+
memorySourceIds: new Set((memory.data ?? []).map((r) => r.source_id)),
|
|
117196
|
+
sourceChunkCountsBySourceId: countBySourceId(sourceChunkSourceIds),
|
|
117197
|
+
retrievalChunkCountsBySourceId: countBySourceId(retrievalChunkSourceIds),
|
|
117198
|
+
embeddedRetrievalChunkCountsBySourceId: countBySourceId(embeddedRetrievalSourceIds),
|
|
117199
|
+
totalSourceChunks: sourceChunkSourceIds.length,
|
|
117200
|
+
totalRetrievalChunks: retrievalChunkSourceIds.length,
|
|
117201
|
+
totalEmbeddedRetrievalChunks: embeddedRetrievalSourceIds.length
|
|
117148
117202
|
};
|
|
117149
117203
|
},
|
|
117150
117204
|
async keywordSearchSourcesMetadata(workspaceId, query, limit) {
|
|
@@ -117200,6 +117254,16 @@ function createSourcePersistence(supabase) {
|
|
|
117200
117254
|
function escapeIlike2(value) {
|
|
117201
117255
|
return value.replace(/[%_\\]/g, (ch) => `\\${ch}`);
|
|
117202
117256
|
}
|
|
117257
|
+
function sourceIdsFromRows(rows) {
|
|
117258
|
+
return (rows ?? []).map((row) => row.source_id).filter((sourceId) => typeof sourceId === "string" && sourceId.trim().length > 0);
|
|
117259
|
+
}
|
|
117260
|
+
function countBySourceId(sourceIds) {
|
|
117261
|
+
const counts = /* @__PURE__ */ new Map();
|
|
117262
|
+
for (const sourceId of sourceIds) {
|
|
117263
|
+
counts.set(sourceId, (counts.get(sourceId) ?? 0) + 1);
|
|
117264
|
+
}
|
|
117265
|
+
return counts;
|
|
117266
|
+
}
|
|
117203
117267
|
var SOURCE_LIST_COLUMNS;
|
|
117204
117268
|
var init_sourcePersistence = __esm({
|
|
117205
117269
|
"features/perchTerminal/persistence/sourcePersistence.ts"() {
|
|
@@ -117877,21 +117941,22 @@ function getSourceRetrievalToolDefinitions() {
|
|
|
117877
117941
|
}
|
|
117878
117942
|
];
|
|
117879
117943
|
}
|
|
117880
|
-
async function dispatchSourceRetrievalTool(toolName, args, session) {
|
|
117881
|
-
if (!isSupabaseConfigured()) {
|
|
117882
|
-
return sessionError("
|
|
117944
|
+
async function dispatchSourceRetrievalTool(toolName, args, session, options = {}) {
|
|
117945
|
+
if (!options.supabase && !isSupabaseConfigured()) {
|
|
117946
|
+
return sessionError("Workspace source search is unavailable in this session.");
|
|
117883
117947
|
}
|
|
117884
117948
|
if (!session.workspaceId) {
|
|
117885
|
-
return sessionError("
|
|
117949
|
+
return sessionError("Sign in to a workspace before using workspace source search.");
|
|
117886
117950
|
}
|
|
117887
117951
|
let supabase;
|
|
117888
117952
|
try {
|
|
117889
|
-
supabase = createClient();
|
|
117953
|
+
supabase = options.supabase ?? createClient();
|
|
117890
117954
|
} catch (err) {
|
|
117891
117955
|
const message = err instanceof Error ? err.message : String(err);
|
|
117892
117956
|
return sessionError(message);
|
|
117893
117957
|
}
|
|
117894
117958
|
const store = createSourcePersistence(supabase);
|
|
117959
|
+
const retrievalStore = createRetrievalPersistence(supabase);
|
|
117895
117960
|
const workspaceId = session.workspaceId;
|
|
117896
117961
|
switch (toolName) {
|
|
117897
117962
|
case TOOL_NAMES.listSources:
|
|
@@ -117907,9 +117972,9 @@ async function dispatchSourceRetrievalTool(toolName, args, session) {
|
|
|
117907
117972
|
case TOOL_NAMES.resolveSourceCandidates:
|
|
117908
117973
|
return resolveSourceCandidatesHandler(store, workspaceId, session, args);
|
|
117909
117974
|
case TOOL_NAMES.retrieveContext:
|
|
117910
|
-
return retrieveContextHandler(store, workspaceId, session, args);
|
|
117975
|
+
return retrieveContextHandler(store, retrievalStore, workspaceId, session, args);
|
|
117911
117976
|
case TOOL_NAMES.semanticSearch:
|
|
117912
|
-
return semanticSearchHandler(store, workspaceId, args);
|
|
117977
|
+
return semanticSearchHandler(store, retrievalStore, workspaceId, args);
|
|
117913
117978
|
case TOOL_NAMES.diagnoseWorkspaceAccess:
|
|
117914
117979
|
return diagnoseWorkspaceAccessHandler(store, workspaceId, session);
|
|
117915
117980
|
default:
|
|
@@ -118172,7 +118237,7 @@ async function resolveSourceCandidatesHandler(store, workspaceId, session, args)
|
|
|
118172
118237
|
schemaVersion: "perch-tool-result-v1"
|
|
118173
118238
|
};
|
|
118174
118239
|
}
|
|
118175
|
-
async function retrieveContextHandler(store, workspaceId, session, args) {
|
|
118240
|
+
async function retrieveContextHandler(store, retrievalStore, workspaceId, session, args) {
|
|
118176
118241
|
const query = stringArg(args.query)?.trim();
|
|
118177
118242
|
if (!query) {
|
|
118178
118243
|
return { ok: false, error: "query is required", toolName: TOOL_NAMES.retrieveContext };
|
|
@@ -118198,7 +118263,6 @@ async function retrieveContextHandler(store, workspaceId, session, args) {
|
|
|
118198
118263
|
let searchMode = "keyword_only";
|
|
118199
118264
|
let fallbackUsed = false;
|
|
118200
118265
|
let semanticError = null;
|
|
118201
|
-
const retrievalStore = createRetrievalPersistence(createClient());
|
|
118202
118266
|
const embeddedCount = await store.countEmbeddedRetrievalChunks(workspaceId).catch(() => 0);
|
|
118203
118267
|
retrievalAttempts.push("semantic_first");
|
|
118204
118268
|
try {
|
|
@@ -118396,7 +118460,7 @@ async function retrieveContextHandler(store, workspaceId, session, args) {
|
|
|
118396
118460
|
schemaVersion: "perch-tool-result-v1"
|
|
118397
118461
|
};
|
|
118398
118462
|
}
|
|
118399
|
-
async function semanticSearchHandler(store, workspaceId, args) {
|
|
118463
|
+
async function semanticSearchHandler(store, retrievalStore, workspaceId, args) {
|
|
118400
118464
|
const query = stringArg(args.query)?.trim();
|
|
118401
118465
|
if (!query) {
|
|
118402
118466
|
return { ok: false, error: "query is required", toolName: TOOL_NAMES.semanticSearch };
|
|
@@ -118405,7 +118469,7 @@ async function semanticSearchHandler(store, workspaceId, args) {
|
|
|
118405
118469
|
const sourceIds = Array.isArray(args.sourceIds) ? args.sourceIds.map(String) : void 0;
|
|
118406
118470
|
const includeStale = args.includeStale === true;
|
|
118407
118471
|
const embeddedCount = await store.countEmbeddedRetrievalChunks(workspaceId);
|
|
118408
|
-
const result2 = await
|
|
118472
|
+
const result2 = await retrievalStore.matchRetrievalChunks({
|
|
118409
118473
|
workspaceId,
|
|
118410
118474
|
query,
|
|
118411
118475
|
limit,
|
|
@@ -118450,7 +118514,7 @@ async function semanticSearchHandler(store, workspaceId, args) {
|
|
|
118450
118514
|
return {
|
|
118451
118515
|
rankSignals: signals,
|
|
118452
118516
|
reason: result2.searchMode === "hybrid" ? "hybrid_sparse_vector_rrf" : result2.searchMode === "sparse" ? "postgres_full_text_rank" : "semantic_similarity",
|
|
118453
|
-
scoreExplanation: result2.searchMode === "hybrid" ? `hybrid
|
|
118517
|
+
scoreExplanation: result2.searchMode === "hybrid" ? `hybrid rank${typeof signals.semanticRank === "number" ? `; vector rank ${signals.semanticRank}` : ""}${typeof signals.sparseRank === "number" ? `; text rank ${signals.sparseRank}` : ""}` : result2.searchMode === "sparse" ? "Workspace text-search rank; vector retrieval unavailable for this result" : `cosine similarity ${hit.similarity.toFixed(3)} from ${hit.embedding_model ?? "indexed embedding"}`
|
|
118454
118518
|
};
|
|
118455
118519
|
})(),
|
|
118456
118520
|
rank: index + 1,
|
|
@@ -118490,7 +118554,7 @@ async function semanticSearchHandler(store, workspaceId, args) {
|
|
|
118490
118554
|
semanticAvailable: result2.embeddingProviderConfigured,
|
|
118491
118555
|
sparseAvailable: result2.sparseProviderConfigured,
|
|
118492
118556
|
fallbackUsed: result2.fallbackUsed,
|
|
118493
|
-
ranking: result2.searchMode === "hybrid" ? "
|
|
118557
|
+
ranking: result2.searchMode === "hybrid" ? "combined vector and text-search ranks" : result2.searchMode === "sparse" ? "workspace text-search rank; vector retrieval not required" : "vector similarity over indexed source chunks"
|
|
118494
118558
|
},
|
|
118495
118559
|
schemaVersion: "perch-tool-result-v1"
|
|
118496
118560
|
};
|
|
@@ -118499,7 +118563,7 @@ async function diagnoseWorkspaceAccessHandler(store, workspaceId, session) {
|
|
|
118499
118563
|
const workspace = await store.getWorkspaceRow(workspaceId);
|
|
118500
118564
|
const blockers = [];
|
|
118501
118565
|
if (!workspace) {
|
|
118502
|
-
blockers.push("Workspace
|
|
118566
|
+
blockers.push("Workspace is not available to this signed-in session.");
|
|
118503
118567
|
}
|
|
118504
118568
|
let sourcesCount = 0;
|
|
118505
118569
|
let sourceChunksCount = 0;
|
|
@@ -133502,6 +133566,8 @@ var init_toolPermissionPolicy = __esm({
|
|
|
133502
133566
|
TOOL_NAMES.deleteLocalFile,
|
|
133503
133567
|
TOOL_NAMES.editLocalFile,
|
|
133504
133568
|
TOOL_NAMES.statPath,
|
|
133569
|
+
TOOL_NAMES.readProjectMemory,
|
|
133570
|
+
TOOL_NAMES.saveToMemory,
|
|
133505
133571
|
TOOL_NAMES.listLocalSources,
|
|
133506
133572
|
TOOL_NAMES.readLocalSourceFile,
|
|
133507
133573
|
TOOL_NAMES.generateAPAuditPacket,
|
|
@@ -135199,7 +135265,7 @@ function getDesktopToolDefinitions() {
|
|
|
135199
135265
|
type: "function",
|
|
135200
135266
|
function: {
|
|
135201
135267
|
name: TOOL_NAMES.readProjectMemory,
|
|
135202
|
-
description: "Read project memory
|
|
135268
|
+
description: "Read local project memory for the active workspace folder when .perch memory is present. Signed-in durable memory is admitted automatically at turn start.",
|
|
135203
135269
|
parameters: {
|
|
135204
135270
|
type: "object",
|
|
135205
135271
|
properties: {},
|
|
@@ -135211,7 +135277,7 @@ function getDesktopToolDefinitions() {
|
|
|
135211
135277
|
type: "function",
|
|
135212
135278
|
function: {
|
|
135213
135279
|
name: TOOL_NAMES.saveToMemory,
|
|
135214
|
-
description: "Save
|
|
135280
|
+
description: "Save durable memory when the user asks you to remember something or when you learn a stable user/project preference. In signed-in CLI sessions this writes server memory; in a .perch project folder it can also write local project memory. Prefer mode='merge' with a sectionHeading for local project memory.",
|
|
135215
135281
|
parameters: {
|
|
135216
135282
|
type: "object",
|
|
135217
135283
|
properties: {
|
|
@@ -135227,6 +135293,15 @@ function getDesktopToolDefinitions() {
|
|
|
135227
135293
|
sectionHeading: {
|
|
135228
135294
|
type: "string",
|
|
135229
135295
|
description: "Required for merge mode \u2014 markdown heading to upsert under."
|
|
135296
|
+
},
|
|
135297
|
+
scope: {
|
|
135298
|
+
type: "string",
|
|
135299
|
+
enum: ["private_user", "workspace", "project"],
|
|
135300
|
+
description: "Optional durable-memory scope for signed-in server memory. Defaults from fileName."
|
|
135301
|
+
},
|
|
135302
|
+
title: {
|
|
135303
|
+
type: "string",
|
|
135304
|
+
description: "Optional short title for signed-in durable memory."
|
|
135230
135305
|
}
|
|
135231
135306
|
},
|
|
135232
135307
|
required: ["fileName", "mode", "content"],
|
|
@@ -135282,7 +135357,7 @@ function getDesktopToolDefinitions() {
|
|
|
135282
135357
|
type: "function",
|
|
135283
135358
|
function: {
|
|
135284
135359
|
name: TOOL_NAMES.getProjectRules,
|
|
135285
|
-
description: "Read PERCH.md and small .perch/rules/*.md or *.txt project rule files for the active folder
|
|
135360
|
+
description: "Read PERCH.md and small .perch/rules/*.md or *.txt project rule files for the active workspace folder.",
|
|
135286
135361
|
parameters: {
|
|
135287
135362
|
type: "object",
|
|
135288
135363
|
properties: {},
|
|
@@ -135300,7 +135375,7 @@ function getDesktopToolDefinitions() {
|
|
|
135300
135375
|
properties: {
|
|
135301
135376
|
path: {
|
|
135302
135377
|
type: "string",
|
|
135303
|
-
description: "Absolute directory path to list (e.g. /Users/you/Desktop). Works with no folder
|
|
135378
|
+
description: "Absolute directory path to list (e.g. /Users/you/Desktop). Works with no workspace folder selected."
|
|
135304
135379
|
},
|
|
135305
135380
|
query: {
|
|
135306
135381
|
type: "string",
|
|
@@ -136488,7 +136563,7 @@ function getNativeToolDefinitions() {
|
|
|
136488
136563
|
type: "function",
|
|
136489
136564
|
function: {
|
|
136490
136565
|
name: TOOL_NAMES.ctxInspect,
|
|
136491
|
-
description: "Inspect current execution context: desktop connection status, selected folder
|
|
136566
|
+
description: "Inspect current execution context: desktop connection status, selected workspace folder, permission mode, tool counts, and available tools.",
|
|
136492
136567
|
parameters: {
|
|
136493
136568
|
type: "object",
|
|
136494
136569
|
properties: {},
|
|
@@ -136522,7 +136597,7 @@ function getNativeToolDefinitions() {
|
|
|
136522
136597
|
type: "function",
|
|
136523
136598
|
function: {
|
|
136524
136599
|
name: TOOL_NAMES.configInspect,
|
|
136525
|
-
description: "Inspect the Perch Terminal configuration: local workspace access, optional folder
|
|
136600
|
+
description: "Inspect the Perch Terminal configuration: local workspace access, optional workspace folder, permission mode, and capabilities.",
|
|
136526
136601
|
parameters: {
|
|
136527
136602
|
type: "object",
|
|
136528
136603
|
properties: {},
|
|
@@ -137811,12 +137886,17 @@ async function restartBashTerminal(request) {
|
|
|
137811
137886
|
async function runBashTerminalCommand(request) {
|
|
137812
137887
|
const bridge = getDesktopBridge();
|
|
137813
137888
|
if (!bridge) return null;
|
|
137814
|
-
return bridge.runBashTerminalCommand(request);
|
|
137889
|
+
return bridge.runBashTerminalCommand(stripBrowserOnlyAbortSignal(request));
|
|
137815
137890
|
}
|
|
137816
137891
|
async function runLocalBash(request) {
|
|
137817
137892
|
const bridge = getDesktopBridge();
|
|
137818
137893
|
if (!bridge) return null;
|
|
137819
|
-
return bridge.runLocalBash(withPermissionMode(request, request));
|
|
137894
|
+
return bridge.runLocalBash(withPermissionMode(stripBrowserOnlyAbortSignal(request), request));
|
|
137895
|
+
}
|
|
137896
|
+
function stripBrowserOnlyAbortSignal(request) {
|
|
137897
|
+
if (typeof window === "undefined" || !("signal" in request)) return request;
|
|
137898
|
+
const { signal: _signal, ...rest2 } = request;
|
|
137899
|
+
return rest2;
|
|
137820
137900
|
}
|
|
137821
137901
|
async function readWorkspaceFile(request) {
|
|
137822
137902
|
const bridge = getDesktopBridge();
|
|
@@ -206110,7 +206190,7 @@ async function requireMemoryRootId(ctx, surface) {
|
|
|
206110
206190
|
if (!rootId) {
|
|
206111
206191
|
return {
|
|
206112
206192
|
ok: false,
|
|
206113
|
-
error: `No selected folder
|
|
206193
|
+
error: `No selected workspace folder is available for ${surface}.`
|
|
206114
206194
|
};
|
|
206115
206195
|
}
|
|
206116
206196
|
return { ok: true, rootId };
|
|
@@ -206135,14 +206215,86 @@ var init_readProjectMemory = __esm({
|
|
|
206135
206215
|
classification: { native: false },
|
|
206136
206216
|
handler: async (_args, ctx) => {
|
|
206137
206217
|
const root2 = await requireMemoryRootId(ctx, "project memory");
|
|
206138
|
-
if (!root2.ok)
|
|
206139
|
-
|
|
206218
|
+
if (!root2.ok) {
|
|
206219
|
+
return ctx.cliServerAppUrl?.trim() && ctx.cliServerAccessToken?.trim() ? {
|
|
206220
|
+
ok: true,
|
|
206221
|
+
backend: "server_memory",
|
|
206222
|
+
message: "Signed-in durable memory is available. Relevant memories are added automatically at the start of each CLI turn."
|
|
206223
|
+
} : {
|
|
206224
|
+
ok: false,
|
|
206225
|
+
errorCode: "memory_unavailable",
|
|
206226
|
+
error: "Project memory is unavailable in this CLI session. Run from a project folder with .perch memory or sign in for durable memory."
|
|
206227
|
+
};
|
|
206228
|
+
}
|
|
206229
|
+
const result2 = await readProjectMemory(root2.rootId);
|
|
206230
|
+
if (!result2.ok && ctx.cliServerAppUrl?.trim() && ctx.cliServerAccessToken?.trim()) {
|
|
206231
|
+
return {
|
|
206232
|
+
ok: true,
|
|
206233
|
+
backend: "server_memory",
|
|
206234
|
+
localProjectMemory: result2,
|
|
206235
|
+
message: "Signed-in durable memory is available. Relevant memories are added automatically at the start of each CLI turn."
|
|
206236
|
+
};
|
|
206237
|
+
}
|
|
206238
|
+
return result2;
|
|
206140
206239
|
}
|
|
206141
206240
|
};
|
|
206142
206241
|
}
|
|
206143
206242
|
});
|
|
206144
206243
|
|
|
206145
206244
|
// features/perchTerminal/runtime/toolSystem/tools/projectMemory/saveToMemory.ts
|
|
206245
|
+
function hasCliServerMemory(ctx) {
|
|
206246
|
+
return Boolean(
|
|
206247
|
+
ctx.cliServerAppUrl?.trim() && ctx.cliServerAccessToken?.trim()
|
|
206248
|
+
);
|
|
206249
|
+
}
|
|
206250
|
+
async function saveMemoryThroughCliServer(args, ctx) {
|
|
206251
|
+
const appUrl = ctx.cliServerAppUrl?.trim();
|
|
206252
|
+
const accessToken = ctx.cliServerAccessToken?.trim();
|
|
206253
|
+
if (!appUrl || !accessToken) return memoryUnavailable();
|
|
206254
|
+
try {
|
|
206255
|
+
const response = await fetch(`${appUrl.replace(/\/+$/, "")}/api/perch-terminal/cli-memory`, {
|
|
206256
|
+
method: "POST",
|
|
206257
|
+
headers: {
|
|
206258
|
+
Accept: "application/json",
|
|
206259
|
+
"Content-Type": "application/json",
|
|
206260
|
+
Authorization: `Bearer ${accessToken}`
|
|
206261
|
+
},
|
|
206262
|
+
body: JSON.stringify({
|
|
206263
|
+
action: "save",
|
|
206264
|
+
args,
|
|
206265
|
+
threadId: ctx.threadId ?? null,
|
|
206266
|
+
runId: ctx.runId ?? null,
|
|
206267
|
+
workspaceRoot: ctx.activeRootPath ?? ctx.workspaceRoot ?? null
|
|
206268
|
+
}),
|
|
206269
|
+
signal: ctx.signal
|
|
206270
|
+
});
|
|
206271
|
+
const payload = await response.json().catch(() => ({}));
|
|
206272
|
+
if (!response.ok) {
|
|
206273
|
+
return {
|
|
206274
|
+
ok: false,
|
|
206275
|
+
backend: "server_memory",
|
|
206276
|
+
errorCode: typeof payload.errorCode === "string" ? payload.errorCode : "memory_save_unavailable",
|
|
206277
|
+
error: typeof payload.message === "string" ? payload.message : "Durable memory is unavailable right now. Try again shortly or update Perch."
|
|
206278
|
+
};
|
|
206279
|
+
}
|
|
206280
|
+
return payload;
|
|
206281
|
+
} catch (error) {
|
|
206282
|
+
const aborted = error instanceof DOMException && error.name === "AbortError";
|
|
206283
|
+
return {
|
|
206284
|
+
ok: false,
|
|
206285
|
+
backend: "server_memory",
|
|
206286
|
+
errorCode: aborted ? "memory_save_cancelled" : "memory_save_unavailable",
|
|
206287
|
+
error: aborted ? "Memory save was stopped." : "Durable memory is unavailable right now. Try again shortly or update Perch."
|
|
206288
|
+
};
|
|
206289
|
+
}
|
|
206290
|
+
}
|
|
206291
|
+
function memoryUnavailable() {
|
|
206292
|
+
return {
|
|
206293
|
+
ok: false,
|
|
206294
|
+
errorCode: "memory_unavailable",
|
|
206295
|
+
error: "Memory is unavailable in this CLI session. Sign in with `perch login` or run from a project folder with .perch memory."
|
|
206296
|
+
};
|
|
206297
|
+
}
|
|
206146
206298
|
var saveToMemoryTool;
|
|
206147
206299
|
var init_saveToMemory = __esm({
|
|
206148
206300
|
"features/perchTerminal/runtime/toolSystem/tools/projectMemory/saveToMemory.ts"() {
|
|
@@ -206154,14 +206306,24 @@ var init_saveToMemory = __esm({
|
|
|
206154
206306
|
name: TOOL_NAMES.saveToMemory,
|
|
206155
206307
|
classification: { native: false },
|
|
206156
206308
|
handler: async (args, ctx) => {
|
|
206309
|
+
const serverResult = hasCliServerMemory(ctx) ? await saveMemoryThroughCliServer(args, ctx) : null;
|
|
206310
|
+
if (serverResult?.ok) return serverResult;
|
|
206157
206311
|
const root2 = await requireMemoryRootId(ctx, "project memory");
|
|
206158
|
-
if (!root2.ok) return
|
|
206159
|
-
|
|
206312
|
+
if (!root2.ok) return serverResult ?? memoryUnavailable();
|
|
206313
|
+
const localResult = await writeMemoryFile(root2.rootId, {
|
|
206160
206314
|
fileName: String(args.fileName),
|
|
206161
206315
|
mode: String(args.mode),
|
|
206162
206316
|
content: String(args.content ?? ""),
|
|
206163
206317
|
sectionHeading: typeof args.sectionHeading === "string" ? args.sectionHeading : void 0
|
|
206164
206318
|
});
|
|
206319
|
+
if (localResult.ok === true) {
|
|
206320
|
+
return {
|
|
206321
|
+
...localResult,
|
|
206322
|
+
backend: "local_project_memory",
|
|
206323
|
+
message: serverResult ? "Saved to local project memory. Signed-in durable memory was unavailable, so this save is scoped to the current project folder." : "Saved to local project memory."
|
|
206324
|
+
};
|
|
206325
|
+
}
|
|
206326
|
+
return serverResult ?? localResult ?? memoryUnavailable();
|
|
206165
206327
|
}
|
|
206166
206328
|
};
|
|
206167
206329
|
}
|
|
@@ -213107,18 +213269,24 @@ var init_terminal = __esm({
|
|
|
213107
213269
|
command,
|
|
213108
213270
|
cwd: cwd2,
|
|
213109
213271
|
timeoutMs,
|
|
213110
|
-
fsAccessOverride: ctx.fsAccessOverride
|
|
213272
|
+
fsAccessOverride: ctx.fsAccessOverride,
|
|
213273
|
+
signal: ctx.cliLocalTools ? ctx.signal : void 0
|
|
213111
213274
|
});
|
|
213112
213275
|
}
|
|
213113
213276
|
};
|
|
213114
213277
|
runBashTerminalCommandTool = {
|
|
213115
213278
|
name: TOOL_NAMES.runBashTerminalCommand,
|
|
213116
213279
|
classification: { native: false },
|
|
213117
|
-
handler: async (args) => {
|
|
213280
|
+
handler: async (args, ctx) => {
|
|
213118
213281
|
const command = String(args.command ?? "");
|
|
213119
213282
|
const cwd2 = args.cwd ? String(args.cwd) : void 0;
|
|
213120
213283
|
const commandArgs = Array.isArray(args.args) ? args.args.map(String) : void 0;
|
|
213121
|
-
return runBashTerminalCommand({
|
|
213284
|
+
return runBashTerminalCommand({
|
|
213285
|
+
command,
|
|
213286
|
+
cwd: cwd2,
|
|
213287
|
+
args: commandArgs,
|
|
213288
|
+
signal: ctx.cliLocalTools ? ctx.signal : void 0
|
|
213289
|
+
});
|
|
213122
213290
|
}
|
|
213123
213291
|
};
|
|
213124
213292
|
terminalTools = [bashTool, runBashTerminalCommandTool];
|
|
@@ -213210,7 +213378,7 @@ var init_localSources = __esm({
|
|
|
213210
213378
|
return {
|
|
213211
213379
|
ok: true,
|
|
213212
213380
|
sources: [],
|
|
213213
|
-
warning: "
|
|
213381
|
+
warning: "Local workspace access is unavailable in this chat. Open Perch Desktop or attach a workspace folder, then try again."
|
|
213214
213382
|
};
|
|
213215
213383
|
}
|
|
213216
213384
|
const maxResults = sanitizeListLocalSourcesMaxResults(args.maxResults);
|
|
@@ -213769,6 +213937,11 @@ var init_fixtureStore = __esm({
|
|
|
213769
213937
|
function calendarTicker() {
|
|
213770
213938
|
return process.env.PERCH_MARKET_CALENDAR_TICKER || "SPY";
|
|
213771
213939
|
}
|
|
213940
|
+
function sourceRank(source) {
|
|
213941
|
+
if (source === "yahoo") return 3;
|
|
213942
|
+
if (source === "stooq") return 2;
|
|
213943
|
+
return 1;
|
|
213944
|
+
}
|
|
213772
213945
|
function createSupabaseMarketStore(supabase) {
|
|
213773
213946
|
return {
|
|
213774
213947
|
async listInstruments() {
|
|
@@ -213786,13 +213959,13 @@ function createSupabaseMarketStore(supabase) {
|
|
|
213786
213959
|
async getBars(ticker, opts) {
|
|
213787
213960
|
const cutoff = endOfDayUtc(opts.upTo);
|
|
213788
213961
|
const wanted = opts.lookbackBars ?? Number.MAX_SAFE_INTEGER;
|
|
213789
|
-
const
|
|
213790
|
-
for (let page2 = 0;
|
|
213791
|
-
const { data, error } = await supabase.from("perch_ai_market_bars").select("bar_date, open, high, low, close, adj_close, volume, known_at").eq("ticker", ticker).lte("bar_date", opts.upTo).lte("known_at", cutoff).order("bar_date", { ascending: false }).range(page2 * PAGE_SIZE, page2 * PAGE_SIZE + PAGE_SIZE - 1);
|
|
213962
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
213963
|
+
for (let page2 = 0; byDate.size < wanted; page2++) {
|
|
213964
|
+
const { data, error } = await supabase.from("perch_ai_market_bars").select("bar_date, open, high, low, close, adj_close, volume, known_at, source").eq("ticker", ticker).lte("bar_date", opts.upTo).lte("known_at", cutoff).order("bar_date", { ascending: false }).order("known_at", { ascending: false }).range(page2 * PAGE_SIZE, page2 * PAGE_SIZE + PAGE_SIZE - 1);
|
|
213792
213965
|
if (error) throw new Error(`market bars read failed: ${error.message}`);
|
|
213793
213966
|
const batch = data ?? [];
|
|
213794
213967
|
for (const row of batch) {
|
|
213795
|
-
|
|
213968
|
+
const bar = {
|
|
213796
213969
|
ticker,
|
|
213797
213970
|
date: row.bar_date,
|
|
213798
213971
|
open: row.open === null ? void 0 : Number(row.open),
|
|
@@ -213801,13 +213974,17 @@ function createSupabaseMarketStore(supabase) {
|
|
|
213801
213974
|
close: Number(row.close),
|
|
213802
213975
|
adjClose: row.adj_close === null ? Number(row.close) : Number(row.adj_close),
|
|
213803
213976
|
volume: row.volume === null ? void 0 : Number(row.volume),
|
|
213804
|
-
knownAt: row.known_at
|
|
213805
|
-
|
|
213806
|
-
|
|
213977
|
+
knownAt: row.known_at,
|
|
213978
|
+
source: row.source ?? void 0
|
|
213979
|
+
};
|
|
213980
|
+
const existing = byDate.get(bar.date);
|
|
213981
|
+
if (!existing || sourceRank(bar.source) > sourceRank(existing.source)) {
|
|
213982
|
+
byDate.set(bar.date, bar);
|
|
213983
|
+
}
|
|
213807
213984
|
}
|
|
213808
213985
|
if (batch.length < PAGE_SIZE) break;
|
|
213809
213986
|
}
|
|
213810
|
-
return
|
|
213987
|
+
return [...byDate.values()].sort((a, b2) => a.date > b2.date ? -1 : a.date < b2.date ? 1 : 0).slice(0, wanted).reverse().map(({ source: _source, ...bar }) => bar);
|
|
213811
213988
|
},
|
|
213812
213989
|
async getTradingDates(opts) {
|
|
213813
213990
|
const dates = [];
|
|
@@ -216685,6 +216862,8 @@ async function executeToolCall(call, opts) {
|
|
|
216685
216862
|
workspaceId: opts.workspaceId,
|
|
216686
216863
|
threadId: opts.threadId,
|
|
216687
216864
|
supabase: opts.supabase,
|
|
216865
|
+
cliServerAppUrl: opts.cliServerAppUrl,
|
|
216866
|
+
cliServerAccessToken: opts.cliServerAccessToken,
|
|
216688
216867
|
marketDeskProxyAppUrl: opts.marketDeskProxyAppUrl,
|
|
216689
216868
|
marketDeskProxyAccessToken: opts.marketDeskProxyAccessToken,
|
|
216690
216869
|
chatMode: opts.chatMode ?? null,
|
|
@@ -216744,7 +216923,7 @@ async function executeToolCall(call, opts) {
|
|
|
216744
216923
|
const result2 = {
|
|
216745
216924
|
ok: false,
|
|
216746
216925
|
errorCode: "supabase_session_required",
|
|
216747
|
-
error: isSupabaseConfigured() ? "Source retrieval tools require an authenticated workspace session." : "
|
|
216926
|
+
error: isSupabaseConfigured() ? "Source retrieval tools require an authenticated workspace session." : "Workspace source search is unavailable in this session.",
|
|
216748
216927
|
schemaVersion: "perch-tool-result-v1",
|
|
216749
216928
|
toolName: call.name
|
|
216750
216929
|
};
|
|
@@ -216825,6 +217004,8 @@ async function executeToolCall(call, opts) {
|
|
|
216825
217004
|
workspaceRoot: opts.workspaceRoot,
|
|
216826
217005
|
threadId: opts.threadId,
|
|
216827
217006
|
runId: opts.runId,
|
|
217007
|
+
cliServerAppUrl: opts.cliServerAppUrl,
|
|
217008
|
+
cliServerAccessToken: opts.cliServerAccessToken,
|
|
216828
217009
|
marketDeskProxyAppUrl: opts.marketDeskProxyAppUrl,
|
|
216829
217010
|
marketDeskProxyAccessToken: opts.marketDeskProxyAccessToken,
|
|
216830
217011
|
onEvent: runtime.onEvent,
|
|
@@ -219025,6 +219206,8 @@ async function runModelToolLoop(input) {
|
|
|
219025
219206
|
selectedSourceId: input.selectedSourceId,
|
|
219026
219207
|
supabaseConfigured: input.supabaseConfigured,
|
|
219027
219208
|
supabase: input.supabase,
|
|
219209
|
+
cliServerAppUrl: input.cliServerAppUrl,
|
|
219210
|
+
cliServerAccessToken: input.cliServerAccessToken,
|
|
219028
219211
|
marketDeskProxyAppUrl: input.marketDeskProxyAppUrl,
|
|
219029
219212
|
marketDeskProxyAccessToken: input.marketDeskProxyAccessToken,
|
|
219030
219213
|
runId: input.runId,
|
|
@@ -219441,6 +219624,8 @@ async function executeInitialToolCall(input) {
|
|
|
219441
219624
|
selectedSourceId: input.input.selectedSourceId,
|
|
219442
219625
|
supabaseConfigured: input.input.supabaseConfigured,
|
|
219443
219626
|
supabase: input.input.supabase,
|
|
219627
|
+
cliServerAppUrl: input.input.cliServerAppUrl,
|
|
219628
|
+
cliServerAccessToken: input.input.cliServerAccessToken,
|
|
219444
219629
|
marketDeskProxyAppUrl: input.input.marketDeskProxyAppUrl,
|
|
219445
219630
|
marketDeskProxyAccessToken: input.input.marketDeskProxyAccessToken,
|
|
219446
219631
|
runId: input.input.runId,
|
|
@@ -220502,7 +220687,7 @@ async function runLiveAgentsLoop(input) {
|
|
|
220502
220687
|
const onEv = onEvent ?? deps.onEvent;
|
|
220503
220688
|
onEv({
|
|
220504
220689
|
type: "activity_delta",
|
|
220505
|
-
text: "Workspace preflight: no folder
|
|
220690
|
+
text: "Workspace preflight: no workspace folder selected; continuing with Desktop tools.",
|
|
220506
220691
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
220507
220692
|
});
|
|
220508
220693
|
}
|
|
@@ -220541,6 +220726,8 @@ async function runLiveAgentsLoop(input) {
|
|
|
220541
220726
|
untrustedContextPresent: context.untrustedContextPresent,
|
|
220542
220727
|
supabaseConfigured: turn.supabaseConfigured,
|
|
220543
220728
|
supabase: turn.supabase,
|
|
220729
|
+
cliServerAppUrl: turn.cliServerAppUrl ?? null,
|
|
220730
|
+
cliServerAccessToken: turn.cliServerAccessToken ?? null,
|
|
220544
220731
|
marketDeskProxyAppUrl: turn.marketDeskProxyAppUrl ?? null,
|
|
220545
220732
|
marketDeskProxyAccessToken: turn.marketDeskProxyAccessToken ?? null,
|
|
220546
220733
|
onEvent: onEvent ?? deps.onEvent,
|
|
@@ -221907,7 +222094,12 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
221907
222094
|
contentHash: null,
|
|
221908
222095
|
chunksBuilt: 0,
|
|
221909
222096
|
chunksUpserted: 0,
|
|
221910
|
-
|
|
222097
|
+
sourceChunksUpserted: 0,
|
|
222098
|
+
retrievalChunksBuilt: 0,
|
|
222099
|
+
retrievalChunksUpserted: 0,
|
|
222100
|
+
embeddedChunksUpserted: 0,
|
|
222101
|
+
vectorReady: false,
|
|
222102
|
+
embeddingConfigured: isBrowserRuntime() ? true : getEmbeddingProviderStatus().configured,
|
|
221911
222103
|
errors: [],
|
|
221912
222104
|
warnings: []
|
|
221913
222105
|
};
|
|
@@ -221940,7 +222132,12 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
221940
222132
|
contentHash,
|
|
221941
222133
|
chunksBuilt: 0,
|
|
221942
222134
|
chunksUpserted: 0,
|
|
221943
|
-
|
|
222135
|
+
sourceChunksUpserted: 0,
|
|
222136
|
+
retrievalChunksBuilt: 0,
|
|
222137
|
+
retrievalChunksUpserted: 0,
|
|
222138
|
+
embeddedChunksUpserted: 0,
|
|
222139
|
+
vectorReady: true,
|
|
222140
|
+
embeddingConfigured: isBrowserRuntime() ? true : getEmbeddingProviderStatus().configured,
|
|
221944
222141
|
errors: [],
|
|
221945
222142
|
warnings: []
|
|
221946
222143
|
};
|
|
@@ -222038,12 +222235,15 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
222038
222235
|
threadId: input.threadId ?? null,
|
|
222039
222236
|
chunks: sourceTextChunks
|
|
222040
222237
|
});
|
|
222041
|
-
const embeddingStatus = getEmbeddingProviderStatus();
|
|
222042
222238
|
let chunksBuilt = textChunks.length;
|
|
222043
222239
|
let chunksUpserted = 0;
|
|
222044
222240
|
const warnings = [...extracted.truncated ? ["Source text was truncated before indexing."] : []];
|
|
222045
|
-
|
|
222046
|
-
|
|
222241
|
+
let embeddingProvider;
|
|
222242
|
+
try {
|
|
222243
|
+
embeddingProvider = input.embeddingProvider ?? createDefaultLocalSourceEmbeddingProvider();
|
|
222244
|
+
} catch (error) {
|
|
222245
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
222246
|
+
warnings.push(message);
|
|
222047
222247
|
return {
|
|
222048
222248
|
ok: true,
|
|
222049
222249
|
status: "indexed",
|
|
@@ -222053,38 +222253,69 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
222053
222253
|
contentHash: indexedContentHash,
|
|
222054
222254
|
chunksBuilt,
|
|
222055
222255
|
chunksUpserted: 0,
|
|
222256
|
+
sourceChunksUpserted: sourceTextChunks.length,
|
|
222257
|
+
retrievalChunksBuilt: chunksBuilt,
|
|
222258
|
+
retrievalChunksUpserted: 0,
|
|
222259
|
+
embeddedChunksUpserted: 0,
|
|
222260
|
+
vectorReady: false,
|
|
222261
|
+
embeddingConfigured: false,
|
|
222262
|
+
errors: [],
|
|
222263
|
+
warnings
|
|
222264
|
+
};
|
|
222265
|
+
}
|
|
222266
|
+
let indexResult;
|
|
222267
|
+
try {
|
|
222268
|
+
const indexer = createRetrievalIndexer(input.supabase, { embeddingProvider });
|
|
222269
|
+
indexResult = pdfPages ? await indexer.indexLocalFilePages({
|
|
222270
|
+
workspaceId: input.workspaceId,
|
|
222271
|
+
sourceId: source.id,
|
|
222272
|
+
fileName: source.file_name,
|
|
222273
|
+
fileType: extracted.fileType,
|
|
222274
|
+
mimeType: source.mime_type,
|
|
222275
|
+
provenanceType: "desktop_local",
|
|
222276
|
+
contentHash: indexedContentHash,
|
|
222277
|
+
threadId: input.threadId ?? null,
|
|
222278
|
+
pages: pdfPages,
|
|
222279
|
+
blocks: pdfBlocks,
|
|
222280
|
+
tables: pdfTables,
|
|
222281
|
+
facts: factPageEntries.length ? factPageEntries : void 0
|
|
222282
|
+
}) : await indexer.indexLocalFileText({
|
|
222283
|
+
workspaceId: input.workspaceId,
|
|
222284
|
+
sourceId: source.id,
|
|
222285
|
+
fileName: source.file_name,
|
|
222286
|
+
fileType: extracted.fileType,
|
|
222287
|
+
mimeType: source.mime_type,
|
|
222288
|
+
provenanceType: "desktop_local",
|
|
222289
|
+
contentHash: indexedContentHash,
|
|
222290
|
+
threadId: input.threadId ?? null,
|
|
222291
|
+
text: extracted.text
|
|
222292
|
+
});
|
|
222293
|
+
} catch (error) {
|
|
222294
|
+
if (!isEmbeddingProviderNotConfigured(error)) throw error;
|
|
222295
|
+
warnings.push(error.message);
|
|
222296
|
+
return {
|
|
222297
|
+
ok: true,
|
|
222298
|
+
status: "indexed",
|
|
222299
|
+
localSourceId: input.localSourceId,
|
|
222300
|
+
supabaseSourceId: source.id,
|
|
222301
|
+
retrievalSourceId: null,
|
|
222302
|
+
contentHash: indexedContentHash,
|
|
222303
|
+
chunksBuilt,
|
|
222304
|
+
chunksUpserted: 0,
|
|
222305
|
+
sourceChunksUpserted: sourceTextChunks.length,
|
|
222306
|
+
retrievalChunksBuilt: chunksBuilt,
|
|
222307
|
+
retrievalChunksUpserted: 0,
|
|
222308
|
+
embeddedChunksUpserted: 0,
|
|
222309
|
+
vectorReady: false,
|
|
222056
222310
|
embeddingConfigured: false,
|
|
222057
222311
|
errors: [],
|
|
222058
222312
|
warnings
|
|
222059
222313
|
};
|
|
222060
222314
|
}
|
|
222061
|
-
const indexer = createRetrievalIndexer(input.supabase);
|
|
222062
|
-
const indexResult = pdfPages ? await indexer.indexLocalFilePages({
|
|
222063
|
-
workspaceId: input.workspaceId,
|
|
222064
|
-
sourceId: source.id,
|
|
222065
|
-
fileName: source.file_name,
|
|
222066
|
-
fileType: extracted.fileType,
|
|
222067
|
-
mimeType: source.mime_type,
|
|
222068
|
-
provenanceType: "desktop_local",
|
|
222069
|
-
contentHash: indexedContentHash,
|
|
222070
|
-
threadId: input.threadId ?? null,
|
|
222071
|
-
pages: pdfPages,
|
|
222072
|
-
blocks: pdfBlocks,
|
|
222073
|
-
tables: pdfTables,
|
|
222074
|
-
facts: factPageEntries.length ? factPageEntries : void 0
|
|
222075
|
-
}) : await indexer.indexLocalFileText({
|
|
222076
|
-
workspaceId: input.workspaceId,
|
|
222077
|
-
sourceId: source.id,
|
|
222078
|
-
fileName: source.file_name,
|
|
222079
|
-
fileType: extracted.fileType,
|
|
222080
|
-
mimeType: source.mime_type,
|
|
222081
|
-
provenanceType: "desktop_local",
|
|
222082
|
-
contentHash: indexedContentHash,
|
|
222083
|
-
threadId: input.threadId ?? null,
|
|
222084
|
-
text: extracted.text
|
|
222085
|
-
});
|
|
222086
222315
|
chunksBuilt = indexResult.chunksBuilt;
|
|
222087
222316
|
chunksUpserted = indexResult.chunksUpserted;
|
|
222317
|
+
const embeddedChunksUpserted = indexResult.chunksEmbedded;
|
|
222318
|
+
const vectorReady = embeddedChunksUpserted > 0;
|
|
222088
222319
|
if (!indexResult.ok) {
|
|
222089
222320
|
return {
|
|
222090
222321
|
ok: false,
|
|
@@ -222095,6 +222326,11 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
222095
222326
|
contentHash: indexedContentHash,
|
|
222096
222327
|
chunksBuilt,
|
|
222097
222328
|
chunksUpserted,
|
|
222329
|
+
sourceChunksUpserted: sourceTextChunks.length,
|
|
222330
|
+
retrievalChunksBuilt: indexResult.chunksBuilt,
|
|
222331
|
+
retrievalChunksUpserted: indexResult.chunksUpserted,
|
|
222332
|
+
embeddedChunksUpserted,
|
|
222333
|
+
vectorReady,
|
|
222098
222334
|
embeddingConfigured: true,
|
|
222099
222335
|
errors: indexResult.errors,
|
|
222100
222336
|
warnings: [...warnings, ...indexResult.warnings]
|
|
@@ -222105,10 +222341,15 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
222105
222341
|
status: "indexed",
|
|
222106
222342
|
localSourceId: input.localSourceId,
|
|
222107
222343
|
supabaseSourceId: source.id,
|
|
222108
|
-
retrievalSourceId: source.id,
|
|
222344
|
+
retrievalSourceId: vectorReady ? source.id : null,
|
|
222109
222345
|
contentHash: indexedContentHash,
|
|
222110
222346
|
chunksBuilt,
|
|
222111
222347
|
chunksUpserted,
|
|
222348
|
+
sourceChunksUpserted: sourceTextChunks.length,
|
|
222349
|
+
retrievalChunksBuilt: indexResult.chunksBuilt,
|
|
222350
|
+
retrievalChunksUpserted: indexResult.chunksUpserted,
|
|
222351
|
+
embeddedChunksUpserted,
|
|
222352
|
+
vectorReady,
|
|
222112
222353
|
embeddingConfigured: true,
|
|
222113
222354
|
errors: [],
|
|
222114
222355
|
warnings
|
|
@@ -222124,6 +222365,12 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
222124
222365
|
};
|
|
222125
222366
|
}
|
|
222126
222367
|
}
|
|
222368
|
+
function createDefaultLocalSourceEmbeddingProvider() {
|
|
222369
|
+
return isBrowserRuntime() ? createBrowserEmbeddingProvider() : createEmbeddingProviderFromEnv();
|
|
222370
|
+
}
|
|
222371
|
+
function isBrowserRuntime() {
|
|
222372
|
+
return typeof window !== "undefined";
|
|
222373
|
+
}
|
|
222127
222374
|
async function resolveRetrievalSourceIdForTurn(input) {
|
|
222128
222375
|
const selected = input.selectedSourceId?.trim() ?? null;
|
|
222129
222376
|
if (!selected) return { retrievalSourceId: null, indexOutcome: null };
|
|
@@ -222399,7 +222646,11 @@ async function loadFolderIndexSummary(input) {
|
|
|
222399
222646
|
}
|
|
222400
222647
|
const files = input.localSources.filter((entry) => entry.rootId === input.rootId && isAutoIndexableLocalSource(entry)).map((entry) => {
|
|
222401
222648
|
const row = rowByLocalId.get(entry.localSourceId) ?? null;
|
|
222402
|
-
const
|
|
222649
|
+
const sourceChunkCount2 = row ? indexSets.sourceChunkCountsBySourceId.get(row.id) ?? 0 : 0;
|
|
222650
|
+
const retrievalChunkCount2 = row ? indexSets.retrievalChunkCountsBySourceId.get(row.id) ?? 0 : 0;
|
|
222651
|
+
const embeddedChunkCount2 = row ? indexSets.embeddedRetrievalChunkCountsBySourceId.get(row.id) ?? 0 : 0;
|
|
222652
|
+
const retrievalReady = retrievalChunkCount2 > 0;
|
|
222653
|
+
const vectorReady = embeddedChunkCount2 > 0;
|
|
222403
222654
|
const stale = row ? Date.parse(entry.modifiedAt) > Date.parse(row.updated_at) : true;
|
|
222404
222655
|
return {
|
|
222405
222656
|
localSourceId: entry.localSourceId,
|
|
@@ -222408,19 +222659,30 @@ async function loadFolderIndexSummary(input) {
|
|
|
222408
222659
|
mimeType: entry.mimeType,
|
|
222409
222660
|
modifiedAt: entry.modifiedAt,
|
|
222410
222661
|
sizeBytes: entry.sizeBytes,
|
|
222411
|
-
status: row ? retrievalReady ? "indexed" : "pending" : "pending",
|
|
222662
|
+
status: row ? sourceChunkCount2 > 0 || retrievalReady ? "indexed" : "pending" : "pending",
|
|
222412
222663
|
supabaseSourceId: row?.id ?? null,
|
|
222413
222664
|
retrievalReady,
|
|
222665
|
+
vectorReady,
|
|
222666
|
+
sourceChunkCount: sourceChunkCount2,
|
|
222667
|
+
retrievalChunkCount: retrievalChunkCount2,
|
|
222668
|
+
embeddedChunkCount: embeddedChunkCount2,
|
|
222414
222669
|
contentHash: row?.content_hash ?? null,
|
|
222415
222670
|
message: stale && row ? "Changed since last indexing pass." : void 0
|
|
222416
222671
|
};
|
|
222417
222672
|
});
|
|
222418
222673
|
const staleFiles = files.filter((file) => file.message?.includes("Changed since last indexing pass")).length;
|
|
222419
222674
|
const retrievalReadySourceIds = files.filter((file) => file.retrievalReady && file.supabaseSourceId).map((file) => file.supabaseSourceId);
|
|
222675
|
+
const vectorReadySourceIds = files.filter((file) => file.vectorReady && file.supabaseSourceId).map((file) => file.supabaseSourceId);
|
|
222420
222676
|
const indexedFiles = files.filter((file) => file.status === "indexed").length;
|
|
222677
|
+
const sourceChunkCount = sumFiles(files, "sourceChunkCount");
|
|
222678
|
+
const retrievalChunkCount = sumFiles(files, "retrievalChunkCount");
|
|
222679
|
+
const embeddedChunkCount = sumFiles(files, "embeddedChunkCount");
|
|
222421
222680
|
const lastIndexedAt = rows[0]?.updated_at ?? null;
|
|
222422
222681
|
const diagnostics = [
|
|
222682
|
+
rows.length > 0 ? `${rows.length} registered source(s).` : null,
|
|
222683
|
+
sourceChunkCount > 0 ? `${sourceChunkCount} text/source chunk(s) available for keyword search.` : null,
|
|
222423
222684
|
retrievalReadySourceIds.length > 0 ? `${retrievalReadySourceIds.length} file(s) have fresh retrieval chunks.` : "No retrieval-ready files yet for this folder.",
|
|
222685
|
+
vectorReadySourceIds.length > 0 ? `${vectorReadySourceIds.length} file(s) have embedded retrieval chunks for vector search.` : sourceChunkCount > 0 ? "Keyword/source search is available; semantic/vector retrieval is pending." : null,
|
|
222424
222686
|
staleFiles > 0 ? `${staleFiles} file(s) changed since the last index update.` : null
|
|
222425
222687
|
].filter(Boolean);
|
|
222426
222688
|
return {
|
|
@@ -222430,8 +222692,14 @@ async function loadFolderIndexSummary(input) {
|
|
|
222430
222692
|
indexedFiles,
|
|
222431
222693
|
skippedFiles: 0,
|
|
222432
222694
|
failedFiles: 0,
|
|
222695
|
+
registeredSources: rows.length,
|
|
222696
|
+
sourceChunkCount,
|
|
222697
|
+
sourceChunkFiles: files.filter((file) => file.sourceChunkCount > 0).length,
|
|
222698
|
+
retrievalChunkCount,
|
|
222433
222699
|
staleFiles,
|
|
222434
222700
|
retrievalReadyFiles: retrievalReadySourceIds.length,
|
|
222701
|
+
embeddedChunkCount,
|
|
222702
|
+
vectorReadyFiles: vectorReadySourceIds.length,
|
|
222435
222703
|
filesNeedingOcr: files.filter((file) => needsOcrHint(file)).length,
|
|
222436
222704
|
lastIndexedAt,
|
|
222437
222705
|
status: files.length === 0 ? "idle" : staleFiles > 0 || indexedFiles < files.length ? "partial" : "ready",
|
|
@@ -222439,6 +222707,7 @@ async function loadFolderIndexSummary(input) {
|
|
|
222439
222707
|
localOnlyEmbeddingsEnabled: false,
|
|
222440
222708
|
remoteIndexArtifacts: [...DEFAULT_REMOTE_INDEX_ARTIFACTS],
|
|
222441
222709
|
retrievalReadySourceIds,
|
|
222710
|
+
vectorReadySourceIds,
|
|
222442
222711
|
diagnostics,
|
|
222443
222712
|
manifestPath: DEFAULT_MANIFEST_PATH,
|
|
222444
222713
|
files
|
|
@@ -222458,6 +222727,9 @@ function needsOcrHint(file) {
|
|
|
222458
222727
|
const message = file.message?.toLowerCase() ?? "";
|
|
222459
222728
|
return message.includes("ocr") || message.includes("scanned") || message.includes("no readable text");
|
|
222460
222729
|
}
|
|
222730
|
+
function sumFiles(files, field) {
|
|
222731
|
+
return files.reduce((total, file) => total + file[field], 0);
|
|
222732
|
+
}
|
|
222461
222733
|
var DEFAULT_MANIFEST_PATH, AUTO_INDEX_EXCLUDED_PATH_PREFIXES, AUTO_INDEX_EXCLUDED_EXACT_PATHS, DEFAULT_STORAGE_MODE, DEFAULT_REMOTE_INDEX_ARTIFACTS;
|
|
222462
222734
|
var init_folderIndexing = __esm({
|
|
222463
222735
|
"features/perchTerminal/runtime/folderIndexing.ts"() {
|
|
@@ -224713,6 +224985,54 @@ var init_objectiveClassifier = __esm({
|
|
|
224713
224985
|
});
|
|
224714
224986
|
|
|
224715
224987
|
// features/perchTerminal/runtime/turn/runOperatorTurn.ts
|
|
224988
|
+
function buildTurnMemoryRetriever(input) {
|
|
224989
|
+
if (input.supabase) {
|
|
224990
|
+
return async ({ userId, workspaceId, limitPerScope, query }) => createPermanentMemoryPersistence(
|
|
224991
|
+
input.supabase
|
|
224992
|
+
).retrievePermanentMemoriesForTurn({
|
|
224993
|
+
userId,
|
|
224994
|
+
workspaceId,
|
|
224995
|
+
limitPerScope,
|
|
224996
|
+
query
|
|
224997
|
+
});
|
|
224998
|
+
}
|
|
224999
|
+
const appUrl = input.cliServerAppUrl?.trim();
|
|
225000
|
+
const accessToken = input.cliServerAccessToken?.trim();
|
|
225001
|
+
if (!appUrl || !accessToken) return null;
|
|
225002
|
+
return createCliServerMemoryRetriever({
|
|
225003
|
+
appUrl,
|
|
225004
|
+
accessToken,
|
|
225005
|
+
threadId: input.threadId
|
|
225006
|
+
});
|
|
225007
|
+
}
|
|
225008
|
+
function createCliServerMemoryRetriever(input) {
|
|
225009
|
+
return async ({ limitPerScope, query }) => {
|
|
225010
|
+
const response = await fetch(`${input.appUrl.replace(/\/+$/, "")}/api/perch-terminal/cli-context`, {
|
|
225011
|
+
method: "POST",
|
|
225012
|
+
headers: {
|
|
225013
|
+
Accept: "application/json",
|
|
225014
|
+
"Content-Type": "application/json",
|
|
225015
|
+
Authorization: `Bearer ${input.accessToken}`
|
|
225016
|
+
},
|
|
225017
|
+
body: JSON.stringify({
|
|
225018
|
+
query,
|
|
225019
|
+
threadId: input.threadId,
|
|
225020
|
+
limitPerScope
|
|
225021
|
+
})
|
|
225022
|
+
});
|
|
225023
|
+
const payload = await response.json().catch(() => ({}));
|
|
225024
|
+
if (!response.ok || payload.ok !== true) {
|
|
225025
|
+
throw new Error("Durable memory is unavailable right now.");
|
|
225026
|
+
}
|
|
225027
|
+
const memories = Array.isArray(payload.permanentMemories) ? payload.permanentMemories.filter(isPermanentMemoryLike) : [];
|
|
225028
|
+
return memories;
|
|
225029
|
+
};
|
|
225030
|
+
}
|
|
225031
|
+
function isPermanentMemoryLike(value) {
|
|
225032
|
+
if (!value || typeof value !== "object") return false;
|
|
225033
|
+
const memory = value;
|
|
225034
|
+
return typeof memory.id === "string" && typeof memory.user_id === "string" && (memory.workspace_id === null || typeof memory.workspace_id === "string") && typeof memory.scope === "string" && typeof memory.type === "string" && typeof memory.title === "string" && typeof memory.body === "string" && typeof memory.source_kind === "string" && typeof memory.confidence === "number" && typeof memory.created_at === "string" && typeof memory.updated_at === "string" && Array.isArray(memory.tags) && Boolean(memory.metadata && typeof memory.metadata === "object");
|
|
225035
|
+
}
|
|
224716
225036
|
async function runOperatorTurn(input, deps) {
|
|
224717
225037
|
const runId = input.clientRunId?.trim() || makeRunId();
|
|
224718
225038
|
const startMs = Date.now();
|
|
@@ -224778,21 +225098,15 @@ async function runOperatorTurn(input, deps) {
|
|
|
224778
225098
|
});
|
|
224779
225099
|
}
|
|
224780
225100
|
try {
|
|
225101
|
+
const memoryRetriever = buildTurnMemoryRetriever(input);
|
|
224781
225102
|
const memoryContext = await buildMemoryContext({
|
|
224782
225103
|
query: input.trimmedInput,
|
|
224783
|
-
userId: input.userId,
|
|
225104
|
+
userId: input.userId ?? (memoryRetriever && input.cliServerAccessToken?.trim() ? "cli_authenticated_user" : null),
|
|
224784
225105
|
workspaceId: input.workspaceId,
|
|
224785
225106
|
threadId: input.threadId,
|
|
224786
225107
|
permanentMemories: input.permanentMemories,
|
|
224787
225108
|
userMemories: input.userMemories,
|
|
224788
|
-
retriever:
|
|
224789
|
-
input.supabase
|
|
224790
|
-
).retrievePermanentMemoriesForTurn({
|
|
224791
|
-
userId,
|
|
224792
|
-
workspaceId,
|
|
224793
|
-
limitPerScope,
|
|
224794
|
-
query
|
|
224795
|
-
}) : null
|
|
225109
|
+
retriever: memoryRetriever
|
|
224796
225110
|
});
|
|
224797
225111
|
const earlySession = input.threadId ? await loadThreadSession(input.threadId, { supabase: input.supabase ?? null }) : null;
|
|
224798
225112
|
const persona = getPersona(input.personaId ?? earlySession?.personaId);
|
|
@@ -225080,8 +225394,15 @@ ${planStateLines}`
|
|
|
225080
225394
|
if (perchMdRow?.status === "not_found") {
|
|
225081
225395
|
extendedWarnings.push("No PERCH.md \u2014 add an operator playbook to .perch/PERCH.md to customise behaviour.");
|
|
225082
225396
|
}
|
|
225083
|
-
|
|
225084
|
-
|
|
225397
|
+
const hostedMemoryAvailable = Boolean(
|
|
225398
|
+
enrichedInput.cliServerAppUrl?.trim() && enrichedInput.cliServerAccessToken?.trim()
|
|
225399
|
+
);
|
|
225400
|
+
const memoryDiagnostics = enrichedInput.memoryContext?.diagnostics ?? null;
|
|
225401
|
+
if (memoryDiagnostics?.warnings.length) {
|
|
225402
|
+
extendedWarnings.push(...memoryDiagnostics.warnings);
|
|
225403
|
+
}
|
|
225404
|
+
if (!enrichedInput.supabase && !hostedMemoryAvailable && memoryDiagnostics?.retrieval.attempted !== true) {
|
|
225405
|
+
extendedWarnings.push("Memory retrieval skipped \u2014 sign in to use durable memory.");
|
|
225085
225406
|
}
|
|
225086
225407
|
const contextJob = resolveJobContextTokens({
|
|
225087
225408
|
threadContextTokens: threadContextAccounting.threadContextTokens,
|
|
@@ -226493,6 +226814,21 @@ function installCliNodeLocalBridge(input) {
|
|
|
226493
226814
|
}
|
|
226494
226815
|
};
|
|
226495
226816
|
}
|
|
226817
|
+
function readCliProjectMemoryState(workspaceRoot) {
|
|
226818
|
+
const root2 = path11.resolve(expandHome4(workspaceRoot));
|
|
226819
|
+
if (!isCliProjectMemoryAvailable(root2)) {
|
|
226820
|
+
return {
|
|
226821
|
+
available: false,
|
|
226822
|
+
meta: null,
|
|
226823
|
+
reason: "Local project memory is unavailable in this folder."
|
|
226824
|
+
};
|
|
226825
|
+
}
|
|
226826
|
+
return {
|
|
226827
|
+
available: true,
|
|
226828
|
+
meta: enrichCliProjectMeta(root2, loadCliStoredMeta(root2)),
|
|
226829
|
+
reason: "Local project memory is available."
|
|
226830
|
+
};
|
|
226831
|
+
}
|
|
226496
226832
|
function createCliNodeLocalBridge(input) {
|
|
226497
226833
|
const workspaceRoot = path11.resolve(input.workspaceRoot);
|
|
226498
226834
|
const now13 = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -226567,7 +226903,8 @@ function createCliNodeLocalBridge(input) {
|
|
|
226567
226903
|
workspaceRoot: resolveRequestRoot(workspaceRoot, request.workspaceRoot),
|
|
226568
226904
|
command: request.command,
|
|
226569
226905
|
cwd: request.cwd,
|
|
226570
|
-
timeoutMs: request.timeoutMs
|
|
226906
|
+
timeoutMs: request.timeoutMs,
|
|
226907
|
+
signal: request.signal
|
|
226571
226908
|
}),
|
|
226572
226909
|
runBashTerminalCommand: async (request) => {
|
|
226573
226910
|
const startedAt = Date.now();
|
|
@@ -226575,7 +226912,8 @@ function createCliNodeLocalBridge(input) {
|
|
|
226575
226912
|
const result2 = await runShellCommand({
|
|
226576
226913
|
workspaceRoot,
|
|
226577
226914
|
command: request.args?.length ? shellQuote([request.command, ...request.args]) : request.command,
|
|
226578
|
-
cwd: request.cwd
|
|
226915
|
+
cwd: request.cwd,
|
|
226916
|
+
signal: request.signal
|
|
226579
226917
|
});
|
|
226580
226918
|
return {
|
|
226581
226919
|
ok: result2.ok,
|
|
@@ -226773,10 +227111,20 @@ function createCliNodeLocalBridge(input) {
|
|
|
226773
227111
|
encoding: "base64"
|
|
226774
227112
|
};
|
|
226775
227113
|
},
|
|
226776
|
-
getProjectRules: async () =>
|
|
226777
|
-
|
|
226778
|
-
|
|
226779
|
-
|
|
227114
|
+
getProjectRules: async () => {
|
|
227115
|
+
const state = readCliProjectMemoryState(workspaceRoot);
|
|
227116
|
+
return state.meta ? [{
|
|
227117
|
+
rootId: CLI_ROOT_ID,
|
|
227118
|
+
perchMd: state.meta.perchMd?.content ?? null,
|
|
227119
|
+
rules: state.meta.rules.map((rule) => ({
|
|
227120
|
+
fileName: rule.fileName,
|
|
227121
|
+
content: rule.content
|
|
227122
|
+
}))
|
|
227123
|
+
}] : [];
|
|
227124
|
+
},
|
|
227125
|
+
readProjectMemory: async () => readCliProjectMemoryBridge(workspaceRoot),
|
|
227126
|
+
writeProjectMemory: async (request) => writeCliProjectMemory(workspaceRoot, request.meta),
|
|
227127
|
+
writeMemoryFile: async (request) => writeCliMemoryFile(workspaceRoot, request),
|
|
226780
227128
|
writeRule: async () => ({ ok: false, error: "Project rule writes are not available in this CLI package yet." }),
|
|
226781
227129
|
writePerchMd: async () => ({ ok: false, error: "PERCH.md writes are not available in this CLI package yet." }),
|
|
226782
227130
|
readGlobalPerchMd: async () => ({ ok: false, error: "Global PERCH.md is not available in this CLI package yet." }),
|
|
@@ -227055,6 +227403,292 @@ function guessCliFileType(filePath) {
|
|
|
227055
227403
|
if ([".doc", ".docx", ".txt", ".md", ".rtf"].includes(ext)) return "document";
|
|
227056
227404
|
return "unknown";
|
|
227057
227405
|
}
|
|
227406
|
+
async function readCliProjectMemoryBridge(workspaceRoot) {
|
|
227407
|
+
const state = readCliProjectMemoryState(workspaceRoot);
|
|
227408
|
+
if (!state.available || !state.meta) {
|
|
227409
|
+
return {
|
|
227410
|
+
ok: false,
|
|
227411
|
+
error: "Local project memory is unavailable in this folder. Run from a project folder with .perch memory or sign in for durable memory."
|
|
227412
|
+
};
|
|
227413
|
+
}
|
|
227414
|
+
return { ok: true, meta: state.meta };
|
|
227415
|
+
}
|
|
227416
|
+
async function writeCliProjectMemory(workspaceRoot, patch) {
|
|
227417
|
+
const root2 = path11.resolve(expandHome4(workspaceRoot));
|
|
227418
|
+
if (!isCliProjectMemoryAvailable(root2)) {
|
|
227419
|
+
return {
|
|
227420
|
+
ok: false,
|
|
227421
|
+
error: "Local project memory is unavailable in this folder. Run from a project folder with .perch memory or sign in for durable memory."
|
|
227422
|
+
};
|
|
227423
|
+
}
|
|
227424
|
+
const current = loadCliStoredMeta(root2);
|
|
227425
|
+
const next = {
|
|
227426
|
+
...current,
|
|
227427
|
+
projectName: patch.projectName !== void 0 ? patch.projectName : current.projectName,
|
|
227428
|
+
notes: patch.notes !== void 0 ? patch.notes : current.notes,
|
|
227429
|
+
preferredRoot: patch.preferredRoot !== void 0 ? patch.preferredRoot : current.preferredRoot,
|
|
227430
|
+
lastOpenedThreadId: patch.lastOpenedThreadId !== void 0 ? patch.lastOpenedThreadId : current.lastOpenedThreadId,
|
|
227431
|
+
memorySummary: patch.memorySummary !== void 0 ? patch.memorySummary : current.memorySummary
|
|
227432
|
+
};
|
|
227433
|
+
await fsp.mkdir(path11.join(root2, PERCH_DIR), { recursive: true });
|
|
227434
|
+
await atomicWriteCliUtf8(getCliProjectFilePath(root2), JSON.stringify(next, null, 2));
|
|
227435
|
+
return { ok: true, meta: enrichCliProjectMeta(root2, next) };
|
|
227436
|
+
}
|
|
227437
|
+
async function writeCliMemoryFile(workspaceRoot, request) {
|
|
227438
|
+
const root2 = path11.resolve(expandHome4(workspaceRoot));
|
|
227439
|
+
if (!isCliProjectMemoryAvailable(root2)) {
|
|
227440
|
+
return {
|
|
227441
|
+
ok: false,
|
|
227442
|
+
error: "Local project memory is unavailable in this folder. Run from a project folder with .perch memory or sign in for durable memory."
|
|
227443
|
+
};
|
|
227444
|
+
}
|
|
227445
|
+
if (request.rootId && request.rootId !== CLI_ROOT_ID) {
|
|
227446
|
+
return { ok: false, error: "Local project memory is unavailable for that workspace root." };
|
|
227447
|
+
}
|
|
227448
|
+
const fileName = normalizeCliMemoryFileName(request.fileName);
|
|
227449
|
+
if (!fileName) return { ok: false, error: "Invalid memory file name." };
|
|
227450
|
+
const relativePath = path11.join(PERCH_DIR, MEMORY_DIR, fileName);
|
|
227451
|
+
const fullPath = path11.join(root2, relativePath);
|
|
227452
|
+
const existing = await fsp.readFile(fullPath, "utf8").catch(() => "");
|
|
227453
|
+
let nextContent;
|
|
227454
|
+
try {
|
|
227455
|
+
nextContent = applyCliMemoryWrite(existing, request);
|
|
227456
|
+
} catch (error) {
|
|
227457
|
+
return {
|
|
227458
|
+
ok: false,
|
|
227459
|
+
error: error instanceof Error ? error.message : "Failed to merge memory content."
|
|
227460
|
+
};
|
|
227461
|
+
}
|
|
227462
|
+
const capError = assertCliMemoryWithinCap(nextContent);
|
|
227463
|
+
if (capError) return { ok: false, error: capError };
|
|
227464
|
+
await fsp.mkdir(path11.dirname(fullPath), { recursive: true });
|
|
227465
|
+
await atomicWriteCliUtf8(fullPath, nextContent);
|
|
227466
|
+
const updatedFile = readCliTextMemoryFile(root2, relativePath, MAX_MEMORY_BYTES);
|
|
227467
|
+
return {
|
|
227468
|
+
ok: true,
|
|
227469
|
+
meta: enrichCliProjectMeta(root2, loadCliStoredMeta(root2)),
|
|
227470
|
+
updatedFile
|
|
227471
|
+
};
|
|
227472
|
+
}
|
|
227473
|
+
function defaultCliProjectMeta() {
|
|
227474
|
+
return {
|
|
227475
|
+
projectName: null,
|
|
227476
|
+
notes: null,
|
|
227477
|
+
preferredRoot: null,
|
|
227478
|
+
lastOpenedThreadId: null,
|
|
227479
|
+
memorySummary: null,
|
|
227480
|
+
perchMd: null,
|
|
227481
|
+
rules: [],
|
|
227482
|
+
memoryFiles: [],
|
|
227483
|
+
missingMemoryFiles: [],
|
|
227484
|
+
projectMemoryEmpty: true,
|
|
227485
|
+
evidenceOnly: true
|
|
227486
|
+
};
|
|
227487
|
+
}
|
|
227488
|
+
function isCliProjectMemoryAvailable(root2) {
|
|
227489
|
+
try {
|
|
227490
|
+
return fs10.statSync(path11.join(root2, PERCH_DIR)).isDirectory();
|
|
227491
|
+
} catch {
|
|
227492
|
+
return false;
|
|
227493
|
+
}
|
|
227494
|
+
}
|
|
227495
|
+
function getCliProjectFilePath(root2) {
|
|
227496
|
+
return path11.join(root2, PERCH_DIR, PROJECT_FILE);
|
|
227497
|
+
}
|
|
227498
|
+
function loadCliStoredMeta(root2) {
|
|
227499
|
+
try {
|
|
227500
|
+
const raw = fs10.readFileSync(getCliProjectFilePath(root2), "utf8");
|
|
227501
|
+
return { ...defaultCliProjectMeta(), ...JSON.parse(raw) };
|
|
227502
|
+
} catch {
|
|
227503
|
+
return defaultCliProjectMeta();
|
|
227504
|
+
}
|
|
227505
|
+
}
|
|
227506
|
+
function enrichCliProjectMeta(root2, base) {
|
|
227507
|
+
const perchMd = readCliFirstExisting(root2, [
|
|
227508
|
+
path11.join(PERCH_DIR, PERCH_INDEX_FILE),
|
|
227509
|
+
PERCH_INDEX_FILE
|
|
227510
|
+
], MAX_TEXT_BYTES);
|
|
227511
|
+
const rules = readCliRules(root2);
|
|
227512
|
+
const { memoryFiles, missingMemoryFiles } = readCliMemoryFiles(root2);
|
|
227513
|
+
const projectMemoryEmpty = !perchMd?.found && rules.length === 0 && memoryFiles.filter((file) => file.found).length === 0;
|
|
227514
|
+
return {
|
|
227515
|
+
...base,
|
|
227516
|
+
perchMd,
|
|
227517
|
+
rules,
|
|
227518
|
+
memoryFiles,
|
|
227519
|
+
missingMemoryFiles,
|
|
227520
|
+
projectMemoryEmpty,
|
|
227521
|
+
evidenceOnly: projectMemoryEmpty
|
|
227522
|
+
};
|
|
227523
|
+
}
|
|
227524
|
+
function normalizeCliMemoryFileName(fileName) {
|
|
227525
|
+
if (typeof fileName !== "string") return null;
|
|
227526
|
+
const trimmed = fileName.trim();
|
|
227527
|
+
if (trimmed.includes("..") || trimmed.includes("/") || trimmed.includes("\\")) return null;
|
|
227528
|
+
return EXPECTED_MEMORY_FILES.includes(trimmed) ? trimmed : null;
|
|
227529
|
+
}
|
|
227530
|
+
function applyCliMemoryWrite(existing, request) {
|
|
227531
|
+
const incoming = request.content ?? "";
|
|
227532
|
+
if (request.mode === "replace") return incoming;
|
|
227533
|
+
if (request.mode === "append") {
|
|
227534
|
+
if (request.sectionHeading?.trim()) {
|
|
227535
|
+
return upsertCliMarkdownSection(existing, request.sectionHeading, incoming, "append");
|
|
227536
|
+
}
|
|
227537
|
+
if (!existing.trim()) return incoming;
|
|
227538
|
+
return `${existing}${existing.endsWith("\n") ? "" : "\n"}${incoming}`;
|
|
227539
|
+
}
|
|
227540
|
+
if (!request.sectionHeading?.trim()) {
|
|
227541
|
+
throw new Error('mode "merge" requires sectionHeading.');
|
|
227542
|
+
}
|
|
227543
|
+
return upsertCliMarkdownSection(existing, request.sectionHeading, incoming, "replace");
|
|
227544
|
+
}
|
|
227545
|
+
function upsertCliMarkdownSection(existing, sectionHeading, body, mode) {
|
|
227546
|
+
const heading = sectionHeading.trim().startsWith("#") ? sectionHeading.trim() : `## ${sectionHeading.trim()}`;
|
|
227547
|
+
const targetKey = normalizeCliHeadingKey(heading);
|
|
227548
|
+
const sections = parseCliMarkdownSections(existing);
|
|
227549
|
+
const index = sections.findIndex((section) => normalizeCliHeadingKey(section.heading) === targetKey);
|
|
227550
|
+
if (index >= 0) {
|
|
227551
|
+
const prior = sections[index];
|
|
227552
|
+
sections[index] = {
|
|
227553
|
+
heading: prior.heading || heading,
|
|
227554
|
+
body: mode === "append" && prior.body.trim() ? `${prior.body.trim()}
|
|
227555
|
+
${body.trim()}` : body.trim()
|
|
227556
|
+
};
|
|
227557
|
+
} else {
|
|
227558
|
+
sections.push({ heading, body: body.trim() });
|
|
227559
|
+
}
|
|
227560
|
+
return sections.map((section) => {
|
|
227561
|
+
if (!section.heading) return section.body.trim();
|
|
227562
|
+
return `${section.heading}
|
|
227563
|
+
|
|
227564
|
+
${section.body.trim()}`.trim();
|
|
227565
|
+
}).filter(Boolean).join("\n\n").concat("\n");
|
|
227566
|
+
}
|
|
227567
|
+
function parseCliMarkdownSections(content) {
|
|
227568
|
+
const sections = [];
|
|
227569
|
+
let current = null;
|
|
227570
|
+
for (const line of content.split(/\r?\n/)) {
|
|
227571
|
+
if (/^#{1,6}\s+/.test(line)) {
|
|
227572
|
+
if (current) sections.push(current);
|
|
227573
|
+
current = { heading: line.trimEnd(), body: "" };
|
|
227574
|
+
continue;
|
|
227575
|
+
}
|
|
227576
|
+
if (!current) {
|
|
227577
|
+
if (!line.trim()) continue;
|
|
227578
|
+
current = { heading: "", body: line };
|
|
227579
|
+
continue;
|
|
227580
|
+
}
|
|
227581
|
+
current.body = current.body ? `${current.body}
|
|
227582
|
+
${line}` : line;
|
|
227583
|
+
}
|
|
227584
|
+
if (current) sections.push(current);
|
|
227585
|
+
return sections;
|
|
227586
|
+
}
|
|
227587
|
+
function normalizeCliHeadingKey(heading) {
|
|
227588
|
+
return heading.replace(/^#+\s*/, "").trim().toLowerCase();
|
|
227589
|
+
}
|
|
227590
|
+
function assertCliMemoryWithinCap(content) {
|
|
227591
|
+
return Buffer.byteLength(content, "utf8") > MAX_MEMORY_BYTES ? "Memory file exceeds 128KB cap." : null;
|
|
227592
|
+
}
|
|
227593
|
+
async function atomicWriteCliUtf8(filePath, content) {
|
|
227594
|
+
const tmpPath = `${filePath}.tmp`;
|
|
227595
|
+
await fsp.writeFile(tmpPath, content, "utf8");
|
|
227596
|
+
try {
|
|
227597
|
+
await fsp.rename(tmpPath, filePath);
|
|
227598
|
+
} catch (error) {
|
|
227599
|
+
await fsp.rm(tmpPath, { force: true }).catch(() => void 0);
|
|
227600
|
+
throw error;
|
|
227601
|
+
}
|
|
227602
|
+
}
|
|
227603
|
+
function readCliFirstExisting(root2, relativePaths, maxBytes) {
|
|
227604
|
+
for (const relativePath of relativePaths) {
|
|
227605
|
+
const file = readCliTextMemoryFile(root2, relativePath, maxBytes);
|
|
227606
|
+
if (file.found) {
|
|
227607
|
+
return {
|
|
227608
|
+
relativePath: file.relativePath,
|
|
227609
|
+
content: file.content,
|
|
227610
|
+
found: true,
|
|
227611
|
+
sizeBytes: file.sizeBytes,
|
|
227612
|
+
modifiedAt: file.modifiedAt
|
|
227613
|
+
};
|
|
227614
|
+
}
|
|
227615
|
+
}
|
|
227616
|
+
return null;
|
|
227617
|
+
}
|
|
227618
|
+
function readCliRules(root2) {
|
|
227619
|
+
const dirPath = path11.join(root2, PERCH_DIR, RULES_DIR);
|
|
227620
|
+
try {
|
|
227621
|
+
return fs10.readdirSync(dirPath).filter((name) => [".md", ".txt"].includes(path11.extname(name).toLowerCase())).slice(0, 40).map((name) => readCliTextMemoryFile(root2, path11.join(PERCH_DIR, RULES_DIR, name), MAX_TEXT_BYTES)).filter((file) => file.found).map((file) => ({
|
|
227622
|
+
fileName: path11.basename(file.relativePath),
|
|
227623
|
+
relativePath: file.relativePath,
|
|
227624
|
+
content: file.content,
|
|
227625
|
+
sizeBytes: file.sizeBytes,
|
|
227626
|
+
modifiedAt: file.modifiedAt
|
|
227627
|
+
}));
|
|
227628
|
+
} catch {
|
|
227629
|
+
return [];
|
|
227630
|
+
}
|
|
227631
|
+
}
|
|
227632
|
+
function readCliMemoryFiles(root2) {
|
|
227633
|
+
const memoryDir = path11.join(root2, PERCH_DIR, MEMORY_DIR);
|
|
227634
|
+
const discovered = /* @__PURE__ */ new Set();
|
|
227635
|
+
const memoryFiles = [];
|
|
227636
|
+
try {
|
|
227637
|
+
for (const name of fs10.readdirSync(memoryDir).slice(0, MAX_MEMORY_FILES)) {
|
|
227638
|
+
const ext = path11.extname(name).toLowerCase();
|
|
227639
|
+
if (ext !== ".md" && ext !== ".txt") continue;
|
|
227640
|
+
discovered.add(name);
|
|
227641
|
+
memoryFiles.push(readCliTextMemoryFile(root2, path11.join(PERCH_DIR, MEMORY_DIR, name), MAX_MEMORY_BYTES));
|
|
227642
|
+
}
|
|
227643
|
+
} catch {
|
|
227644
|
+
}
|
|
227645
|
+
return {
|
|
227646
|
+
memoryFiles,
|
|
227647
|
+
missingMemoryFiles: EXPECTED_MEMORY_FILES.filter((name) => !discovered.has(name)).map((name) => ({
|
|
227648
|
+
fileName: name,
|
|
227649
|
+
relativePath: path11.join(PERCH_DIR, MEMORY_DIR, name),
|
|
227650
|
+
reason: "expected project memory file was not found"
|
|
227651
|
+
}))
|
|
227652
|
+
};
|
|
227653
|
+
}
|
|
227654
|
+
function readCliTextMemoryFile(root2, relativePath, maxBytes) {
|
|
227655
|
+
const fullPath = path11.join(root2, relativePath);
|
|
227656
|
+
const fileName = path11.basename(relativePath);
|
|
227657
|
+
try {
|
|
227658
|
+
const stat2 = fs10.statSync(fullPath);
|
|
227659
|
+
if (!stat2.isFile()) {
|
|
227660
|
+
return { fileName, relativePath, content: "", found: false, error: "not a regular file" };
|
|
227661
|
+
}
|
|
227662
|
+
const content = fs10.readFileSync(fullPath, "utf8");
|
|
227663
|
+
if (stat2.size > maxBytes) {
|
|
227664
|
+
return {
|
|
227665
|
+
fileName,
|
|
227666
|
+
relativePath,
|
|
227667
|
+
content: `${content.slice(0, maxBytes)}
|
|
227668
|
+
[truncated: file exceeds ${maxBytes} bytes]`,
|
|
227669
|
+
found: true,
|
|
227670
|
+
sizeBytes: stat2.size,
|
|
227671
|
+
modifiedAt: stat2.mtime.toISOString()
|
|
227672
|
+
};
|
|
227673
|
+
}
|
|
227674
|
+
return {
|
|
227675
|
+
fileName,
|
|
227676
|
+
relativePath,
|
|
227677
|
+
content,
|
|
227678
|
+
found: true,
|
|
227679
|
+
sizeBytes: stat2.size,
|
|
227680
|
+
modifiedAt: stat2.mtime.toISOString()
|
|
227681
|
+
};
|
|
227682
|
+
} catch (error) {
|
|
227683
|
+
return {
|
|
227684
|
+
fileName,
|
|
227685
|
+
relativePath,
|
|
227686
|
+
content: "",
|
|
227687
|
+
found: false,
|
|
227688
|
+
error: error instanceof Error ? error.message : "read failed"
|
|
227689
|
+
};
|
|
227690
|
+
}
|
|
227691
|
+
}
|
|
227058
227692
|
async function runShellCommand(input) {
|
|
227059
227693
|
const startedAt = Date.now();
|
|
227060
227694
|
const cwd2 = input.cwd ? resolveReadPath(input.workspaceRoot, input.cwd) : input.workspaceRoot;
|
|
@@ -227063,15 +227697,28 @@ async function runShellCommand(input) {
|
|
|
227063
227697
|
const child = spawn2(process.env.SHELL || "/bin/zsh", ["-lc", input.command], {
|
|
227064
227698
|
cwd: cwd2,
|
|
227065
227699
|
env: process.env,
|
|
227066
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
227700
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
227701
|
+
detached: true
|
|
227067
227702
|
});
|
|
227068
227703
|
let stdout = "";
|
|
227069
227704
|
let stderr = "";
|
|
227070
227705
|
let timedOut = false;
|
|
227706
|
+
let aborted = false;
|
|
227707
|
+
let settled = false;
|
|
227708
|
+
const stopChild = (reason) => {
|
|
227709
|
+
if (reason === "abort") aborted = true;
|
|
227710
|
+
if (reason === "timeout") timedOut = true;
|
|
227711
|
+
terminateProcessGroup(child, "SIGTERM");
|
|
227712
|
+
setTimeout(() => {
|
|
227713
|
+
if (!settled) terminateProcessGroup(child, "SIGKILL");
|
|
227714
|
+
}, 1e3).unref?.();
|
|
227715
|
+
};
|
|
227071
227716
|
const timer = setTimeout(() => {
|
|
227072
|
-
|
|
227073
|
-
child.kill("SIGTERM");
|
|
227717
|
+
stopChild("timeout");
|
|
227074
227718
|
}, timeoutMs);
|
|
227719
|
+
const onAbort = () => stopChild("abort");
|
|
227720
|
+
if (input.signal?.aborted) onAbort();
|
|
227721
|
+
input.signal?.addEventListener("abort", onAbort, { once: true });
|
|
227075
227722
|
child.stdout.on("data", (chunk2) => {
|
|
227076
227723
|
stdout += String(chunk2);
|
|
227077
227724
|
});
|
|
@@ -227079,7 +227726,9 @@ async function runShellCommand(input) {
|
|
|
227079
227726
|
stderr += String(chunk2);
|
|
227080
227727
|
});
|
|
227081
227728
|
child.on("error", (error) => {
|
|
227729
|
+
settled = true;
|
|
227082
227730
|
clearTimeout(timer);
|
|
227731
|
+
input.signal?.removeEventListener("abort", onAbort);
|
|
227083
227732
|
resolve5({
|
|
227084
227733
|
ok: false,
|
|
227085
227734
|
stdout,
|
|
@@ -227095,10 +227744,12 @@ async function runShellCommand(input) {
|
|
|
227095
227744
|
});
|
|
227096
227745
|
});
|
|
227097
227746
|
child.on("close", (code, signal) => {
|
|
227747
|
+
settled = true;
|
|
227098
227748
|
clearTimeout(timer);
|
|
227749
|
+
input.signal?.removeEventListener("abort", onAbort);
|
|
227099
227750
|
const capped = capOutput(stdout, stderr);
|
|
227100
227751
|
resolve5({
|
|
227101
|
-
ok: code === 0 && !timedOut,
|
|
227752
|
+
ok: code === 0 && !timedOut && !aborted,
|
|
227102
227753
|
stdout: capped.stdout,
|
|
227103
227754
|
stderr: capped.stderr,
|
|
227104
227755
|
exitCode: code,
|
|
@@ -227109,11 +227760,25 @@ async function runShellCommand(input) {
|
|
|
227109
227760
|
command: input.command,
|
|
227110
227761
|
cwd: cwd2,
|
|
227111
227762
|
executionHost: "electron_desktop",
|
|
227112
|
-
...timedOut ? { errorCode: "timed_out", errorMessage: `Command timed out after ${timeoutMs}ms.` } : {}
|
|
227763
|
+
...timedOut ? { errorCode: "timed_out", errorMessage: `Command timed out after ${timeoutMs}ms.` } : {},
|
|
227764
|
+
...aborted ? { errorCode: "aborted", errorMessage: "Command stopped by user." } : {}
|
|
227113
227765
|
});
|
|
227114
227766
|
});
|
|
227115
227767
|
});
|
|
227116
227768
|
}
|
|
227769
|
+
function terminateProcessGroup(child, signal) {
|
|
227770
|
+
if (child.pid) {
|
|
227771
|
+
try {
|
|
227772
|
+
process.kill(-child.pid, signal);
|
|
227773
|
+
return;
|
|
227774
|
+
} catch {
|
|
227775
|
+
}
|
|
227776
|
+
}
|
|
227777
|
+
try {
|
|
227778
|
+
child.kill(signal);
|
|
227779
|
+
} catch {
|
|
227780
|
+
}
|
|
227781
|
+
}
|
|
227117
227782
|
function resolveReadPath(root2, inputPath) {
|
|
227118
227783
|
const expanded = expandHome4(inputPath || ".");
|
|
227119
227784
|
return path11.resolve(path11.isAbsolute(expanded) ? expanded : path11.join(root2, expanded));
|
|
@@ -227261,7 +227926,7 @@ function capOutput(stdout, stderr) {
|
|
|
227261
227926
|
function shellQuote(parts) {
|
|
227262
227927
|
return parts.map((part) => `'${part.replace(/'/g, "'\\''")}'`).join(" ");
|
|
227263
227928
|
}
|
|
227264
|
-
var CLI_ROOT_ID, DEFAULT_MAX_RESULTS, MAX_READ_BYTES, IGNORED_DIRS;
|
|
227929
|
+
var CLI_ROOT_ID, DEFAULT_MAX_RESULTS, MAX_READ_BYTES, IGNORED_DIRS, PERCH_DIR, PROJECT_FILE, PERCH_INDEX_FILE, MEMORY_DIR, RULES_DIR, MAX_TEXT_BYTES, MAX_MEMORY_BYTES, MAX_MEMORY_FILES, EXPECTED_MEMORY_FILES;
|
|
227265
227930
|
var init_nodeLocalBridge = __esm({
|
|
227266
227931
|
"features/perchTerminal/runtime/cliHost/nodeLocalBridge.ts"() {
|
|
227267
227932
|
"use strict";
|
|
@@ -227272,6 +227937,20 @@ var init_nodeLocalBridge = __esm({
|
|
|
227272
227937
|
DEFAULT_MAX_RESULTS = 200;
|
|
227273
227938
|
MAX_READ_BYTES = 2e6;
|
|
227274
227939
|
IGNORED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", "release"]);
|
|
227940
|
+
PERCH_DIR = ".perch";
|
|
227941
|
+
PROJECT_FILE = "project.json";
|
|
227942
|
+
PERCH_INDEX_FILE = "PERCH.md";
|
|
227943
|
+
MEMORY_DIR = "memory";
|
|
227944
|
+
RULES_DIR = "rules";
|
|
227945
|
+
MAX_TEXT_BYTES = 64e3;
|
|
227946
|
+
MAX_MEMORY_BYTES = 128e3;
|
|
227947
|
+
MAX_MEMORY_FILES = 80;
|
|
227948
|
+
EXPECTED_MEMORY_FILES = [
|
|
227949
|
+
"project.md",
|
|
227950
|
+
"preferences.md",
|
|
227951
|
+
"decisions.md",
|
|
227952
|
+
"permanent.md"
|
|
227953
|
+
];
|
|
227275
227954
|
}
|
|
227276
227955
|
});
|
|
227277
227956
|
|
|
@@ -227353,10 +228032,13 @@ function buildCliTurnInput(input, resolved) {
|
|
|
227353
228032
|
activeRootPath: input.activeRootPath ?? resolved.cwd,
|
|
227354
228033
|
localSources: [],
|
|
227355
228034
|
localSourcesMeta: null,
|
|
228035
|
+
projectMeta: readCliProjectMemoryState(resolved.cwd).meta,
|
|
227356
228036
|
permanentMemories: input.permanentMemories ?? [],
|
|
227357
228037
|
userMemories: input.userMemories ?? [],
|
|
227358
228038
|
supabase: null,
|
|
227359
228039
|
supabaseConfigured: false,
|
|
228040
|
+
cliServerAppUrl: input.cliServerAppUrl ?? input.marketDeskProxyAppUrl ?? input.appUrl ?? null,
|
|
228041
|
+
cliServerAccessToken: input.cliServerAccessToken ?? input.marketDeskProxyAccessToken ?? null,
|
|
227360
228042
|
marketDeskProxyAppUrl: input.marketDeskProxyAppUrl ?? input.appUrl ?? null,
|
|
227361
228043
|
marketDeskProxyAccessToken: input.marketDeskProxyAccessToken ?? null,
|
|
227362
228044
|
permissionMode: normalizePermissionMode(input.permissionMode ?? "default"),
|
|
@@ -227432,6 +228114,7 @@ var init_runCliTurn = __esm({
|
|
|
227432
228114
|
init_personaRegistry();
|
|
227433
228115
|
init_runOperatorTurn();
|
|
227434
228116
|
init_nodeLocalBridge();
|
|
228117
|
+
init_nodeLocalBridge();
|
|
227435
228118
|
}
|
|
227436
228119
|
});
|
|
227437
228120
|
|
|
@@ -227601,12 +228284,12 @@ import os2 from "node:os";
|
|
|
227601
228284
|
import path13 from "node:path";
|
|
227602
228285
|
import { promisify } from "node:util";
|
|
227603
228286
|
async function readStoredCliAuthSession() {
|
|
227604
|
-
const raw = process.platform === "darwin" ? await readKeychainSecret().catch(() => null) : await readFallbackSecret().catch(() => null);
|
|
228287
|
+
const raw = shouldUseFallbackAuthFile() ? await readFallbackSecret().catch(() => null) : process.platform === "darwin" ? await readKeychainSecret().catch(() => null) : await readFallbackSecret().catch(() => null);
|
|
227605
228288
|
return parseStoredCliAuthSession(raw);
|
|
227606
228289
|
}
|
|
227607
228290
|
async function writeStoredCliAuthSession(session) {
|
|
227608
228291
|
const raw = JSON.stringify(session);
|
|
227609
|
-
if (process.platform === "darwin") {
|
|
228292
|
+
if (process.platform === "darwin" && !shouldUseFallbackAuthFile()) {
|
|
227610
228293
|
await execFileAsync("security", [
|
|
227611
228294
|
"add-generic-password",
|
|
227612
228295
|
"-U",
|
|
@@ -227622,7 +228305,7 @@ async function writeStoredCliAuthSession(session) {
|
|
|
227622
228305
|
await writeFallbackSecret(raw);
|
|
227623
228306
|
}
|
|
227624
228307
|
async function clearStoredCliAuthSession() {
|
|
227625
|
-
if (process.platform === "darwin") {
|
|
228308
|
+
if (process.platform === "darwin" && !shouldUseFallbackAuthFile()) {
|
|
227626
228309
|
await execFileAsync("security", [
|
|
227627
228310
|
"delete-generic-password",
|
|
227628
228311
|
"-s",
|
|
@@ -227634,6 +228317,9 @@ async function clearStoredCliAuthSession() {
|
|
|
227634
228317
|
}
|
|
227635
228318
|
await fs11.rm(fallbackSessionPath(), { force: true }).catch(() => void 0);
|
|
227636
228319
|
}
|
|
228320
|
+
function shouldUseFallbackAuthFile() {
|
|
228321
|
+
return Boolean(process.env.PERCH_CLI_AUTH_DIR?.trim());
|
|
228322
|
+
}
|
|
227637
228323
|
function isStoredCliAuthSessionUsable(session, nowSeconds = Math.floor(Date.now() / 1e3)) {
|
|
227638
228324
|
if (!session?.accessToken?.trim()) return false;
|
|
227639
228325
|
if (!session.appUrl?.trim()) return false;
|
|
@@ -283754,6 +284440,7 @@ function parseInteractiveSlashCommand(input) {
|
|
|
283754
284440
|
}
|
|
283755
284441
|
function renderInteractiveStatus(state, connection, session, workspaceId) {
|
|
283756
284442
|
const storedAuth = session === void 0 ? renderCliAuthSummary(connection) : isStoredCliAuthSessionUsable(session) ? `signed in${session.email ? ` as ${session.email}` : ""}` : "not signed in";
|
|
284443
|
+
const signedIn = session === void 0 ? isCliModelConnectionReady(connection) : isStoredCliAuthSessionUsable(session);
|
|
283757
284444
|
const connectionStatus = isCliModelConnectionReady(connection) ? "connected" : "locked \xB7 run /login";
|
|
283758
284445
|
const color = shouldUseCliColor();
|
|
283759
284446
|
const lines = [
|
|
@@ -283761,6 +284448,7 @@ function renderInteractiveStatus(state, connection, session, workspaceId) {
|
|
|
283761
284448
|
["cwd", state.cwd],
|
|
283762
284449
|
["auth", storedAuth],
|
|
283763
284450
|
["connection", connectionStatus],
|
|
284451
|
+
["memory", renderCliMemoryAvailability(state.cwd, workspaceId ?? null, signedIn)],
|
|
283764
284452
|
["tools", renderCliCapabilityCount(state, workspaceId ?? null)],
|
|
283765
284453
|
["permission", state.permissionMode],
|
|
283766
284454
|
["mode", state.chatMode],
|
|
@@ -283772,6 +284460,15 @@ function renderInteractiveStatus(state, connection, session, workspaceId) {
|
|
|
283772
284460
|
];
|
|
283773
284461
|
return lines.map(([key, value]) => `${paint(key.padEnd(11), "muted", color)} ${value}`).join("\n") + "\n";
|
|
283774
284462
|
}
|
|
284463
|
+
function renderCliMemoryAvailability(cwd2, workspaceId, signedIn) {
|
|
284464
|
+
const local = readCliProjectMemoryState(cwd2).available;
|
|
284465
|
+
const parts = [];
|
|
284466
|
+
if (signedIn && workspaceId) parts.push("server memory");
|
|
284467
|
+
if (local) parts.push("local project memory");
|
|
284468
|
+
if (parts.length) return parts.join(" + ");
|
|
284469
|
+
if (signedIn) return "unavailable until a workspace is available";
|
|
284470
|
+
return "unavailable; sign in or run from a .perch project";
|
|
284471
|
+
}
|
|
283775
284472
|
function renderCliCapabilityCount(state, workspaceId) {
|
|
283776
284473
|
const count = getExecutableToolDefinitions({
|
|
283777
284474
|
surface: "cli",
|
|
@@ -283966,7 +284663,21 @@ async function fetchCliHostedContext(connection, input) {
|
|
|
283966
284663
|
return {
|
|
283967
284664
|
userId: typeof context.session?.userId === "string" ? context.session.userId : session.userId ?? null,
|
|
283968
284665
|
workspaceId: typeof context.session?.workspaceId === "string" ? context.session.workspaceId : null,
|
|
283969
|
-
permanentMemories: Array.isArray(context.permanentMemories) ? context.permanentMemories.filter(
|
|
284666
|
+
permanentMemories: Array.isArray(context.permanentMemories) ? context.permanentMemories.filter(isPermanentMemoryLike2) : []
|
|
284667
|
+
};
|
|
284668
|
+
}
|
|
284669
|
+
async function fetchCliHostedContextForSession(session) {
|
|
284670
|
+
if (!isStoredCliAuthSessionUsable(session)) return null;
|
|
284671
|
+
const body = await postCliJson(session.appUrl, session, "/api/perch-terminal/cli-context", {
|
|
284672
|
+
query: "/status",
|
|
284673
|
+
threadId: "cli-status",
|
|
284674
|
+
limitPerScope: 1
|
|
284675
|
+
});
|
|
284676
|
+
if (!body || body.ok !== true) return null;
|
|
284677
|
+
const context = body;
|
|
284678
|
+
return {
|
|
284679
|
+
userId: typeof context.session?.userId === "string" ? context.session.userId : session.userId ?? null,
|
|
284680
|
+
workspaceId: typeof context.session?.workspaceId === "string" ? context.session.workspaceId : null
|
|
283970
284681
|
};
|
|
283971
284682
|
}
|
|
283972
284683
|
async function resolveCliMarketDeskProxy(connection) {
|
|
@@ -283974,6 +284685,8 @@ async function resolveCliMarketDeskProxy(connection) {
|
|
|
283974
284685
|
const session = await readStoredCliAuthSession();
|
|
283975
284686
|
if (!isStoredCliAuthSessionUsable(session)) return {};
|
|
283976
284687
|
return {
|
|
284688
|
+
cliServerAppUrl: session.appUrl || connection.appUrl,
|
|
284689
|
+
cliServerAccessToken: session.accessToken,
|
|
283977
284690
|
marketDeskProxyAppUrl: session.appUrl || connection.appUrl,
|
|
283978
284691
|
marketDeskProxyAccessToken: session.accessToken
|
|
283979
284692
|
};
|
|
@@ -284018,7 +284731,7 @@ async function postCliJson(appUrl, session, pathname, payload) {
|
|
|
284018
284731
|
clearTimeout(timeout);
|
|
284019
284732
|
}
|
|
284020
284733
|
}
|
|
284021
|
-
function
|
|
284734
|
+
function isPermanentMemoryLike2(value) {
|
|
284022
284735
|
if (!value || typeof value !== "object") return false;
|
|
284023
284736
|
const memory = value;
|
|
284024
284737
|
return typeof memory.id === "string" && typeof memory.title === "string" && typeof memory.body === "string";
|
|
@@ -284318,12 +285031,17 @@ async function runAuthCommand(parsed, writer) {
|
|
|
284318
285031
|
if (parsed.action === "status") {
|
|
284319
285032
|
const session = await readStoredCliAuthSession();
|
|
284320
285033
|
if (isStoredCliAuthSessionUsable(session)) {
|
|
284321
|
-
|
|
284322
|
-
|
|
285034
|
+
const hostedContext = await fetchCliHostedContextForSession(session).catch(() => null);
|
|
285035
|
+
writer.stdout([
|
|
285036
|
+
`Perch CLI ${CLI_PACKAGE_VERSION} \xB7 Signed in${session.email ? ` as ${session.email}` : ""} \xB7 ${session.appUrl}`,
|
|
285037
|
+
`Memory: ${renderCliMemoryAvailability(process.cwd(), hostedContext?.workspaceId ?? null, true)}`
|
|
285038
|
+
].join("\n") + "\n");
|
|
284323
285039
|
return 0;
|
|
284324
285040
|
}
|
|
284325
|
-
writer.stdout(
|
|
284326
|
-
`
|
|
285041
|
+
writer.stdout([
|
|
285042
|
+
`Perch CLI ${CLI_PACKAGE_VERSION} \xB7 Not signed in. Run \`perch login\`.`,
|
|
285043
|
+
`Memory: ${renderCliMemoryAvailability(process.cwd(), null, false)}`
|
|
285044
|
+
].join("\n") + "\n");
|
|
284327
285045
|
return 2;
|
|
284328
285046
|
}
|
|
284329
285047
|
const appUrl = resolveCliAppUrl(parsed.appUrl, DEFAULT_CLI_LOGIN_APP_URL) ?? DEFAULT_CLI_LOGIN_APP_URL;
|
|
@@ -284583,6 +285301,7 @@ var init_perch_cli = __esm({
|
|
|
284583
285301
|
init_runRegistry();
|
|
284584
285302
|
init_learningMemory();
|
|
284585
285303
|
init_toolDefinitions();
|
|
285304
|
+
init_nodeLocalBridge();
|
|
284586
285305
|
execFileAsync3 = promisify3(execFile3);
|
|
284587
285306
|
DEFAULT_CLI_LOGIN_APP_URL = "https://app.perchai.app";
|
|
284588
285307
|
CLI_PACKAGE_VERSION = readCliPackageVersion();
|
|
@@ -284616,7 +285335,7 @@ Usage:
|
|
|
284616
285335
|
perch login
|
|
284617
285336
|
perch status
|
|
284618
285337
|
perch logout
|
|
284619
|
-
perch run "<task>" [--json] [--cwd <dir>] [--mode ask|agents|plan] [--persona saffron|quill]
|
|
285338
|
+
perch run "<task>" [--json] [--thread <id>] [--cwd <dir>] [--mode ask|agents|plan] [--persona saffron|quill]
|
|
284620
285339
|
perch ap evidence <folder> [--json] [--out <dir>] [--timestamp <id>]
|
|
284621
285340
|
perch ap packet <folder> [--json] [--out <dir>] [--timestamp <id>]
|
|
284622
285341
|
perch test ap <folder> [--json] [--out <dir>] [--timestamp <id>] [--profile financial-lab-v2] [--expect key=value]
|