perchai-cli 2.4.19 → 2.4.21
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 +1059 -213
- 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",
|
|
@@ -78259,7 +78260,7 @@ var init_permissionModes = __esm({
|
|
|
78259
78260
|
});
|
|
78260
78261
|
|
|
78261
78262
|
// features/perchTerminal/runtime/perchMemoryGuidance.ts
|
|
78262
|
-
var VISIBLE_OUTPUT_STYLE_GUIDANCE, SAFFRON_CORE_IDENTITY, QUILL_CORE_IDENTITY, PERCH_MEMORY_GUIDANCE;
|
|
78263
|
+
var VISIBLE_OUTPUT_STYLE_GUIDANCE, SAFFRON_STYLE_CONTRACT, QUILL_STYLE_CONTRACT, SAFFRON_CORE_IDENTITY, QUILL_CORE_IDENTITY, PERCH_MEMORY_GUIDANCE;
|
|
78263
78264
|
var init_perchMemoryGuidance = __esm({
|
|
78264
78265
|
"features/perchTerminal/runtime/perchMemoryGuidance.ts"() {
|
|
78265
78266
|
"use strict";
|
|
@@ -78274,6 +78275,31 @@ Avoid stock AI phrases: "delve," "nuanced," "robust," "seamless,"
|
|
|
78274
78275
|
Be concise when the user is moving fast.
|
|
78275
78276
|
Preserve the persona: Quill is warm, literate, and direct; Saffron is sharp,
|
|
78276
78277
|
practical, and confident.
|
|
78278
|
+
`.trim();
|
|
78279
|
+
SAFFRON_STYLE_CONTRACT = `
|
|
78280
|
+
## Voice contract \u2014 Saffron (visible output only)
|
|
78281
|
+
|
|
78282
|
+
This contract shapes how your replies read. It never changes which tools you
|
|
78283
|
+
use, which tasks you take on, or how the work itself is done.
|
|
78284
|
+
|
|
78285
|
+
- Direct, sharp, practical, operator-like, confident.
|
|
78286
|
+
- Lead with the finding or the action. Decoration is at most a dry aside.
|
|
78287
|
+
- Memory texture: use remembered facts operationally ("your close lands on the
|
|
78288
|
+
5th, so this ships before it") \u2014 state the fact, use it, move on.
|
|
78289
|
+
- Concise. Confidence reads as economy, not volume.
|
|
78290
|
+
`.trim();
|
|
78291
|
+
QUILL_STYLE_CONTRACT = `
|
|
78292
|
+
## Voice contract \u2014 Quill (visible output only)
|
|
78293
|
+
|
|
78294
|
+
This contract shapes how your replies read. It never changes which tools you
|
|
78295
|
+
use, which tasks you take on, or how the work itself is done.
|
|
78296
|
+
|
|
78297
|
+
- Warm, personable, literate, more emotionally present than a status report.
|
|
78298
|
+
- Lightly humorous when it arises naturally; never forced, never a bit.
|
|
78299
|
+
- Memory texture: when relevant personal memories are in context, weave them in
|
|
78300
|
+
naturally ("you mentioned the board reads these, so I kept it tight").
|
|
78301
|
+
- Still concise and useful. Warmth lives in the engagement, not the word count.
|
|
78302
|
+
- Hard limits: not verbose, not therapeutic, not flirty, not over-familiar.
|
|
78277
78303
|
`.trim();
|
|
78278
78304
|
SAFFRON_CORE_IDENTITY = `
|
|
78279
78305
|
You are Saffron \u2014 Perch's workspace operator.
|
|
@@ -78410,6 +78436,11 @@ working through a paper. Founders tightening a pitch. Anyone who wants their
|
|
|
78410
78436
|
words to land. You don't try to write *for* people; you help them write
|
|
78411
78437
|
*better*. You know the difference, and you respect the difference.
|
|
78412
78438
|
|
|
78439
|
+
Writing is your craft home, not your fence. You're a full Perch operator: when
|
|
78440
|
+
the user needs analysis, files, data, an audit, or delivery, you do that work
|
|
78441
|
+
yourself with the same tools \u2014 in your own voice. You never punt real work to
|
|
78442
|
+
another persona.
|
|
78443
|
+
|
|
78413
78444
|
## How you work
|
|
78414
78445
|
|
|
78415
78446
|
Read first. Before you draft a single sentence, you know what the piece is for,
|
|
@@ -80877,6 +80908,7 @@ var init_personaRegistry = __esm({
|
|
|
80877
80908
|
label: "Saffron",
|
|
80878
80909
|
shortDescription: "Operator. Direct, sharp, finds the thing.",
|
|
80879
80910
|
identityPrompt: SAFFRON_CORE_IDENTITY,
|
|
80911
|
+
styleContract: SAFFRON_STYLE_CONTRACT,
|
|
80880
80912
|
suggestedPrompts: [
|
|
80881
80913
|
"Audit the AP folder for duplicates and anomalies",
|
|
80882
80914
|
"Reconcile the GL against last month's subledger",
|
|
@@ -80900,6 +80932,7 @@ var init_personaRegistry = __esm({
|
|
|
80900
80932
|
label: "Quill",
|
|
80901
80933
|
shortDescription: "Writing companion. Warm, craft-aware, reads first.",
|
|
80902
80934
|
identityPrompt: QUILL_CORE_IDENTITY,
|
|
80935
|
+
styleContract: QUILL_STYLE_CONTRACT,
|
|
80903
80936
|
suggestedPrompts: [
|
|
80904
80937
|
"Draft a memo on personal jurisdiction",
|
|
80905
80938
|
"Brief Pennoyer v. Neff",
|
|
@@ -80931,12 +80964,14 @@ function buildDesktopContextSection(input) {
|
|
|
80931
80964
|
"grep",
|
|
80932
80965
|
"statPath",
|
|
80933
80966
|
"readLocalFile",
|
|
80967
|
+
"readProjectMemory",
|
|
80934
80968
|
"listLocalSources",
|
|
80935
80969
|
"readLocalSourceFile"
|
|
80936
80970
|
].filter((name) => enabledToolSet2.has(name));
|
|
80937
80971
|
const localWriteTools = [
|
|
80938
80972
|
"writeLocalFile",
|
|
80939
80973
|
"editLocalFile",
|
|
80974
|
+
"saveToMemory",
|
|
80940
80975
|
"bash",
|
|
80941
80976
|
"runBashTerminalCommand",
|
|
80942
80977
|
"generateAPAuditPacket",
|
|
@@ -80948,16 +80983,16 @@ function buildDesktopContextSection(input) {
|
|
|
80948
80983
|
return {
|
|
80949
80984
|
id: "desktop-context",
|
|
80950
80985
|
lane: "desktop",
|
|
80951
|
-
label: "
|
|
80986
|
+
label: "Terminal workspace",
|
|
80952
80987
|
content: [
|
|
80953
|
-
"##
|
|
80954
|
-
"Perch is running from a terminal.
|
|
80988
|
+
"## Terminal workspace",
|
|
80989
|
+
"Perch is running from a terminal. Files, shell, sandbox-code, and AP evidence tools are available for the current workspace.",
|
|
80955
80990
|
input.activeRootPath?.trim() ? `Workspace root: ${input.activeRootPath}. Treat relative paths as relative to this root.` : "Workspace root: current terminal directory.",
|
|
80956
|
-
"
|
|
80991
|
+
"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
80992
|
localReadTools.length > 0 ? `Read tools: ${localReadTools.join(", ")}.` : "Read tools: none exposed for this turn.",
|
|
80958
80993
|
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
80994
|
].join("\n"),
|
|
80960
|
-
reason: "Terminal
|
|
80995
|
+
reason: "Terminal workspace and tool availability for this turn.",
|
|
80961
80996
|
sourcePath: input.activeRootPath,
|
|
80962
80997
|
metadata: {
|
|
80963
80998
|
desktopConnected: false,
|
|
@@ -81016,8 +81051,14 @@ function buildDesktopContextSection(input) {
|
|
|
81016
81051
|
(file) => `- ${file.relativePath} (${file.matchReason})`
|
|
81017
81052
|
) : [];
|
|
81018
81053
|
const hasVisionSupport = input.selectedModelVisionSupport === true;
|
|
81019
|
-
const
|
|
81020
|
-
|
|
81054
|
+
const hasIndexedSourceSearch = Boolean(
|
|
81055
|
+
input.folderIndexSummary && (input.folderIndexSummary.sourceChunkCount > 0 || input.folderIndexSummary.retrievalChunkCount > 0)
|
|
81056
|
+
);
|
|
81057
|
+
const hasVectorSearch = Boolean(
|
|
81058
|
+
input.folderIndexSummary && (input.folderIndexSummary.embeddedChunkCount > 0 || input.folderIndexSummary.vectorReadyFiles > 0)
|
|
81059
|
+
);
|
|
81060
|
+
const searchHints = hasIndexedSourceSearch ? [
|
|
81061
|
+
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
81062
|
"Use glob for named files like *Screenshot* or exact extensions like *.png.",
|
|
81022
81063
|
"Use statPath or glob before claiming a file does not exist.",
|
|
81023
81064
|
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 +81067,16 @@ function buildDesktopContextSection(input) {
|
|
|
81026
81067
|
"Use statPath or glob before claiming a file does not exist.",
|
|
81027
81068
|
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
81069
|
];
|
|
81029
|
-
const folderIndexLine = input.folderIndexSummary ?
|
|
81030
|
-
|
|
81070
|
+
const folderIndexLine = input.folderIndexSummary ? [
|
|
81071
|
+
`Folder index: ${input.folderIndexSummary.indexedFiles}/${input.folderIndexSummary.totalFiles} indexed`,
|
|
81072
|
+
`${input.folderIndexSummary.registeredSources} registered source(s)`,
|
|
81073
|
+
`${input.folderIndexSummary.sourceChunkCount} text/source chunk(s)`,
|
|
81074
|
+
`${input.folderIndexSummary.retrievalChunkCount} retrieval chunk(s)`,
|
|
81075
|
+
`${input.folderIndexSummary.embeddedChunkCount} embedded retrieval chunk(s)`,
|
|
81076
|
+
`${input.folderIndexSummary.vectorReadyFiles} vector-ready file(s)`,
|
|
81077
|
+
`${input.folderIndexSummary.filesNeedingOcr} need OCR`
|
|
81078
|
+
].join(" \xB7 ") + "." : "Folder index: not loaded for this turn.";
|
|
81079
|
+
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
81080
|
const mcpToolNames = enabledToolSet.has("mcp__") ? [] : Array.from(enabledToolSet).filter((tool) => tool.startsWith("mcp__"));
|
|
81032
81081
|
const mcpServerNames = Array.from(
|
|
81033
81082
|
new Set(mcpToolNames.map((tool) => tool.split("__")[1]).filter(Boolean))
|
|
@@ -81060,7 +81109,7 @@ function buildDesktopContextSection(input) {
|
|
|
81060
81109
|
content: [
|
|
81061
81110
|
"## Desktop environment",
|
|
81062
81111
|
"Local workspace access is connected. Local filesystem tools are available.",
|
|
81063
|
-
input.activeRootPath?.trim() ? `
|
|
81112
|
+
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
81113
|
`Visible local sources: ${totalVisibleFiles}`,
|
|
81065
81114
|
input.localSourcesMeta?.refreshedAt ? `Local source snapshot refreshed: ${input.localSourcesMeta.refreshedAt}` : null,
|
|
81066
81115
|
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 +81126,7 @@ function buildDesktopContextSection(input) {
|
|
|
81077
81126
|
availableReadTools.length > 0 ? `Read tools: ${availableReadTools.join(", ")}.` : "Read tools: none exposed for this turn.",
|
|
81078
81127
|
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
81128
|
].filter(Boolean).join("\n"),
|
|
81080
|
-
reason: "Local workspace access, optional folder
|
|
81129
|
+
reason: "Local workspace access, optional workspace folder, and tool availability for this turn.",
|
|
81081
81130
|
sourcePath: input.activeRootPath,
|
|
81082
81131
|
metadata: {
|
|
81083
81132
|
desktopConnected: true,
|
|
@@ -85234,7 +85283,7 @@ function buildProjectMemorySections(meta) {
|
|
|
85234
85283
|
lane: "project_memory",
|
|
85235
85284
|
label: "PERCH.md",
|
|
85236
85285
|
content: ["## PERCH.md", meta.perchMd.content].join("\n"),
|
|
85237
|
-
reason: "Project operator memory loaded from the selected
|
|
85286
|
+
reason: "Project operator memory loaded from the selected workspace folder.",
|
|
85238
85287
|
sourcePath: meta.perchMd.relativePath,
|
|
85239
85288
|
metadata: {
|
|
85240
85289
|
relativePath: meta.perchMd.relativePath,
|
|
@@ -85271,7 +85320,7 @@ function buildProjectMemorySections(meta) {
|
|
|
85271
85320
|
lane: "project_memory",
|
|
85272
85321
|
label: `Rule: ${rule.fileName}`,
|
|
85273
85322
|
content: [`## Project rule: ${rule.fileName}`, rule.content].join("\n"),
|
|
85274
|
-
reason: "Rules-style project instruction loaded from the selected
|
|
85323
|
+
reason: "Rules-style project instruction loaded from the selected workspace folder.",
|
|
85275
85324
|
sourcePath: rule.relativePath,
|
|
85276
85325
|
metadata: {
|
|
85277
85326
|
relativePath: rule.relativePath,
|
|
@@ -85301,7 +85350,7 @@ function buildProjectMemorySections(meta) {
|
|
|
85301
85350
|
content: [`## Project memory: ${memory.fileName}`, memory.content].join(
|
|
85302
85351
|
"\n"
|
|
85303
85352
|
),
|
|
85304
|
-
reason: "Typed project memory loaded from the selected
|
|
85353
|
+
reason: "Typed project memory loaded from the selected workspace folder.",
|
|
85305
85354
|
sourcePath: memory.relativePath,
|
|
85306
85355
|
metadata: {
|
|
85307
85356
|
relativePath: memory.relativePath,
|
|
@@ -85412,7 +85461,7 @@ Tool strengths:
|
|
|
85412
85461
|
- 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
85462
|
- 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
85463
|
- 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
|
|
85464
|
+
- For a Mac folder: use glob and grep with the path; use listLocalSources/readLocalSourceFile only when a workspace folder is already selected
|
|
85416
85465
|
- Do not run a tool just because a keyword matches; reason from the user's current request and the existing thread state
|
|
85417
85466
|
- Do not redo an audit, document, or email draft that is already done unless the user asks for a revision
|
|
85418
85467
|
- Summarize tool outputs; don't stream raw JSON to the user
|
|
@@ -85426,9 +85475,9 @@ var QUILL_SPECIALIST_AGENTS_PROMPT;
|
|
|
85426
85475
|
var init_quillSpecialistPrompt = __esm({
|
|
85427
85476
|
"features/perchTerminal/agentPlatform/quillSpecialistPrompt.ts"() {
|
|
85428
85477
|
QUILL_SPECIALIST_AGENTS_PROMPT = `
|
|
85429
|
-
##
|
|
85478
|
+
## Writing & Research \u2014 Specialist Delegation
|
|
85430
85479
|
|
|
85431
|
-
|
|
85480
|
+
This task is writing/research/legal shaped. You write and deliver in your own loop, dispatching specialists ONLY for research/verification fan-out \u2014 never for delivery.
|
|
85432
85481
|
|
|
85433
85482
|
Use specialists (dispatch_agent) when independent research or verification benefits from separation:
|
|
85434
85483
|
- general_writer: drafts polished letters, memos, essays, emails, and revisions from the user's request plus provided context. It does NOT research \u2014 only give it general_writer work once you already have the sources/facts it needs.
|
|
@@ -85466,7 +85515,6 @@ Success rules:
|
|
|
85466
85515
|
- Do not say a Google Doc was created or an email was sent unless you have a verified receipt (URL / sent confirmation) or a screenshot showing the result.
|
|
85467
85516
|
- Safety: never click anything that grants access or changes sharing/permissions, such as "Share & send", "Share", or "Grant access". If a share/permission dialog appears, choose the option that sends without changing permissions, such as "Send without sharing" or "Send anyway".
|
|
85468
85517
|
- If delivery hits a problem, recover in the same loop. Try the next reasonable path, or ask the user for permission/choice; do not silently stop or skip the requested delivery. Never claim success without proof.
|
|
85469
|
-
- If the user asks for finance, AP, payroll, KYC, market, meeting, or close work, suggest Saffron unless the task is purely writing the prose.
|
|
85470
85518
|
`.trim();
|
|
85471
85519
|
}
|
|
85472
85520
|
});
|
|
@@ -91867,6 +91915,7 @@ function buildCoreSystemSection(input) {
|
|
|
91867
91915
|
].join("\n")
|
|
91868
91916
|
);
|
|
91869
91917
|
}
|
|
91918
|
+
lines.push("", persona.styleContract);
|
|
91870
91919
|
return lines.join("\n");
|
|
91871
91920
|
}
|
|
91872
91921
|
function appendAgentsModeGuidance(input) {
|
|
@@ -91899,9 +91948,8 @@ function appendAgentsModeGuidance(input) {
|
|
|
91899
91948
|
});
|
|
91900
91949
|
return;
|
|
91901
91950
|
}
|
|
91902
|
-
const isQuill = personaId === "quill";
|
|
91903
91951
|
lines.push(
|
|
91904
|
-
|
|
91952
|
+
"You're in Agents mode. Get the work done.",
|
|
91905
91953
|
"Execute directly when the task is clear and bounded.",
|
|
91906
91954
|
"For greetings, thanks, quick check-ins, or explanation-only questions, answer directly without tools unless the user asks you to inspect, search, run, create, send, or change something.",
|
|
91907
91955
|
"For delivery, write/send/change, filesystem, browser, or other external action turns, call the appropriate tool in the same turn and never end with only a promise, preamble, apology, or status line. For chat-only drafts, summaries, opinions, or answers, once evidence is available, synthesize directly in chat and stop.",
|
|
@@ -91921,9 +91969,10 @@ function appendAgentsModeGuidance(input) {
|
|
|
91921
91969
|
if (approvedPlanBlock) {
|
|
91922
91970
|
lines.push("", approvedPlanBlock);
|
|
91923
91971
|
}
|
|
91924
|
-
if (
|
|
91972
|
+
if (personaId === "quill" && isWritingResearchIntent(assemblyInput.trimmedInput)) {
|
|
91925
91973
|
lines.push("", QUILL_SPECIALIST_AGENTS_PROMPT);
|
|
91926
|
-
}
|
|
91974
|
+
}
|
|
91975
|
+
if (assemblyInput.coordinatorMode || isFinancialOperatorIntent(assemblyInput.trimmedInput)) {
|
|
91927
91976
|
lines.push("", FINANCIAL_OPERATOR_AGENTS_PROMPT);
|
|
91928
91977
|
}
|
|
91929
91978
|
lines.push("", PLATFORM_DELIVERY_GUIDANCE);
|
|
@@ -92036,6 +92085,11 @@ function isFinancialOperatorIntent(input) {
|
|
|
92036
92085
|
input
|
|
92037
92086
|
);
|
|
92038
92087
|
}
|
|
92088
|
+
function isWritingResearchIntent(input) {
|
|
92089
|
+
return /\b(writ(?:e|ing|ten)|draft|redraft|rewrite|revise|edit|proofread|polish|memo(?:randum)?|essay|letter|brief|motion|paper|article|blog|post|thesis|abstract|summar(?:y|ize|ise)|cit(?:e|ation|ations)|research|sources?|irac|case[-\s]?law|statute|regulation|legal|law\s+review)\b/i.test(
|
|
92090
|
+
input
|
|
92091
|
+
);
|
|
92092
|
+
}
|
|
92039
92093
|
function buildApprovedGeneralPlanBlock(session) {
|
|
92040
92094
|
const approvedPlan = session?.approvedGeneralPlan;
|
|
92041
92095
|
if (!approvedPlan) return null;
|
|
@@ -92427,7 +92481,7 @@ function buildContextSummary(input, rows) {
|
|
|
92427
92481
|
`mode=${input.chatMode}`,
|
|
92428
92482
|
`messages=${input.recentMessages.length}`,
|
|
92429
92483
|
`source=${input.selectedSourceId ?? "none"}`,
|
|
92430
|
-
`desktop=${input.desktopConnected ? "connected" : input.cliLocalTools === true ? "
|
|
92484
|
+
`desktop=${input.desktopConnected ? "connected" : input.cliLocalTools === true ? "terminal" : "browser"}`,
|
|
92431
92485
|
`sent=${sentCount}`,
|
|
92432
92486
|
`compacted=${compactedCount}`,
|
|
92433
92487
|
`not_found=${notFoundCount}`,
|
|
@@ -92786,9 +92840,15 @@ function createEmbeddingProviderFromEnv(env4 = readEnv()) {
|
|
|
92786
92840
|
};
|
|
92787
92841
|
}
|
|
92788
92842
|
function createBrowserEmbeddingProvider(route = "/api/perch-terminal/embeddings") {
|
|
92843
|
+
let resolvedModel = "server-configured";
|
|
92844
|
+
let resolvedDimensions = DEFAULT_RETRIEVAL_EMBEDDING_DIMENSIONS;
|
|
92789
92845
|
return {
|
|
92790
|
-
model
|
|
92791
|
-
|
|
92846
|
+
get model() {
|
|
92847
|
+
return resolvedModel;
|
|
92848
|
+
},
|
|
92849
|
+
get dimensions() {
|
|
92850
|
+
return resolvedDimensions;
|
|
92851
|
+
},
|
|
92792
92852
|
async embed(text) {
|
|
92793
92853
|
return (await this.embedBatch([text]))[0];
|
|
92794
92854
|
},
|
|
@@ -92811,6 +92871,12 @@ function createBrowserEmbeddingProvider(route = "/api/perch-terminal/embeddings"
|
|
|
92811
92871
|
if (!Array.isArray(payload.embeddings)) {
|
|
92812
92872
|
throw new EmbeddingProviderError("Embedding route returned no embeddings.");
|
|
92813
92873
|
}
|
|
92874
|
+
if (typeof payload.model === "string" && payload.model.trim()) {
|
|
92875
|
+
resolvedModel = payload.model;
|
|
92876
|
+
}
|
|
92877
|
+
if (typeof payload.dimensions === "number" && Number.isFinite(payload.dimensions)) {
|
|
92878
|
+
resolvedDimensions = payload.dimensions;
|
|
92879
|
+
}
|
|
92814
92880
|
return payload.embeddings;
|
|
92815
92881
|
}
|
|
92816
92882
|
};
|
|
@@ -92885,7 +92951,7 @@ var init_embeddingProvider = __esm({
|
|
|
92885
92951
|
|
|
92886
92952
|
// features/perchTerminal/persistence/permanentMemoryPersistence.ts
|
|
92887
92953
|
function createPermanentMemoryPersistence(supabase, options = {}) {
|
|
92888
|
-
const embeddingProvider = options.embeddingProvider ??
|
|
92954
|
+
const embeddingProvider = options.embeddingProvider ?? createDefaultPermanentMemoryEmbeddingProvider();
|
|
92889
92955
|
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
92956
|
const api2 = {
|
|
92891
92957
|
async listPermanentMemories({
|
|
@@ -93145,6 +93211,21 @@ function createPermanentMemoryPersistence(supabase, options = {}) {
|
|
|
93145
93211
|
};
|
|
93146
93212
|
return api2;
|
|
93147
93213
|
}
|
|
93214
|
+
function createDefaultPermanentMemoryEmbeddingProvider() {
|
|
93215
|
+
if (typeof window !== "undefined") return createBrowserEmbeddingProvider();
|
|
93216
|
+
const status = getEmbeddingProviderStatus();
|
|
93217
|
+
let provider = null;
|
|
93218
|
+
const load = () => {
|
|
93219
|
+
provider ??= createEmbeddingProviderFromEnv();
|
|
93220
|
+
return provider;
|
|
93221
|
+
};
|
|
93222
|
+
return {
|
|
93223
|
+
model: status.configured ? status.model : DEFAULT_RETRIEVAL_EMBEDDING_MODEL,
|
|
93224
|
+
dimensions: status.configured ? status.dimensions : DEFAULT_RETRIEVAL_EMBEDDING_DIMENSIONS,
|
|
93225
|
+
embed: async (text) => load().embed(text),
|
|
93226
|
+
embedBatch: async (texts) => load().embedBatch(texts)
|
|
93227
|
+
};
|
|
93228
|
+
}
|
|
93148
93229
|
async function findSimilarPermanentMemoryForInput(input) {
|
|
93149
93230
|
const queryEmbedding = await input.embeddingProvider.embed(canonicalMemoryEmbeddingText(input.input));
|
|
93150
93231
|
const { data, error } = await input.supabase.rpc("perch_ai_match_permanent_memories", {
|
|
@@ -117025,7 +117106,7 @@ function createSourcePersistence(supabase) {
|
|
|
117025
117106
|
return count ?? 0;
|
|
117026
117107
|
},
|
|
117027
117108
|
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);
|
|
117109
|
+
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
117110
|
if (error) throw error;
|
|
117030
117111
|
return count ?? 0;
|
|
117031
117112
|
},
|
|
@@ -117131,18 +117212,30 @@ function createSourcePersistence(supabase) {
|
|
|
117131
117212
|
return data ?? [];
|
|
117132
117213
|
},
|
|
117133
117214
|
async listIndexedSourceIds(workspaceId) {
|
|
117134
|
-
const [chunks, retrieval, memory] = await Promise.all([
|
|
117215
|
+
const [chunks, retrieval, embeddedRetrieval, memory] = await Promise.all([
|
|
117135
117216
|
supabase.from("perch_ai_source_chunks").select("source_id").eq("workspace_id", workspaceId),
|
|
117136
117217
|
supabase.from("perch_ai_retrieval_chunks").select("source_id").eq("workspace_id", workspaceId).eq("is_stale", false),
|
|
117218
|
+
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
117219
|
supabase.from("perch_ai_source_memory").select("source_id").eq("workspace_id", workspaceId)
|
|
117138
117220
|
]);
|
|
117139
117221
|
if (chunks.error) throw chunks.error;
|
|
117140
117222
|
if (retrieval.error) throw retrieval.error;
|
|
117223
|
+
if (embeddedRetrieval.error) throw embeddedRetrieval.error;
|
|
117141
117224
|
if (memory.error) throw memory.error;
|
|
117225
|
+
const sourceChunkSourceIds = sourceIdsFromRows(chunks.data);
|
|
117226
|
+
const retrievalChunkSourceIds = sourceIdsFromRows(retrieval.data);
|
|
117227
|
+
const embeddedRetrievalSourceIds = sourceIdsFromRows(embeddedRetrieval.data);
|
|
117142
117228
|
return {
|
|
117143
|
-
sourceChunkIds: new Set(
|
|
117144
|
-
retrievalChunkIds: new Set(
|
|
117145
|
-
|
|
117229
|
+
sourceChunkIds: new Set(sourceChunkSourceIds),
|
|
117230
|
+
retrievalChunkIds: new Set(retrievalChunkSourceIds),
|
|
117231
|
+
embeddedRetrievalChunkIds: new Set(embeddedRetrievalSourceIds),
|
|
117232
|
+
memorySourceIds: new Set((memory.data ?? []).map((r) => r.source_id)),
|
|
117233
|
+
sourceChunkCountsBySourceId: countBySourceId(sourceChunkSourceIds),
|
|
117234
|
+
retrievalChunkCountsBySourceId: countBySourceId(retrievalChunkSourceIds),
|
|
117235
|
+
embeddedRetrievalChunkCountsBySourceId: countBySourceId(embeddedRetrievalSourceIds),
|
|
117236
|
+
totalSourceChunks: sourceChunkSourceIds.length,
|
|
117237
|
+
totalRetrievalChunks: retrievalChunkSourceIds.length,
|
|
117238
|
+
totalEmbeddedRetrievalChunks: embeddedRetrievalSourceIds.length
|
|
117146
117239
|
};
|
|
117147
117240
|
},
|
|
117148
117241
|
async keywordSearchSourcesMetadata(workspaceId, query, limit) {
|
|
@@ -117198,6 +117291,16 @@ function createSourcePersistence(supabase) {
|
|
|
117198
117291
|
function escapeIlike2(value) {
|
|
117199
117292
|
return value.replace(/[%_\\]/g, (ch) => `\\${ch}`);
|
|
117200
117293
|
}
|
|
117294
|
+
function sourceIdsFromRows(rows) {
|
|
117295
|
+
return (rows ?? []).map((row) => row.source_id).filter((sourceId) => typeof sourceId === "string" && sourceId.trim().length > 0);
|
|
117296
|
+
}
|
|
117297
|
+
function countBySourceId(sourceIds) {
|
|
117298
|
+
const counts = /* @__PURE__ */ new Map();
|
|
117299
|
+
for (const sourceId of sourceIds) {
|
|
117300
|
+
counts.set(sourceId, (counts.get(sourceId) ?? 0) + 1);
|
|
117301
|
+
}
|
|
117302
|
+
return counts;
|
|
117303
|
+
}
|
|
117201
117304
|
var SOURCE_LIST_COLUMNS;
|
|
117202
117305
|
var init_sourcePersistence = __esm({
|
|
117203
117306
|
"features/perchTerminal/persistence/sourcePersistence.ts"() {
|
|
@@ -117875,21 +117978,22 @@ function getSourceRetrievalToolDefinitions() {
|
|
|
117875
117978
|
}
|
|
117876
117979
|
];
|
|
117877
117980
|
}
|
|
117878
|
-
async function dispatchSourceRetrievalTool(toolName, args, session) {
|
|
117879
|
-
if (!isSupabaseConfigured()) {
|
|
117880
|
-
return sessionError("
|
|
117981
|
+
async function dispatchSourceRetrievalTool(toolName, args, session, options = {}) {
|
|
117982
|
+
if (!options.supabase && !isSupabaseConfigured()) {
|
|
117983
|
+
return sessionError("Workspace source search is unavailable in this session.");
|
|
117881
117984
|
}
|
|
117882
117985
|
if (!session.workspaceId) {
|
|
117883
|
-
return sessionError("
|
|
117986
|
+
return sessionError("Sign in to a workspace before using workspace source search.");
|
|
117884
117987
|
}
|
|
117885
117988
|
let supabase;
|
|
117886
117989
|
try {
|
|
117887
|
-
supabase = createClient();
|
|
117990
|
+
supabase = options.supabase ?? createClient();
|
|
117888
117991
|
} catch (err) {
|
|
117889
117992
|
const message = err instanceof Error ? err.message : String(err);
|
|
117890
117993
|
return sessionError(message);
|
|
117891
117994
|
}
|
|
117892
117995
|
const store = createSourcePersistence(supabase);
|
|
117996
|
+
const retrievalStore = createRetrievalPersistence(supabase);
|
|
117893
117997
|
const workspaceId = session.workspaceId;
|
|
117894
117998
|
switch (toolName) {
|
|
117895
117999
|
case TOOL_NAMES.listSources:
|
|
@@ -117905,9 +118009,9 @@ async function dispatchSourceRetrievalTool(toolName, args, session) {
|
|
|
117905
118009
|
case TOOL_NAMES.resolveSourceCandidates:
|
|
117906
118010
|
return resolveSourceCandidatesHandler(store, workspaceId, session, args);
|
|
117907
118011
|
case TOOL_NAMES.retrieveContext:
|
|
117908
|
-
return retrieveContextHandler(store, workspaceId, session, args);
|
|
118012
|
+
return retrieveContextHandler(store, retrievalStore, workspaceId, session, args);
|
|
117909
118013
|
case TOOL_NAMES.semanticSearch:
|
|
117910
|
-
return semanticSearchHandler(store, workspaceId, args);
|
|
118014
|
+
return semanticSearchHandler(store, retrievalStore, workspaceId, args);
|
|
117911
118015
|
case TOOL_NAMES.diagnoseWorkspaceAccess:
|
|
117912
118016
|
return diagnoseWorkspaceAccessHandler(store, workspaceId, session);
|
|
117913
118017
|
default:
|
|
@@ -118170,7 +118274,7 @@ async function resolveSourceCandidatesHandler(store, workspaceId, session, args)
|
|
|
118170
118274
|
schemaVersion: "perch-tool-result-v1"
|
|
118171
118275
|
};
|
|
118172
118276
|
}
|
|
118173
|
-
async function retrieveContextHandler(store, workspaceId, session, args) {
|
|
118277
|
+
async function retrieveContextHandler(store, retrievalStore, workspaceId, session, args) {
|
|
118174
118278
|
const query = stringArg(args.query)?.trim();
|
|
118175
118279
|
if (!query) {
|
|
118176
118280
|
return { ok: false, error: "query is required", toolName: TOOL_NAMES.retrieveContext };
|
|
@@ -118196,7 +118300,6 @@ async function retrieveContextHandler(store, workspaceId, session, args) {
|
|
|
118196
118300
|
let searchMode = "keyword_only";
|
|
118197
118301
|
let fallbackUsed = false;
|
|
118198
118302
|
let semanticError = null;
|
|
118199
|
-
const retrievalStore = createRetrievalPersistence(createClient());
|
|
118200
118303
|
const embeddedCount = await store.countEmbeddedRetrievalChunks(workspaceId).catch(() => 0);
|
|
118201
118304
|
retrievalAttempts.push("semantic_first");
|
|
118202
118305
|
try {
|
|
@@ -118394,7 +118497,7 @@ async function retrieveContextHandler(store, workspaceId, session, args) {
|
|
|
118394
118497
|
schemaVersion: "perch-tool-result-v1"
|
|
118395
118498
|
};
|
|
118396
118499
|
}
|
|
118397
|
-
async function semanticSearchHandler(store, workspaceId, args) {
|
|
118500
|
+
async function semanticSearchHandler(store, retrievalStore, workspaceId, args) {
|
|
118398
118501
|
const query = stringArg(args.query)?.trim();
|
|
118399
118502
|
if (!query) {
|
|
118400
118503
|
return { ok: false, error: "query is required", toolName: TOOL_NAMES.semanticSearch };
|
|
@@ -118403,7 +118506,7 @@ async function semanticSearchHandler(store, workspaceId, args) {
|
|
|
118403
118506
|
const sourceIds = Array.isArray(args.sourceIds) ? args.sourceIds.map(String) : void 0;
|
|
118404
118507
|
const includeStale = args.includeStale === true;
|
|
118405
118508
|
const embeddedCount = await store.countEmbeddedRetrievalChunks(workspaceId);
|
|
118406
|
-
const result2 = await
|
|
118509
|
+
const result2 = await retrievalStore.matchRetrievalChunks({
|
|
118407
118510
|
workspaceId,
|
|
118408
118511
|
query,
|
|
118409
118512
|
limit,
|
|
@@ -118448,7 +118551,7 @@ async function semanticSearchHandler(store, workspaceId, args) {
|
|
|
118448
118551
|
return {
|
|
118449
118552
|
rankSignals: signals,
|
|
118450
118553
|
reason: result2.searchMode === "hybrid" ? "hybrid_sparse_vector_rrf" : result2.searchMode === "sparse" ? "postgres_full_text_rank" : "semantic_similarity",
|
|
118451
|
-
scoreExplanation: result2.searchMode === "hybrid" ? `hybrid
|
|
118554
|
+
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
118555
|
};
|
|
118453
118556
|
})(),
|
|
118454
118557
|
rank: index + 1,
|
|
@@ -118488,7 +118591,7 @@ async function semanticSearchHandler(store, workspaceId, args) {
|
|
|
118488
118591
|
semanticAvailable: result2.embeddingProviderConfigured,
|
|
118489
118592
|
sparseAvailable: result2.sparseProviderConfigured,
|
|
118490
118593
|
fallbackUsed: result2.fallbackUsed,
|
|
118491
|
-
ranking: result2.searchMode === "hybrid" ? "
|
|
118594
|
+
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
118595
|
},
|
|
118493
118596
|
schemaVersion: "perch-tool-result-v1"
|
|
118494
118597
|
};
|
|
@@ -118497,7 +118600,7 @@ async function diagnoseWorkspaceAccessHandler(store, workspaceId, session) {
|
|
|
118497
118600
|
const workspace = await store.getWorkspaceRow(workspaceId);
|
|
118498
118601
|
const blockers = [];
|
|
118499
118602
|
if (!workspace) {
|
|
118500
|
-
blockers.push("Workspace
|
|
118603
|
+
blockers.push("Workspace is not available to this signed-in session.");
|
|
118501
118604
|
}
|
|
118502
118605
|
let sourcesCount = 0;
|
|
118503
118606
|
let sourceChunksCount = 0;
|
|
@@ -133500,6 +133603,8 @@ var init_toolPermissionPolicy = __esm({
|
|
|
133500
133603
|
TOOL_NAMES.deleteLocalFile,
|
|
133501
133604
|
TOOL_NAMES.editLocalFile,
|
|
133502
133605
|
TOOL_NAMES.statPath,
|
|
133606
|
+
TOOL_NAMES.readProjectMemory,
|
|
133607
|
+
TOOL_NAMES.saveToMemory,
|
|
133503
133608
|
TOOL_NAMES.listLocalSources,
|
|
133504
133609
|
TOOL_NAMES.readLocalSourceFile,
|
|
133505
133610
|
TOOL_NAMES.generateAPAuditPacket,
|
|
@@ -135197,7 +135302,7 @@ function getDesktopToolDefinitions() {
|
|
|
135197
135302
|
type: "function",
|
|
135198
135303
|
function: {
|
|
135199
135304
|
name: TOOL_NAMES.readProjectMemory,
|
|
135200
|
-
description: "Read project memory
|
|
135305
|
+
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
135306
|
parameters: {
|
|
135202
135307
|
type: "object",
|
|
135203
135308
|
properties: {},
|
|
@@ -135209,7 +135314,7 @@ function getDesktopToolDefinitions() {
|
|
|
135209
135314
|
type: "function",
|
|
135210
135315
|
function: {
|
|
135211
135316
|
name: TOOL_NAMES.saveToMemory,
|
|
135212
|
-
description: "Save
|
|
135317
|
+
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
135318
|
parameters: {
|
|
135214
135319
|
type: "object",
|
|
135215
135320
|
properties: {
|
|
@@ -135225,6 +135330,15 @@ function getDesktopToolDefinitions() {
|
|
|
135225
135330
|
sectionHeading: {
|
|
135226
135331
|
type: "string",
|
|
135227
135332
|
description: "Required for merge mode \u2014 markdown heading to upsert under."
|
|
135333
|
+
},
|
|
135334
|
+
scope: {
|
|
135335
|
+
type: "string",
|
|
135336
|
+
enum: ["private_user", "workspace", "project"],
|
|
135337
|
+
description: "Optional durable-memory scope for signed-in server memory. Defaults from fileName."
|
|
135338
|
+
},
|
|
135339
|
+
title: {
|
|
135340
|
+
type: "string",
|
|
135341
|
+
description: "Optional short title for signed-in durable memory."
|
|
135228
135342
|
}
|
|
135229
135343
|
},
|
|
135230
135344
|
required: ["fileName", "mode", "content"],
|
|
@@ -135280,7 +135394,7 @@ function getDesktopToolDefinitions() {
|
|
|
135280
135394
|
type: "function",
|
|
135281
135395
|
function: {
|
|
135282
135396
|
name: TOOL_NAMES.getProjectRules,
|
|
135283
|
-
description: "Read PERCH.md and small .perch/rules/*.md or *.txt project rule files for the active folder
|
|
135397
|
+
description: "Read PERCH.md and small .perch/rules/*.md or *.txt project rule files for the active workspace folder.",
|
|
135284
135398
|
parameters: {
|
|
135285
135399
|
type: "object",
|
|
135286
135400
|
properties: {},
|
|
@@ -135298,7 +135412,7 @@ function getDesktopToolDefinitions() {
|
|
|
135298
135412
|
properties: {
|
|
135299
135413
|
path: {
|
|
135300
135414
|
type: "string",
|
|
135301
|
-
description: "Absolute directory path to list (e.g. /Users/you/Desktop). Works with no folder
|
|
135415
|
+
description: "Absolute directory path to list (e.g. /Users/you/Desktop). Works with no workspace folder selected."
|
|
135302
135416
|
},
|
|
135303
135417
|
query: {
|
|
135304
135418
|
type: "string",
|
|
@@ -136486,7 +136600,7 @@ function getNativeToolDefinitions() {
|
|
|
136486
136600
|
type: "function",
|
|
136487
136601
|
function: {
|
|
136488
136602
|
name: TOOL_NAMES.ctxInspect,
|
|
136489
|
-
description: "Inspect current execution context: desktop connection status, selected folder
|
|
136603
|
+
description: "Inspect current execution context: desktop connection status, selected workspace folder, permission mode, tool counts, and available tools.",
|
|
136490
136604
|
parameters: {
|
|
136491
136605
|
type: "object",
|
|
136492
136606
|
properties: {},
|
|
@@ -136520,7 +136634,7 @@ function getNativeToolDefinitions() {
|
|
|
136520
136634
|
type: "function",
|
|
136521
136635
|
function: {
|
|
136522
136636
|
name: TOOL_NAMES.configInspect,
|
|
136523
|
-
description: "Inspect the Perch Terminal configuration: local workspace access, optional folder
|
|
136637
|
+
description: "Inspect the Perch Terminal configuration: local workspace access, optional workspace folder, permission mode, and capabilities.",
|
|
136524
136638
|
parameters: {
|
|
136525
136639
|
type: "object",
|
|
136526
136640
|
properties: {},
|
|
@@ -199835,6 +199949,7 @@ function containsBrowserDeliveryTask(tasks) {
|
|
|
199835
199949
|
var BROWSER_DELIVERY_ROLE_IDS;
|
|
199836
199950
|
var init_browserDeliveryLock = __esm({
|
|
199837
199951
|
"features/perchTerminal/agentPlatform/browserDeliveryLock.ts"() {
|
|
199952
|
+
"use strict";
|
|
199838
199953
|
BROWSER_DELIVERY_ROLE_IDS = /* @__PURE__ */ new Set([
|
|
199839
199954
|
"doc_writer",
|
|
199840
199955
|
"email_sender",
|
|
@@ -202460,8 +202575,6 @@ async function dispatchAgentHandler(args, ctx) {
|
|
|
202460
202575
|
parentToolCallId: ctx.parentToolCallId,
|
|
202461
202576
|
mcpTools: ctx.mcpTools ?? []
|
|
202462
202577
|
};
|
|
202463
|
-
const isQuillNormalTurn = ctx.personaId === "quill" && ctx.chatMode !== "coordinator" && !ctx.allowedCallableAgents?.length;
|
|
202464
|
-
void isQuillNormalTurn;
|
|
202465
202578
|
if (Array.isArray(args.tasks) && args.tasks.length > 0) {
|
|
202466
202579
|
const tasks = args.tasks.map(
|
|
202467
202580
|
(t) => ({
|
|
@@ -206113,7 +206226,7 @@ async function requireMemoryRootId(ctx, surface) {
|
|
|
206113
206226
|
if (!rootId) {
|
|
206114
206227
|
return {
|
|
206115
206228
|
ok: false,
|
|
206116
|
-
error: `No selected folder
|
|
206229
|
+
error: `No selected workspace folder is available for ${surface}.`
|
|
206117
206230
|
};
|
|
206118
206231
|
}
|
|
206119
206232
|
return { ok: true, rootId };
|
|
@@ -206138,14 +206251,86 @@ var init_readProjectMemory = __esm({
|
|
|
206138
206251
|
classification: { native: false },
|
|
206139
206252
|
handler: async (_args, ctx) => {
|
|
206140
206253
|
const root2 = await requireMemoryRootId(ctx, "project memory");
|
|
206141
|
-
if (!root2.ok)
|
|
206142
|
-
|
|
206254
|
+
if (!root2.ok) {
|
|
206255
|
+
return ctx.cliServerAppUrl?.trim() && ctx.cliServerAccessToken?.trim() ? {
|
|
206256
|
+
ok: true,
|
|
206257
|
+
backend: "server_memory",
|
|
206258
|
+
message: "Signed-in durable memory is available. Relevant memories are added automatically at the start of each CLI turn."
|
|
206259
|
+
} : {
|
|
206260
|
+
ok: false,
|
|
206261
|
+
errorCode: "memory_unavailable",
|
|
206262
|
+
error: "Project memory is unavailable in this CLI session. Run from a project folder with .perch memory or sign in for durable memory."
|
|
206263
|
+
};
|
|
206264
|
+
}
|
|
206265
|
+
const result2 = await readProjectMemory(root2.rootId);
|
|
206266
|
+
if (!result2.ok && ctx.cliServerAppUrl?.trim() && ctx.cliServerAccessToken?.trim()) {
|
|
206267
|
+
return {
|
|
206268
|
+
ok: true,
|
|
206269
|
+
backend: "server_memory",
|
|
206270
|
+
localProjectMemory: result2,
|
|
206271
|
+
message: "Signed-in durable memory is available. Relevant memories are added automatically at the start of each CLI turn."
|
|
206272
|
+
};
|
|
206273
|
+
}
|
|
206274
|
+
return result2;
|
|
206143
206275
|
}
|
|
206144
206276
|
};
|
|
206145
206277
|
}
|
|
206146
206278
|
});
|
|
206147
206279
|
|
|
206148
206280
|
// features/perchTerminal/runtime/toolSystem/tools/projectMemory/saveToMemory.ts
|
|
206281
|
+
function hasCliServerMemory(ctx) {
|
|
206282
|
+
return Boolean(
|
|
206283
|
+
ctx.cliServerAppUrl?.trim() && ctx.cliServerAccessToken?.trim()
|
|
206284
|
+
);
|
|
206285
|
+
}
|
|
206286
|
+
async function saveMemoryThroughCliServer(args, ctx) {
|
|
206287
|
+
const appUrl = ctx.cliServerAppUrl?.trim();
|
|
206288
|
+
const accessToken = ctx.cliServerAccessToken?.trim();
|
|
206289
|
+
if (!appUrl || !accessToken) return memoryUnavailable();
|
|
206290
|
+
try {
|
|
206291
|
+
const response = await fetch(`${appUrl.replace(/\/+$/, "")}/api/perch-terminal/cli-memory`, {
|
|
206292
|
+
method: "POST",
|
|
206293
|
+
headers: {
|
|
206294
|
+
Accept: "application/json",
|
|
206295
|
+
"Content-Type": "application/json",
|
|
206296
|
+
Authorization: `Bearer ${accessToken}`
|
|
206297
|
+
},
|
|
206298
|
+
body: JSON.stringify({
|
|
206299
|
+
action: "save",
|
|
206300
|
+
args,
|
|
206301
|
+
threadId: ctx.threadId ?? null,
|
|
206302
|
+
runId: ctx.runId ?? null,
|
|
206303
|
+
workspaceRoot: ctx.activeRootPath ?? ctx.workspaceRoot ?? null
|
|
206304
|
+
}),
|
|
206305
|
+
signal: ctx.signal
|
|
206306
|
+
});
|
|
206307
|
+
const payload = await response.json().catch(() => ({}));
|
|
206308
|
+
if (!response.ok) {
|
|
206309
|
+
return {
|
|
206310
|
+
ok: false,
|
|
206311
|
+
backend: "server_memory",
|
|
206312
|
+
errorCode: typeof payload.errorCode === "string" ? payload.errorCode : "memory_save_unavailable",
|
|
206313
|
+
error: typeof payload.message === "string" ? payload.message : "Durable memory is unavailable right now. Try again shortly or update Perch."
|
|
206314
|
+
};
|
|
206315
|
+
}
|
|
206316
|
+
return payload;
|
|
206317
|
+
} catch (error) {
|
|
206318
|
+
const aborted = error instanceof DOMException && error.name === "AbortError";
|
|
206319
|
+
return {
|
|
206320
|
+
ok: false,
|
|
206321
|
+
backend: "server_memory",
|
|
206322
|
+
errorCode: aborted ? "memory_save_cancelled" : "memory_save_unavailable",
|
|
206323
|
+
error: aborted ? "Memory save was stopped." : "Durable memory is unavailable right now. Try again shortly or update Perch."
|
|
206324
|
+
};
|
|
206325
|
+
}
|
|
206326
|
+
}
|
|
206327
|
+
function memoryUnavailable() {
|
|
206328
|
+
return {
|
|
206329
|
+
ok: false,
|
|
206330
|
+
errorCode: "memory_unavailable",
|
|
206331
|
+
error: "Memory is unavailable in this CLI session. Sign in with `perch login` or run from a project folder with .perch memory."
|
|
206332
|
+
};
|
|
206333
|
+
}
|
|
206149
206334
|
var saveToMemoryTool;
|
|
206150
206335
|
var init_saveToMemory = __esm({
|
|
206151
206336
|
"features/perchTerminal/runtime/toolSystem/tools/projectMemory/saveToMemory.ts"() {
|
|
@@ -206157,14 +206342,24 @@ var init_saveToMemory = __esm({
|
|
|
206157
206342
|
name: TOOL_NAMES.saveToMemory,
|
|
206158
206343
|
classification: { native: false },
|
|
206159
206344
|
handler: async (args, ctx) => {
|
|
206345
|
+
const serverResult = hasCliServerMemory(ctx) ? await saveMemoryThroughCliServer(args, ctx) : null;
|
|
206346
|
+
if (serverResult?.ok) return serverResult;
|
|
206160
206347
|
const root2 = await requireMemoryRootId(ctx, "project memory");
|
|
206161
|
-
if (!root2.ok) return
|
|
206162
|
-
|
|
206348
|
+
if (!root2.ok) return serverResult ?? memoryUnavailable();
|
|
206349
|
+
const localResult = await writeMemoryFile(root2.rootId, {
|
|
206163
206350
|
fileName: String(args.fileName),
|
|
206164
206351
|
mode: String(args.mode),
|
|
206165
206352
|
content: String(args.content ?? ""),
|
|
206166
206353
|
sectionHeading: typeof args.sectionHeading === "string" ? args.sectionHeading : void 0
|
|
206167
206354
|
});
|
|
206355
|
+
if (localResult.ok === true) {
|
|
206356
|
+
return {
|
|
206357
|
+
...localResult,
|
|
206358
|
+
backend: "local_project_memory",
|
|
206359
|
+
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."
|
|
206360
|
+
};
|
|
206361
|
+
}
|
|
206362
|
+
return serverResult ?? localResult ?? memoryUnavailable();
|
|
206168
206363
|
}
|
|
206169
206364
|
};
|
|
206170
206365
|
}
|
|
@@ -207133,7 +207328,7 @@ var init_sendWorkerMessage2 = __esm({
|
|
|
207133
207328
|
});
|
|
207134
207329
|
|
|
207135
207330
|
// features/perchTerminal/runtime/toolSystem/tools/workers/spawnWorker.ts
|
|
207136
|
-
var
|
|
207331
|
+
var spawnWorkerTool;
|
|
207137
207332
|
var init_spawnWorker2 = __esm({
|
|
207138
207333
|
"features/perchTerminal/runtime/toolSystem/tools/workers/spawnWorker.ts"() {
|
|
207139
207334
|
"use strict";
|
|
@@ -207142,11 +207337,6 @@ var init_spawnWorker2 = __esm({
|
|
|
207142
207337
|
init_agentDispatch();
|
|
207143
207338
|
init_localScope();
|
|
207144
207339
|
init_toolNames();
|
|
207145
|
-
QUILL_BLOCKED_DELIVERY_WORKER_IDS = /* @__PURE__ */ new Set([
|
|
207146
|
-
"doc_writer",
|
|
207147
|
-
"email_sender",
|
|
207148
|
-
"calendar_scheduler"
|
|
207149
|
-
]);
|
|
207150
207340
|
spawnWorkerTool = {
|
|
207151
207341
|
name: TOOL_NAMES.spawnWorker,
|
|
207152
207342
|
classification: { native: false },
|
|
@@ -207164,8 +207354,6 @@ var init_spawnWorker2 = __esm({
|
|
|
207164
207354
|
errorCode: "worker_event_sink_missing"
|
|
207165
207355
|
};
|
|
207166
207356
|
}
|
|
207167
|
-
if (ctx.personaId === "quill" && ctx.chatMode !== "coordinator" && !ctx.allowedCallableAgents?.length && QUILL_BLOCKED_DELIVERY_WORKER_IDS.has(workerId)) {
|
|
207168
|
-
}
|
|
207169
207357
|
const enrichedContext = await threadPriorSpecialistContext({
|
|
207170
207358
|
threadId: ctx.threadId,
|
|
207171
207359
|
roleId: workerId,
|
|
@@ -213219,7 +213407,7 @@ var init_localSources = __esm({
|
|
|
213219
213407
|
return {
|
|
213220
213408
|
ok: true,
|
|
213221
213409
|
sources: [],
|
|
213222
|
-
warning: "
|
|
213410
|
+
warning: "Local workspace access is unavailable in this chat. Open Perch Desktop or attach a workspace folder, then try again."
|
|
213223
213411
|
};
|
|
213224
213412
|
}
|
|
213225
213413
|
const maxResults = sanitizeListLocalSourcesMaxResults(args.maxResults);
|
|
@@ -216703,6 +216891,8 @@ async function executeToolCall(call, opts) {
|
|
|
216703
216891
|
workspaceId: opts.workspaceId,
|
|
216704
216892
|
threadId: opts.threadId,
|
|
216705
216893
|
supabase: opts.supabase,
|
|
216894
|
+
cliServerAppUrl: opts.cliServerAppUrl,
|
|
216895
|
+
cliServerAccessToken: opts.cliServerAccessToken,
|
|
216706
216896
|
marketDeskProxyAppUrl: opts.marketDeskProxyAppUrl,
|
|
216707
216897
|
marketDeskProxyAccessToken: opts.marketDeskProxyAccessToken,
|
|
216708
216898
|
chatMode: opts.chatMode ?? null,
|
|
@@ -216762,7 +216952,7 @@ async function executeToolCall(call, opts) {
|
|
|
216762
216952
|
const result2 = {
|
|
216763
216953
|
ok: false,
|
|
216764
216954
|
errorCode: "supabase_session_required",
|
|
216765
|
-
error: isSupabaseConfigured() ? "Source retrieval tools require an authenticated workspace session." : "
|
|
216955
|
+
error: isSupabaseConfigured() ? "Source retrieval tools require an authenticated workspace session." : "Workspace source search is unavailable in this session.",
|
|
216766
216956
|
schemaVersion: "perch-tool-result-v1",
|
|
216767
216957
|
toolName: call.name
|
|
216768
216958
|
};
|
|
@@ -216843,6 +217033,8 @@ async function executeToolCall(call, opts) {
|
|
|
216843
217033
|
workspaceRoot: opts.workspaceRoot,
|
|
216844
217034
|
threadId: opts.threadId,
|
|
216845
217035
|
runId: opts.runId,
|
|
217036
|
+
cliServerAppUrl: opts.cliServerAppUrl,
|
|
217037
|
+
cliServerAccessToken: opts.cliServerAccessToken,
|
|
216846
217038
|
marketDeskProxyAppUrl: opts.marketDeskProxyAppUrl,
|
|
216847
217039
|
marketDeskProxyAccessToken: opts.marketDeskProxyAccessToken,
|
|
216848
217040
|
onEvent: runtime.onEvent,
|
|
@@ -219043,6 +219235,8 @@ async function runModelToolLoop(input) {
|
|
|
219043
219235
|
selectedSourceId: input.selectedSourceId,
|
|
219044
219236
|
supabaseConfigured: input.supabaseConfigured,
|
|
219045
219237
|
supabase: input.supabase,
|
|
219238
|
+
cliServerAppUrl: input.cliServerAppUrl,
|
|
219239
|
+
cliServerAccessToken: input.cliServerAccessToken,
|
|
219046
219240
|
marketDeskProxyAppUrl: input.marketDeskProxyAppUrl,
|
|
219047
219241
|
marketDeskProxyAccessToken: input.marketDeskProxyAccessToken,
|
|
219048
219242
|
runId: input.runId,
|
|
@@ -219459,6 +219653,8 @@ async function executeInitialToolCall(input) {
|
|
|
219459
219653
|
selectedSourceId: input.input.selectedSourceId,
|
|
219460
219654
|
supabaseConfigured: input.input.supabaseConfigured,
|
|
219461
219655
|
supabase: input.input.supabase,
|
|
219656
|
+
cliServerAppUrl: input.input.cliServerAppUrl,
|
|
219657
|
+
cliServerAccessToken: input.input.cliServerAccessToken,
|
|
219462
219658
|
marketDeskProxyAppUrl: input.input.marketDeskProxyAppUrl,
|
|
219463
219659
|
marketDeskProxyAccessToken: input.input.marketDeskProxyAccessToken,
|
|
219464
219660
|
runId: input.input.runId,
|
|
@@ -220320,52 +220516,15 @@ var init_toolLoop = __esm({
|
|
|
220320
220516
|
}
|
|
220321
220517
|
});
|
|
220322
220518
|
|
|
220323
|
-
// features/perchTerminal/runtime/personas/
|
|
220324
|
-
function isQuillBlockedToolName(toolName) {
|
|
220325
|
-
return QUILL_BLOCKED_TOOL_NAMES.has(toolName);
|
|
220326
|
-
}
|
|
220327
|
-
function filterToolsForQuill(toolDefinitions) {
|
|
220328
|
-
return toolDefinitions.filter((tool) => !isQuillBlockedToolName(tool.function.name));
|
|
220329
|
-
}
|
|
220330
|
-
var QUILL_BLOCKED_TOOL_NAMES;
|
|
220331
|
-
var init_quillToolPolicy = __esm({
|
|
220332
|
-
"features/perchTerminal/runtime/personas/quillToolPolicy.ts"() {
|
|
220333
|
-
"use strict";
|
|
220334
|
-
init_toolNames();
|
|
220335
|
-
QUILL_BLOCKED_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
220336
|
-
TOOL_NAMES.runSuite,
|
|
220337
|
-
TOOL_NAMES.runManagedPlaybook,
|
|
220338
|
-
TOOL_NAMES.listSuiteCatalog,
|
|
220339
|
-
TOOL_NAMES.proposeSuitePlan,
|
|
220340
|
-
TOOL_NAMES.executeSuitePlan,
|
|
220341
|
-
TOOL_NAMES.proposeWork,
|
|
220342
|
-
TOOL_NAMES.executeWork,
|
|
220343
|
-
TOOL_NAMES.generateAPAuditPacket,
|
|
220344
|
-
TOOL_NAMES.safeBrowserAction,
|
|
220345
|
-
// Deprecated non-verified shortcuts — superseded by the verified surface tools.
|
|
220346
|
-
TOOL_NAMES.gmailSendEmail,
|
|
220347
|
-
TOOL_NAMES.gmailSaveDraft,
|
|
220348
|
-
TOOL_NAMES.googleDocsCreate,
|
|
220349
|
-
TOOL_NAMES.googleDocsAppend,
|
|
220350
|
-
TOOL_NAMES.googleCalendarCreateEvent,
|
|
220351
|
-
TOOL_NAMES.googleSheetsCreate,
|
|
220352
|
-
TOOL_NAMES.googleSheetsAppendRows
|
|
220353
|
-
]);
|
|
220354
|
-
}
|
|
220355
|
-
});
|
|
220356
|
-
|
|
220357
|
-
// features/perchTerminal/runtime/personas/saffronToolPolicy.ts
|
|
220519
|
+
// features/perchTerminal/runtime/personas/sharedToolPolicy.ts
|
|
220358
220520
|
function filterSuiteRelayTools(toolDefinitions, opts = {}) {
|
|
220359
220521
|
if (opts.allowSuiteRelay) return toolDefinitions;
|
|
220360
220522
|
return toolDefinitions.filter(
|
|
220361
220523
|
(tool) => !SUITE_RELAY_TOOL_NAMES.has(tool.function.name)
|
|
220362
220524
|
);
|
|
220363
220525
|
}
|
|
220364
|
-
|
|
220365
|
-
|
|
220366
|
-
}
|
|
220367
|
-
var init_saffronToolPolicy = __esm({
|
|
220368
|
-
"features/perchTerminal/runtime/personas/saffronToolPolicy.ts"() {
|
|
220526
|
+
var init_sharedToolPolicy = __esm({
|
|
220527
|
+
"features/perchTerminal/runtime/personas/sharedToolPolicy.ts"() {
|
|
220369
220528
|
"use strict";
|
|
220370
220529
|
init_suiteRelayKillSwitch();
|
|
220371
220530
|
}
|
|
@@ -220483,10 +220642,7 @@ async function runLiveAgentsLoop(input) {
|
|
|
220483
220642
|
PLAN_MODE_ALLOWED_TOOL_NAMES
|
|
220484
220643
|
) : effectiveChatMode === "ask" ? [] : getReadOnlyToolDefinitions(toolOpts);
|
|
220485
220644
|
const suiteRelayEnabled = isSuiteRelayEnabled();
|
|
220486
|
-
const
|
|
220487
|
-
allowSuiteRelay: suiteRelayEnabled
|
|
220488
|
-
}) : baseAgentsTools;
|
|
220489
|
-
const suiteRelayFilteredTools = filterSuiteRelayTools(personaFilteredTools, {
|
|
220645
|
+
const suiteRelayFilteredTools = filterSuiteRelayTools(baseAgentsTools, {
|
|
220490
220646
|
allowSuiteRelay: suiteRelayEnabled
|
|
220491
220647
|
});
|
|
220492
220648
|
const deliveryPolicyDeliveryOperatorOnly = turn.deliveryOperatorOnly === true;
|
|
@@ -220520,7 +220676,7 @@ async function runLiveAgentsLoop(input) {
|
|
|
220520
220676
|
const onEv = onEvent ?? deps.onEvent;
|
|
220521
220677
|
onEv({
|
|
220522
220678
|
type: "activity_delta",
|
|
220523
|
-
text: "Workspace preflight: no folder
|
|
220679
|
+
text: "Workspace preflight: no workspace folder selected; continuing with Desktop tools.",
|
|
220524
220680
|
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
220525
220681
|
});
|
|
220526
220682
|
}
|
|
@@ -220559,6 +220715,8 @@ async function runLiveAgentsLoop(input) {
|
|
|
220559
220715
|
untrustedContextPresent: context.untrustedContextPresent,
|
|
220560
220716
|
supabaseConfigured: turn.supabaseConfigured,
|
|
220561
220717
|
supabase: turn.supabase,
|
|
220718
|
+
cliServerAppUrl: turn.cliServerAppUrl ?? null,
|
|
220719
|
+
cliServerAccessToken: turn.cliServerAccessToken ?? null,
|
|
220562
220720
|
marketDeskProxyAppUrl: turn.marketDeskProxyAppUrl ?? null,
|
|
220563
220721
|
marketDeskProxyAccessToken: turn.marketDeskProxyAccessToken ?? null,
|
|
220564
220722
|
onEvent: onEvent ?? deps.onEvent,
|
|
@@ -220937,8 +221095,7 @@ var init_liveAgentsLoop = __esm({
|
|
|
220937
221095
|
init_toolNames();
|
|
220938
221096
|
init_planModeStateMachine();
|
|
220939
221097
|
init_toolPermissionPolicy();
|
|
220940
|
-
|
|
220941
|
-
init_saffronToolPolicy();
|
|
221098
|
+
init_sharedToolPolicy();
|
|
220942
221099
|
init_deliveryToolPolicy();
|
|
220943
221100
|
init_threadSession();
|
|
220944
221101
|
init_sandboxProvenance();
|
|
@@ -221925,7 +222082,12 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
221925
222082
|
contentHash: null,
|
|
221926
222083
|
chunksBuilt: 0,
|
|
221927
222084
|
chunksUpserted: 0,
|
|
221928
|
-
|
|
222085
|
+
sourceChunksUpserted: 0,
|
|
222086
|
+
retrievalChunksBuilt: 0,
|
|
222087
|
+
retrievalChunksUpserted: 0,
|
|
222088
|
+
embeddedChunksUpserted: 0,
|
|
222089
|
+
vectorReady: false,
|
|
222090
|
+
embeddingConfigured: isBrowserRuntime() ? true : getEmbeddingProviderStatus().configured,
|
|
221929
222091
|
errors: [],
|
|
221930
222092
|
warnings: []
|
|
221931
222093
|
};
|
|
@@ -221958,7 +222120,12 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
221958
222120
|
contentHash,
|
|
221959
222121
|
chunksBuilt: 0,
|
|
221960
222122
|
chunksUpserted: 0,
|
|
221961
|
-
|
|
222123
|
+
sourceChunksUpserted: 0,
|
|
222124
|
+
retrievalChunksBuilt: 0,
|
|
222125
|
+
retrievalChunksUpserted: 0,
|
|
222126
|
+
embeddedChunksUpserted: 0,
|
|
222127
|
+
vectorReady: true,
|
|
222128
|
+
embeddingConfigured: isBrowserRuntime() ? true : getEmbeddingProviderStatus().configured,
|
|
221962
222129
|
errors: [],
|
|
221963
222130
|
warnings: []
|
|
221964
222131
|
};
|
|
@@ -222056,12 +222223,15 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
222056
222223
|
threadId: input.threadId ?? null,
|
|
222057
222224
|
chunks: sourceTextChunks
|
|
222058
222225
|
});
|
|
222059
|
-
const embeddingStatus = getEmbeddingProviderStatus();
|
|
222060
222226
|
let chunksBuilt = textChunks.length;
|
|
222061
222227
|
let chunksUpserted = 0;
|
|
222062
222228
|
const warnings = [...extracted.truncated ? ["Source text was truncated before indexing."] : []];
|
|
222063
|
-
|
|
222064
|
-
|
|
222229
|
+
let embeddingProvider;
|
|
222230
|
+
try {
|
|
222231
|
+
embeddingProvider = input.embeddingProvider ?? createDefaultLocalSourceEmbeddingProvider();
|
|
222232
|
+
} catch (error) {
|
|
222233
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
222234
|
+
warnings.push(message);
|
|
222065
222235
|
return {
|
|
222066
222236
|
ok: true,
|
|
222067
222237
|
status: "indexed",
|
|
@@ -222071,38 +222241,69 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
222071
222241
|
contentHash: indexedContentHash,
|
|
222072
222242
|
chunksBuilt,
|
|
222073
222243
|
chunksUpserted: 0,
|
|
222244
|
+
sourceChunksUpserted: sourceTextChunks.length,
|
|
222245
|
+
retrievalChunksBuilt: chunksBuilt,
|
|
222246
|
+
retrievalChunksUpserted: 0,
|
|
222247
|
+
embeddedChunksUpserted: 0,
|
|
222248
|
+
vectorReady: false,
|
|
222249
|
+
embeddingConfigured: false,
|
|
222250
|
+
errors: [],
|
|
222251
|
+
warnings
|
|
222252
|
+
};
|
|
222253
|
+
}
|
|
222254
|
+
let indexResult;
|
|
222255
|
+
try {
|
|
222256
|
+
const indexer = createRetrievalIndexer(input.supabase, { embeddingProvider });
|
|
222257
|
+
indexResult = pdfPages ? await indexer.indexLocalFilePages({
|
|
222258
|
+
workspaceId: input.workspaceId,
|
|
222259
|
+
sourceId: source.id,
|
|
222260
|
+
fileName: source.file_name,
|
|
222261
|
+
fileType: extracted.fileType,
|
|
222262
|
+
mimeType: source.mime_type,
|
|
222263
|
+
provenanceType: "desktop_local",
|
|
222264
|
+
contentHash: indexedContentHash,
|
|
222265
|
+
threadId: input.threadId ?? null,
|
|
222266
|
+
pages: pdfPages,
|
|
222267
|
+
blocks: pdfBlocks,
|
|
222268
|
+
tables: pdfTables,
|
|
222269
|
+
facts: factPageEntries.length ? factPageEntries : void 0
|
|
222270
|
+
}) : await indexer.indexLocalFileText({
|
|
222271
|
+
workspaceId: input.workspaceId,
|
|
222272
|
+
sourceId: source.id,
|
|
222273
|
+
fileName: source.file_name,
|
|
222274
|
+
fileType: extracted.fileType,
|
|
222275
|
+
mimeType: source.mime_type,
|
|
222276
|
+
provenanceType: "desktop_local",
|
|
222277
|
+
contentHash: indexedContentHash,
|
|
222278
|
+
threadId: input.threadId ?? null,
|
|
222279
|
+
text: extracted.text
|
|
222280
|
+
});
|
|
222281
|
+
} catch (error) {
|
|
222282
|
+
if (!isEmbeddingProviderNotConfigured(error)) throw error;
|
|
222283
|
+
warnings.push(error.message);
|
|
222284
|
+
return {
|
|
222285
|
+
ok: true,
|
|
222286
|
+
status: "indexed",
|
|
222287
|
+
localSourceId: input.localSourceId,
|
|
222288
|
+
supabaseSourceId: source.id,
|
|
222289
|
+
retrievalSourceId: null,
|
|
222290
|
+
contentHash: indexedContentHash,
|
|
222291
|
+
chunksBuilt,
|
|
222292
|
+
chunksUpserted: 0,
|
|
222293
|
+
sourceChunksUpserted: sourceTextChunks.length,
|
|
222294
|
+
retrievalChunksBuilt: chunksBuilt,
|
|
222295
|
+
retrievalChunksUpserted: 0,
|
|
222296
|
+
embeddedChunksUpserted: 0,
|
|
222297
|
+
vectorReady: false,
|
|
222074
222298
|
embeddingConfigured: false,
|
|
222075
222299
|
errors: [],
|
|
222076
222300
|
warnings
|
|
222077
222301
|
};
|
|
222078
222302
|
}
|
|
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
222303
|
chunksBuilt = indexResult.chunksBuilt;
|
|
222105
222304
|
chunksUpserted = indexResult.chunksUpserted;
|
|
222305
|
+
const embeddedChunksUpserted = indexResult.chunksEmbedded;
|
|
222306
|
+
const vectorReady = embeddedChunksUpserted > 0;
|
|
222106
222307
|
if (!indexResult.ok) {
|
|
222107
222308
|
return {
|
|
222108
222309
|
ok: false,
|
|
@@ -222113,6 +222314,11 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
222113
222314
|
contentHash: indexedContentHash,
|
|
222114
222315
|
chunksBuilt,
|
|
222115
222316
|
chunksUpserted,
|
|
222317
|
+
sourceChunksUpserted: sourceTextChunks.length,
|
|
222318
|
+
retrievalChunksBuilt: indexResult.chunksBuilt,
|
|
222319
|
+
retrievalChunksUpserted: indexResult.chunksUpserted,
|
|
222320
|
+
embeddedChunksUpserted,
|
|
222321
|
+
vectorReady,
|
|
222116
222322
|
embeddingConfigured: true,
|
|
222117
222323
|
errors: indexResult.errors,
|
|
222118
222324
|
warnings: [...warnings, ...indexResult.warnings]
|
|
@@ -222123,10 +222329,15 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
222123
222329
|
status: "indexed",
|
|
222124
222330
|
localSourceId: input.localSourceId,
|
|
222125
222331
|
supabaseSourceId: source.id,
|
|
222126
|
-
retrievalSourceId: source.id,
|
|
222332
|
+
retrievalSourceId: vectorReady ? source.id : null,
|
|
222127
222333
|
contentHash: indexedContentHash,
|
|
222128
222334
|
chunksBuilt,
|
|
222129
222335
|
chunksUpserted,
|
|
222336
|
+
sourceChunksUpserted: sourceTextChunks.length,
|
|
222337
|
+
retrievalChunksBuilt: indexResult.chunksBuilt,
|
|
222338
|
+
retrievalChunksUpserted: indexResult.chunksUpserted,
|
|
222339
|
+
embeddedChunksUpserted,
|
|
222340
|
+
vectorReady,
|
|
222130
222341
|
embeddingConfigured: true,
|
|
222131
222342
|
errors: [],
|
|
222132
222343
|
warnings
|
|
@@ -222142,6 +222353,12 @@ async function ensureLocalSourceIndexed(input) {
|
|
|
222142
222353
|
};
|
|
222143
222354
|
}
|
|
222144
222355
|
}
|
|
222356
|
+
function createDefaultLocalSourceEmbeddingProvider() {
|
|
222357
|
+
return isBrowserRuntime() ? createBrowserEmbeddingProvider() : createEmbeddingProviderFromEnv();
|
|
222358
|
+
}
|
|
222359
|
+
function isBrowserRuntime() {
|
|
222360
|
+
return typeof window !== "undefined";
|
|
222361
|
+
}
|
|
222145
222362
|
async function resolveRetrievalSourceIdForTurn(input) {
|
|
222146
222363
|
const selected = input.selectedSourceId?.trim() ?? null;
|
|
222147
222364
|
if (!selected) return { retrievalSourceId: null, indexOutcome: null };
|
|
@@ -222417,7 +222634,11 @@ async function loadFolderIndexSummary(input) {
|
|
|
222417
222634
|
}
|
|
222418
222635
|
const files = input.localSources.filter((entry) => entry.rootId === input.rootId && isAutoIndexableLocalSource(entry)).map((entry) => {
|
|
222419
222636
|
const row = rowByLocalId.get(entry.localSourceId) ?? null;
|
|
222420
|
-
const
|
|
222637
|
+
const sourceChunkCount2 = row ? indexSets.sourceChunkCountsBySourceId.get(row.id) ?? 0 : 0;
|
|
222638
|
+
const retrievalChunkCount2 = row ? indexSets.retrievalChunkCountsBySourceId.get(row.id) ?? 0 : 0;
|
|
222639
|
+
const embeddedChunkCount2 = row ? indexSets.embeddedRetrievalChunkCountsBySourceId.get(row.id) ?? 0 : 0;
|
|
222640
|
+
const retrievalReady = retrievalChunkCount2 > 0;
|
|
222641
|
+
const vectorReady = embeddedChunkCount2 > 0;
|
|
222421
222642
|
const stale = row ? Date.parse(entry.modifiedAt) > Date.parse(row.updated_at) : true;
|
|
222422
222643
|
return {
|
|
222423
222644
|
localSourceId: entry.localSourceId,
|
|
@@ -222426,19 +222647,30 @@ async function loadFolderIndexSummary(input) {
|
|
|
222426
222647
|
mimeType: entry.mimeType,
|
|
222427
222648
|
modifiedAt: entry.modifiedAt,
|
|
222428
222649
|
sizeBytes: entry.sizeBytes,
|
|
222429
|
-
status: row ? retrievalReady ? "indexed" : "pending" : "pending",
|
|
222650
|
+
status: row ? sourceChunkCount2 > 0 || retrievalReady ? "indexed" : "pending" : "pending",
|
|
222430
222651
|
supabaseSourceId: row?.id ?? null,
|
|
222431
222652
|
retrievalReady,
|
|
222653
|
+
vectorReady,
|
|
222654
|
+
sourceChunkCount: sourceChunkCount2,
|
|
222655
|
+
retrievalChunkCount: retrievalChunkCount2,
|
|
222656
|
+
embeddedChunkCount: embeddedChunkCount2,
|
|
222432
222657
|
contentHash: row?.content_hash ?? null,
|
|
222433
222658
|
message: stale && row ? "Changed since last indexing pass." : void 0
|
|
222434
222659
|
};
|
|
222435
222660
|
});
|
|
222436
222661
|
const staleFiles = files.filter((file) => file.message?.includes("Changed since last indexing pass")).length;
|
|
222437
222662
|
const retrievalReadySourceIds = files.filter((file) => file.retrievalReady && file.supabaseSourceId).map((file) => file.supabaseSourceId);
|
|
222663
|
+
const vectorReadySourceIds = files.filter((file) => file.vectorReady && file.supabaseSourceId).map((file) => file.supabaseSourceId);
|
|
222438
222664
|
const indexedFiles = files.filter((file) => file.status === "indexed").length;
|
|
222665
|
+
const sourceChunkCount = sumFiles(files, "sourceChunkCount");
|
|
222666
|
+
const retrievalChunkCount = sumFiles(files, "retrievalChunkCount");
|
|
222667
|
+
const embeddedChunkCount = sumFiles(files, "embeddedChunkCount");
|
|
222439
222668
|
const lastIndexedAt = rows[0]?.updated_at ?? null;
|
|
222440
222669
|
const diagnostics = [
|
|
222670
|
+
rows.length > 0 ? `${rows.length} registered source(s).` : null,
|
|
222671
|
+
sourceChunkCount > 0 ? `${sourceChunkCount} text/source chunk(s) available for keyword search.` : null,
|
|
222441
222672
|
retrievalReadySourceIds.length > 0 ? `${retrievalReadySourceIds.length} file(s) have fresh retrieval chunks.` : "No retrieval-ready files yet for this folder.",
|
|
222673
|
+
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
222674
|
staleFiles > 0 ? `${staleFiles} file(s) changed since the last index update.` : null
|
|
222443
222675
|
].filter(Boolean);
|
|
222444
222676
|
return {
|
|
@@ -222448,8 +222680,14 @@ async function loadFolderIndexSummary(input) {
|
|
|
222448
222680
|
indexedFiles,
|
|
222449
222681
|
skippedFiles: 0,
|
|
222450
222682
|
failedFiles: 0,
|
|
222683
|
+
registeredSources: rows.length,
|
|
222684
|
+
sourceChunkCount,
|
|
222685
|
+
sourceChunkFiles: files.filter((file) => file.sourceChunkCount > 0).length,
|
|
222686
|
+
retrievalChunkCount,
|
|
222451
222687
|
staleFiles,
|
|
222452
222688
|
retrievalReadyFiles: retrievalReadySourceIds.length,
|
|
222689
|
+
embeddedChunkCount,
|
|
222690
|
+
vectorReadyFiles: vectorReadySourceIds.length,
|
|
222453
222691
|
filesNeedingOcr: files.filter((file) => needsOcrHint(file)).length,
|
|
222454
222692
|
lastIndexedAt,
|
|
222455
222693
|
status: files.length === 0 ? "idle" : staleFiles > 0 || indexedFiles < files.length ? "partial" : "ready",
|
|
@@ -222457,6 +222695,7 @@ async function loadFolderIndexSummary(input) {
|
|
|
222457
222695
|
localOnlyEmbeddingsEnabled: false,
|
|
222458
222696
|
remoteIndexArtifacts: [...DEFAULT_REMOTE_INDEX_ARTIFACTS],
|
|
222459
222697
|
retrievalReadySourceIds,
|
|
222698
|
+
vectorReadySourceIds,
|
|
222460
222699
|
diagnostics,
|
|
222461
222700
|
manifestPath: DEFAULT_MANIFEST_PATH,
|
|
222462
222701
|
files
|
|
@@ -222476,6 +222715,9 @@ function needsOcrHint(file) {
|
|
|
222476
222715
|
const message = file.message?.toLowerCase() ?? "";
|
|
222477
222716
|
return message.includes("ocr") || message.includes("scanned") || message.includes("no readable text");
|
|
222478
222717
|
}
|
|
222718
|
+
function sumFiles(files, field) {
|
|
222719
|
+
return files.reduce((total, file) => total + file[field], 0);
|
|
222720
|
+
}
|
|
222479
222721
|
var DEFAULT_MANIFEST_PATH, AUTO_INDEX_EXCLUDED_PATH_PREFIXES, AUTO_INDEX_EXCLUDED_EXACT_PATHS, DEFAULT_STORAGE_MODE, DEFAULT_REMOTE_INDEX_ARTIFACTS;
|
|
222480
222722
|
var init_folderIndexing = __esm({
|
|
222481
222723
|
"features/perchTerminal/runtime/folderIndexing.ts"() {
|
|
@@ -224731,6 +224973,54 @@ var init_objectiveClassifier = __esm({
|
|
|
224731
224973
|
});
|
|
224732
224974
|
|
|
224733
224975
|
// features/perchTerminal/runtime/turn/runOperatorTurn.ts
|
|
224976
|
+
function buildTurnMemoryRetriever(input) {
|
|
224977
|
+
if (input.supabase) {
|
|
224978
|
+
return async ({ userId, workspaceId, limitPerScope, query }) => createPermanentMemoryPersistence(
|
|
224979
|
+
input.supabase
|
|
224980
|
+
).retrievePermanentMemoriesForTurn({
|
|
224981
|
+
userId,
|
|
224982
|
+
workspaceId,
|
|
224983
|
+
limitPerScope,
|
|
224984
|
+
query
|
|
224985
|
+
});
|
|
224986
|
+
}
|
|
224987
|
+
const appUrl = input.cliServerAppUrl?.trim();
|
|
224988
|
+
const accessToken = input.cliServerAccessToken?.trim();
|
|
224989
|
+
if (!appUrl || !accessToken) return null;
|
|
224990
|
+
return createCliServerMemoryRetriever({
|
|
224991
|
+
appUrl,
|
|
224992
|
+
accessToken,
|
|
224993
|
+
threadId: input.threadId
|
|
224994
|
+
});
|
|
224995
|
+
}
|
|
224996
|
+
function createCliServerMemoryRetriever(input) {
|
|
224997
|
+
return async ({ limitPerScope, query }) => {
|
|
224998
|
+
const response = await fetch(`${input.appUrl.replace(/\/+$/, "")}/api/perch-terminal/cli-context`, {
|
|
224999
|
+
method: "POST",
|
|
225000
|
+
headers: {
|
|
225001
|
+
Accept: "application/json",
|
|
225002
|
+
"Content-Type": "application/json",
|
|
225003
|
+
Authorization: `Bearer ${input.accessToken}`
|
|
225004
|
+
},
|
|
225005
|
+
body: JSON.stringify({
|
|
225006
|
+
query,
|
|
225007
|
+
threadId: input.threadId,
|
|
225008
|
+
limitPerScope
|
|
225009
|
+
})
|
|
225010
|
+
});
|
|
225011
|
+
const payload = await response.json().catch(() => ({}));
|
|
225012
|
+
if (!response.ok || payload.ok !== true) {
|
|
225013
|
+
throw new Error("Durable memory is unavailable right now.");
|
|
225014
|
+
}
|
|
225015
|
+
const memories = Array.isArray(payload.permanentMemories) ? payload.permanentMemories.filter(isPermanentMemoryLike) : [];
|
|
225016
|
+
return memories;
|
|
225017
|
+
};
|
|
225018
|
+
}
|
|
225019
|
+
function isPermanentMemoryLike(value) {
|
|
225020
|
+
if (!value || typeof value !== "object") return false;
|
|
225021
|
+
const memory = value;
|
|
225022
|
+
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");
|
|
225023
|
+
}
|
|
224734
225024
|
async function runOperatorTurn(input, deps) {
|
|
224735
225025
|
const runId = input.clientRunId?.trim() || makeRunId();
|
|
224736
225026
|
const startMs = Date.now();
|
|
@@ -224796,21 +225086,15 @@ async function runOperatorTurn(input, deps) {
|
|
|
224796
225086
|
});
|
|
224797
225087
|
}
|
|
224798
225088
|
try {
|
|
225089
|
+
const memoryRetriever = buildTurnMemoryRetriever(input);
|
|
224799
225090
|
const memoryContext = await buildMemoryContext({
|
|
224800
225091
|
query: input.trimmedInput,
|
|
224801
|
-
userId: input.userId,
|
|
225092
|
+
userId: input.userId ?? (memoryRetriever && input.cliServerAccessToken?.trim() ? "cli_authenticated_user" : null),
|
|
224802
225093
|
workspaceId: input.workspaceId,
|
|
224803
225094
|
threadId: input.threadId,
|
|
224804
225095
|
permanentMemories: input.permanentMemories,
|
|
224805
225096
|
userMemories: input.userMemories,
|
|
224806
|
-
retriever:
|
|
224807
|
-
input.supabase
|
|
224808
|
-
).retrievePermanentMemoriesForTurn({
|
|
224809
|
-
userId,
|
|
224810
|
-
workspaceId,
|
|
224811
|
-
limitPerScope,
|
|
224812
|
-
query
|
|
224813
|
-
}) : null
|
|
225097
|
+
retriever: memoryRetriever
|
|
224814
225098
|
});
|
|
224815
225099
|
const earlySession = input.threadId ? await loadThreadSession(input.threadId, { supabase: input.supabase ?? null }) : null;
|
|
224816
225100
|
const persona = getPersona(input.personaId ?? earlySession?.personaId);
|
|
@@ -224949,10 +225233,7 @@ async function runOperatorTurn(input, deps) {
|
|
|
224949
225233
|
getEnabledToolDefinitions(toolOpts),
|
|
224950
225234
|
PLAN_MODE_ALLOWED_TOOL_NAMES
|
|
224951
225235
|
) : effectiveChatMode === "agents" ? getExecutableToolDefinitions(toolOpts) : effectiveChatMode === "ask" ? [] : getReadOnlyToolDefinitions(toolOpts);
|
|
224952
|
-
const
|
|
224953
|
-
allowSuiteRelay: suiteRelayEnabled
|
|
224954
|
-
}) : baseTurnToolDefinitions;
|
|
224955
|
-
const suiteRelayFilteredTools = filterSuiteRelayTools(personaFilteredTools, {
|
|
225236
|
+
const suiteRelayFilteredTools = filterSuiteRelayTools(baseTurnToolDefinitions, {
|
|
224956
225237
|
allowSuiteRelay: suiteRelayEnabled
|
|
224957
225238
|
});
|
|
224958
225239
|
const turnToolDefinitions = filterMainPersonaDeliveryTools(suiteRelayFilteredTools, {
|
|
@@ -225098,8 +225379,15 @@ ${planStateLines}`
|
|
|
225098
225379
|
if (perchMdRow?.status === "not_found") {
|
|
225099
225380
|
extendedWarnings.push("No PERCH.md \u2014 add an operator playbook to .perch/PERCH.md to customise behaviour.");
|
|
225100
225381
|
}
|
|
225101
|
-
|
|
225102
|
-
|
|
225382
|
+
const hostedMemoryAvailable = Boolean(
|
|
225383
|
+
enrichedInput.cliServerAppUrl?.trim() && enrichedInput.cliServerAccessToken?.trim()
|
|
225384
|
+
);
|
|
225385
|
+
const memoryDiagnostics = enrichedInput.memoryContext?.diagnostics ?? null;
|
|
225386
|
+
if (memoryDiagnostics?.warnings.length) {
|
|
225387
|
+
extendedWarnings.push(...memoryDiagnostics.warnings);
|
|
225388
|
+
}
|
|
225389
|
+
if (!enrichedInput.supabase && !hostedMemoryAvailable && memoryDiagnostics?.retrieval.attempted !== true) {
|
|
225390
|
+
extendedWarnings.push("Memory retrieval skipped \u2014 sign in to use durable memory.");
|
|
225103
225391
|
}
|
|
225104
225392
|
const contextJob = resolveJobContextTokens({
|
|
225105
225393
|
threadContextTokens: threadContextAccounting.threadContextTokens,
|
|
@@ -225633,8 +225921,7 @@ var init_runOperatorTurn = __esm({
|
|
|
225633
225921
|
init_folderIndexing();
|
|
225634
225922
|
init_approvalResume();
|
|
225635
225923
|
init_personaRegistry();
|
|
225636
|
-
|
|
225637
|
-
init_saffronToolPolicy();
|
|
225924
|
+
init_sharedToolPolicy();
|
|
225638
225925
|
init_deliveryToolPolicy();
|
|
225639
225926
|
init_voiceFilters();
|
|
225640
225927
|
init_progressEventBridge();
|
|
@@ -226511,6 +226798,21 @@ function installCliNodeLocalBridge(input) {
|
|
|
226511
226798
|
}
|
|
226512
226799
|
};
|
|
226513
226800
|
}
|
|
226801
|
+
function readCliProjectMemoryState(workspaceRoot) {
|
|
226802
|
+
const root2 = path11.resolve(expandHome4(workspaceRoot));
|
|
226803
|
+
if (!isCliProjectMemoryAvailable(root2)) {
|
|
226804
|
+
return {
|
|
226805
|
+
available: false,
|
|
226806
|
+
meta: null,
|
|
226807
|
+
reason: "Local project memory is unavailable in this folder."
|
|
226808
|
+
};
|
|
226809
|
+
}
|
|
226810
|
+
return {
|
|
226811
|
+
available: true,
|
|
226812
|
+
meta: enrichCliProjectMeta(root2, loadCliStoredMeta(root2)),
|
|
226813
|
+
reason: "Local project memory is available."
|
|
226814
|
+
};
|
|
226815
|
+
}
|
|
226514
226816
|
function createCliNodeLocalBridge(input) {
|
|
226515
226817
|
const workspaceRoot = path11.resolve(input.workspaceRoot);
|
|
226516
226818
|
const now13 = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -226793,10 +227095,20 @@ function createCliNodeLocalBridge(input) {
|
|
|
226793
227095
|
encoding: "base64"
|
|
226794
227096
|
};
|
|
226795
227097
|
},
|
|
226796
|
-
getProjectRules: async () =>
|
|
226797
|
-
|
|
226798
|
-
|
|
226799
|
-
|
|
227098
|
+
getProjectRules: async () => {
|
|
227099
|
+
const state = readCliProjectMemoryState(workspaceRoot);
|
|
227100
|
+
return state.meta ? [{
|
|
227101
|
+
rootId: CLI_ROOT_ID,
|
|
227102
|
+
perchMd: state.meta.perchMd?.content ?? null,
|
|
227103
|
+
rules: state.meta.rules.map((rule) => ({
|
|
227104
|
+
fileName: rule.fileName,
|
|
227105
|
+
content: rule.content
|
|
227106
|
+
}))
|
|
227107
|
+
}] : [];
|
|
227108
|
+
},
|
|
227109
|
+
readProjectMemory: async () => readCliProjectMemoryBridge(workspaceRoot),
|
|
227110
|
+
writeProjectMemory: async (request) => writeCliProjectMemory(workspaceRoot, request.meta),
|
|
227111
|
+
writeMemoryFile: async (request) => writeCliMemoryFile(workspaceRoot, request),
|
|
226800
227112
|
writeRule: async () => ({ ok: false, error: "Project rule writes are not available in this CLI package yet." }),
|
|
226801
227113
|
writePerchMd: async () => ({ ok: false, error: "PERCH.md writes are not available in this CLI package yet." }),
|
|
226802
227114
|
readGlobalPerchMd: async () => ({ ok: false, error: "Global PERCH.md is not available in this CLI package yet." }),
|
|
@@ -227075,6 +227387,292 @@ function guessCliFileType(filePath) {
|
|
|
227075
227387
|
if ([".doc", ".docx", ".txt", ".md", ".rtf"].includes(ext)) return "document";
|
|
227076
227388
|
return "unknown";
|
|
227077
227389
|
}
|
|
227390
|
+
async function readCliProjectMemoryBridge(workspaceRoot) {
|
|
227391
|
+
const state = readCliProjectMemoryState(workspaceRoot);
|
|
227392
|
+
if (!state.available || !state.meta) {
|
|
227393
|
+
return {
|
|
227394
|
+
ok: false,
|
|
227395
|
+
error: "Local project memory is unavailable in this folder. Run from a project folder with .perch memory or sign in for durable memory."
|
|
227396
|
+
};
|
|
227397
|
+
}
|
|
227398
|
+
return { ok: true, meta: state.meta };
|
|
227399
|
+
}
|
|
227400
|
+
async function writeCliProjectMemory(workspaceRoot, patch) {
|
|
227401
|
+
const root2 = path11.resolve(expandHome4(workspaceRoot));
|
|
227402
|
+
if (!isCliProjectMemoryAvailable(root2)) {
|
|
227403
|
+
return {
|
|
227404
|
+
ok: false,
|
|
227405
|
+
error: "Local project memory is unavailable in this folder. Run from a project folder with .perch memory or sign in for durable memory."
|
|
227406
|
+
};
|
|
227407
|
+
}
|
|
227408
|
+
const current = loadCliStoredMeta(root2);
|
|
227409
|
+
const next = {
|
|
227410
|
+
...current,
|
|
227411
|
+
projectName: patch.projectName !== void 0 ? patch.projectName : current.projectName,
|
|
227412
|
+
notes: patch.notes !== void 0 ? patch.notes : current.notes,
|
|
227413
|
+
preferredRoot: patch.preferredRoot !== void 0 ? patch.preferredRoot : current.preferredRoot,
|
|
227414
|
+
lastOpenedThreadId: patch.lastOpenedThreadId !== void 0 ? patch.lastOpenedThreadId : current.lastOpenedThreadId,
|
|
227415
|
+
memorySummary: patch.memorySummary !== void 0 ? patch.memorySummary : current.memorySummary
|
|
227416
|
+
};
|
|
227417
|
+
await fsp.mkdir(path11.join(root2, PERCH_DIR), { recursive: true });
|
|
227418
|
+
await atomicWriteCliUtf8(getCliProjectFilePath(root2), JSON.stringify(next, null, 2));
|
|
227419
|
+
return { ok: true, meta: enrichCliProjectMeta(root2, next) };
|
|
227420
|
+
}
|
|
227421
|
+
async function writeCliMemoryFile(workspaceRoot, request) {
|
|
227422
|
+
const root2 = path11.resolve(expandHome4(workspaceRoot));
|
|
227423
|
+
if (!isCliProjectMemoryAvailable(root2)) {
|
|
227424
|
+
return {
|
|
227425
|
+
ok: false,
|
|
227426
|
+
error: "Local project memory is unavailable in this folder. Run from a project folder with .perch memory or sign in for durable memory."
|
|
227427
|
+
};
|
|
227428
|
+
}
|
|
227429
|
+
if (request.rootId && request.rootId !== CLI_ROOT_ID) {
|
|
227430
|
+
return { ok: false, error: "Local project memory is unavailable for that workspace root." };
|
|
227431
|
+
}
|
|
227432
|
+
const fileName = normalizeCliMemoryFileName(request.fileName);
|
|
227433
|
+
if (!fileName) return { ok: false, error: "Invalid memory file name." };
|
|
227434
|
+
const relativePath = path11.join(PERCH_DIR, MEMORY_DIR, fileName);
|
|
227435
|
+
const fullPath = path11.join(root2, relativePath);
|
|
227436
|
+
const existing = await fsp.readFile(fullPath, "utf8").catch(() => "");
|
|
227437
|
+
let nextContent;
|
|
227438
|
+
try {
|
|
227439
|
+
nextContent = applyCliMemoryWrite(existing, request);
|
|
227440
|
+
} catch (error) {
|
|
227441
|
+
return {
|
|
227442
|
+
ok: false,
|
|
227443
|
+
error: error instanceof Error ? error.message : "Failed to merge memory content."
|
|
227444
|
+
};
|
|
227445
|
+
}
|
|
227446
|
+
const capError = assertCliMemoryWithinCap(nextContent);
|
|
227447
|
+
if (capError) return { ok: false, error: capError };
|
|
227448
|
+
await fsp.mkdir(path11.dirname(fullPath), { recursive: true });
|
|
227449
|
+
await atomicWriteCliUtf8(fullPath, nextContent);
|
|
227450
|
+
const updatedFile = readCliTextMemoryFile(root2, relativePath, MAX_MEMORY_BYTES);
|
|
227451
|
+
return {
|
|
227452
|
+
ok: true,
|
|
227453
|
+
meta: enrichCliProjectMeta(root2, loadCliStoredMeta(root2)),
|
|
227454
|
+
updatedFile
|
|
227455
|
+
};
|
|
227456
|
+
}
|
|
227457
|
+
function defaultCliProjectMeta() {
|
|
227458
|
+
return {
|
|
227459
|
+
projectName: null,
|
|
227460
|
+
notes: null,
|
|
227461
|
+
preferredRoot: null,
|
|
227462
|
+
lastOpenedThreadId: null,
|
|
227463
|
+
memorySummary: null,
|
|
227464
|
+
perchMd: null,
|
|
227465
|
+
rules: [],
|
|
227466
|
+
memoryFiles: [],
|
|
227467
|
+
missingMemoryFiles: [],
|
|
227468
|
+
projectMemoryEmpty: true,
|
|
227469
|
+
evidenceOnly: true
|
|
227470
|
+
};
|
|
227471
|
+
}
|
|
227472
|
+
function isCliProjectMemoryAvailable(root2) {
|
|
227473
|
+
try {
|
|
227474
|
+
return fs10.statSync(path11.join(root2, PERCH_DIR)).isDirectory();
|
|
227475
|
+
} catch {
|
|
227476
|
+
return false;
|
|
227477
|
+
}
|
|
227478
|
+
}
|
|
227479
|
+
function getCliProjectFilePath(root2) {
|
|
227480
|
+
return path11.join(root2, PERCH_DIR, PROJECT_FILE);
|
|
227481
|
+
}
|
|
227482
|
+
function loadCliStoredMeta(root2) {
|
|
227483
|
+
try {
|
|
227484
|
+
const raw = fs10.readFileSync(getCliProjectFilePath(root2), "utf8");
|
|
227485
|
+
return { ...defaultCliProjectMeta(), ...JSON.parse(raw) };
|
|
227486
|
+
} catch {
|
|
227487
|
+
return defaultCliProjectMeta();
|
|
227488
|
+
}
|
|
227489
|
+
}
|
|
227490
|
+
function enrichCliProjectMeta(root2, base) {
|
|
227491
|
+
const perchMd = readCliFirstExisting(root2, [
|
|
227492
|
+
path11.join(PERCH_DIR, PERCH_INDEX_FILE),
|
|
227493
|
+
PERCH_INDEX_FILE
|
|
227494
|
+
], MAX_TEXT_BYTES);
|
|
227495
|
+
const rules = readCliRules(root2);
|
|
227496
|
+
const { memoryFiles, missingMemoryFiles } = readCliMemoryFiles(root2);
|
|
227497
|
+
const projectMemoryEmpty = !perchMd?.found && rules.length === 0 && memoryFiles.filter((file) => file.found).length === 0;
|
|
227498
|
+
return {
|
|
227499
|
+
...base,
|
|
227500
|
+
perchMd,
|
|
227501
|
+
rules,
|
|
227502
|
+
memoryFiles,
|
|
227503
|
+
missingMemoryFiles,
|
|
227504
|
+
projectMemoryEmpty,
|
|
227505
|
+
evidenceOnly: projectMemoryEmpty
|
|
227506
|
+
};
|
|
227507
|
+
}
|
|
227508
|
+
function normalizeCliMemoryFileName(fileName) {
|
|
227509
|
+
if (typeof fileName !== "string") return null;
|
|
227510
|
+
const trimmed = fileName.trim();
|
|
227511
|
+
if (trimmed.includes("..") || trimmed.includes("/") || trimmed.includes("\\")) return null;
|
|
227512
|
+
return EXPECTED_MEMORY_FILES.includes(trimmed) ? trimmed : null;
|
|
227513
|
+
}
|
|
227514
|
+
function applyCliMemoryWrite(existing, request) {
|
|
227515
|
+
const incoming = request.content ?? "";
|
|
227516
|
+
if (request.mode === "replace") return incoming;
|
|
227517
|
+
if (request.mode === "append") {
|
|
227518
|
+
if (request.sectionHeading?.trim()) {
|
|
227519
|
+
return upsertCliMarkdownSection(existing, request.sectionHeading, incoming, "append");
|
|
227520
|
+
}
|
|
227521
|
+
if (!existing.trim()) return incoming;
|
|
227522
|
+
return `${existing}${existing.endsWith("\n") ? "" : "\n"}${incoming}`;
|
|
227523
|
+
}
|
|
227524
|
+
if (!request.sectionHeading?.trim()) {
|
|
227525
|
+
throw new Error('mode "merge" requires sectionHeading.');
|
|
227526
|
+
}
|
|
227527
|
+
return upsertCliMarkdownSection(existing, request.sectionHeading, incoming, "replace");
|
|
227528
|
+
}
|
|
227529
|
+
function upsertCliMarkdownSection(existing, sectionHeading, body, mode) {
|
|
227530
|
+
const heading = sectionHeading.trim().startsWith("#") ? sectionHeading.trim() : `## ${sectionHeading.trim()}`;
|
|
227531
|
+
const targetKey = normalizeCliHeadingKey(heading);
|
|
227532
|
+
const sections = parseCliMarkdownSections(existing);
|
|
227533
|
+
const index = sections.findIndex((section) => normalizeCliHeadingKey(section.heading) === targetKey);
|
|
227534
|
+
if (index >= 0) {
|
|
227535
|
+
const prior = sections[index];
|
|
227536
|
+
sections[index] = {
|
|
227537
|
+
heading: prior.heading || heading,
|
|
227538
|
+
body: mode === "append" && prior.body.trim() ? `${prior.body.trim()}
|
|
227539
|
+
${body.trim()}` : body.trim()
|
|
227540
|
+
};
|
|
227541
|
+
} else {
|
|
227542
|
+
sections.push({ heading, body: body.trim() });
|
|
227543
|
+
}
|
|
227544
|
+
return sections.map((section) => {
|
|
227545
|
+
if (!section.heading) return section.body.trim();
|
|
227546
|
+
return `${section.heading}
|
|
227547
|
+
|
|
227548
|
+
${section.body.trim()}`.trim();
|
|
227549
|
+
}).filter(Boolean).join("\n\n").concat("\n");
|
|
227550
|
+
}
|
|
227551
|
+
function parseCliMarkdownSections(content) {
|
|
227552
|
+
const sections = [];
|
|
227553
|
+
let current = null;
|
|
227554
|
+
for (const line of content.split(/\r?\n/)) {
|
|
227555
|
+
if (/^#{1,6}\s+/.test(line)) {
|
|
227556
|
+
if (current) sections.push(current);
|
|
227557
|
+
current = { heading: line.trimEnd(), body: "" };
|
|
227558
|
+
continue;
|
|
227559
|
+
}
|
|
227560
|
+
if (!current) {
|
|
227561
|
+
if (!line.trim()) continue;
|
|
227562
|
+
current = { heading: "", body: line };
|
|
227563
|
+
continue;
|
|
227564
|
+
}
|
|
227565
|
+
current.body = current.body ? `${current.body}
|
|
227566
|
+
${line}` : line;
|
|
227567
|
+
}
|
|
227568
|
+
if (current) sections.push(current);
|
|
227569
|
+
return sections;
|
|
227570
|
+
}
|
|
227571
|
+
function normalizeCliHeadingKey(heading) {
|
|
227572
|
+
return heading.replace(/^#+\s*/, "").trim().toLowerCase();
|
|
227573
|
+
}
|
|
227574
|
+
function assertCliMemoryWithinCap(content) {
|
|
227575
|
+
return Buffer.byteLength(content, "utf8") > MAX_MEMORY_BYTES ? "Memory file exceeds 128KB cap." : null;
|
|
227576
|
+
}
|
|
227577
|
+
async function atomicWriteCliUtf8(filePath, content) {
|
|
227578
|
+
const tmpPath = `${filePath}.tmp`;
|
|
227579
|
+
await fsp.writeFile(tmpPath, content, "utf8");
|
|
227580
|
+
try {
|
|
227581
|
+
await fsp.rename(tmpPath, filePath);
|
|
227582
|
+
} catch (error) {
|
|
227583
|
+
await fsp.rm(tmpPath, { force: true }).catch(() => void 0);
|
|
227584
|
+
throw error;
|
|
227585
|
+
}
|
|
227586
|
+
}
|
|
227587
|
+
function readCliFirstExisting(root2, relativePaths, maxBytes) {
|
|
227588
|
+
for (const relativePath of relativePaths) {
|
|
227589
|
+
const file = readCliTextMemoryFile(root2, relativePath, maxBytes);
|
|
227590
|
+
if (file.found) {
|
|
227591
|
+
return {
|
|
227592
|
+
relativePath: file.relativePath,
|
|
227593
|
+
content: file.content,
|
|
227594
|
+
found: true,
|
|
227595
|
+
sizeBytes: file.sizeBytes,
|
|
227596
|
+
modifiedAt: file.modifiedAt
|
|
227597
|
+
};
|
|
227598
|
+
}
|
|
227599
|
+
}
|
|
227600
|
+
return null;
|
|
227601
|
+
}
|
|
227602
|
+
function readCliRules(root2) {
|
|
227603
|
+
const dirPath = path11.join(root2, PERCH_DIR, RULES_DIR);
|
|
227604
|
+
try {
|
|
227605
|
+
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) => ({
|
|
227606
|
+
fileName: path11.basename(file.relativePath),
|
|
227607
|
+
relativePath: file.relativePath,
|
|
227608
|
+
content: file.content,
|
|
227609
|
+
sizeBytes: file.sizeBytes,
|
|
227610
|
+
modifiedAt: file.modifiedAt
|
|
227611
|
+
}));
|
|
227612
|
+
} catch {
|
|
227613
|
+
return [];
|
|
227614
|
+
}
|
|
227615
|
+
}
|
|
227616
|
+
function readCliMemoryFiles(root2) {
|
|
227617
|
+
const memoryDir = path11.join(root2, PERCH_DIR, MEMORY_DIR);
|
|
227618
|
+
const discovered = /* @__PURE__ */ new Set();
|
|
227619
|
+
const memoryFiles = [];
|
|
227620
|
+
try {
|
|
227621
|
+
for (const name of fs10.readdirSync(memoryDir).slice(0, MAX_MEMORY_FILES)) {
|
|
227622
|
+
const ext = path11.extname(name).toLowerCase();
|
|
227623
|
+
if (ext !== ".md" && ext !== ".txt") continue;
|
|
227624
|
+
discovered.add(name);
|
|
227625
|
+
memoryFiles.push(readCliTextMemoryFile(root2, path11.join(PERCH_DIR, MEMORY_DIR, name), MAX_MEMORY_BYTES));
|
|
227626
|
+
}
|
|
227627
|
+
} catch {
|
|
227628
|
+
}
|
|
227629
|
+
return {
|
|
227630
|
+
memoryFiles,
|
|
227631
|
+
missingMemoryFiles: EXPECTED_MEMORY_FILES.filter((name) => !discovered.has(name)).map((name) => ({
|
|
227632
|
+
fileName: name,
|
|
227633
|
+
relativePath: path11.join(PERCH_DIR, MEMORY_DIR, name),
|
|
227634
|
+
reason: "expected project memory file was not found"
|
|
227635
|
+
}))
|
|
227636
|
+
};
|
|
227637
|
+
}
|
|
227638
|
+
function readCliTextMemoryFile(root2, relativePath, maxBytes) {
|
|
227639
|
+
const fullPath = path11.join(root2, relativePath);
|
|
227640
|
+
const fileName = path11.basename(relativePath);
|
|
227641
|
+
try {
|
|
227642
|
+
const stat2 = fs10.statSync(fullPath);
|
|
227643
|
+
if (!stat2.isFile()) {
|
|
227644
|
+
return { fileName, relativePath, content: "", found: false, error: "not a regular file" };
|
|
227645
|
+
}
|
|
227646
|
+
const content = fs10.readFileSync(fullPath, "utf8");
|
|
227647
|
+
if (stat2.size > maxBytes) {
|
|
227648
|
+
return {
|
|
227649
|
+
fileName,
|
|
227650
|
+
relativePath,
|
|
227651
|
+
content: `${content.slice(0, maxBytes)}
|
|
227652
|
+
[truncated: file exceeds ${maxBytes} bytes]`,
|
|
227653
|
+
found: true,
|
|
227654
|
+
sizeBytes: stat2.size,
|
|
227655
|
+
modifiedAt: stat2.mtime.toISOString()
|
|
227656
|
+
};
|
|
227657
|
+
}
|
|
227658
|
+
return {
|
|
227659
|
+
fileName,
|
|
227660
|
+
relativePath,
|
|
227661
|
+
content,
|
|
227662
|
+
found: true,
|
|
227663
|
+
sizeBytes: stat2.size,
|
|
227664
|
+
modifiedAt: stat2.mtime.toISOString()
|
|
227665
|
+
};
|
|
227666
|
+
} catch (error) {
|
|
227667
|
+
return {
|
|
227668
|
+
fileName,
|
|
227669
|
+
relativePath,
|
|
227670
|
+
content: "",
|
|
227671
|
+
found: false,
|
|
227672
|
+
error: error instanceof Error ? error.message : "read failed"
|
|
227673
|
+
};
|
|
227674
|
+
}
|
|
227675
|
+
}
|
|
227078
227676
|
async function runShellCommand(input) {
|
|
227079
227677
|
const startedAt = Date.now();
|
|
227080
227678
|
const cwd2 = input.cwd ? resolveReadPath(input.workspaceRoot, input.cwd) : input.workspaceRoot;
|
|
@@ -227312,7 +227910,7 @@ function capOutput(stdout, stderr) {
|
|
|
227312
227910
|
function shellQuote(parts) {
|
|
227313
227911
|
return parts.map((part) => `'${part.replace(/'/g, "'\\''")}'`).join(" ");
|
|
227314
227912
|
}
|
|
227315
|
-
var CLI_ROOT_ID, DEFAULT_MAX_RESULTS, MAX_READ_BYTES, IGNORED_DIRS;
|
|
227913
|
+
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
227914
|
var init_nodeLocalBridge = __esm({
|
|
227317
227915
|
"features/perchTerminal/runtime/cliHost/nodeLocalBridge.ts"() {
|
|
227318
227916
|
"use strict";
|
|
@@ -227323,6 +227921,20 @@ var init_nodeLocalBridge = __esm({
|
|
|
227323
227921
|
DEFAULT_MAX_RESULTS = 200;
|
|
227324
227922
|
MAX_READ_BYTES = 2e6;
|
|
227325
227923
|
IGNORED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", "release"]);
|
|
227924
|
+
PERCH_DIR = ".perch";
|
|
227925
|
+
PROJECT_FILE = "project.json";
|
|
227926
|
+
PERCH_INDEX_FILE = "PERCH.md";
|
|
227927
|
+
MEMORY_DIR = "memory";
|
|
227928
|
+
RULES_DIR = "rules";
|
|
227929
|
+
MAX_TEXT_BYTES = 64e3;
|
|
227930
|
+
MAX_MEMORY_BYTES = 128e3;
|
|
227931
|
+
MAX_MEMORY_FILES = 80;
|
|
227932
|
+
EXPECTED_MEMORY_FILES = [
|
|
227933
|
+
"project.md",
|
|
227934
|
+
"preferences.md",
|
|
227935
|
+
"decisions.md",
|
|
227936
|
+
"permanent.md"
|
|
227937
|
+
];
|
|
227326
227938
|
}
|
|
227327
227939
|
});
|
|
227328
227940
|
|
|
@@ -227404,10 +228016,13 @@ function buildCliTurnInput(input, resolved) {
|
|
|
227404
228016
|
activeRootPath: input.activeRootPath ?? resolved.cwd,
|
|
227405
228017
|
localSources: [],
|
|
227406
228018
|
localSourcesMeta: null,
|
|
228019
|
+
projectMeta: readCliProjectMemoryState(resolved.cwd).meta,
|
|
227407
228020
|
permanentMemories: input.permanentMemories ?? [],
|
|
227408
228021
|
userMemories: input.userMemories ?? [],
|
|
227409
228022
|
supabase: null,
|
|
227410
228023
|
supabaseConfigured: false,
|
|
228024
|
+
cliServerAppUrl: input.cliServerAppUrl ?? input.marketDeskProxyAppUrl ?? input.appUrl ?? null,
|
|
228025
|
+
cliServerAccessToken: input.cliServerAccessToken ?? input.marketDeskProxyAccessToken ?? null,
|
|
227411
228026
|
marketDeskProxyAppUrl: input.marketDeskProxyAppUrl ?? input.appUrl ?? null,
|
|
227412
228027
|
marketDeskProxyAccessToken: input.marketDeskProxyAccessToken ?? null,
|
|
227413
228028
|
permissionMode: normalizePermissionMode(input.permissionMode ?? "default"),
|
|
@@ -227483,6 +228098,7 @@ var init_runCliTurn = __esm({
|
|
|
227483
228098
|
init_personaRegistry();
|
|
227484
228099
|
init_runOperatorTurn();
|
|
227485
228100
|
init_nodeLocalBridge();
|
|
228101
|
+
init_nodeLocalBridge();
|
|
227486
228102
|
}
|
|
227487
228103
|
});
|
|
227488
228104
|
|
|
@@ -227652,12 +228268,12 @@ import os2 from "node:os";
|
|
|
227652
228268
|
import path13 from "node:path";
|
|
227653
228269
|
import { promisify } from "node:util";
|
|
227654
228270
|
async function readStoredCliAuthSession() {
|
|
227655
|
-
const raw = process.platform === "darwin" ? await readKeychainSecret().catch(() => null) : await readFallbackSecret().catch(() => null);
|
|
228271
|
+
const raw = shouldUseFallbackAuthFile() ? await readFallbackSecret().catch(() => null) : process.platform === "darwin" ? await readKeychainSecret().catch(() => null) : await readFallbackSecret().catch(() => null);
|
|
227656
228272
|
return parseStoredCliAuthSession(raw);
|
|
227657
228273
|
}
|
|
227658
228274
|
async function writeStoredCliAuthSession(session) {
|
|
227659
228275
|
const raw = JSON.stringify(session);
|
|
227660
|
-
if (process.platform === "darwin") {
|
|
228276
|
+
if (process.platform === "darwin" && !shouldUseFallbackAuthFile()) {
|
|
227661
228277
|
await execFileAsync("security", [
|
|
227662
228278
|
"add-generic-password",
|
|
227663
228279
|
"-U",
|
|
@@ -227673,7 +228289,7 @@ async function writeStoredCliAuthSession(session) {
|
|
|
227673
228289
|
await writeFallbackSecret(raw);
|
|
227674
228290
|
}
|
|
227675
228291
|
async function clearStoredCliAuthSession() {
|
|
227676
|
-
if (process.platform === "darwin") {
|
|
228292
|
+
if (process.platform === "darwin" && !shouldUseFallbackAuthFile()) {
|
|
227677
228293
|
await execFileAsync("security", [
|
|
227678
228294
|
"delete-generic-password",
|
|
227679
228295
|
"-s",
|
|
@@ -227685,6 +228301,9 @@ async function clearStoredCliAuthSession() {
|
|
|
227685
228301
|
}
|
|
227686
228302
|
await fs11.rm(fallbackSessionPath(), { force: true }).catch(() => void 0);
|
|
227687
228303
|
}
|
|
228304
|
+
function shouldUseFallbackAuthFile() {
|
|
228305
|
+
return Boolean(process.env.PERCH_CLI_AUTH_DIR?.trim());
|
|
228306
|
+
}
|
|
227688
228307
|
function isStoredCliAuthSessionUsable(session, nowSeconds = Math.floor(Date.now() / 1e3)) {
|
|
227689
228308
|
if (!session?.accessToken?.trim()) return false;
|
|
227690
228309
|
if (!session.appUrl?.trim()) return false;
|
|
@@ -283213,7 +283832,7 @@ async function runInkInteractivePerchCli(writer, deps, options) {
|
|
|
283213
283832
|
text: `bash \xB7 ${truncateMiddle(commandText, 54)} \xB7 running`,
|
|
283214
283833
|
tone: "muted",
|
|
283215
283834
|
detailLines: [
|
|
283216
|
-
{ tone: "command", text: `$ ${commandText}
|
|
283835
|
+
{ tone: "command", text: `$ ${commandText}`, language: "bash" },
|
|
283217
283836
|
{ tone: "meta", text: `cwd ${event.cwd || "."}` }
|
|
283218
283837
|
],
|
|
283219
283838
|
expanded: false
|
|
@@ -283261,7 +283880,7 @@ async function runInkInteractivePerchCli(writer, deps, options) {
|
|
|
283261
283880
|
richToolIds.current.add(itemId2);
|
|
283262
283881
|
const commandText = renderCommandLine(event.command, event.args);
|
|
283263
283882
|
const details = [
|
|
283264
|
-
{ tone: "command", text: `$ ${commandText}
|
|
283883
|
+
{ tone: "command", text: `$ ${commandText}`, language: "bash" },
|
|
283265
283884
|
{ tone: "meta", text: `cwd ${event.cwd || "."}` },
|
|
283266
283885
|
...outputChunkToDetailLines(event.stdout, "stdout"),
|
|
283267
283886
|
...outputChunkToDetailLines(event.stderr, "stderr")
|
|
@@ -283294,9 +283913,10 @@ async function runInkInteractivePerchCli(writer, deps, options) {
|
|
|
283294
283913
|
detailLines: [
|
|
283295
283914
|
{
|
|
283296
283915
|
tone: "command",
|
|
283297
|
-
text: event.language === "shell" ? `$ ${event.command}` : `${event.language} cell
|
|
283916
|
+
text: event.language === "shell" ? `$ ${event.command}` : `${event.language} cell`,
|
|
283917
|
+
language: event.language === "shell" ? "bash" : void 0
|
|
283298
283918
|
},
|
|
283299
|
-
...event.language === "shell" ? [] : codePreviewDetailLines(event.command)
|
|
283919
|
+
...event.language === "shell" ? [] : codePreviewDetailLines(event.command, cliLanguageForSandbox(event.language))
|
|
283300
283920
|
],
|
|
283301
283921
|
expanded: false
|
|
283302
283922
|
});
|
|
@@ -283489,14 +284109,7 @@ async function runInkInteractivePerchCli(writer, deps, options) {
|
|
|
283489
284109
|
React11.createElement(
|
|
283490
284110
|
Ink2.Box,
|
|
283491
284111
|
{ flexGrow: 1 },
|
|
283492
|
-
React11
|
|
283493
|
-
Ink2.Text,
|
|
283494
|
-
{
|
|
283495
|
-
color: colorForInkDetailTone(line.tone),
|
|
283496
|
-
dimColor: line.tone === "meta"
|
|
283497
|
-
},
|
|
283498
|
-
formatInkDetailLine(line)
|
|
283499
|
-
)
|
|
284112
|
+
renderInkDetailContent(React11, Ink2, line)
|
|
283500
284113
|
)
|
|
283501
284114
|
);
|
|
283502
284115
|
const renderTranscriptItem = (item, index) => {
|
|
@@ -283805,6 +284418,7 @@ function parseInteractiveSlashCommand(input) {
|
|
|
283805
284418
|
}
|
|
283806
284419
|
function renderInteractiveStatus(state, connection, session, workspaceId) {
|
|
283807
284420
|
const storedAuth = session === void 0 ? renderCliAuthSummary(connection) : isStoredCliAuthSessionUsable(session) ? `signed in${session.email ? ` as ${session.email}` : ""}` : "not signed in";
|
|
284421
|
+
const signedIn = session === void 0 ? isCliModelConnectionReady(connection) : isStoredCliAuthSessionUsable(session);
|
|
283808
284422
|
const connectionStatus = isCliModelConnectionReady(connection) ? "connected" : "locked \xB7 run /login";
|
|
283809
284423
|
const color = shouldUseCliColor();
|
|
283810
284424
|
const lines = [
|
|
@@ -283812,6 +284426,7 @@ function renderInteractiveStatus(state, connection, session, workspaceId) {
|
|
|
283812
284426
|
["cwd", state.cwd],
|
|
283813
284427
|
["auth", storedAuth],
|
|
283814
284428
|
["connection", connectionStatus],
|
|
284429
|
+
["memory", renderCliMemoryAvailability(state.cwd, workspaceId ?? null, signedIn)],
|
|
283815
284430
|
["tools", renderCliCapabilityCount(state, workspaceId ?? null)],
|
|
283816
284431
|
["permission", state.permissionMode],
|
|
283817
284432
|
["mode", state.chatMode],
|
|
@@ -283823,6 +284438,15 @@ function renderInteractiveStatus(state, connection, session, workspaceId) {
|
|
|
283823
284438
|
];
|
|
283824
284439
|
return lines.map(([key, value]) => `${paint(key.padEnd(11), "muted", color)} ${value}`).join("\n") + "\n";
|
|
283825
284440
|
}
|
|
284441
|
+
function renderCliMemoryAvailability(cwd2, workspaceId, signedIn) {
|
|
284442
|
+
const local = readCliProjectMemoryState(cwd2).available;
|
|
284443
|
+
const parts = [];
|
|
284444
|
+
if (signedIn && workspaceId) parts.push("server memory");
|
|
284445
|
+
if (local) parts.push("local project memory");
|
|
284446
|
+
if (parts.length) return parts.join(" + ");
|
|
284447
|
+
if (signedIn) return "unavailable until a workspace is available";
|
|
284448
|
+
return "unavailable; sign in or run from a .perch project";
|
|
284449
|
+
}
|
|
283826
284450
|
function renderCliCapabilityCount(state, workspaceId) {
|
|
283827
284451
|
const count = getExecutableToolDefinitions({
|
|
283828
284452
|
surface: "cli",
|
|
@@ -284017,7 +284641,21 @@ async function fetchCliHostedContext(connection, input) {
|
|
|
284017
284641
|
return {
|
|
284018
284642
|
userId: typeof context.session?.userId === "string" ? context.session.userId : session.userId ?? null,
|
|
284019
284643
|
workspaceId: typeof context.session?.workspaceId === "string" ? context.session.workspaceId : null,
|
|
284020
|
-
permanentMemories: Array.isArray(context.permanentMemories) ? context.permanentMemories.filter(
|
|
284644
|
+
permanentMemories: Array.isArray(context.permanentMemories) ? context.permanentMemories.filter(isPermanentMemoryLike2) : []
|
|
284645
|
+
};
|
|
284646
|
+
}
|
|
284647
|
+
async function fetchCliHostedContextForSession(session) {
|
|
284648
|
+
if (!isStoredCliAuthSessionUsable(session)) return null;
|
|
284649
|
+
const body = await postCliJson(session.appUrl, session, "/api/perch-terminal/cli-context", {
|
|
284650
|
+
query: "/status",
|
|
284651
|
+
threadId: "cli-status",
|
|
284652
|
+
limitPerScope: 1
|
|
284653
|
+
});
|
|
284654
|
+
if (!body || body.ok !== true) return null;
|
|
284655
|
+
const context = body;
|
|
284656
|
+
return {
|
|
284657
|
+
userId: typeof context.session?.userId === "string" ? context.session.userId : session.userId ?? null,
|
|
284658
|
+
workspaceId: typeof context.session?.workspaceId === "string" ? context.session.workspaceId : null
|
|
284021
284659
|
};
|
|
284022
284660
|
}
|
|
284023
284661
|
async function resolveCliMarketDeskProxy(connection) {
|
|
@@ -284025,6 +284663,8 @@ async function resolveCliMarketDeskProxy(connection) {
|
|
|
284025
284663
|
const session = await readStoredCliAuthSession();
|
|
284026
284664
|
if (!isStoredCliAuthSessionUsable(session)) return {};
|
|
284027
284665
|
return {
|
|
284666
|
+
cliServerAppUrl: session.appUrl || connection.appUrl,
|
|
284667
|
+
cliServerAccessToken: session.accessToken,
|
|
284028
284668
|
marketDeskProxyAppUrl: session.appUrl || connection.appUrl,
|
|
284029
284669
|
marketDeskProxyAccessToken: session.accessToken
|
|
284030
284670
|
};
|
|
@@ -284069,7 +284709,7 @@ async function postCliJson(appUrl, session, pathname, payload) {
|
|
|
284069
284709
|
clearTimeout(timeout);
|
|
284070
284710
|
}
|
|
284071
284711
|
}
|
|
284072
|
-
function
|
|
284712
|
+
function isPermanentMemoryLike2(value) {
|
|
284073
284713
|
if (!value || typeof value !== "object") return false;
|
|
284074
284714
|
const memory = value;
|
|
284075
284715
|
return typeof memory.id === "string" && typeof memory.title === "string" && typeof memory.body === "string";
|
|
@@ -284156,6 +284796,53 @@ function bodyColorForInkTone(tone) {
|
|
|
284156
284796
|
return "#fff8f0";
|
|
284157
284797
|
}
|
|
284158
284798
|
}
|
|
284799
|
+
function renderInkDetailContent(React11, Ink2, line) {
|
|
284800
|
+
const prefix = inkDetailPrefix(line.tone);
|
|
284801
|
+
const baseColor = colorForInkDetailTone(line.tone);
|
|
284802
|
+
const language = normalizeCliDetailLanguage(line.language);
|
|
284803
|
+
const tokens = shouldSyntaxHighlightDetail(line) ? tokenizeCliDetailSyntax(language, line.text) : [{ text: line.text, tone: "plain" }];
|
|
284804
|
+
return React11.createElement(
|
|
284805
|
+
Ink2.Text,
|
|
284806
|
+
null,
|
|
284807
|
+
prefix ? React11.createElement(
|
|
284808
|
+
Ink2.Text,
|
|
284809
|
+
{ color: baseColor, bold: line.tone === "add" || line.tone === "remove" },
|
|
284810
|
+
prefix
|
|
284811
|
+
) : null,
|
|
284812
|
+
...tokens.map(
|
|
284813
|
+
(token, index) => React11.createElement(
|
|
284814
|
+
Ink2.Text,
|
|
284815
|
+
{
|
|
284816
|
+
key: `${line.tone}-${index}-${token.tone}`,
|
|
284817
|
+
color: colorForCliSyntaxTone(token.tone, line.tone),
|
|
284818
|
+
dimColor: line.tone === "meta" || token.tone === "comment"
|
|
284819
|
+
},
|
|
284820
|
+
token.text
|
|
284821
|
+
)
|
|
284822
|
+
)
|
|
284823
|
+
);
|
|
284824
|
+
}
|
|
284825
|
+
function shouldSyntaxHighlightDetail(line) {
|
|
284826
|
+
if (line.tone === "meta" || line.tone === "hunk") return false;
|
|
284827
|
+
if (line.tone === "stdout" || line.tone === "stderr") return false;
|
|
284828
|
+
return Boolean(normalizeCliDetailLanguage(line.language)) || line.tone === "command";
|
|
284829
|
+
}
|
|
284830
|
+
function inkDetailPrefix(tone) {
|
|
284831
|
+
switch (tone) {
|
|
284832
|
+
case "add":
|
|
284833
|
+
return "+ ";
|
|
284834
|
+
case "remove":
|
|
284835
|
+
return "- ";
|
|
284836
|
+
case "stderr":
|
|
284837
|
+
return "! ";
|
|
284838
|
+
case "meta":
|
|
284839
|
+
return "# ";
|
|
284840
|
+
case "stdout":
|
|
284841
|
+
return " ";
|
|
284842
|
+
default:
|
|
284843
|
+
return "";
|
|
284844
|
+
}
|
|
284845
|
+
}
|
|
284159
284846
|
function colorForInkDetailTone(tone) {
|
|
284160
284847
|
switch (tone) {
|
|
284161
284848
|
case "add":
|
|
@@ -284174,21 +284861,168 @@ function colorForInkDetailTone(tone) {
|
|
|
284174
284861
|
return CLI_BRAND.cream;
|
|
284175
284862
|
}
|
|
284176
284863
|
}
|
|
284177
|
-
function
|
|
284178
|
-
|
|
284179
|
-
|
|
284180
|
-
|
|
284181
|
-
|
|
284182
|
-
|
|
284183
|
-
|
|
284184
|
-
|
|
284185
|
-
|
|
284186
|
-
|
|
284187
|
-
|
|
284188
|
-
|
|
284189
|
-
|
|
284190
|
-
|
|
284864
|
+
function colorForCliSyntaxTone(tone, lineTone) {
|
|
284865
|
+
if (lineTone === "remove") {
|
|
284866
|
+
switch (tone) {
|
|
284867
|
+
case "comment":
|
|
284868
|
+
return "#8e6a55";
|
|
284869
|
+
case "string":
|
|
284870
|
+
return "#c48656";
|
|
284871
|
+
case "keyword":
|
|
284872
|
+
case "flag":
|
|
284873
|
+
return CLI_BRAND.bronzeGlint;
|
|
284874
|
+
case "number":
|
|
284875
|
+
return "#c77952";
|
|
284876
|
+
case "operator":
|
|
284877
|
+
return CLI_BRAND.bronzeDeep;
|
|
284878
|
+
default:
|
|
284879
|
+
return CLI_BRAND.bronzeGlint;
|
|
284880
|
+
}
|
|
284881
|
+
}
|
|
284882
|
+
if (lineTone === "add") {
|
|
284883
|
+
switch (tone) {
|
|
284884
|
+
case "comment":
|
|
284885
|
+
return "#7f9586";
|
|
284886
|
+
case "string":
|
|
284887
|
+
return "#b7c989";
|
|
284888
|
+
case "keyword":
|
|
284889
|
+
case "flag":
|
|
284890
|
+
return CLI_BRAND.patinaActive;
|
|
284891
|
+
case "number":
|
|
284892
|
+
return "#8fd19c";
|
|
284893
|
+
case "operator":
|
|
284894
|
+
return "#7e9f87";
|
|
284895
|
+
default:
|
|
284896
|
+
return CLI_BRAND.cream;
|
|
284897
|
+
}
|
|
284898
|
+
}
|
|
284899
|
+
if (lineTone === "command") {
|
|
284900
|
+
switch (tone) {
|
|
284901
|
+
case "comment":
|
|
284902
|
+
return "#7a6f66";
|
|
284903
|
+
case "string":
|
|
284904
|
+
case "path":
|
|
284905
|
+
return "#e5bc75";
|
|
284906
|
+
case "keyword":
|
|
284907
|
+
return CLI_BRAND.bronzeGlint;
|
|
284908
|
+
case "flag":
|
|
284909
|
+
return CLI_BRAND.patinaActive;
|
|
284910
|
+
case "number":
|
|
284911
|
+
return "#8fd19c";
|
|
284912
|
+
case "operator":
|
|
284913
|
+
return CLI_BRAND.muted;
|
|
284914
|
+
default:
|
|
284915
|
+
return CLI_BRAND.cream;
|
|
284916
|
+
}
|
|
284917
|
+
}
|
|
284918
|
+
return colorForInkDetailTone(lineTone);
|
|
284919
|
+
}
|
|
284920
|
+
function tokenizeCliDetailSyntax(language, text) {
|
|
284921
|
+
const normalized = language ?? "text";
|
|
284922
|
+
const pattern = cliSyntaxPatternForLanguage(normalized);
|
|
284923
|
+
if (!pattern) return [{ text, tone: "plain" }];
|
|
284924
|
+
const tokens = [];
|
|
284925
|
+
let lastIndex = 0;
|
|
284926
|
+
pattern.lastIndex = 0;
|
|
284927
|
+
for (let match = pattern.exec(text); match; match = pattern.exec(text)) {
|
|
284928
|
+
if (match.index > lastIndex) {
|
|
284929
|
+
tokens.push({ text: text.slice(lastIndex, match.index), tone: "plain" });
|
|
284930
|
+
}
|
|
284931
|
+
tokens.push({ text: match[0], tone: cliToneForSyntaxToken(normalized, match[0]) });
|
|
284932
|
+
lastIndex = match.index + match[0].length;
|
|
284933
|
+
}
|
|
284934
|
+
if (lastIndex < text.length) {
|
|
284935
|
+
tokens.push({ text: text.slice(lastIndex), tone: "plain" });
|
|
284191
284936
|
}
|
|
284937
|
+
return tokens.length ? tokens : [{ text, tone: "plain" }];
|
|
284938
|
+
}
|
|
284939
|
+
function cliSyntaxPatternForLanguage(language) {
|
|
284940
|
+
if (["javascript", "typescript", "jsx", "tsx"].includes(language)) {
|
|
284941
|
+
return /(\/\/.*$|\/\*.*?\*\/|`(?:\\.|[^`])*`|'(?:\\.|[^'])*'|"(?:\\.|[^"])*"|\b(?:const|let|var|function|return|if|else|for|while|import|export|from|class|extends|new|await|async|try|catch|throw|type|interface|implements|switch|case|break|continue|null|undefined|true|false)\b|\b\d+(?:\.\d+)?\b|=>|===|!==|==|!=|\|\||&&|[{}()[\].,:;<>/+*=-])/g;
|
|
284942
|
+
}
|
|
284943
|
+
if (language === "python") {
|
|
284944
|
+
return /(#.*$|'''[\s\S]*?'''|"""[\s\S]*?"""|'(?:\\.|[^'])*'|"(?:\\.|[^"])*"|\b(?:def|class|return|if|elif|else|for|while|import|from|as|try|except|raise|with|yield|lambda|None|True|False|async|await|pass|break|continue|and|or|not)\b|\b\d+(?:\.\d+)?\b|[{}()[\].,:;<>/+*=-])/g;
|
|
284945
|
+
}
|
|
284946
|
+
if (language === "bash" || language === "shell") {
|
|
284947
|
+
return /(#.*$|'[^']*'|"(?:\\.|[^"])*"|\$(?:\w+|\{[^}]+\})|(?:^|\s)-{1,2}[A-Za-z0-9][\w-]*|\b(?:if|then|fi|for|do|done|case|esac|function|export|local|sudo|cd|echo|grep|sed|awk|find|cat|ls|mkdir|rm|mv|cp|npm|node|tsx|python|git|rg)\b|\b\d+\b|[|&;()<>])/g;
|
|
284948
|
+
}
|
|
284949
|
+
if (language === "json" || language === "yaml") {
|
|
284950
|
+
return /("(?:\\.|[^"\\])*"(?=\s*:)|"(?:\\.|[^"\\])*"|\b(?:true|false|null)\b|\b\d+(?:\.\d+)?\b|[:{}\[\],-])/g;
|
|
284951
|
+
}
|
|
284952
|
+
if (language === "css") {
|
|
284953
|
+
return /(\/\*.*?\*\/|"(?:\\.|[^"])*"|'(?:\\.|[^'])*'|#[0-9a-fA-F]{3,8}\b|\b\d+(?:\.\d+)?(?:px|rem|em|%)?\b|[{}:;(),])/g;
|
|
284954
|
+
}
|
|
284955
|
+
if (language === "html" || language === "xml") {
|
|
284956
|
+
return /(<!--.*?-->|<\/?[A-Za-z0-9:-]+|"(?:\\.|[^"])*"|'(?:\\.|[^'])*'|\/?>)/g;
|
|
284957
|
+
}
|
|
284958
|
+
return null;
|
|
284959
|
+
}
|
|
284960
|
+
function cliToneForSyntaxToken(language, text) {
|
|
284961
|
+
if (text.startsWith("//") || text.startsWith("/*") || text.startsWith("#") || text.startsWith("<!--")) {
|
|
284962
|
+
return "comment";
|
|
284963
|
+
}
|
|
284964
|
+
if (text.startsWith("'") || text.startsWith('"') || text.startsWith("`")) {
|
|
284965
|
+
return language === "json" && text.endsWith(":") ? "keyword" : "string";
|
|
284966
|
+
}
|
|
284967
|
+
if (/^\s*-{1,2}[A-Za-z0-9]/.test(text)) return "flag";
|
|
284968
|
+
if (/^\$/.test(text)) return "keyword";
|
|
284969
|
+
if (/^\d/.test(text)) return "number";
|
|
284970
|
+
if (/^[{}()[\].,:;<>/+*=\-|&]+$/.test(text)) return "operator";
|
|
284971
|
+
if (/[\\/]/.test(text) && !/\s/.test(text)) return "path";
|
|
284972
|
+
return "keyword";
|
|
284973
|
+
}
|
|
284974
|
+
function normalizeCliDetailLanguage(language) {
|
|
284975
|
+
const value = language?.trim().toLowerCase();
|
|
284976
|
+
if (!value) return null;
|
|
284977
|
+
const map2 = {
|
|
284978
|
+
js: "javascript",
|
|
284979
|
+
mjs: "javascript",
|
|
284980
|
+
cjs: "javascript",
|
|
284981
|
+
jsx: "jsx",
|
|
284982
|
+
ts: "typescript",
|
|
284983
|
+
mts: "typescript",
|
|
284984
|
+
cts: "typescript",
|
|
284985
|
+
tsx: "tsx",
|
|
284986
|
+
py: "python",
|
|
284987
|
+
python3: "python",
|
|
284988
|
+
sh: "bash",
|
|
284989
|
+
shell: "bash",
|
|
284990
|
+
zsh: "bash",
|
|
284991
|
+
yml: "yaml",
|
|
284992
|
+
htm: "html"
|
|
284993
|
+
};
|
|
284994
|
+
return map2[value] ?? value;
|
|
284995
|
+
}
|
|
284996
|
+
function cliLanguageForSandbox(language) {
|
|
284997
|
+
if (language === "node") return "javascript";
|
|
284998
|
+
if (language === "shell") return "bash";
|
|
284999
|
+
return normalizeCliDetailLanguage(language) ?? "text";
|
|
285000
|
+
}
|
|
285001
|
+
function inferCliLanguageFromPath(filePath) {
|
|
285002
|
+
const extension2 = filePath.split(/[./\\]/).pop()?.toLowerCase();
|
|
285003
|
+
if (!extension2 || extension2 === filePath.toLowerCase()) return null;
|
|
285004
|
+
const map2 = {
|
|
285005
|
+
js: "javascript",
|
|
285006
|
+
mjs: "javascript",
|
|
285007
|
+
cjs: "javascript",
|
|
285008
|
+
jsx: "jsx",
|
|
285009
|
+
ts: "typescript",
|
|
285010
|
+
mts: "typescript",
|
|
285011
|
+
cts: "typescript",
|
|
285012
|
+
tsx: "tsx",
|
|
285013
|
+
py: "python",
|
|
285014
|
+
sh: "bash",
|
|
285015
|
+
zsh: "bash",
|
|
285016
|
+
json: "json",
|
|
285017
|
+
jsonc: "json",
|
|
285018
|
+
yml: "yaml",
|
|
285019
|
+
yaml: "yaml",
|
|
285020
|
+
css: "css",
|
|
285021
|
+
html: "html",
|
|
285022
|
+
htm: "html",
|
|
285023
|
+
xml: "xml"
|
|
285024
|
+
};
|
|
285025
|
+
return map2[extension2] ?? null;
|
|
284192
285026
|
}
|
|
284193
285027
|
function buildFileToolDisplay(toolName, input, phase, summary) {
|
|
284194
285028
|
const normalizedName = toolName.toLowerCase();
|
|
@@ -284199,12 +285033,13 @@ function buildFileToolDisplay(toolName, input, phase, summary) {
|
|
|
284199
285033
|
const filePath = stringValue8(input.path) ?? stringValue8(input.filePath) ?? summary?.filePath ?? "file";
|
|
284200
285034
|
const short = shortFilePath(filePath);
|
|
284201
285035
|
const status = phase === "running" ? "running" : summary?.changeKind ?? "done";
|
|
285036
|
+
const language = inferCliLanguageFromPath(filePath);
|
|
284202
285037
|
if (isWrite) {
|
|
284203
285038
|
const content = stringValue8(input.content) ?? "";
|
|
284204
285039
|
const added = summary?.linesAdded ?? countTextLines(content);
|
|
284205
285040
|
const detailLines = [
|
|
284206
285041
|
{ tone: "hunk", text: `@@ ${short}` },
|
|
284207
|
-
...textToDetailLines(content, "add")
|
|
285042
|
+
...textToDetailLines(content, "add", language)
|
|
284208
285043
|
];
|
|
284209
285044
|
if (detailLines.length === 1 && summary) {
|
|
284210
285045
|
detailLines.push({ tone: "meta", text: describeChangeSummary(summary) });
|
|
@@ -284221,8 +285056,8 @@ function buildFileToolDisplay(toolName, input, phase, summary) {
|
|
|
284221
285056
|
const added = summary?.linesAdded ?? countTextLines(newText);
|
|
284222
285057
|
const detailLines = [
|
|
284223
285058
|
{ tone: "hunk", text: `@@ ${short}` },
|
|
284224
|
-
...textToDetailLines(oldText, "remove"),
|
|
284225
|
-
...textToDetailLines(newText, "add")
|
|
285059
|
+
...textToDetailLines(oldText, "remove", language),
|
|
285060
|
+
...textToDetailLines(newText, "add", language)
|
|
284226
285061
|
];
|
|
284227
285062
|
if (detailLines.length === 1 && summary) {
|
|
284228
285063
|
detailLines.push({ tone: "meta", text: describeChangeSummary(summary) });
|
|
@@ -284248,18 +285083,23 @@ function describeChangeSummary(summary) {
|
|
|
284248
285083
|
const removed = summary.linesRemoved ?? 0;
|
|
284249
285084
|
return `${kind} \xB7 +${added} -${removed}`;
|
|
284250
285085
|
}
|
|
284251
|
-
function textToDetailLines(text, tone) {
|
|
285086
|
+
function textToDetailLines(text, tone, language) {
|
|
284252
285087
|
if (!text) return [];
|
|
284253
285088
|
const lines = text.split(/\r?\n/);
|
|
284254
|
-
const
|
|
285089
|
+
const normalizedLanguage = normalizeCliDetailLanguage(language);
|
|
285090
|
+
const preview = lines.slice(0, 38).map((line) => ({
|
|
285091
|
+
tone,
|
|
285092
|
+
text: line,
|
|
285093
|
+
...normalizedLanguage ? { language: normalizedLanguage } : {}
|
|
285094
|
+
}));
|
|
284255
285095
|
if (lines.length > preview.length) {
|
|
284256
285096
|
preview.push({ tone: "meta", text: `${lines.length - preview.length} more line(s)` });
|
|
284257
285097
|
}
|
|
284258
285098
|
return preview;
|
|
284259
285099
|
}
|
|
284260
|
-
function codePreviewDetailLines(code) {
|
|
285100
|
+
function codePreviewDetailLines(code, language) {
|
|
284261
285101
|
if (!code.trim()) return [];
|
|
284262
|
-
return textToDetailLines(code, "command").slice(0, 18);
|
|
285102
|
+
return textToDetailLines(code, "command", language).slice(0, 18);
|
|
284263
285103
|
}
|
|
284264
285104
|
function outputChunkToDetailLines(text, tone) {
|
|
284265
285105
|
if (!text) return [];
|
|
@@ -284369,12 +285209,17 @@ async function runAuthCommand(parsed, writer) {
|
|
|
284369
285209
|
if (parsed.action === "status") {
|
|
284370
285210
|
const session = await readStoredCliAuthSession();
|
|
284371
285211
|
if (isStoredCliAuthSessionUsable(session)) {
|
|
284372
|
-
|
|
284373
|
-
|
|
285212
|
+
const hostedContext = await fetchCliHostedContextForSession(session).catch(() => null);
|
|
285213
|
+
writer.stdout([
|
|
285214
|
+
`Perch CLI ${CLI_PACKAGE_VERSION} \xB7 Signed in${session.email ? ` as ${session.email}` : ""} \xB7 ${session.appUrl}`,
|
|
285215
|
+
`Memory: ${renderCliMemoryAvailability(process.cwd(), hostedContext?.workspaceId ?? null, true)}`
|
|
285216
|
+
].join("\n") + "\n");
|
|
284374
285217
|
return 0;
|
|
284375
285218
|
}
|
|
284376
|
-
writer.stdout(
|
|
284377
|
-
`
|
|
285219
|
+
writer.stdout([
|
|
285220
|
+
`Perch CLI ${CLI_PACKAGE_VERSION} \xB7 Not signed in. Run \`perch login\`.`,
|
|
285221
|
+
`Memory: ${renderCliMemoryAvailability(process.cwd(), null, false)}`
|
|
285222
|
+
].join("\n") + "\n");
|
|
284378
285223
|
return 2;
|
|
284379
285224
|
}
|
|
284380
285225
|
const appUrl = resolveCliAppUrl(parsed.appUrl, DEFAULT_CLI_LOGIN_APP_URL) ?? DEFAULT_CLI_LOGIN_APP_URL;
|
|
@@ -284634,6 +285479,7 @@ var init_perch_cli = __esm({
|
|
|
284634
285479
|
init_runRegistry();
|
|
284635
285480
|
init_learningMemory();
|
|
284636
285481
|
init_toolDefinitions();
|
|
285482
|
+
init_nodeLocalBridge();
|
|
284637
285483
|
execFileAsync3 = promisify3(execFile3);
|
|
284638
285484
|
DEFAULT_CLI_LOGIN_APP_URL = "https://app.perchai.app";
|
|
284639
285485
|
CLI_PACKAGE_VERSION = readCliPackageVersion();
|