lsd-pi 1.1.4 → 1.1.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/README.md +2 -1
- package/dist/headless-ui.js +2 -0
- package/dist/onboarding.js +11 -8
- package/dist/resources/extensions/async-jobs/async-bash-tool.js +14 -0
- package/dist/resources/extensions/async-jobs/await-tool.js +14 -0
- package/dist/resources/extensions/async-jobs/cancel-job-tool.js +7 -0
- package/dist/resources/extensions/cache-timer/index.js +5 -0
- package/dist/resources/extensions/codex-rotate/IMPLEMENTATION.md +18 -13
- package/dist/resources/extensions/codex-rotate/README.md +9 -3
- package/dist/resources/extensions/codex-rotate/commands.js +15 -8
- package/dist/resources/extensions/codex-rotate/index.js +17 -8
- package/dist/resources/extensions/memory/auto-extract.js +196 -80
- package/dist/resources/extensions/memory/dream.js +86 -19
- package/dist/resources/extensions/shared/rtk.js +89 -87
- package/dist/resources/extensions/subagent/index.js +33 -7
- package/dist/startup-model-validation.js +12 -2
- package/dist/update-check.js +2 -2
- package/dist/update-cmd.js +3 -3
- package/dist/welcome-screen.js +43 -14
- package/package.json +3 -2
- package/packages/pi-coding-agent/dist/core/agent-session.clear-queue.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/agent-session.clear-queue.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session.clear-queue.test.js +46 -0
- package/packages/pi-coding-agent/dist/core/agent-session.clear-queue.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +8 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +43 -4
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.js +2 -0
- package/packages/pi-coding-agent/dist/core/keybindings.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/pty-executor.d.ts +48 -0
- package/packages/pi-coding-agent/dist/core/pty-executor.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/pty-executor.js +173 -0
- package/packages/pi-coding-agent/dist/core/pty-executor.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +16 -3
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +18 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tool-approval.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tool-approval.js +2 -2
- package/packages/pi-coding-agent/dist/core/tool-approval.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/index.js +23 -2
- package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/pty.d.ts +50 -0
- package/packages/pi-coding-agent/dist/core/tools/pty.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/tools/pty.js +289 -0
- package/packages/pi-coding-agent/dist/core/tools/pty.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +36 -22
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts +3 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +23 -62
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.js +1 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js +1 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.d.ts +39 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.js +182 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/embedded-terminal.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +6 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +36 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +2 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +106 -77
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +2 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +4 -13
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts +11 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +49 -13
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +27 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +251 -39
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +2 -2
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
- package/packages/pi-coding-agent/dist/utils/terminal-screen.d.ts +10 -0
- package/packages/pi-coding-agent/dist/utils/terminal-screen.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/utils/terminal-screen.js +67 -0
- package/packages/pi-coding-agent/dist/utils/terminal-screen.js.map +1 -0
- package/packages/pi-coding-agent/dist/utils/terminal-serializer.d.ts +7 -0
- package/packages/pi-coding-agent/dist/utils/terminal-serializer.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/utils/terminal-serializer.js +67 -0
- package/packages/pi-coding-agent/dist/utils/terminal-serializer.js.map +1 -0
- package/packages/pi-coding-agent/package.json +9 -4
- package/packages/pi-coding-agent/src/core/agent-session.clear-queue.test.ts +50 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +50 -4
- package/packages/pi-coding-agent/src/core/extensions/types.ts +1 -1
- package/packages/pi-coding-agent/src/core/keybindings.ts +4 -1
- package/packages/pi-coding-agent/src/core/pty-executor.ts +229 -0
- package/packages/pi-coding-agent/src/core/sdk.ts +16 -3
- package/packages/pi-coding-agent/src/core/settings-manager.ts +27 -0
- package/packages/pi-coding-agent/src/core/tool-approval.ts +2 -2
- package/packages/pi-coding-agent/src/core/tools/index.ts +35 -2
- package/packages/pi-coding-agent/src/core/tools/pty.ts +354 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +37 -24
- package/packages/pi-coding-agent/src/modes/interactive/components/bash-execution.ts +22 -70
- package/packages/pi-coding-agent/src/modes/interactive/components/branch-summary-message.ts +1 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/compaction-summary-message.ts +1 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/embedded-terminal.ts +224 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +45 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +2 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +104 -81
- package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +5 -19
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +55 -13
- package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +2 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +3 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +296 -48
- package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +2 -2
- package/packages/pi-coding-agent/src/utils/terminal-screen.ts +77 -0
- package/packages/pi-coding-agent/src/utils/terminal-serializer.ts +72 -0
- package/packages/pi-tui/dist/components/__tests__/editor-dropped-image.test.d.ts +2 -0
- package/packages/pi-tui/dist/components/__tests__/editor-dropped-image.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/__tests__/editor-dropped-image.test.js +105 -0
- package/packages/pi-tui/dist/components/__tests__/editor-dropped-image.test.js.map +1 -0
- package/packages/pi-tui/dist/components/editor.d.ts +4 -0
- package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/editor.js +57 -3
- package/packages/pi-tui/dist/components/editor.js.map +1 -1
- package/packages/pi-tui/dist/components/loader.d.ts +26 -6
- package/packages/pi-tui/dist/components/loader.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/loader.js +178 -18
- package/packages/pi-tui/dist/components/loader.js.map +1 -1
- package/packages/pi-tui/src/components/editor.ts +65 -3
- package/packages/pi-tui/src/components/loader.ts +196 -19
- package/pkg/dist/modes/interactive/theme/themes.js +2 -2
- package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/async-jobs/async-bash-tool.ts +13 -0
- package/src/resources/extensions/async-jobs/await-tool.ts +13 -0
- package/src/resources/extensions/async-jobs/cancel-job-tool.ts +8 -0
- package/src/resources/extensions/cache-timer/index.ts +102 -96
- package/src/resources/extensions/codex-rotate/IMPLEMENTATION.md +18 -13
- package/src/resources/extensions/codex-rotate/README.md +9 -3
- package/src/resources/extensions/codex-rotate/commands.ts +335 -329
- package/src/resources/extensions/codex-rotate/index.ts +85 -75
- package/src/resources/extensions/memory/auto-extract.ts +330 -204
- package/src/resources/extensions/memory/dream.ts +88 -21
- package/src/resources/extensions/memory/tests/auto-extract.test.ts +200 -144
- package/src/resources/extensions/shared/rtk.js +112 -0
- package/src/resources/extensions/subagent/index.ts +35 -6
|
@@ -15,9 +15,63 @@ import { getMemoryDir } from './memory-paths.js';
|
|
|
15
15
|
import { scanMemoryFiles, formatMemoryManifest } from './memory-scan.js';
|
|
16
16
|
import { normalizeSubagentModel } from '../subagent/model-resolution.js';
|
|
17
17
|
const AUTO_EXTRACT_ANSI_PATTERN = /\u001B\[[0-?]*[ -/]*[@-~]/g;
|
|
18
|
+
const AUTO_EXTRACT_CACHE_TIMER_RE = /^\[phase\]\s+cache-timer(?:\s*:\s*.*)?\s*$/i;
|
|
19
|
+
const AUTO_EXTRACT_SESSION_ENDED_RE = /^\[agent\]\s+Session ended/;
|
|
20
|
+
const AUTO_EXTRACT_HEADLESS_STATUS_RE = /^\[headless\]\s+Status:\s+(\w+)\s*$/i;
|
|
18
21
|
export function stripAnsiForAutoExtractLog(text) {
|
|
19
22
|
return text.replace(AUTO_EXTRACT_ANSI_PATTERN, '');
|
|
20
23
|
}
|
|
24
|
+
export function classifyAutoExtractLogLine(rawLine) {
|
|
25
|
+
const stripped = stripAnsiForAutoExtractLog(rawLine).trim();
|
|
26
|
+
if (!stripped) {
|
|
27
|
+
return {
|
|
28
|
+
stripped,
|
|
29
|
+
keep: false,
|
|
30
|
+
completion: 'none',
|
|
31
|
+
completionReason: null,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
if (AUTO_EXTRACT_CACHE_TIMER_RE.test(stripped)) {
|
|
35
|
+
return {
|
|
36
|
+
stripped,
|
|
37
|
+
keep: false,
|
|
38
|
+
completion: 'none',
|
|
39
|
+
completionReason: null,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (AUTO_EXTRACT_SESSION_ENDED_RE.test(stripped)) {
|
|
43
|
+
return {
|
|
44
|
+
stripped,
|
|
45
|
+
keep: true,
|
|
46
|
+
completion: 'success',
|
|
47
|
+
completionReason: 'session_end_detected',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const headlessStatusMatch = stripped.match(AUTO_EXTRACT_HEADLESS_STATUS_RE);
|
|
51
|
+
if (headlessStatusMatch) {
|
|
52
|
+
const status = headlessStatusMatch[1].toLowerCase();
|
|
53
|
+
if (status === 'complete') {
|
|
54
|
+
return {
|
|
55
|
+
stripped,
|
|
56
|
+
keep: true,
|
|
57
|
+
completion: 'success',
|
|
58
|
+
completionReason: 'headless_status_complete',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
stripped,
|
|
63
|
+
keep: true,
|
|
64
|
+
completion: 'failure',
|
|
65
|
+
completionReason: `headless_status_${status}`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
stripped,
|
|
70
|
+
keep: true,
|
|
71
|
+
completion: 'none',
|
|
72
|
+
completionReason: null,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
21
75
|
/**
|
|
22
76
|
* Build a plain-text transcript from session entries, keeping only
|
|
23
77
|
* human-readable message content (no tool_use / tool_result blocks).
|
|
@@ -72,7 +126,7 @@ This directory already exists — write files directly.
|
|
|
72
126
|
|
|
73
127
|
Rules:
|
|
74
128
|
- Save ONLY: user preferences/role, feedback/corrections, project context (deadlines, decisions), external references
|
|
75
|
-
- Do NOT save: code
|
|
129
|
+
- Do NOT save: raw code snippets, low-level implementation details, file paths, git history, one-off debugging steps, ephemeral task details
|
|
76
130
|
- Check existing memories below — update existing files rather than creating duplicates
|
|
77
131
|
- Use frontmatter: ---\\nname: ...\\ndescription: ...\\ntype: user|feedback|project|reference\\n---
|
|
78
132
|
- After writing topic files, update MEMORY.md with one-line index entries
|
|
@@ -124,62 +178,8 @@ export function readBudgetMemoryModel() {
|
|
|
124
178
|
return undefined;
|
|
125
179
|
}
|
|
126
180
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
*
|
|
130
|
-
* Reads the conversation transcript, builds an extraction prompt,
|
|
131
|
-
* and spawns a detached headless agent to process it.
|
|
132
|
-
* Fire-and-forget: the parent can exit without killing the child.
|
|
133
|
-
*/
|
|
134
|
-
export function extractMemories(ctx, cwd) {
|
|
135
|
-
// Guard: prevent recursive extraction
|
|
136
|
-
if (process.env.LSD_MEMORY_EXTRACT === '1')
|
|
137
|
-
return;
|
|
138
|
-
// Guard: user opt-out
|
|
139
|
-
if (process.env.CLAUDE_CODE_DISABLE_AUTO_MEMORY)
|
|
140
|
-
return;
|
|
141
|
-
const entries = ctx.sessionManager.getEntries();
|
|
142
|
-
// Guard: need at least one user message to extract from
|
|
143
|
-
const userMessageCount = entries.filter((e) => e.type === 'message' && e.message?.role === 'user').length;
|
|
144
|
-
if (userMessageCount < 1)
|
|
145
|
-
return;
|
|
146
|
-
const transcript = buildTranscriptSummary(entries);
|
|
147
|
-
if (!transcript)
|
|
148
|
-
return;
|
|
149
|
-
const memoryDir = getMemoryDir(cwd);
|
|
150
|
-
mkdirSync(memoryDir, { recursive: true });
|
|
151
|
-
const prompt = buildExtractionPrompt(memoryDir, transcript);
|
|
152
|
-
const auditPath = join(memoryDir, '.last-auto-extract.txt');
|
|
153
|
-
const logPath = join(memoryDir, '.last-auto-extract.log');
|
|
154
|
-
// Write prompt to a temp file so the spawned agent can read it
|
|
155
|
-
const tmpPromptPath = join(tmpdir(), `lsd-memory-extract-${randomUUID()}.md`);
|
|
156
|
-
writeFileSync(tmpPromptPath, prompt, 'utf-8');
|
|
157
|
-
const cliPath = resolveCliPath();
|
|
158
|
-
const budgetModel = readBudgetMemoryModel();
|
|
159
|
-
if (!cliPath) {
|
|
160
|
-
writeFileSync(auditPath, [
|
|
161
|
-
`timestamp: ${new Date().toISOString()}`,
|
|
162
|
-
'status: skipped',
|
|
163
|
-
'reason: cli_path_not_found',
|
|
164
|
-
`cwd: ${cwd}`,
|
|
165
|
-
`userMessages: ${userMessageCount}`,
|
|
166
|
-
`transcriptLength: ${transcript.length}`,
|
|
167
|
-
`budgetModel: ${budgetModel ?? 'default'}`,
|
|
168
|
-
].join('\n') + '\n', 'utf-8');
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
writeFileSync(auditPath, [
|
|
172
|
-
`timestamp: ${new Date().toISOString()}`,
|
|
173
|
-
'status: spawning',
|
|
174
|
-
`cwd: ${cwd}`,
|
|
175
|
-
`userMessages: ${userMessageCount}`,
|
|
176
|
-
`transcriptLength: ${transcript.length}`,
|
|
177
|
-
`cliPath: ${cliPath}`,
|
|
178
|
-
`budgetModel: ${budgetModel ?? 'default'}`,
|
|
179
|
-
`logPath: ${logPath}`,
|
|
180
|
-
].join('\n') + '\n', 'utf-8');
|
|
181
|
-
const instruction = 'Extract memories from the transcript above. Write any worth-saving memories to the memory directory, then update MEMORY.md.';
|
|
182
|
-
const helperScript = `
|
|
181
|
+
export function buildAutoExtractHelperScript() {
|
|
182
|
+
return String.raw `
|
|
183
183
|
const { spawn } = require('node:child_process');
|
|
184
184
|
const { appendFileSync, writeFileSync, readFileSync, readdirSync, statSync, existsSync } = require('node:fs');
|
|
185
185
|
const { join, delimiter } = require('node:path');
|
|
@@ -187,41 +187,67 @@ const { join, delimiter } = require('node:path');
|
|
|
187
187
|
const [cliPath, cwd, tmpPromptPath, auditPath, logPath, memoryDir, instruction, model, userMessageCount, transcriptLength] = process.argv.slice(1);
|
|
188
188
|
const startedAt = new Date().toISOString();
|
|
189
189
|
let finalized = false;
|
|
190
|
-
let
|
|
191
|
-
let
|
|
190
|
+
let completionState = null;
|
|
191
|
+
let completionTimer = null;
|
|
192
192
|
let hardTimeout = null;
|
|
193
193
|
let pendingLogText = '';
|
|
194
194
|
const ANSI_PATTERN = /\u001B\[[0-?]*[ -/]*[@-~]/g;
|
|
195
|
+
const CACHE_TIMER_RE = /^\[phase\]\s+cache-timer(?:\s*:\s*.*)?\s*$/i;
|
|
196
|
+
const SESSION_ENDED_RE = /^\[agent\]\s+Session ended/;
|
|
197
|
+
const HEADLESS_STATUS_RE = /^\[headless\]\s+Status:\s+(\w+)\s*$/i;
|
|
195
198
|
|
|
196
199
|
function stripAnsi(text) {
|
|
197
200
|
return String(text).replace(ANSI_PATTERN, '');
|
|
198
201
|
}
|
|
199
202
|
|
|
203
|
+
function classifyLogLine(rawLine) {
|
|
204
|
+
const stripped = stripAnsi(rawLine).trim();
|
|
205
|
+
if (!stripped) {
|
|
206
|
+
return { stripped, keep: false, completion: null, completionReason: null };
|
|
207
|
+
}
|
|
208
|
+
if (CACHE_TIMER_RE.test(stripped)) {
|
|
209
|
+
return { stripped, keep: false, completion: null, completionReason: null };
|
|
210
|
+
}
|
|
211
|
+
if (SESSION_ENDED_RE.test(stripped)) {
|
|
212
|
+
return { stripped, keep: true, completion: 'finished', completionReason: 'session_end_detected' };
|
|
213
|
+
}
|
|
214
|
+
const headlessStatusMatch = stripped.match(HEADLESS_STATUS_RE);
|
|
215
|
+
if (headlessStatusMatch) {
|
|
216
|
+
const status = String(headlessStatusMatch[1] || '').toLowerCase();
|
|
217
|
+
if (status === 'complete') {
|
|
218
|
+
return { stripped, keep: true, completion: 'finished', completionReason: 'headless_status_complete' };
|
|
219
|
+
}
|
|
220
|
+
return { stripped, keep: true, completion: 'failed', completionReason: 'headless_status_' + status };
|
|
221
|
+
}
|
|
222
|
+
return { stripped, keep: true, completion: null, completionReason: null };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function scheduleCompletion(completion, completionReason) {
|
|
226
|
+
if (!completion || completionState || completionTimer) return;
|
|
227
|
+
completionState = { completion, completionReason };
|
|
228
|
+
completionTimer = setTimeout(() => {
|
|
229
|
+
finalize(completion, completion === 'finished' ? 0 : 1, null, completionReason);
|
|
230
|
+
try { child.kill('SIGTERM'); } catch {}
|
|
231
|
+
setTimeout(() => { try { child.kill('SIGKILL'); } catch {} }, 2000).unref();
|
|
232
|
+
}, 1500);
|
|
233
|
+
completionTimer.unref();
|
|
234
|
+
}
|
|
235
|
+
|
|
200
236
|
function flushLogText(text, force = false) {
|
|
201
237
|
pendingLogText += text;
|
|
202
|
-
const parts = pendingLogText.split(
|
|
238
|
+
const parts = pendingLogText.split(/(?:\r?\n|\r)/);
|
|
203
239
|
pendingLogText = force ? '' : (parts.pop() ?? '');
|
|
204
240
|
|
|
205
241
|
const kept = [];
|
|
206
242
|
for (const rawLine of parts) {
|
|
207
|
-
const
|
|
208
|
-
if (
|
|
209
|
-
if (
|
|
210
|
-
|
|
211
|
-
if (/^\[agent\]\s+Session ended/.test(line)) {
|
|
212
|
-
sawSessionEnded = true;
|
|
243
|
+
const classified = classifyLogLine(rawLine);
|
|
244
|
+
if (classified.keep) kept.push(rawLine);
|
|
245
|
+
if (classified.completion) {
|
|
246
|
+
scheduleCompletion(classified.completion, classified.completionReason);
|
|
213
247
|
}
|
|
214
248
|
}
|
|
215
249
|
|
|
216
250
|
if (kept.length > 0) appendFileSync(logPath, kept.join('\n') + '\n');
|
|
217
|
-
if (sawSessionEnded && !sessionEndTimer) {
|
|
218
|
-
sessionEndTimer = setTimeout(() => {
|
|
219
|
-
finalize('finished', 0, null, 'session_end_detected');
|
|
220
|
-
try { child.kill('SIGTERM'); } catch {}
|
|
221
|
-
setTimeout(() => { try { child.kill('SIGKILL'); } catch {} }, 2000).unref();
|
|
222
|
-
}, 1500);
|
|
223
|
-
sessionEndTimer.unref();
|
|
224
|
-
}
|
|
225
251
|
}
|
|
226
252
|
|
|
227
253
|
function appendLog(chunk) {
|
|
@@ -243,7 +269,7 @@ function writeAudit(status, extra = []) {
|
|
|
243
269
|
'model: ' + (model || 'default'),
|
|
244
270
|
'logPath: ' + logPath,
|
|
245
271
|
...extra,
|
|
246
|
-
].join('
|
|
272
|
+
].join('\n') + '\n', 'utf-8');
|
|
247
273
|
} catch {}
|
|
248
274
|
}
|
|
249
275
|
|
|
@@ -265,7 +291,7 @@ function newestMemoryMtime(dir) {
|
|
|
265
291
|
function finalize(status, code, signal, completionReason) {
|
|
266
292
|
if (finalized) return;
|
|
267
293
|
finalized = true;
|
|
268
|
-
if (
|
|
294
|
+
if (completionTimer) clearTimeout(completionTimer);
|
|
269
295
|
if (hardTimeout) clearTimeout(hardTimeout);
|
|
270
296
|
const afterMtime = newestMemoryMtime(memoryDir);
|
|
271
297
|
const logText = existsSync(logPath) ? readFileSync(logPath, 'utf-8') : '';
|
|
@@ -292,7 +318,10 @@ const bundledPaths = Array.from(
|
|
|
292
318
|
new Set(
|
|
293
319
|
[process.env.GSD_BUNDLED_EXTENSION_PATHS, process.env.LSD_BUNDLED_EXTENSION_PATHS]
|
|
294
320
|
.filter(Boolean)
|
|
295
|
-
.flatMap((value) => String(value).split(delimiter).map((entry) => entry.trim()).filter(Boolean))
|
|
321
|
+
.flatMap((value) => String(value).split(delimiter).map((entry) => entry.trim()).filter(Boolean))
|
|
322
|
+
// Explicitly disable cache-timer extension for auto-memory workers.
|
|
323
|
+
// It is noisy in headless logs and provides no value for maintenance runs.
|
|
324
|
+
.filter((entry) => !/[\\/]cache-timer[\\/]/i.test(entry)),
|
|
296
325
|
),
|
|
297
326
|
);
|
|
298
327
|
for (const extensionPath of bundledPaths) childArgs.push('--extension', extensionPath);
|
|
@@ -302,11 +331,25 @@ childArgs.push('--bare', '--context', tmpPromptPath, '--context-text', instructi
|
|
|
302
331
|
const child = spawn(
|
|
303
332
|
process.execPath,
|
|
304
333
|
childArgs,
|
|
305
|
-
{
|
|
334
|
+
{
|
|
335
|
+
cwd,
|
|
336
|
+
env: {
|
|
337
|
+
...process.env,
|
|
338
|
+
LSD_MEMORY_EXTRACT: '1',
|
|
339
|
+
// Hard-disable cache timer in maintenance workers.
|
|
340
|
+
LSD_DISABLE_CACHE_TIMER: '1',
|
|
341
|
+
// Memory maintenance workers must not run in auto permission mode.
|
|
342
|
+
// In auto mode, write/edit calls require classifier approval, but headless
|
|
343
|
+
// maintenance workers have no interactive classifier responder.
|
|
344
|
+
// Keep writes safe via extension-level path restrictions instead.
|
|
345
|
+
LUCENT_CODE_PERMISSION_MODE: 'danger-full-access',
|
|
346
|
+
},
|
|
347
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
348
|
+
},
|
|
306
349
|
);
|
|
307
350
|
|
|
308
351
|
hardTimeout = setTimeout(() => {
|
|
309
|
-
finalize('failed', null, 'timeout',
|
|
352
|
+
finalize('failed', null, 'timeout', completionState?.completionReason ?? 'timeout');
|
|
310
353
|
try { child.kill('SIGTERM'); } catch {}
|
|
311
354
|
setTimeout(() => { try { child.kill('SIGKILL'); } catch {} }, 2000).unref();
|
|
312
355
|
}, 120000);
|
|
@@ -315,7 +358,7 @@ hardTimeout.unref();
|
|
|
315
358
|
child.stdout.on('data', appendLog);
|
|
316
359
|
child.stderr.on('data', appendLog);
|
|
317
360
|
child.on('error', (err) => {
|
|
318
|
-
appendLog(String(err && err.stack ? err.stack : err) + '
|
|
361
|
+
appendLog(String(err && err.stack ? err.stack : err) + '\n');
|
|
319
362
|
flushLogText('', true);
|
|
320
363
|
finalize('failed', null, 'spawn_error', String(err && err.message ? err.message : err));
|
|
321
364
|
});
|
|
@@ -324,6 +367,79 @@ child.on('exit', (code, signal) => {
|
|
|
324
367
|
finalize(code === 0 ? 'finished' : 'failed', code, signal, 'child_exit');
|
|
325
368
|
});
|
|
326
369
|
`;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Main entry point — called from the session_shutdown hook.
|
|
373
|
+
*
|
|
374
|
+
* Reads the conversation transcript, builds an extraction prompt,
|
|
375
|
+
* and spawns a detached headless agent to process it.
|
|
376
|
+
* Fire-and-forget: the parent can exit without killing the child.
|
|
377
|
+
*/
|
|
378
|
+
function readAutoMemoryEnabled() {
|
|
379
|
+
try {
|
|
380
|
+
const settingsPath = join(getAgentDir(), 'settings.json');
|
|
381
|
+
if (!existsSync(settingsPath))
|
|
382
|
+
return false;
|
|
383
|
+
const raw = readFileSync(settingsPath, 'utf-8');
|
|
384
|
+
const parsed = JSON.parse(raw);
|
|
385
|
+
return parsed.autoMemory === true;
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
export function extractMemories(ctx, cwd) {
|
|
392
|
+
// Guard: prevent recursive extraction
|
|
393
|
+
if (process.env.LSD_MEMORY_EXTRACT === '1')
|
|
394
|
+
return;
|
|
395
|
+
// Guard: user opt-out via env var
|
|
396
|
+
if (process.env.CLAUDE_CODE_DISABLE_AUTO_MEMORY)
|
|
397
|
+
return;
|
|
398
|
+
// Guard: auto memory must be enabled in settings (default: disabled)
|
|
399
|
+
if (!readAutoMemoryEnabled())
|
|
400
|
+
return;
|
|
401
|
+
const entries = ctx.sessionManager.getEntries();
|
|
402
|
+
// Guard: need at least one user message to extract from
|
|
403
|
+
const userMessageCount = entries.filter((e) => e.type === 'message' && e.message?.role === 'user').length;
|
|
404
|
+
if (userMessageCount < 1)
|
|
405
|
+
return;
|
|
406
|
+
const transcript = buildTranscriptSummary(entries);
|
|
407
|
+
if (!transcript)
|
|
408
|
+
return;
|
|
409
|
+
const memoryDir = getMemoryDir(cwd);
|
|
410
|
+
mkdirSync(memoryDir, { recursive: true });
|
|
411
|
+
const prompt = buildExtractionPrompt(memoryDir, transcript);
|
|
412
|
+
const auditPath = join(memoryDir, '.last-auto-extract.txt');
|
|
413
|
+
const logPath = join(memoryDir, '.last-auto-extract.log');
|
|
414
|
+
// Write prompt to a temp file so the spawned agent can read it
|
|
415
|
+
const tmpPromptPath = join(tmpdir(), `lsd-memory-extract-${randomUUID()}.md`);
|
|
416
|
+
writeFileSync(tmpPromptPath, prompt, 'utf-8');
|
|
417
|
+
const cliPath = resolveCliPath();
|
|
418
|
+
const budgetModel = readBudgetMemoryModel();
|
|
419
|
+
if (!cliPath) {
|
|
420
|
+
writeFileSync(auditPath, [
|
|
421
|
+
`timestamp: ${new Date().toISOString()}`,
|
|
422
|
+
'status: skipped',
|
|
423
|
+
'reason: cli_path_not_found',
|
|
424
|
+
`cwd: ${cwd}`,
|
|
425
|
+
`userMessages: ${userMessageCount}`,
|
|
426
|
+
`transcriptLength: ${transcript.length}`,
|
|
427
|
+
`budgetModel: ${budgetModel ?? 'default'}`,
|
|
428
|
+
].join('\n') + '\n', 'utf-8');
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
writeFileSync(auditPath, [
|
|
432
|
+
`timestamp: ${new Date().toISOString()}`,
|
|
433
|
+
'status: spawning',
|
|
434
|
+
`cwd: ${cwd}`,
|
|
435
|
+
`userMessages: ${userMessageCount}`,
|
|
436
|
+
`transcriptLength: ${transcript.length}`,
|
|
437
|
+
`cliPath: ${cliPath}`,
|
|
438
|
+
`budgetModel: ${budgetModel ?? 'default'}`,
|
|
439
|
+
`logPath: ${logPath}`,
|
|
440
|
+
].join('\n') + '\n', 'utf-8');
|
|
441
|
+
const instruction = 'Extract memories from the transcript above. Write any worth-saving memories to the memory directory, then update MEMORY.md.';
|
|
442
|
+
const helperScript = buildAutoExtractHelperScript();
|
|
327
443
|
const proc = spawn(process.execPath, [
|
|
328
444
|
'-e',
|
|
329
445
|
helperScript,
|
|
@@ -34,19 +34,21 @@ function readJsonFile(path) {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
function parseAutoDreamSettings(source) {
|
|
37
|
+
// Check top-level autoDream field first (set via /settings UI)
|
|
38
|
+
const topLevel = source.autoDream;
|
|
37
39
|
const memory = source.memory;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
40
|
+
const nested = (memory && typeof memory === 'object') ? memory : undefined;
|
|
41
|
+
// Top-level takes precedence for enabled; nested.memory for thresholds
|
|
42
|
+
const enabledSource = typeof topLevel === 'boolean' ? topLevel
|
|
43
|
+
: nested && typeof nested.autoDream === 'boolean' ? nested.autoDream
|
|
44
|
+
: undefined;
|
|
41
45
|
return {
|
|
42
|
-
enabled:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
minHours: typeof settings.autoDreamMinHours === 'number' && Number.isFinite(settings.autoDreamMinHours)
|
|
46
|
-
? Math.max(1, settings.autoDreamMinHours)
|
|
46
|
+
...(enabledSource !== undefined ? { enabled: enabledSource } : {}),
|
|
47
|
+
minHours: nested && typeof nested.autoDreamMinHours === 'number' && Number.isFinite(nested.autoDreamMinHours)
|
|
48
|
+
? Math.max(1, nested.autoDreamMinHours)
|
|
47
49
|
: DEFAULT_AUTO_DREAM_SETTINGS.minHours,
|
|
48
|
-
minSessions: typeof
|
|
49
|
-
? Math.max(1, Math.floor(
|
|
50
|
+
minSessions: nested && typeof nested.autoDreamMinSessions === 'number' && Number.isFinite(nested.autoDreamMinSessions)
|
|
51
|
+
? Math.max(1, Math.floor(nested.autoDreamMinSessions))
|
|
50
52
|
: DEFAULT_AUTO_DREAM_SETTINGS.minSessions,
|
|
51
53
|
};
|
|
52
54
|
}
|
|
@@ -320,6 +322,13 @@ const { join, delimiter } = require('node:path');
|
|
|
320
322
|
const [cliPath, cwd, tmpPromptPath, auditPath, logPath, memoryDir, sessionDir, instruction, model, trigger, priorMtime, sessionCount] = process.argv.slice(1);
|
|
321
323
|
let finalized = false;
|
|
322
324
|
let pendingLogText = '';
|
|
325
|
+
let completionState = null;
|
|
326
|
+
let completionTimer = null;
|
|
327
|
+
let hardTimeout = null;
|
|
328
|
+
const ANSI_PATTERN = /\u001B\[[0-?]*[ -/]*[@-~]/g;
|
|
329
|
+
const CACHE_TIMER_RE = /^\[phase\]\s+cache-timer(?:\s*:\s*.*)?\s*$/i;
|
|
330
|
+
const SESSION_ENDED_RE = /^\[agent\]\s+Session ended/;
|
|
331
|
+
const HEADLESS_STATUS_RE = /^\[headless\]\s+Status:\s+(\w+)\s*$/i;
|
|
323
332
|
|
|
324
333
|
function newestMemoryMtime(dir) {
|
|
325
334
|
try {
|
|
@@ -391,6 +400,43 @@ function pruneBrokenMemoryRefs(dir) {
|
|
|
391
400
|
}
|
|
392
401
|
}
|
|
393
402
|
|
|
403
|
+
function stripAnsi(text) {
|
|
404
|
+
return String(text).replace(ANSI_PATTERN, '');
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function classifyLogLine(rawLine) {
|
|
408
|
+
const stripped = stripAnsi(rawLine).trim();
|
|
409
|
+
if (!stripped) {
|
|
410
|
+
return { stripped, keep: false, completion: null, completionReason: null };
|
|
411
|
+
}
|
|
412
|
+
if (CACHE_TIMER_RE.test(stripped)) {
|
|
413
|
+
return { stripped, keep: false, completion: null, completionReason: null };
|
|
414
|
+
}
|
|
415
|
+
if (SESSION_ENDED_RE.test(stripped)) {
|
|
416
|
+
return { stripped, keep: true, completion: 'finished', completionReason: 'session_end_detected' };
|
|
417
|
+
}
|
|
418
|
+
const headlessStatusMatch = stripped.match(HEADLESS_STATUS_RE);
|
|
419
|
+
if (headlessStatusMatch) {
|
|
420
|
+
const status = String(headlessStatusMatch[1] || '').toLowerCase();
|
|
421
|
+
if (status === 'complete') {
|
|
422
|
+
return { stripped, keep: true, completion: 'finished', completionReason: 'headless_status_complete' };
|
|
423
|
+
}
|
|
424
|
+
return { stripped, keep: true, completion: 'failed', completionReason: 'headless_status_' + status };
|
|
425
|
+
}
|
|
426
|
+
return { stripped, keep: true, completion: null, completionReason: null };
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function scheduleCompletion(completion, completionReason) {
|
|
430
|
+
if (!completion || completionState || completionTimer) return;
|
|
431
|
+
completionState = { completion, completionReason };
|
|
432
|
+
completionTimer = setTimeout(() => {
|
|
433
|
+
finalize(completion, completion === 'finished' ? 0 : 1, null, completionReason);
|
|
434
|
+
try { child.kill('SIGTERM'); } catch {}
|
|
435
|
+
setTimeout(() => { try { child.kill('SIGKILL'); } catch {} }, 2000).unref();
|
|
436
|
+
}, 1500);
|
|
437
|
+
completionTimer.unref();
|
|
438
|
+
}
|
|
439
|
+
|
|
394
440
|
function writeAudit(status, extra = []) {
|
|
395
441
|
try {
|
|
396
442
|
writeFileSync(auditPath, [
|
|
@@ -425,9 +471,18 @@ function rollbackLock() {
|
|
|
425
471
|
|
|
426
472
|
function flushLogText(text, force = false) {
|
|
427
473
|
pendingLogText += text;
|
|
428
|
-
const parts = pendingLogText.split(
|
|
474
|
+
const parts = pendingLogText.split(/(?:\r?\n|\r)/);
|
|
429
475
|
pendingLogText = force ? '' : (parts.pop() ?? '');
|
|
430
|
-
|
|
476
|
+
|
|
477
|
+
const kept = [];
|
|
478
|
+
for (const rawLine of parts) {
|
|
479
|
+
const classified = classifyLogLine(rawLine);
|
|
480
|
+
if (classified.keep) kept.push(rawLine);
|
|
481
|
+
if (classified.completion) {
|
|
482
|
+
scheduleCompletion(classified.completion, classified.completionReason);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
431
486
|
if (kept.length > 0) appendFileSync(logPath, kept.join('\n') + '\n');
|
|
432
487
|
}
|
|
433
488
|
|
|
@@ -441,6 +496,8 @@ function appendLog(chunk) {
|
|
|
441
496
|
function finalize(status, code, signal, completionReason) {
|
|
442
497
|
if (finalized) return;
|
|
443
498
|
finalized = true;
|
|
499
|
+
if (completionTimer) clearTimeout(completionTimer);
|
|
500
|
+
if (hardTimeout) clearTimeout(hardTimeout);
|
|
444
501
|
flushLogText('', true);
|
|
445
502
|
const beforeBrokenRefs = listBrokenMemoryRefs(memoryDir);
|
|
446
503
|
const prunedRefs = beforeBrokenRefs.length > 0 ? pruneBrokenMemoryRefs(memoryDir) : [];
|
|
@@ -476,7 +533,9 @@ const bundledPaths = Array.from(
|
|
|
476
533
|
new Set(
|
|
477
534
|
[process.env.GSD_BUNDLED_EXTENSION_PATHS, process.env.LSD_BUNDLED_EXTENSION_PATHS]
|
|
478
535
|
.filter(Boolean)
|
|
479
|
-
.flatMap((value) => String(value).split(delimiter).map((entry) => entry.trim()).filter(Boolean))
|
|
536
|
+
.flatMap((value) => String(value).split(delimiter).map((entry) => entry.trim()).filter(Boolean))
|
|
537
|
+
// Explicitly disable cache-timer extension for dream workers.
|
|
538
|
+
.filter((entry) => !/[\\/]cache-timer[\\/]/i.test(entry)),
|
|
480
539
|
),
|
|
481
540
|
);
|
|
482
541
|
for (const extensionPath of bundledPaths) childArgs.push('--extension', extensionPath);
|
|
@@ -485,12 +544,20 @@ childArgs.push('--bare', '--context', tmpPromptPath, '--context-text', instructi
|
|
|
485
544
|
|
|
486
545
|
const child = spawn(process.execPath, childArgs, {
|
|
487
546
|
cwd,
|
|
488
|
-
env: {
|
|
547
|
+
env: {
|
|
548
|
+
...process.env,
|
|
549
|
+
LSD_MEMORY_DREAM: '1',
|
|
550
|
+
// Hard-disable cache timer in maintenance workers.
|
|
551
|
+
LSD_DISABLE_CACHE_TIMER: '1',
|
|
552
|
+
// Dream workers run headless and cannot answer auto-mode classifier prompts.
|
|
553
|
+
// Force non-auto permissions and rely on memory-extension path/tool guards.
|
|
554
|
+
LUCENT_CODE_PERMISSION_MODE: 'danger-full-access',
|
|
555
|
+
},
|
|
489
556
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
490
557
|
});
|
|
491
558
|
|
|
492
|
-
|
|
493
|
-
finalize('failed', null, 'timeout', 'timeout');
|
|
559
|
+
hardTimeout = setTimeout(() => {
|
|
560
|
+
finalize('failed', null, 'timeout', completionState?.completionReason ?? 'timeout');
|
|
494
561
|
try { child.kill('SIGTERM'); } catch {}
|
|
495
562
|
setTimeout(() => { try { child.kill('SIGKILL'); } catch {} }, 2000).unref();
|
|
496
563
|
}, 180000);
|
|
@@ -500,12 +567,12 @@ child.stdout.on('data', appendLog);
|
|
|
500
567
|
child.stderr.on('data', appendLog);
|
|
501
568
|
child.on('error', (err) => {
|
|
502
569
|
appendLog(String(err && err.stack ? err.stack : err) + '\n');
|
|
503
|
-
|
|
570
|
+
flushLogText('', true);
|
|
504
571
|
finalize('failed', null, 'spawn_error', String(err && err.message ? err.message : err));
|
|
505
572
|
});
|
|
506
573
|
child.on('exit', (code, signal) => {
|
|
507
|
-
|
|
508
|
-
finalize(code === 0 ? 'finished' : 'failed', code, signal, 'child_exit');
|
|
574
|
+
flushLogText('', true);
|
|
575
|
+
finalize(code === 0 ? 'finished' : 'failed', code, signal, completionState?.completionReason ?? 'child_exit');
|
|
509
576
|
});
|
|
510
577
|
`;
|
|
511
578
|
const proc = spawn(process.execPath, [
|