perchai-cli 2.4.19 → 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 +778 -110
- package/package.json +1 -1
package/dist/perch.mjs
CHANGED
|
@@ -76200,6 +76200,7 @@ function getToolDisplayName(toolName) {
|
|
|
76200
76200
|
var NON_MODULE_TOOL_OWNERS, TOOL_RISK, TOOL_DISPLAY_NAMES;
|
|
76201
76201
|
var init_catalog = __esm({
|
|
76202
76202
|
"features/perchTerminal/runtime/toolSystem/catalog.ts"() {
|
|
76203
|
+
"use strict";
|
|
76203
76204
|
init_toolNames();
|
|
76204
76205
|
NON_MODULE_TOOL_OWNERS = {
|
|
76205
76206
|
[TOOL_NAMES.listSources]: "lane",
|
|
@@ -80931,12 +80932,14 @@ function buildDesktopContextSection(input) {
|
|
|
80931
80932
|
"grep",
|
|
80932
80933
|
"statPath",
|
|
80933
80934
|
"readLocalFile",
|
|
80935
|
+
"readProjectMemory",
|
|
80934
80936
|
"listLocalSources",
|
|
80935
80937
|
"readLocalSourceFile"
|
|
80936
80938
|
].filter((name) => enabledToolSet2.has(name));
|
|
80937
80939
|
const localWriteTools = [
|
|
80938
80940
|
"writeLocalFile",
|
|
80939
80941
|
"editLocalFile",
|
|
80942
|
+
"saveToMemory",
|
|
80940
80943
|
"bash",
|
|
80941
80944
|
"runBashTerminalCommand",
|
|
80942
80945
|
"generateAPAuditPacket",
|
|
@@ -80948,16 +80951,16 @@ function buildDesktopContextSection(input) {
|
|
|
80948
80951
|
return {
|
|
80949
80952
|
id: "desktop-context",
|
|
80950
80953
|
lane: "desktop",
|
|
80951
|
-
label: "
|
|
80954
|
+
label: "Terminal workspace",
|
|
80952
80955
|
content: [
|
|
80953
|
-
"##
|
|
80954
|
-
"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.",
|
|
80955
80958
|
input.activeRootPath?.trim() ? `Workspace root: ${input.activeRootPath}. Treat relative paths as relative to this root.` : "Workspace root: current terminal directory.",
|
|
80956
|
-
"
|
|
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.",
|
|
80957
80960
|
localReadTools.length > 0 ? `Read tools: ${localReadTools.join(", ")}.` : "Read tools: none exposed for this turn.",
|
|
80958
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."
|
|
80959
80962
|
].join("\n"),
|
|
80960
|
-
reason: "Terminal
|
|
80963
|
+
reason: "Terminal workspace and tool availability for this turn.",
|
|
80961
80964
|
sourcePath: input.activeRootPath,
|
|
80962
80965
|
metadata: {
|
|
80963
80966
|
desktopConnected: false,
|
|
@@ -81016,8 +81019,14 @@ function buildDesktopContextSection(input) {
|
|
|
81016
81019
|
(file) => `- ${file.relativePath} (${file.matchReason})`
|
|
81017
81020
|
) : [];
|
|
81018
81021
|
const hasVisionSupport = input.selectedModelVisionSupport === true;
|
|
81019
|
-
const
|
|
81020
|
-
|
|
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.",
|
|
81021
81030
|
"Use glob for named files like *Screenshot* or exact extensions like *.png.",
|
|
81022
81031
|
"Use statPath or glob before claiming a file does not exist.",
|
|
81023
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."
|
|
@@ -81026,8 +81035,16 @@ function buildDesktopContextSection(input) {
|
|
|
81026
81035
|
"Use statPath or glob before claiming a file does not exist.",
|
|
81027
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."
|
|
81028
81037
|
];
|
|
81029
|
-
const folderIndexLine = input.folderIndexSummary ?
|
|
81030
|
-
|
|
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;
|
|
81031
81048
|
const mcpToolNames = enabledToolSet.has("mcp__") ? [] : Array.from(enabledToolSet).filter((tool) => tool.startsWith("mcp__"));
|
|
81032
81049
|
const mcpServerNames = Array.from(
|
|
81033
81050
|
new Set(mcpToolNames.map((tool) => tool.split("__")[1]).filter(Boolean))
|
|
@@ -81060,7 +81077,7 @@ function buildDesktopContextSection(input) {
|
|
|
81060
81077
|
content: [
|
|
81061
81078
|
"## Desktop environment",
|
|
81062
81079
|
"Local workspace access is connected. Local filesystem tools are available.",
|
|
81063
|
-
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).",
|
|
81064
81081
|
`Visible local sources: ${totalVisibleFiles}`,
|
|
81065
81082
|
input.localSourcesMeta?.refreshedAt ? `Local source snapshot refreshed: ${input.localSourcesMeta.refreshedAt}` : null,
|
|
81066
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,
|
|
@@ -81077,7 +81094,7 @@ function buildDesktopContextSection(input) {
|
|
|
81077
81094
|
availableReadTools.length > 0 ? `Read tools: ${availableReadTools.join(", ")}.` : "Read tools: none exposed for this turn.",
|
|
81078
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."
|
|
81079
81096
|
].filter(Boolean).join("\n"),
|
|
81080
|
-
reason: "Local workspace access, optional folder
|
|
81097
|
+
reason: "Local workspace access, optional workspace folder, and tool availability for this turn.",
|
|
81081
81098
|
sourcePath: input.activeRootPath,
|
|
81082
81099
|
metadata: {
|
|
81083
81100
|
desktopConnected: true,
|
|
@@ -85234,7 +85251,7 @@ function buildProjectMemorySections(meta) {
|
|
|
85234
85251
|
lane: "project_memory",
|
|
85235
85252
|
label: "PERCH.md",
|
|
85236
85253
|
content: ["## PERCH.md", meta.perchMd.content].join("\n"),
|
|
85237
|
-
reason: "Project operator memory loaded from the selected
|
|
85254
|
+
reason: "Project operator memory loaded from the selected workspace folder.",
|
|
85238
85255
|
sourcePath: meta.perchMd.relativePath,
|
|
85239
85256
|
metadata: {
|
|
85240
85257
|
relativePath: meta.perchMd.relativePath,
|
|
@@ -85271,7 +85288,7 @@ function buildProjectMemorySections(meta) {
|
|
|
85271
85288
|
lane: "project_memory",
|
|
85272
85289
|
label: `Rule: ${rule.fileName}`,
|
|
85273
85290
|
content: [`## Project rule: ${rule.fileName}`, rule.content].join("\n"),
|
|
85274
|
-
reason: "Rules-style project instruction loaded from the selected
|
|
85291
|
+
reason: "Rules-style project instruction loaded from the selected workspace folder.",
|
|
85275
85292
|
sourcePath: rule.relativePath,
|
|
85276
85293
|
metadata: {
|
|
85277
85294
|
relativePath: rule.relativePath,
|
|
@@ -85301,7 +85318,7 @@ function buildProjectMemorySections(meta) {
|
|
|
85301
85318
|
content: [`## Project memory: ${memory.fileName}`, memory.content].join(
|
|
85302
85319
|
"\n"
|
|
85303
85320
|
),
|
|
85304
|
-
reason: "Typed project memory loaded from the selected
|
|
85321
|
+
reason: "Typed project memory loaded from the selected workspace folder.",
|
|
85305
85322
|
sourcePath: memory.relativePath,
|
|
85306
85323
|
metadata: {
|
|
85307
85324
|
relativePath: memory.relativePath,
|
|
@@ -85412,7 +85429,7 @@ Tool strengths:
|
|
|
85412
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.
|
|
85413
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.
|
|
85414
85431
|
- Cite sources for every reported figure. If a figure can't be traced to a document, mark it [UNSOURCED] rather than omitting it
|
|
85415
|
-
- 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
|
|
85416
85433
|
- Do not run a tool just because a keyword matches; reason from the user's current request and the existing thread state
|
|
85417
85434
|
- Do not redo an audit, document, or email draft that is already done unless the user asks for a revision
|
|
85418
85435
|
- Summarize tool outputs; don't stream raw JSON to the user
|
|
@@ -92427,7 +92444,7 @@ function buildContextSummary(input, rows) {
|
|
|
92427
92444
|
`mode=${input.chatMode}`,
|
|
92428
92445
|
`messages=${input.recentMessages.length}`,
|
|
92429
92446
|
`source=${input.selectedSourceId ?? "none"}`,
|
|
92430
|
-
`desktop=${input.desktopConnected ? "connected" : input.cliLocalTools === true ? "
|
|
92447
|
+
`desktop=${input.desktopConnected ? "connected" : input.cliLocalTools === true ? "terminal" : "browser"}`,
|
|
92431
92448
|
`sent=${sentCount}`,
|
|
92432
92449
|
`compacted=${compactedCount}`,
|
|
92433
92450
|
`not_found=${notFoundCount}`,
|
|
@@ -92786,9 +92803,15 @@ function createEmbeddingProviderFromEnv(env4 = readEnv()) {
|
|
|
92786
92803
|
};
|
|
92787
92804
|
}
|
|
92788
92805
|
function createBrowserEmbeddingProvider(route = "/api/perch-terminal/embeddings") {
|
|
92806
|
+
let resolvedModel = "server-configured";
|
|
92807
|
+
let resolvedDimensions = DEFAULT_RETRIEVAL_EMBEDDING_DIMENSIONS;
|
|
92789
92808
|
return {
|
|
92790
|
-
model
|
|
92791
|
-
|
|
92809
|
+
get model() {
|
|
92810
|
+
return resolvedModel;
|
|
92811
|
+
},
|
|
92812
|
+
get dimensions() {
|
|
92813
|
+
return resolvedDimensions;
|
|
92814
|
+
},
|
|
92792
92815
|
async embed(text) {
|
|
92793
92816
|
return (await this.embedBatch([text]))[0];
|
|
92794
92817
|
},
|
|
@@ -92811,6 +92834,12 @@ function createBrowserEmbeddingProvider(route = "/api/perch-terminal/embeddings"
|
|
|
92811
92834
|
if (!Array.isArray(payload.embeddings)) {
|
|
92812
92835
|
throw new EmbeddingProviderError("Embedding route returned no embeddings.");
|
|
92813
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
|
+
}
|
|
92814
92843
|
return payload.embeddings;
|
|
92815
92844
|
}
|
|
92816
92845
|
};
|
|
@@ -92885,7 +92914,7 @@ var init_embeddingProvider = __esm({
|
|
|
92885
92914
|
|
|
92886
92915
|
// features/perchTerminal/persistence/permanentMemoryPersistence.ts
|
|
92887
92916
|
function createPermanentMemoryPersistence(supabase, options = {}) {
|
|
92888
|
-
const embeddingProvider = options.embeddingProvider ??
|
|
92917
|
+
const embeddingProvider = options.embeddingProvider ?? createDefaultPermanentMemoryEmbeddingProvider();
|
|
92889
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";
|
|
92890
92919
|
const api2 = {
|
|
92891
92920
|
async listPermanentMemories({
|
|
@@ -93145,6 +93174,21 @@ function createPermanentMemoryPersistence(supabase, options = {}) {
|
|
|
93145
93174
|
};
|
|
93146
93175
|
return api2;
|
|
93147
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
|
+
}
|
|
93148
93192
|
async function findSimilarPermanentMemoryForInput(input) {
|
|
93149
93193
|
const queryEmbedding = await input.embeddingProvider.embed(canonicalMemoryEmbeddingText(input.input));
|
|
93150
93194
|
const { data, error } = await input.supabase.rpc("perch_ai_match_permanent_memories", {
|
|
@@ -117025,7 +117069,7 @@ function createSourcePersistence(supabase) {
|
|
|
117025
117069
|
return count ?? 0;
|
|
117026
117070
|
},
|
|
117027
117071
|
async countEmbeddedRetrievalChunks(workspaceId) {
|
|
117028
|
-
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);
|
|
117029
117073
|
if (error) throw error;
|
|
117030
117074
|
return count ?? 0;
|
|
117031
117075
|
},
|
|
@@ -117131,18 +117175,30 @@ function createSourcePersistence(supabase) {
|
|
|
117131
117175
|
return data ?? [];
|
|
117132
117176
|
},
|
|
117133
117177
|
async listIndexedSourceIds(workspaceId) {
|
|
117134
|
-
const [chunks, retrieval, memory] = await Promise.all([
|
|
117178
|
+
const [chunks, retrieval, embeddedRetrieval, memory] = await Promise.all([
|
|
117135
117179
|
supabase.from("perch_ai_source_chunks").select("source_id").eq("workspace_id", workspaceId),
|
|
117136
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),
|
|
117137
117182
|
supabase.from("perch_ai_source_memory").select("source_id").eq("workspace_id", workspaceId)
|
|
117138
117183
|
]);
|
|
117139
117184
|
if (chunks.error) throw chunks.error;
|
|
117140
117185
|
if (retrieval.error) throw retrieval.error;
|
|
117186
|
+
if (embeddedRetrieval.error) throw embeddedRetrieval.error;
|
|
117141
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);
|
|
117142
117191
|
return {
|
|
117143
|
-
sourceChunkIds: new Set(
|
|
117144
|
-
retrievalChunkIds: new Set(
|
|
117145
|
-
|
|
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
|
|
117146
117202
|
};
|
|
117147
117203
|
},
|
|
117148
117204
|
async keywordSearchSourcesMetadata(workspaceId, query, limit) {
|
|
@@ -117198,6 +117254,16 @@ function createSourcePersistence(supabase) {
|
|
|
117198
117254
|
function escapeIlike2(value) {
|
|
117199
117255
|
return value.replace(/[%_\\]/g, (ch) => `\\${ch}`);
|
|
117200
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
|
+
}
|
|
117201
117267
|
var SOURCE_LIST_COLUMNS;
|
|
117202
117268
|
var init_sourcePersistence = __esm({
|
|
117203
117269
|
"features/perchTerminal/persistence/sourcePersistence.ts"() {
|
|
@@ -117875,21 +117941,22 @@ function getSourceRetrievalToolDefinitions() {
|
|
|
117875
117941
|
}
|
|
117876
117942
|
];
|
|
117877
117943
|
}
|
|
117878
|
-
async function dispatchSourceRetrievalTool(toolName, args, session) {
|
|
117879
|
-
if (!isSupabaseConfigured()) {
|
|
117880
|
-
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.");
|
|
117881
117947
|
}
|
|
117882
117948
|
if (!session.workspaceId) {
|
|
117883
|
-
return sessionError("
|
|
117949
|
+
return sessionError("Sign in to a workspace before using workspace source search.");
|
|
117884
117950
|
}
|
|
117885
117951
|
let supabase;
|
|
117886
117952
|
try {
|
|
117887
|
-
supabase = createClient();
|
|
117953
|
+
supabase = options.supabase ?? createClient();
|
|
117888
117954
|
} catch (err) {
|
|
117889
117955
|
const message = err instanceof Error ? err.message : String(err);
|
|
117890
117956
|
return sessionError(message);
|
|
117891
117957
|
}
|
|
117892
117958
|
const store = createSourcePersistence(supabase);
|
|
117959
|
+
const retrievalStore = createRetrievalPersistence(supabase);
|
|
117893
117960
|
const workspaceId = session.workspaceId;
|
|
117894
117961
|
switch (toolName) {
|
|
117895
117962
|
case TOOL_NAMES.listSources:
|
|
@@ -117905,9 +117972,9 @@ async function dispatchSourceRetrievalTool(toolName, args, session) {
|
|
|
117905
117972
|
case TOOL_NAMES.resolveSourceCandidates:
|
|
117906
117973
|
return resolveSourceCandidatesHandler(store, workspaceId, session, args);
|
|
117907
117974
|
case TOOL_NAMES.retrieveContext:
|
|
117908
|
-
return retrieveContextHandler(store, workspaceId, session, args);
|
|
117975
|
+
return retrieveContextHandler(store, retrievalStore, workspaceId, session, args);
|
|
117909
117976
|
case TOOL_NAMES.semanticSearch:
|
|
117910
|
-
return semanticSearchHandler(store, workspaceId, args);
|
|
117977
|
+
return semanticSearchHandler(store, retrievalStore, workspaceId, args);
|
|
117911
117978
|
case TOOL_NAMES.diagnoseWorkspaceAccess:
|
|
117912
117979
|
return diagnoseWorkspaceAccessHandler(store, workspaceId, session);
|
|
117913
117980
|
default:
|
|
@@ -118170,7 +118237,7 @@ async function resolveSourceCandidatesHandler(store, workspaceId, session, args)
|
|
|
118170
118237
|
schemaVersion: "perch-tool-result-v1"
|
|
118171
118238
|
};
|
|
118172
118239
|
}
|
|
118173
|
-
async function retrieveContextHandler(store, workspaceId, session, args) {
|
|
118240
|
+
async function retrieveContextHandler(store, retrievalStore, workspaceId, session, args) {
|
|
118174
118241
|
const query = stringArg(args.query)?.trim();
|
|
118175
118242
|
if (!query) {
|
|
118176
118243
|
return { ok: false, error: "query is required", toolName: TOOL_NAMES.retrieveContext };
|
|
@@ -118196,7 +118263,6 @@ async function retrieveContextHandler(store, workspaceId, session, args) {
|
|
|
118196
118263
|
let searchMode = "keyword_only";
|
|
118197
118264
|
let fallbackUsed = false;
|
|
118198
118265
|
let semanticError = null;
|
|
118199
|
-
const retrievalStore = createRetrievalPersistence(createClient());
|
|
118200
118266
|
const embeddedCount = await store.countEmbeddedRetrievalChunks(workspaceId).catch(() => 0);
|
|
118201
118267
|
retrievalAttempts.push("semantic_first");
|
|
118202
118268
|
try {
|
|
@@ -118394,7 +118460,7 @@ async function retrieveContextHandler(store, workspaceId, session, args) {
|
|
|
118394
118460
|
schemaVersion: "perch-tool-result-v1"
|
|
118395
118461
|
};
|
|
118396
118462
|
}
|
|
118397
|
-
async function semanticSearchHandler(store, workspaceId, args) {
|
|
118463
|
+
async function semanticSearchHandler(store, retrievalStore, workspaceId, args) {
|
|
118398
118464
|
const query = stringArg(args.query)?.trim();
|
|
118399
118465
|
if (!query) {
|
|
118400
118466
|
return { ok: false, error: "query is required", toolName: TOOL_NAMES.semanticSearch };
|
|
@@ -118403,7 +118469,7 @@ async function semanticSearchHandler(store, workspaceId, args) {
|
|
|
118403
118469
|
const sourceIds = Array.isArray(args.sourceIds) ? args.sourceIds.map(String) : void 0;
|
|
118404
118470
|
const includeStale = args.includeStale === true;
|
|
118405
118471
|
const embeddedCount = await store.countEmbeddedRetrievalChunks(workspaceId);
|
|
118406
|
-
const result2 = await
|
|
118472
|
+
const result2 = await retrievalStore.matchRetrievalChunks({
|
|
118407
118473
|
workspaceId,
|
|
118408
118474
|
query,
|
|
118409
118475
|
limit,
|
|
@@ -118448,7 +118514,7 @@ async function semanticSearchHandler(store, workspaceId, args) {
|
|
|
118448
118514
|
return {
|
|
118449
118515
|
rankSignals: signals,
|
|
118450
118516
|
reason: result2.searchMode === "hybrid" ? "hybrid_sparse_vector_rrf" : result2.searchMode === "sparse" ? "postgres_full_text_rank" : "semantic_similarity",
|
|
118451
|
-
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"}`
|
|
118452
118518
|
};
|
|
118453
118519
|
})(),
|
|
118454
118520
|
rank: index + 1,
|
|
@@ -118488,7 +118554,7 @@ async function semanticSearchHandler(store, workspaceId, args) {
|
|
|
118488
118554
|
semanticAvailable: result2.embeddingProviderConfigured,
|
|
118489
118555
|
sparseAvailable: result2.sparseProviderConfigured,
|
|
118490
118556
|
fallbackUsed: result2.fallbackUsed,
|
|
118491
|
-
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"
|
|
118492
118558
|
},
|
|
118493
118559
|
schemaVersion: "perch-tool-result-v1"
|
|
118494
118560
|
};
|
|
@@ -118497,7 +118563,7 @@ async function diagnoseWorkspaceAccessHandler(store, workspaceId, session) {
|
|
|
118497
118563
|
const workspace = await store.getWorkspaceRow(workspaceId);
|
|
118498
118564
|
const blockers = [];
|
|
118499
118565
|
if (!workspace) {
|
|
118500
|
-
blockers.push("Workspace
|
|
118566
|
+
blockers.push("Workspace is not available to this signed-in session.");
|
|
118501
118567
|
}
|
|
118502
118568
|
let sourcesCount = 0;
|
|
118503
118569
|
let sourceChunksCount = 0;
|
|
@@ -133500,6 +133566,8 @@ var init_toolPermissionPolicy = __esm({
|
|
|
133500
133566
|
TOOL_NAMES.deleteLocalFile,
|
|
133501
133567
|
TOOL_NAMES.editLocalFile,
|
|
133502
133568
|
TOOL_NAMES.statPath,
|
|
133569
|
+
TOOL_NAMES.readProjectMemory,
|
|
133570
|
+
TOOL_NAMES.saveToMemory,
|
|
133503
133571
|
TOOL_NAMES.listLocalSources,
|
|
133504
133572
|
TOOL_NAMES.readLocalSourceFile,
|
|
133505
133573
|
TOOL_NAMES.generateAPAuditPacket,
|
|
@@ -135197,7 +135265,7 @@ function getDesktopToolDefinitions() {
|
|
|
135197
135265
|
type: "function",
|
|
135198
135266
|
function: {
|
|
135199
135267
|
name: TOOL_NAMES.readProjectMemory,
|
|
135200
|
-
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.",
|
|
135201
135269
|
parameters: {
|
|
135202
135270
|
type: "object",
|
|
135203
135271
|
properties: {},
|
|
@@ -135209,7 +135277,7 @@ function getDesktopToolDefinitions() {
|
|
|
135209
135277
|
type: "function",
|
|
135210
135278
|
function: {
|
|
135211
135279
|
name: TOOL_NAMES.saveToMemory,
|
|
135212
|
-
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.",
|
|
135213
135281
|
parameters: {
|
|
135214
135282
|
type: "object",
|
|
135215
135283
|
properties: {
|
|
@@ -135225,6 +135293,15 @@ function getDesktopToolDefinitions() {
|
|
|
135225
135293
|
sectionHeading: {
|
|
135226
135294
|
type: "string",
|
|
135227
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."
|
|
135228
135305
|
}
|
|
135229
135306
|
},
|
|
135230
135307
|
required: ["fileName", "mode", "content"],
|
|
@@ -135280,7 +135357,7 @@ function getDesktopToolDefinitions() {
|
|
|
135280
135357
|
type: "function",
|
|
135281
135358
|
function: {
|
|
135282
135359
|
name: TOOL_NAMES.getProjectRules,
|
|
135283
|
-
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.",
|
|
135284
135361
|
parameters: {
|
|
135285
135362
|
type: "object",
|
|
135286
135363
|
properties: {},
|
|
@@ -135298,7 +135375,7 @@ function getDesktopToolDefinitions() {
|
|
|
135298
135375
|
properties: {
|
|
135299
135376
|
path: {
|
|
135300
135377
|
type: "string",
|
|
135301
|
-
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."
|
|
135302
135379
|
},
|
|
135303
135380
|
query: {
|
|
135304
135381
|
type: "string",
|
|
@@ -136486,7 +136563,7 @@ function getNativeToolDefinitions() {
|
|
|
136486
136563
|
type: "function",
|
|
136487
136564
|
function: {
|
|
136488
136565
|
name: TOOL_NAMES.ctxInspect,
|
|
136489
|
-
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.",
|
|
136490
136567
|
parameters: {
|
|
136491
136568
|
type: "object",
|
|
136492
136569
|
properties: {},
|
|
@@ -136520,7 +136597,7 @@ function getNativeToolDefinitions() {
|
|
|
136520
136597
|
type: "function",
|
|
136521
136598
|
function: {
|
|
136522
136599
|
name: TOOL_NAMES.configInspect,
|
|
136523
|
-
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.",
|
|
136524
136601
|
parameters: {
|
|
136525
136602
|
type: "object",
|
|
136526
136603
|
properties: {},
|
|
@@ -206113,7 +206190,7 @@ async function requireMemoryRootId(ctx, surface) {
|
|
|
206113
206190
|
if (!rootId) {
|
|
206114
206191
|
return {
|
|
206115
206192
|
ok: false,
|
|
206116
|
-
error: `No selected folder
|
|
206193
|
+
error: `No selected workspace folder is available for ${surface}.`
|
|
206117
206194
|
};
|
|
206118
206195
|
}
|
|
206119
206196
|
return { ok: true, rootId };
|
|
@@ -206138,14 +206215,86 @@ var init_readProjectMemory = __esm({
|
|
|
206138
206215
|
classification: { native: false },
|
|
206139
206216
|
handler: async (_args, ctx) => {
|
|
206140
206217
|
const root2 = await requireMemoryRootId(ctx, "project memory");
|
|
206141
|
-
if (!root2.ok)
|
|
206142
|
-
|
|
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;
|
|
206143
206239
|
}
|
|
206144
206240
|
};
|
|
206145
206241
|
}
|
|
206146
206242
|
});
|
|
206147
206243
|
|
|
206148
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
|
+
}
|
|
206149
206298
|
var saveToMemoryTool;
|
|
206150
206299
|
var init_saveToMemory = __esm({
|
|
206151
206300
|
"features/perchTerminal/runtime/toolSystem/tools/projectMemory/saveToMemory.ts"() {
|
|
@@ -206157,14 +206306,24 @@ var init_saveToMemory = __esm({
|
|
|
206157
206306
|
name: TOOL_NAMES.saveToMemory,
|
|
206158
206307
|
classification: { native: false },
|
|
206159
206308
|
handler: async (args, ctx) => {
|
|
206309
|
+
const serverResult = hasCliServerMemory(ctx) ? await saveMemoryThroughCliServer(args, ctx) : null;
|
|
206310
|
+
if (serverResult?.ok) return serverResult;
|
|
206160
206311
|
const root2 = await requireMemoryRootId(ctx, "project memory");
|
|
206161
|
-
if (!root2.ok) return
|
|
206162
|
-
|
|
206312
|
+
if (!root2.ok) return serverResult ?? memoryUnavailable();
|
|
206313
|
+
const localResult = await writeMemoryFile(root2.rootId, {
|
|
206163
206314
|
fileName: String(args.fileName),
|
|
206164
206315
|
mode: String(args.mode),
|
|
206165
206316
|
content: String(args.content ?? ""),
|
|
206166
206317
|
sectionHeading: typeof args.sectionHeading === "string" ? args.sectionHeading : void 0
|
|
206167
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();
|
|
206168
206327
|
}
|
|
206169
206328
|
};
|
|
206170
206329
|
}
|
|
@@ -213219,7 +213378,7 @@ var init_localSources = __esm({
|
|
|
213219
213378
|
return {
|
|
213220
213379
|
ok: true,
|
|
213221
213380
|
sources: [],
|
|
213222
|
-
warning: "
|
|
213381
|
+
warning: "Local workspace access is unavailable in this chat. Open Perch Desktop or attach a workspace folder, then try again."
|
|
213223
213382
|
};
|
|
213224
213383
|
}
|
|
213225
213384
|
const maxResults = sanitizeListLocalSourcesMaxResults(args.maxResults);
|
|
@@ -216703,6 +216862,8 @@ async function executeToolCall(call, opts) {
|
|
|
216703
216862
|
workspaceId: opts.workspaceId,
|
|
216704
216863
|
threadId: opts.threadId,
|
|
216705
216864
|
supabase: opts.supabase,
|
|
216865
|
+
cliServerAppUrl: opts.cliServerAppUrl,
|
|
216866
|
+
cliServerAccessToken: opts.cliServerAccessToken,
|
|
216706
216867
|
marketDeskProxyAppUrl: opts.marketDeskProxyAppUrl,
|
|
216707
216868
|
marketDeskProxyAccessToken: opts.marketDeskProxyAccessToken,
|
|
216708
216869
|
chatMode: opts.chatMode ?? null,
|
|
@@ -216762,7 +216923,7 @@ async function executeToolCall(call, opts) {
|
|
|
216762
216923
|
const result2 = {
|
|
216763
216924
|
ok: false,
|
|
216764
216925
|
errorCode: "supabase_session_required",
|
|
216765
|
-
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.",
|
|
216766
216927
|
schemaVersion: "perch-tool-result-v1",
|
|
216767
216928
|
toolName: call.name
|
|
216768
216929
|
};
|
|
@@ -216843,6 +217004,8 @@ async function executeToolCall(call, opts) {
|
|
|
216843
217004
|
workspaceRoot: opts.workspaceRoot,
|
|
216844
217005
|
threadId: opts.threadId,
|
|
216845
217006
|
runId: opts.runId,
|
|
217007
|
+
cliServerAppUrl: opts.cliServerAppUrl,
|
|
217008
|
+
cliServerAccessToken: opts.cliServerAccessToken,
|
|
216846
217009
|
marketDeskProxyAppUrl: opts.marketDeskProxyAppUrl,
|
|
216847
217010
|
marketDeskProxyAccessToken: opts.marketDeskProxyAccessToken,
|
|
216848
217011
|
onEvent: runtime.onEvent,
|
|
@@ -219043,6 +219206,8 @@ async function runModelToolLoop(input) {
|
|
|
219043
219206
|
selectedSourceId: input.selectedSourceId,
|
|
219044
219207
|
supabaseConfigured: input.supabaseConfigured,
|
|
219045
219208
|
supabase: input.supabase,
|
|
219209
|
+
cliServerAppUrl: input.cliServerAppUrl,
|
|
219210
|
+
cliServerAccessToken: input.cliServerAccessToken,
|
|
219046
219211
|
marketDeskProxyAppUrl: input.marketDeskProxyAppUrl,
|
|
219047
219212
|
marketDeskProxyAccessToken: input.marketDeskProxyAccessToken,
|
|
219048
219213
|
runId: input.runId,
|
|
@@ -219459,6 +219624,8 @@ async function executeInitialToolCall(input) {
|
|
|
219459
219624
|
selectedSourceId: input.input.selectedSourceId,
|
|
219460
219625
|
supabaseConfigured: input.input.supabaseConfigured,
|
|
219461
219626
|
supabase: input.input.supabase,
|
|
219627
|
+
cliServerAppUrl: input.input.cliServerAppUrl,
|
|
219628
|
+
cliServerAccessToken: input.input.cliServerAccessToken,
|
|
219462
219629
|
marketDeskProxyAppUrl: input.input.marketDeskProxyAppUrl,
|
|
219463
219630
|
marketDeskProxyAccessToken: input.input.marketDeskProxyAccessToken,
|
|
219464
219631
|
runId: input.input.runId,
|
|
@@ -220520,7 +220687,7 @@ async function runLiveAgentsLoop(input) {
|
|
|
220520
220687
|
const onEv = onEvent ?? deps.onEvent;
|
|
220521
220688
|
onEv({
|
|
220522
220689
|
type: "activity_delta",
|
|
220523
|
-
text: "Workspace preflight: no folder
|
|
220690
|
+
text: "Workspace preflight: no workspace folder selected; continuing with Desktop tools.",
|
|
220524
220691
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
220525
220692
|
});
|
|
220526
220693
|
}
|
|
@@ -220559,6 +220726,8 @@ async function runLiveAgentsLoop(input) {
|
|
|
220559
220726
|
untrustedContextPresent: context.untrustedContextPresent,
|
|
220560
220727
|
supabaseConfigured: turn.supabaseConfigured,
|
|
220561
220728
|
supabase: turn.supabase,
|
|
220729
|
+
cliServerAppUrl: turn.cliServerAppUrl ?? null,
|
|
220730
|
+
cliServerAccessToken: turn.cliServerAccessToken ?? null,
|
|
220562
220731
|
marketDeskProxyAppUrl: turn.marketDeskProxyAppUrl ?? null,
|
|
220563
220732
|
marketDeskProxyAccessToken: turn.marketDeskProxyAccessToken ?? null,
|
|
220564
220733
|
onEvent: onEvent ?? deps.onEvent,
|
|
@@ -221925,7 +222094,12 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
221925
222094
|
contentHash: null,
|
|
221926
222095
|
chunksBuilt: 0,
|
|
221927
222096
|
chunksUpserted: 0,
|
|
221928
|
-
|
|
222097
|
+
sourceChunksUpserted: 0,
|
|
222098
|
+
retrievalChunksBuilt: 0,
|
|
222099
|
+
retrievalChunksUpserted: 0,
|
|
222100
|
+
embeddedChunksUpserted: 0,
|
|
222101
|
+
vectorReady: false,
|
|
222102
|
+
embeddingConfigured: isBrowserRuntime() ? true : getEmbeddingProviderStatus().configured,
|
|
221929
222103
|
errors: [],
|
|
221930
222104
|
warnings: []
|
|
221931
222105
|
};
|
|
@@ -221958,7 +222132,12 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
221958
222132
|
contentHash,
|
|
221959
222133
|
chunksBuilt: 0,
|
|
221960
222134
|
chunksUpserted: 0,
|
|
221961
|
-
|
|
222135
|
+
sourceChunksUpserted: 0,
|
|
222136
|
+
retrievalChunksBuilt: 0,
|
|
222137
|
+
retrievalChunksUpserted: 0,
|
|
222138
|
+
embeddedChunksUpserted: 0,
|
|
222139
|
+
vectorReady: true,
|
|
222140
|
+
embeddingConfigured: isBrowserRuntime() ? true : getEmbeddingProviderStatus().configured,
|
|
221962
222141
|
errors: [],
|
|
221963
222142
|
warnings: []
|
|
221964
222143
|
};
|
|
@@ -222056,12 +222235,15 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
222056
222235
|
threadId: input.threadId ?? null,
|
|
222057
222236
|
chunks: sourceTextChunks
|
|
222058
222237
|
});
|
|
222059
|
-
const embeddingStatus = getEmbeddingProviderStatus();
|
|
222060
222238
|
let chunksBuilt = textChunks.length;
|
|
222061
222239
|
let chunksUpserted = 0;
|
|
222062
222240
|
const warnings = [...extracted.truncated ? ["Source text was truncated before indexing."] : []];
|
|
222063
|
-
|
|
222064
|
-
|
|
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);
|
|
222065
222247
|
return {
|
|
222066
222248
|
ok: true,
|
|
222067
222249
|
status: "indexed",
|
|
@@ -222071,38 +222253,69 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
222071
222253
|
contentHash: indexedContentHash,
|
|
222072
222254
|
chunksBuilt,
|
|
222073
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,
|
|
222074
222310
|
embeddingConfigured: false,
|
|
222075
222311
|
errors: [],
|
|
222076
222312
|
warnings
|
|
222077
222313
|
};
|
|
222078
222314
|
}
|
|
222079
|
-
const indexer = createRetrievalIndexer(input.supabase);
|
|
222080
|
-
const indexResult = pdfPages ? await indexer.indexLocalFilePages({
|
|
222081
|
-
workspaceId: input.workspaceId,
|
|
222082
|
-
sourceId: source.id,
|
|
222083
|
-
fileName: source.file_name,
|
|
222084
|
-
fileType: extracted.fileType,
|
|
222085
|
-
mimeType: source.mime_type,
|
|
222086
|
-
provenanceType: "desktop_local",
|
|
222087
|
-
contentHash: indexedContentHash,
|
|
222088
|
-
threadId: input.threadId ?? null,
|
|
222089
|
-
pages: pdfPages,
|
|
222090
|
-
blocks: pdfBlocks,
|
|
222091
|
-
tables: pdfTables,
|
|
222092
|
-
facts: factPageEntries.length ? factPageEntries : void 0
|
|
222093
|
-
}) : await indexer.indexLocalFileText({
|
|
222094
|
-
workspaceId: input.workspaceId,
|
|
222095
|
-
sourceId: source.id,
|
|
222096
|
-
fileName: source.file_name,
|
|
222097
|
-
fileType: extracted.fileType,
|
|
222098
|
-
mimeType: source.mime_type,
|
|
222099
|
-
provenanceType: "desktop_local",
|
|
222100
|
-
contentHash: indexedContentHash,
|
|
222101
|
-
threadId: input.threadId ?? null,
|
|
222102
|
-
text: extracted.text
|
|
222103
|
-
});
|
|
222104
222315
|
chunksBuilt = indexResult.chunksBuilt;
|
|
222105
222316
|
chunksUpserted = indexResult.chunksUpserted;
|
|
222317
|
+
const embeddedChunksUpserted = indexResult.chunksEmbedded;
|
|
222318
|
+
const vectorReady = embeddedChunksUpserted > 0;
|
|
222106
222319
|
if (!indexResult.ok) {
|
|
222107
222320
|
return {
|
|
222108
222321
|
ok: false,
|
|
@@ -222113,6 +222326,11 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
222113
222326
|
contentHash: indexedContentHash,
|
|
222114
222327
|
chunksBuilt,
|
|
222115
222328
|
chunksUpserted,
|
|
222329
|
+
sourceChunksUpserted: sourceTextChunks.length,
|
|
222330
|
+
retrievalChunksBuilt: indexResult.chunksBuilt,
|
|
222331
|
+
retrievalChunksUpserted: indexResult.chunksUpserted,
|
|
222332
|
+
embeddedChunksUpserted,
|
|
222333
|
+
vectorReady,
|
|
222116
222334
|
embeddingConfigured: true,
|
|
222117
222335
|
errors: indexResult.errors,
|
|
222118
222336
|
warnings: [...warnings, ...indexResult.warnings]
|
|
@@ -222123,10 +222341,15 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
222123
222341
|
status: "indexed",
|
|
222124
222342
|
localSourceId: input.localSourceId,
|
|
222125
222343
|
supabaseSourceId: source.id,
|
|
222126
|
-
retrievalSourceId: source.id,
|
|
222344
|
+
retrievalSourceId: vectorReady ? source.id : null,
|
|
222127
222345
|
contentHash: indexedContentHash,
|
|
222128
222346
|
chunksBuilt,
|
|
222129
222347
|
chunksUpserted,
|
|
222348
|
+
sourceChunksUpserted: sourceTextChunks.length,
|
|
222349
|
+
retrievalChunksBuilt: indexResult.chunksBuilt,
|
|
222350
|
+
retrievalChunksUpserted: indexResult.chunksUpserted,
|
|
222351
|
+
embeddedChunksUpserted,
|
|
222352
|
+
vectorReady,
|
|
222130
222353
|
embeddingConfigured: true,
|
|
222131
222354
|
errors: [],
|
|
222132
222355
|
warnings
|
|
@@ -222142,6 +222365,12 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
222142
222365
|
};
|
|
222143
222366
|
}
|
|
222144
222367
|
}
|
|
222368
|
+
function createDefaultLocalSourceEmbeddingProvider() {
|
|
222369
|
+
return isBrowserRuntime() ? createBrowserEmbeddingProvider() : createEmbeddingProviderFromEnv();
|
|
222370
|
+
}
|
|
222371
|
+
function isBrowserRuntime() {
|
|
222372
|
+
return typeof window !== "undefined";
|
|
222373
|
+
}
|
|
222145
222374
|
async function resolveRetrievalSourceIdForTurn(input) {
|
|
222146
222375
|
const selected = input.selectedSourceId?.trim() ?? null;
|
|
222147
222376
|
if (!selected) return { retrievalSourceId: null, indexOutcome: null };
|
|
@@ -222417,7 +222646,11 @@ async function loadFolderIndexSummary(input) {
|
|
|
222417
222646
|
}
|
|
222418
222647
|
const files = input.localSources.filter((entry) => entry.rootId === input.rootId && isAutoIndexableLocalSource(entry)).map((entry) => {
|
|
222419
222648
|
const row = rowByLocalId.get(entry.localSourceId) ?? null;
|
|
222420
|
-
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;
|
|
222421
222654
|
const stale = row ? Date.parse(entry.modifiedAt) > Date.parse(row.updated_at) : true;
|
|
222422
222655
|
return {
|
|
222423
222656
|
localSourceId: entry.localSourceId,
|
|
@@ -222426,19 +222659,30 @@ async function loadFolderIndexSummary(input) {
|
|
|
222426
222659
|
mimeType: entry.mimeType,
|
|
222427
222660
|
modifiedAt: entry.modifiedAt,
|
|
222428
222661
|
sizeBytes: entry.sizeBytes,
|
|
222429
|
-
status: row ? retrievalReady ? "indexed" : "pending" : "pending",
|
|
222662
|
+
status: row ? sourceChunkCount2 > 0 || retrievalReady ? "indexed" : "pending" : "pending",
|
|
222430
222663
|
supabaseSourceId: row?.id ?? null,
|
|
222431
222664
|
retrievalReady,
|
|
222665
|
+
vectorReady,
|
|
222666
|
+
sourceChunkCount: sourceChunkCount2,
|
|
222667
|
+
retrievalChunkCount: retrievalChunkCount2,
|
|
222668
|
+
embeddedChunkCount: embeddedChunkCount2,
|
|
222432
222669
|
contentHash: row?.content_hash ?? null,
|
|
222433
222670
|
message: stale && row ? "Changed since last indexing pass." : void 0
|
|
222434
222671
|
};
|
|
222435
222672
|
});
|
|
222436
222673
|
const staleFiles = files.filter((file) => file.message?.includes("Changed since last indexing pass")).length;
|
|
222437
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);
|
|
222438
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");
|
|
222439
222680
|
const lastIndexedAt = rows[0]?.updated_at ?? null;
|
|
222440
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,
|
|
222441
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,
|
|
222442
222686
|
staleFiles > 0 ? `${staleFiles} file(s) changed since the last index update.` : null
|
|
222443
222687
|
].filter(Boolean);
|
|
222444
222688
|
return {
|
|
@@ -222448,8 +222692,14 @@ async function loadFolderIndexSummary(input) {
|
|
|
222448
222692
|
indexedFiles,
|
|
222449
222693
|
skippedFiles: 0,
|
|
222450
222694
|
failedFiles: 0,
|
|
222695
|
+
registeredSources: rows.length,
|
|
222696
|
+
sourceChunkCount,
|
|
222697
|
+
sourceChunkFiles: files.filter((file) => file.sourceChunkCount > 0).length,
|
|
222698
|
+
retrievalChunkCount,
|
|
222451
222699
|
staleFiles,
|
|
222452
222700
|
retrievalReadyFiles: retrievalReadySourceIds.length,
|
|
222701
|
+
embeddedChunkCount,
|
|
222702
|
+
vectorReadyFiles: vectorReadySourceIds.length,
|
|
222453
222703
|
filesNeedingOcr: files.filter((file) => needsOcrHint(file)).length,
|
|
222454
222704
|
lastIndexedAt,
|
|
222455
222705
|
status: files.length === 0 ? "idle" : staleFiles > 0 || indexedFiles < files.length ? "partial" : "ready",
|
|
@@ -222457,6 +222707,7 @@ async function loadFolderIndexSummary(input) {
|
|
|
222457
222707
|
localOnlyEmbeddingsEnabled: false,
|
|
222458
222708
|
remoteIndexArtifacts: [...DEFAULT_REMOTE_INDEX_ARTIFACTS],
|
|
222459
222709
|
retrievalReadySourceIds,
|
|
222710
|
+
vectorReadySourceIds,
|
|
222460
222711
|
diagnostics,
|
|
222461
222712
|
manifestPath: DEFAULT_MANIFEST_PATH,
|
|
222462
222713
|
files
|
|
@@ -222476,6 +222727,9 @@ function needsOcrHint(file) {
|
|
|
222476
222727
|
const message = file.message?.toLowerCase() ?? "";
|
|
222477
222728
|
return message.includes("ocr") || message.includes("scanned") || message.includes("no readable text");
|
|
222478
222729
|
}
|
|
222730
|
+
function sumFiles(files, field) {
|
|
222731
|
+
return files.reduce((total, file) => total + file[field], 0);
|
|
222732
|
+
}
|
|
222479
222733
|
var DEFAULT_MANIFEST_PATH, AUTO_INDEX_EXCLUDED_PATH_PREFIXES, AUTO_INDEX_EXCLUDED_EXACT_PATHS, DEFAULT_STORAGE_MODE, DEFAULT_REMOTE_INDEX_ARTIFACTS;
|
|
222480
222734
|
var init_folderIndexing = __esm({
|
|
222481
222735
|
"features/perchTerminal/runtime/folderIndexing.ts"() {
|
|
@@ -224731,6 +224985,54 @@ var init_objectiveClassifier = __esm({
|
|
|
224731
224985
|
});
|
|
224732
224986
|
|
|
224733
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
|
+
}
|
|
224734
225036
|
async function runOperatorTurn(input, deps) {
|
|
224735
225037
|
const runId = input.clientRunId?.trim() || makeRunId();
|
|
224736
225038
|
const startMs = Date.now();
|
|
@@ -224796,21 +225098,15 @@ async function runOperatorTurn(input, deps) {
|
|
|
224796
225098
|
});
|
|
224797
225099
|
}
|
|
224798
225100
|
try {
|
|
225101
|
+
const memoryRetriever = buildTurnMemoryRetriever(input);
|
|
224799
225102
|
const memoryContext = await buildMemoryContext({
|
|
224800
225103
|
query: input.trimmedInput,
|
|
224801
|
-
userId: input.userId,
|
|
225104
|
+
userId: input.userId ?? (memoryRetriever && input.cliServerAccessToken?.trim() ? "cli_authenticated_user" : null),
|
|
224802
225105
|
workspaceId: input.workspaceId,
|
|
224803
225106
|
threadId: input.threadId,
|
|
224804
225107
|
permanentMemories: input.permanentMemories,
|
|
224805
225108
|
userMemories: input.userMemories,
|
|
224806
|
-
retriever:
|
|
224807
|
-
input.supabase
|
|
224808
|
-
).retrievePermanentMemoriesForTurn({
|
|
224809
|
-
userId,
|
|
224810
|
-
workspaceId,
|
|
224811
|
-
limitPerScope,
|
|
224812
|
-
query
|
|
224813
|
-
}) : null
|
|
225109
|
+
retriever: memoryRetriever
|
|
224814
225110
|
});
|
|
224815
225111
|
const earlySession = input.threadId ? await loadThreadSession(input.threadId, { supabase: input.supabase ?? null }) : null;
|
|
224816
225112
|
const persona = getPersona(input.personaId ?? earlySession?.personaId);
|
|
@@ -225098,8 +225394,15 @@ ${planStateLines}`
|
|
|
225098
225394
|
if (perchMdRow?.status === "not_found") {
|
|
225099
225395
|
extendedWarnings.push("No PERCH.md \u2014 add an operator playbook to .perch/PERCH.md to customise behaviour.");
|
|
225100
225396
|
}
|
|
225101
|
-
|
|
225102
|
-
|
|
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.");
|
|
225103
225406
|
}
|
|
225104
225407
|
const contextJob = resolveJobContextTokens({
|
|
225105
225408
|
threadContextTokens: threadContextAccounting.threadContextTokens,
|
|
@@ -226511,6 +226814,21 @@ function installCliNodeLocalBridge(input) {
|
|
|
226511
226814
|
}
|
|
226512
226815
|
};
|
|
226513
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
|
+
}
|
|
226514
226832
|
function createCliNodeLocalBridge(input) {
|
|
226515
226833
|
const workspaceRoot = path11.resolve(input.workspaceRoot);
|
|
226516
226834
|
const now13 = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -226793,10 +227111,20 @@ function createCliNodeLocalBridge(input) {
|
|
|
226793
227111
|
encoding: "base64"
|
|
226794
227112
|
};
|
|
226795
227113
|
},
|
|
226796
|
-
getProjectRules: async () =>
|
|
226797
|
-
|
|
226798
|
-
|
|
226799
|
-
|
|
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),
|
|
226800
227128
|
writeRule: async () => ({ ok: false, error: "Project rule writes are not available in this CLI package yet." }),
|
|
226801
227129
|
writePerchMd: async () => ({ ok: false, error: "PERCH.md writes are not available in this CLI package yet." }),
|
|
226802
227130
|
readGlobalPerchMd: async () => ({ ok: false, error: "Global PERCH.md is not available in this CLI package yet." }),
|
|
@@ -227075,6 +227403,292 @@ function guessCliFileType(filePath) {
|
|
|
227075
227403
|
if ([".doc", ".docx", ".txt", ".md", ".rtf"].includes(ext)) return "document";
|
|
227076
227404
|
return "unknown";
|
|
227077
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
|
+
}
|
|
227078
227692
|
async function runShellCommand(input) {
|
|
227079
227693
|
const startedAt = Date.now();
|
|
227080
227694
|
const cwd2 = input.cwd ? resolveReadPath(input.workspaceRoot, input.cwd) : input.workspaceRoot;
|
|
@@ -227312,7 +227926,7 @@ function capOutput(stdout, stderr) {
|
|
|
227312
227926
|
function shellQuote(parts) {
|
|
227313
227927
|
return parts.map((part) => `'${part.replace(/'/g, "'\\''")}'`).join(" ");
|
|
227314
227928
|
}
|
|
227315
|
-
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;
|
|
227316
227930
|
var init_nodeLocalBridge = __esm({
|
|
227317
227931
|
"features/perchTerminal/runtime/cliHost/nodeLocalBridge.ts"() {
|
|
227318
227932
|
"use strict";
|
|
@@ -227323,6 +227937,20 @@ var init_nodeLocalBridge = __esm({
|
|
|
227323
227937
|
DEFAULT_MAX_RESULTS = 200;
|
|
227324
227938
|
MAX_READ_BYTES = 2e6;
|
|
227325
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
|
+
];
|
|
227326
227954
|
}
|
|
227327
227955
|
});
|
|
227328
227956
|
|
|
@@ -227404,10 +228032,13 @@ function buildCliTurnInput(input, resolved) {
|
|
|
227404
228032
|
activeRootPath: input.activeRootPath ?? resolved.cwd,
|
|
227405
228033
|
localSources: [],
|
|
227406
228034
|
localSourcesMeta: null,
|
|
228035
|
+
projectMeta: readCliProjectMemoryState(resolved.cwd).meta,
|
|
227407
228036
|
permanentMemories: input.permanentMemories ?? [],
|
|
227408
228037
|
userMemories: input.userMemories ?? [],
|
|
227409
228038
|
supabase: null,
|
|
227410
228039
|
supabaseConfigured: false,
|
|
228040
|
+
cliServerAppUrl: input.cliServerAppUrl ?? input.marketDeskProxyAppUrl ?? input.appUrl ?? null,
|
|
228041
|
+
cliServerAccessToken: input.cliServerAccessToken ?? input.marketDeskProxyAccessToken ?? null,
|
|
227411
228042
|
marketDeskProxyAppUrl: input.marketDeskProxyAppUrl ?? input.appUrl ?? null,
|
|
227412
228043
|
marketDeskProxyAccessToken: input.marketDeskProxyAccessToken ?? null,
|
|
227413
228044
|
permissionMode: normalizePermissionMode(input.permissionMode ?? "default"),
|
|
@@ -227483,6 +228114,7 @@ var init_runCliTurn = __esm({
|
|
|
227483
228114
|
init_personaRegistry();
|
|
227484
228115
|
init_runOperatorTurn();
|
|
227485
228116
|
init_nodeLocalBridge();
|
|
228117
|
+
init_nodeLocalBridge();
|
|
227486
228118
|
}
|
|
227487
228119
|
});
|
|
227488
228120
|
|
|
@@ -227652,12 +228284,12 @@ import os2 from "node:os";
|
|
|
227652
228284
|
import path13 from "node:path";
|
|
227653
228285
|
import { promisify } from "node:util";
|
|
227654
228286
|
async function readStoredCliAuthSession() {
|
|
227655
|
-
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);
|
|
227656
228288
|
return parseStoredCliAuthSession(raw);
|
|
227657
228289
|
}
|
|
227658
228290
|
async function writeStoredCliAuthSession(session) {
|
|
227659
228291
|
const raw = JSON.stringify(session);
|
|
227660
|
-
if (process.platform === "darwin") {
|
|
228292
|
+
if (process.platform === "darwin" && !shouldUseFallbackAuthFile()) {
|
|
227661
228293
|
await execFileAsync("security", [
|
|
227662
228294
|
"add-generic-password",
|
|
227663
228295
|
"-U",
|
|
@@ -227673,7 +228305,7 @@ async function writeStoredCliAuthSession(session) {
|
|
|
227673
228305
|
await writeFallbackSecret(raw);
|
|
227674
228306
|
}
|
|
227675
228307
|
async function clearStoredCliAuthSession() {
|
|
227676
|
-
if (process.platform === "darwin") {
|
|
228308
|
+
if (process.platform === "darwin" && !shouldUseFallbackAuthFile()) {
|
|
227677
228309
|
await execFileAsync("security", [
|
|
227678
228310
|
"delete-generic-password",
|
|
227679
228311
|
"-s",
|
|
@@ -227685,6 +228317,9 @@ async function clearStoredCliAuthSession() {
|
|
|
227685
228317
|
}
|
|
227686
228318
|
await fs11.rm(fallbackSessionPath(), { force: true }).catch(() => void 0);
|
|
227687
228319
|
}
|
|
228320
|
+
function shouldUseFallbackAuthFile() {
|
|
228321
|
+
return Boolean(process.env.PERCH_CLI_AUTH_DIR?.trim());
|
|
228322
|
+
}
|
|
227688
228323
|
function isStoredCliAuthSessionUsable(session, nowSeconds = Math.floor(Date.now() / 1e3)) {
|
|
227689
228324
|
if (!session?.accessToken?.trim()) return false;
|
|
227690
228325
|
if (!session.appUrl?.trim()) return false;
|
|
@@ -283805,6 +284440,7 @@ function parseInteractiveSlashCommand(input) {
|
|
|
283805
284440
|
}
|
|
283806
284441
|
function renderInteractiveStatus(state, connection, session, workspaceId) {
|
|
283807
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);
|
|
283808
284444
|
const connectionStatus = isCliModelConnectionReady(connection) ? "connected" : "locked \xB7 run /login";
|
|
283809
284445
|
const color = shouldUseCliColor();
|
|
283810
284446
|
const lines = [
|
|
@@ -283812,6 +284448,7 @@ function renderInteractiveStatus(state, connection, session, workspaceId) {
|
|
|
283812
284448
|
["cwd", state.cwd],
|
|
283813
284449
|
["auth", storedAuth],
|
|
283814
284450
|
["connection", connectionStatus],
|
|
284451
|
+
["memory", renderCliMemoryAvailability(state.cwd, workspaceId ?? null, signedIn)],
|
|
283815
284452
|
["tools", renderCliCapabilityCount(state, workspaceId ?? null)],
|
|
283816
284453
|
["permission", state.permissionMode],
|
|
283817
284454
|
["mode", state.chatMode],
|
|
@@ -283823,6 +284460,15 @@ function renderInteractiveStatus(state, connection, session, workspaceId) {
|
|
|
283823
284460
|
];
|
|
283824
284461
|
return lines.map(([key, value]) => `${paint(key.padEnd(11), "muted", color)} ${value}`).join("\n") + "\n";
|
|
283825
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
|
+
}
|
|
283826
284472
|
function renderCliCapabilityCount(state, workspaceId) {
|
|
283827
284473
|
const count = getExecutableToolDefinitions({
|
|
283828
284474
|
surface: "cli",
|
|
@@ -284017,7 +284663,21 @@ async function fetchCliHostedContext(connection, input) {
|
|
|
284017
284663
|
return {
|
|
284018
284664
|
userId: typeof context.session?.userId === "string" ? context.session.userId : session.userId ?? null,
|
|
284019
284665
|
workspaceId: typeof context.session?.workspaceId === "string" ? context.session.workspaceId : null,
|
|
284020
|
-
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
|
|
284021
284681
|
};
|
|
284022
284682
|
}
|
|
284023
284683
|
async function resolveCliMarketDeskProxy(connection) {
|
|
@@ -284025,6 +284685,8 @@ async function resolveCliMarketDeskProxy(connection) {
|
|
|
284025
284685
|
const session = await readStoredCliAuthSession();
|
|
284026
284686
|
if (!isStoredCliAuthSessionUsable(session)) return {};
|
|
284027
284687
|
return {
|
|
284688
|
+
cliServerAppUrl: session.appUrl || connection.appUrl,
|
|
284689
|
+
cliServerAccessToken: session.accessToken,
|
|
284028
284690
|
marketDeskProxyAppUrl: session.appUrl || connection.appUrl,
|
|
284029
284691
|
marketDeskProxyAccessToken: session.accessToken
|
|
284030
284692
|
};
|
|
@@ -284069,7 +284731,7 @@ async function postCliJson(appUrl, session, pathname, payload) {
|
|
|
284069
284731
|
clearTimeout(timeout);
|
|
284070
284732
|
}
|
|
284071
284733
|
}
|
|
284072
|
-
function
|
|
284734
|
+
function isPermanentMemoryLike2(value) {
|
|
284073
284735
|
if (!value || typeof value !== "object") return false;
|
|
284074
284736
|
const memory = value;
|
|
284075
284737
|
return typeof memory.id === "string" && typeof memory.title === "string" && typeof memory.body === "string";
|
|
@@ -284369,12 +285031,17 @@ async function runAuthCommand(parsed, writer) {
|
|
|
284369
285031
|
if (parsed.action === "status") {
|
|
284370
285032
|
const session = await readStoredCliAuthSession();
|
|
284371
285033
|
if (isStoredCliAuthSessionUsable(session)) {
|
|
284372
|
-
|
|
284373
|
-
|
|
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");
|
|
284374
285039
|
return 0;
|
|
284375
285040
|
}
|
|
284376
|
-
writer.stdout(
|
|
284377
|
-
`
|
|
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");
|
|
284378
285045
|
return 2;
|
|
284379
285046
|
}
|
|
284380
285047
|
const appUrl = resolveCliAppUrl(parsed.appUrl, DEFAULT_CLI_LOGIN_APP_URL) ?? DEFAULT_CLI_LOGIN_APP_URL;
|
|
@@ -284634,6 +285301,7 @@ var init_perch_cli = __esm({
|
|
|
284634
285301
|
init_runRegistry();
|
|
284635
285302
|
init_learningMemory();
|
|
284636
285303
|
init_toolDefinitions();
|
|
285304
|
+
init_nodeLocalBridge();
|
|
284637
285305
|
execFileAsync3 = promisify3(execFile3);
|
|
284638
285306
|
DEFAULT_CLI_LOGIN_APP_URL = "https://app.perchai.app";
|
|
284639
285307
|
CLI_PACKAGE_VERSION = readCliPackageVersion();
|