kushi-agents 5.7.5 → 5.7.6
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.7.
|
|
3
|
+
"version": "5.7.6",
|
|
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": {
|
|
@@ -4,6 +4,33 @@ Newest on top. Format defined in [`README.md`](./README.md). Use this file when
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
### 2026-05-29 — WorkIQ stdout is fully-buffered through the Windows spawn chain
|
|
8
|
+
|
|
9
|
+
**Symptom**: After v5.7.5 added intelligent heartbeats, user re-ran discover. Heartbeats showed `...waiting for first byte (10s/180s, no output yet)` for 170+ seconds, then `✗ TIMEOUT after 180045ms (received 0 bytes before kill)`. Yet running `workiq.cmd ask -q "..."` directly from PowerShell returned 4 emails in 29 seconds.
|
|
10
|
+
|
|
11
|
+
**Root cause** (reproduced via standalone node-spawn test): The Windows WorkIQ CLI is a 6-process chain — `cmd.exe → workiq.cmd → node-runner.exe → Clawpilot.exe → workiq-wrapper.cjs → workiq.exe`. When invoked from a child process (not a TTY), stdout is **fully-buffered**: WorkIQ accumulates the entire response and only flushes at process exit. A single 4649-byte response that takes 43 seconds shows zero bytes for the first 42 seconds and all 4649 bytes in one chunk at second 43. Direct PowerShell invocation gets line-buffered output via TTY, which is why interactive testing hides this.
|
|
12
|
+
|
|
13
|
+
**Implication**: There's no way for `discover.mjs` to stream WorkIQ partial output. We can only wait for completion. Therefore:
|
|
14
|
+
- Timeouts must be generous enough for the slowest legitimate query (5+ minutes, not 90s).
|
|
15
|
+
- "0 bytes received" during heartbeat is **NOT** a stuck signal on Windows — it's the steady state for the entire query. Heartbeat copy must reflect that.
|
|
16
|
+
- Total worst-case discover walltime is `7 × timeout` (35 min at 5min budget) but typical ≈ 30s/source ≈ 4 min total.
|
|
17
|
+
|
|
18
|
+
**Fix shipped (v5.7.6, 2026-05-29)**:
|
|
19
|
+
|
|
20
|
+
1. **Default `--timeout-ms` bumped from 180s to 300s** (5 min per source).
|
|
21
|
+
2. **Heartbeat message updated** when stdout is empty: `...still running (Xs/Ys, WorkIQ buffers stdout until exit, this is normal)` — explains the 0-byte state instead of misleadingly saying "no output yet".
|
|
22
|
+
3. **Test mirror updated** to accept the new heartbeat copy.
|
|
23
|
+
|
|
24
|
+
**Lesson — pipe-buffered stdio invalidates "no output = stuck" heuristics**:
|
|
25
|
+
- On Windows especially, complex CLI chains (Electron/wrapper-based tools) often pipe-buffer their stdout, releasing all output at process exit.
|
|
26
|
+
- Heartbeat / progress UI must distinguish "0 bytes" from "stuck" carefully. The fact that the runtime is *running* (process alive, parent still reading from pipe) is more meaningful than byte count.
|
|
27
|
+
- For runners shelling out to such tools, default timeouts must scale to the slowest legitimate query, not the median. 5 min/source is a safer default than 90s.
|
|
28
|
+
- This is a property of the *child tool*, not of our code. We can't make WorkIQ stream; we can only set realistic budgets and explain the wait honestly.
|
|
29
|
+
|
|
30
|
+
**Files changed**: `plugin/runners/discover.mjs` (timeout default + heartbeat copy), `plugin/runners/test/unit/discover.test.mjs` (heartbeat assertion).
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
7
34
|
### 2026-05-29 — Per-step stderr alone is not enough; long single steps need heartbeat ticks
|
|
8
35
|
|
|
9
36
|
**Symptom**: After v5.7.4 added per-source `[discover] → email ...` stderr lines, a user re-ran discover from VS Code Copilot Chat. Each WorkIQ call still took 30–90s, and the host **still** killed the runner because between sources there was no output for 30+ seconds. The single `[discover] → email ...` line at the start of each source wasn't enough — the host watchdog measures *idle output* (no bytes on either stream for N seconds), not just *no progress at all*.
|
|
@@ -24,7 +24,7 @@ import { ask as workiqAsk, resolveWorkiqBin } from './lib/workiq.mjs';
|
|
|
24
24
|
const ALL_SOURCES = ['email', 'teams', 'meetings', 'onenote', 'sharepoint', 'crm', 'ado'];
|
|
25
25
|
|
|
26
26
|
function parseArgs(argv) {
|
|
27
|
-
const args = { force: false, dryRun: false, timeoutMs:
|
|
27
|
+
const args = { force: false, dryRun: false, timeoutMs: 300_000, sources: null };
|
|
28
28
|
for (let i = 0; i < argv.length; i++) {
|
|
29
29
|
const a = argv[i];
|
|
30
30
|
if (a === '--project') args.project = argv[++i];
|
|
@@ -32,7 +32,7 @@ function parseArgs(argv) {
|
|
|
32
32
|
else if (a === '--source') (args.sources ??= []).push(argv[++i]);
|
|
33
33
|
else if (a === '--force') args.force = true;
|
|
34
34
|
else if (a === '--dry-run') args.dryRun = true;
|
|
35
|
-
else if (a === '--timeout-ms') args.timeoutMs = Number(argv[++i]) ||
|
|
35
|
+
else if (a === '--timeout-ms') args.timeoutMs = Number(argv[++i]) || 300_000;
|
|
36
36
|
else if (a === '--help' || a === '-h') args.help = true;
|
|
37
37
|
}
|
|
38
38
|
return args;
|
|
@@ -224,7 +224,10 @@ async function main() {
|
|
|
224
224
|
const sec = Math.round(elapsedMs / 1000);
|
|
225
225
|
const budget = Math.round(args.timeoutMs / 1000);
|
|
226
226
|
if (stdoutBytes === 0) {
|
|
227
|
-
|
|
227
|
+
// WorkIQ on Windows pipes stdout fully-buffered (cmd.exe → node-runner →
|
|
228
|
+
// Clawpilot → wrapper.cjs → workiq.exe chain). Output won't appear until
|
|
229
|
+
// workiq exits. 0 bytes ≠ stuck — it's normal until the final flush.
|
|
230
|
+
log(` ${source}: ...still running (${sec}s/${budget}s, WorkIQ buffers stdout until exit, this is normal)`);
|
|
228
231
|
} else {
|
|
229
232
|
log(` ${source}: ...still working (${sec}s/${budget}s, ${stdoutBytes} bytes received)`);
|
|
230
233
|
}
|
|
@@ -195,6 +195,6 @@ test('discover: heartbeat ticks emit during slow workiq calls (v5.7.5 host-watch
|
|
|
195
195
|
KUSHI_DISCOVER_HEARTBEAT_MS: '1000', // 1s heartbeat for the test
|
|
196
196
|
});
|
|
197
197
|
assert.equal(r.code, 0, r.stderr);
|
|
198
|
-
assert.match(r.stderr, /still working|
|
|
198
|
+
assert.match(r.stderr, /still working|still running/,
|
|
199
199
|
`expected heartbeat tick on stderr, got: ${r.stderr}`);
|
|
200
200
|
});
|