kushi-agents 5.8.2 → 5.8.4
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kushi-agents",
|
|
3
|
-
"version": "5.8.
|
|
3
|
+
"version": "5.8.4",
|
|
4
4
|
"description": "Install Kushi — multi-source project evidence agent with Comprehensive Structured Capture (CSC) into weekly-only files across Email, Teams, OneNote, Loop, SharePoint, Meetings, CRM, ADO. Meetings retain a sibling verbatim/ audit folder. WorkIQ-only for M365 sources (Graph / m365_* FORBIDDEN as fallbacks; user-paste is first-class). Host-agnostic.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -89,16 +89,29 @@ function buildPrompt(source, projectName, scope = null) {
|
|
|
89
89
|
? scope.folders
|
|
90
90
|
: ['Inbox']; // safe default — bounds the query so WorkIQ uses Graph filter, not mailbox-wide semantic search
|
|
91
91
|
const isDefault = !(Array.isArray(scope.folders) && scope.folders.length > 0);
|
|
92
|
+
// matchingPolicy.mode drives whether we search by subfolder name, by mail content, or both.
|
|
93
|
+
// Recognized values: 'subfolder-only', 'keyword-only', 'hybrid' (default).
|
|
94
|
+
const mode = (scope.matchMode || 'hybrid').toLowerCase();
|
|
95
|
+
const doFolder = mode !== 'keyword-only';
|
|
96
|
+
const doKeyword = mode !== 'subfolder-only';
|
|
92
97
|
lines.push('');
|
|
93
|
-
|
|
94
|
-
lines.push('Restrict your search to Outlook mail folders whose name CONTAINS any of these tokens (case-insensitive, fuzzy substring match — e.g. "FDE" matches "1. FDE", "01 FDE Active", "FDE-archive"):');
|
|
95
|
-
} else {
|
|
96
|
-
lines.push('Restrict your search to ONLY these Outlook mail folders (exact name match):');
|
|
97
|
-
}
|
|
98
|
+
lines.push('SCOPE BOUNDARY — search ONLY within these Outlook parent folders and ALL nested descendants:');
|
|
98
99
|
for (const f of folders) {
|
|
99
|
-
lines.push(` • "${f}"${scope.includeSubfolders ? ' (
|
|
100
|
+
lines.push(` • "${f}"${scope.includeSubfolders !== false ? ' (recursively include every subfolder underneath)' : ' (this folder only)'}`);
|
|
101
|
+
}
|
|
102
|
+
lines.push('Parent folder name match: case-insensitive, fuzzy contains (so "1. FDE" matches "1. FDE", "01. FDE", "1 FDE").');
|
|
103
|
+
lines.push('Do NOT look outside this boundary.');
|
|
104
|
+
lines.push('');
|
|
105
|
+
lines.push(`SEARCH STRATEGY for project "${projectName}" (matchingPolicy.mode = "${mode}"):`);
|
|
106
|
+
if (doFolder && doKeyword) {
|
|
107
|
+
lines.push(` 1. Subfolder match: look for any subfolder whose name fuzzy-contains "${projectName}" (e.g. "${projectName}", "102. ${projectName}", "${projectName} - Engagement"). Emit each as a match.`);
|
|
108
|
+
lines.push(` 2. Mail-content match: also search email subjects, bodies, sender/recipient names within the boundary for "${projectName}" (case-insensitive). Group matching mail by folder and emit each folder.`);
|
|
109
|
+
lines.push(` Run BOTH and merge results — do not stop at step 1.`);
|
|
110
|
+
} else if (doFolder) {
|
|
111
|
+
lines.push(` Subfolder match only: emit subfolders whose name fuzzy-contains "${projectName}".`);
|
|
112
|
+
} else {
|
|
113
|
+
lines.push(` Mail-content match only: search email subjects, bodies, sender/recipient names within the boundary for "${projectName}" (case-insensitive). Group matching mail by folder and emit each folder.`);
|
|
100
114
|
}
|
|
101
|
-
lines.push('Do NOT scan any other mailbox folders.');
|
|
102
115
|
if (isDefault) {
|
|
103
116
|
lines.push('(Note: no project-specific folders configured — defaulting to Inbox+subfolders. For faster, more accurate results, populate emailContext.folders in m365-auth.json.)');
|
|
104
117
|
}
|
|
@@ -59,7 +59,11 @@ export async function ask(prompt, { bin, timeoutMs = 120_000, env = process.env,
|
|
|
59
59
|
err.code = 'WORKIQ_NOT_FOUND';
|
|
60
60
|
throw err;
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
// Pass prompt via stdin instead of `-q "<prompt>"` argument. Multi-line prompts
|
|
63
|
+
// with embedded quotes get mangled by cmd.exe's argument parser even with
|
|
64
|
+
// windowsVerbatimArguments, causing workiq to receive corrupted input and
|
|
65
|
+
// hang indefinitely. stdin is a clean byte channel.
|
|
66
|
+
const { stdout, stderr, exitCode } = await runProcess(exe, ['ask'], { timeoutMs, env, onHeartbeat, heartbeatMs, stdin: prompt });
|
|
63
67
|
if (exitCode !== 0) {
|
|
64
68
|
const err = new Error(`workiq exited ${exitCode}: ${stderr.slice(0, 1000)}`);
|
|
65
69
|
err.code = 'WORKIQ_EXIT_NONZERO';
|
|
@@ -117,7 +121,7 @@ function parseKvLines(s) {
|
|
|
117
121
|
|
|
118
122
|
async function pathExists(p) { try { await fs.access(p); return true; } catch { return false; } }
|
|
119
123
|
|
|
120
|
-
function runProcess(exe, args, { timeoutMs, env, onHeartbeat = null, heartbeatMs = 10_000 }) {
|
|
124
|
+
function runProcess(exe, args, { timeoutMs, env, onHeartbeat = null, heartbeatMs = 10_000, stdin = null }) {
|
|
121
125
|
return new Promise((resolve, reject) => {
|
|
122
126
|
// Node 20.12+ refuses to spawn .cmd/.bat on Windows without shell:true
|
|
123
127
|
// (CVE-2024-27980). Detect and route through cmd.exe explicitly with
|
|
@@ -133,6 +137,10 @@ function runProcess(exe, args, { timeoutMs, env, onHeartbeat = null, heartbeatMs
|
|
|
133
137
|
spawnOpts.windowsVerbatimArguments = true;
|
|
134
138
|
}
|
|
135
139
|
const child = spawn(spawnExe, spawnArgs, spawnOpts);
|
|
140
|
+
if (stdin != null) {
|
|
141
|
+
try { child.stdin.write(stdin); child.stdin.end(); }
|
|
142
|
+
catch (e) { /* if stdin already closed, child error handler will catch */ }
|
|
143
|
+
}
|
|
136
144
|
let stdout = '';
|
|
137
145
|
let stderr = '';
|
|
138
146
|
let stdoutBytes = 0;
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"sourceCoverageLabel": "",
|
|
42
42
|
"matchingPolicy": {
|
|
43
43
|
"mode": "hybrid",
|
|
44
|
+
"_mode_note": "How to find project-related mail within the SCOPE BOUNDARY (folders[] + dateFloor). One of: 'subfolder-only' (only match subfolder names containing the project name), 'keyword-only' (only search mail subjects/bodies for the project name), 'hybrid' (do both and merge — recommended default).",
|
|
44
45
|
"rankingOrder": ["exact", "prefix", "contains"],
|
|
45
46
|
"minConfidenceForFolderScopedSearch": "high",
|
|
46
47
|
"fallbackToFullRootScanWhenAmbiguous": true,
|