@kelceyp/caw-server 1.0.274 → 1.0.276

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.
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Side-effect-free extraction of `emitResult()` from `pty-wrapper.mjs`.
3
+ *
4
+ * The wrapper's `onExit` handler reads the Stop-hook sentinel file, then hands
5
+ * the parsed payload to this function to build either the `type:"result"` or
6
+ * `type:"error"` stdout line. Splitting the branch construction out of the
7
+ * wrapper mirrors `pty-deferred-teardown.mjs` (already extracts
8
+ * `readSentinelPayload` and `hasPendingWork`) so the sentinel-success and
9
+ * error branches can be unit-tested without spawning a real PTY.
10
+ *
11
+ * Inputs are function arguments only — no module-level state, no top-level
12
+ * side effects, no file IO. The caller (the wrapper's delegator) reads the
13
+ * sentinel via `readSentinelPayload()` and passes the parsed payload in as
14
+ * `hookData`; a null / falsy `hookData` is a no-op, mirroring the wrapper's
15
+ * pre-extraction `if (!existsSync(sentinelPath)) return;` guard one level up.
16
+ *
17
+ * Wire is preserved byte-for-byte relative to the pre-extraction wrapper:
18
+ * - success line: `{ type: 'result', result, metadata }` with
19
+ * `metadata = { sessionId, transcriptPath, permissionMode[, durationMs] }`
20
+ * — same field set, same field order, same fallback chains
21
+ * (`hookData.transcript_path || transcriptPath || null`,
22
+ * `hookData.session_id || effectiveSessionId`, `hookData.permission_mode || null`).
23
+ * - error line: `{ type: 'error', error, message }` — same shape, no
24
+ * `metadata` field at all.
25
+ *
26
+ * The double-emit guard (previously `if (resultEmitted || failureEmitted) return;`
27
+ * at `pty-wrapper.mjs:401`) is preserved via the `alreadyEmitted` input: the
28
+ * caller passes the current flag state, and the function no-ops when true.
29
+ * The return value `{ emitted: 'result' | 'error' | null }` lets the caller
30
+ * translate a successful call back into its module-level `resultEmitted` /
31
+ * `failureEmitted` flags, which the prompt-too-long branch at
32
+ * `pty-wrapper.mjs:488` still reads externally.
33
+ */
34
+
35
+ const defaultWrite = (line) => process.stdout.write(line);
36
+ const defaultLog = () => {};
37
+
38
+ export function emitResult({
39
+ hookData,
40
+ transcriptPath = null,
41
+ effectiveSessionId = null,
42
+ startedAt = null,
43
+ endedAt = null,
44
+ alreadyEmitted = false,
45
+ suppressFallback = false,
46
+ write = defaultWrite,
47
+ log = defaultLog
48
+ } = {}) {
49
+ if (alreadyEmitted) return { emitted: null };
50
+ if (!hookData) return { emitted: null };
51
+
52
+ try {
53
+ if (hookData.error) {
54
+ write(JSON.stringify({
55
+ type: 'error',
56
+ error: hookData.error,
57
+ message: hookData.last_assistant_message || ''
58
+ }) + '\n');
59
+ return { emitted: 'error' };
60
+ }
61
+
62
+ const metadata = {
63
+ sessionId: hookData.session_id || effectiveSessionId,
64
+ transcriptPath: hookData.transcript_path || transcriptPath || null,
65
+ permissionMode: hookData.permission_mode || null
66
+ };
67
+ if (startedAt !== null && endedAt !== null) {
68
+ metadata.durationMs = endedAt - startedAt;
69
+ }
70
+ // D11 test-only override: when the wrapper's CAW_PTY_SUPPRESS_STOP_FALLBACK
71
+ // gate is set, blank the `result` field on the success branch only. The
72
+ // wire is deliberately identical to a real empty-message turn — that IS
73
+ // the point of the override. Metadata, field order, and everything else
74
+ // stay byte-for-byte identical (Task 3's guarantee).
75
+ const result = suppressFallback ? '' : (hookData.last_assistant_message || '');
76
+ write(JSON.stringify({
77
+ type: 'result',
78
+ result,
79
+ metadata
80
+ }) + '\n');
81
+ return { emitted: 'result' };
82
+ } catch (err) {
83
+ log(`Error reading sentinel: ${err.message}`);
84
+ return { emitted: null };
85
+ }
86
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kelceyp/caw-server",
3
- "version": "1.0.274",
3
+ "version": "1.0.276",
4
4
  "description": "CAW server - REST API, MCP server and core features",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",
@@ -19,7 +19,7 @@
19
19
  "build": "bash scripts/bun-build.sh",
20
20
  "build:client": "cd ../client && bun run build",
21
21
  "build:cli": "cd ../cli && bun run build && cp dist/cli.js ../server/dist/",
22
- "build:all": "bun run clean && bun run generate:applyPojo && bun run build:client && bun run build && cp src/core/services/embedding-worker.js dist/embedding-worker.js && cp src/core/agents/pty-wrapper.mjs dist/pty-wrapper.mjs && cp src/core/agents/pty-deferred-teardown.mjs dist/pty-deferred-teardown.mjs && bun run build:cli",
22
+ "build:all": "bun run clean && bun run generate:applyPojo && bun run build:client && bun run build && cp src/core/services/embedding-worker.js dist/embedding-worker.js && cp src/core/agents/pty-wrapper.mjs dist/pty-wrapper.mjs && cp src/core/agents/pty-deferred-teardown.mjs dist/pty-deferred-teardown.mjs && cp src/core/agents/pty-emit-result.mjs dist/pty-emit-result.mjs && bun run build:cli",
23
23
  "dev": "STATIC_PATH=../client/src bun --define __BUILTIN_POSTHOG_KEY__=null run src/main.js",
24
24
  "start": "node dist/main.js",
25
25
  "test": "bun test --define __BUILTIN_POSTHOG_KEY__=null test/unit test/integration test/component test/cli test/investigation && (cd ../client && bun test)",