@adhdev/daemon-core 0.9.82-rc.13 → 0.9.82-rc.131
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/chat/subscription-updates.d.ts +1 -0
- package/dist/cli-adapter-types.d.ts +4 -1
- package/dist/cli-adapters/provider-cli-adapter.d.ts +25 -1
- package/dist/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +14 -0
- package/dist/commands/router.d.ts +22 -0
- package/dist/config/chat-history.d.ts +4 -0
- package/dist/config/mesh-config.d.ts +68 -1
- package/dist/git/git-commands.d.ts +5 -1
- package/dist/index.d.ts +15 -5
- package/dist/index.js +7538 -1380
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7494 -1364
- package/dist/index.mjs.map +1 -1
- package/dist/installer.d.ts +1 -4
- package/dist/launch.d.ts +1 -1
- package/dist/logging/async-batch-writer.d.ts +10 -0
- package/dist/mesh/beads-db.d.ts +18 -0
- package/dist/mesh/mesh-active-work.d.ts +73 -0
- package/dist/mesh/mesh-events.d.ts +54 -5
- package/dist/mesh/mesh-fast-forward.d.ts +39 -0
- package/dist/mesh/mesh-host-ownership.d.ts +9 -0
- package/dist/mesh/mesh-ledger.d.ts +38 -1
- package/dist/mesh/mesh-refine-status.d.ts +27 -0
- package/dist/mesh/mesh-work-queue.d.ts +27 -5
- package/dist/mesh/preview-freshness.d.ts +18 -0
- package/dist/mesh/refine-config.d.ts +193 -0
- package/dist/mesh/worktree-bootstrap-config.d.ts +115 -0
- package/dist/providers/chat-message-normalization.d.ts +1 -0
- package/dist/providers/cli-provider-instance.d.ts +6 -1
- package/dist/repo-mesh-types.d.ts +62 -0
- package/dist/status/reporter.d.ts +2 -0
- package/package.json +3 -1
- package/src/boot/daemon-lifecycle.ts +1 -0
- package/src/chat/subscription-updates.ts +5 -1
- package/src/cli-adapter-types.ts +2 -1
- package/src/cli-adapters/provider-cli-adapter.ts +473 -18
- package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/src/cli-adapters/provider-cli-parse.ts +4 -0
- package/src/cli-adapters/provider-cli-runtime.ts +3 -1
- package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
- package/src/cli-adapters/provider-cli-shared.ts +32 -10
- package/src/commands/chat-commands.ts +1065 -40
- package/src/commands/cli-manager.ts +138 -2
- package/src/commands/handler.ts +8 -1
- package/src/commands/mesh-coordinator.ts +13 -143
- package/src/commands/router.ts +3238 -423
- package/src/config/chat-history.ts +37 -9
- package/src/config/mesh-config.ts +249 -2
- package/src/config/recent-activity.ts +8 -2
- package/src/daemon/dev-cli-debug.ts +10 -1
- package/src/detection/ide-detector.ts +26 -16
- package/src/git/git-commands.ts +17 -5
- package/src/index.ts +41 -4
- package/src/installer.d.ts +1 -1
- package/src/installer.ts +8 -6
- package/src/launch.d.ts +1 -1
- package/src/launch.ts +37 -28
- package/src/logging/async-batch-writer.ts +55 -0
- package/src/logging/logger.ts +2 -1
- package/src/mesh/beads-db.ts +176 -0
- package/src/mesh/coordinator-prompt.ts +31 -8
- package/src/mesh/mesh-active-work.ts +295 -0
- package/src/mesh/mesh-events.ts +595 -48
- package/src/mesh/mesh-fast-forward.ts +430 -0
- package/src/mesh/mesh-host-ownership.ts +73 -0
- package/src/mesh/mesh-ledger.ts +138 -1
- package/src/mesh/mesh-refine-status.ts +145 -0
- package/src/mesh/mesh-work-queue.ts +199 -137
- package/src/mesh/preview-freshness.ts +118 -0
- package/src/mesh/refine-config.ts +366 -0
- package/src/mesh/worktree-bootstrap-config.ts +234 -0
- package/src/providers/approval-utils.ts +12 -5
- package/src/providers/chat-message-normalization.ts +7 -12
- package/src/providers/cli-provider-instance.ts +289 -36
- package/src/providers/ide-provider-instance.ts +17 -3
- package/src/providers/provider-loader.ts +10 -4
- package/src/providers/read-chat-contract.ts +1 -1
- package/src/providers/version-archive.ts +38 -20
- package/src/repo-mesh-types.ts +67 -0
- package/src/status/reporter.ts +15 -0
- package/src/system/host-memory.ts +29 -12
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
|
2
|
+
import { join, resolve as pathResolve } from 'path';
|
|
3
|
+
import { execFile } from 'node:child_process';
|
|
4
|
+
import { promisify } from 'node:util';
|
|
5
|
+
import * as yaml from 'js-yaml';
|
|
6
|
+
import {
|
|
7
|
+
isMeshConfigRecord,
|
|
8
|
+
normalizeMeshCommandConfig,
|
|
9
|
+
type MeshRefineValidationCommandPlan,
|
|
10
|
+
type RepoMeshRefineValidationCommandConfig,
|
|
11
|
+
} from './refine-config.js';
|
|
12
|
+
|
|
13
|
+
export type WorktreeBootstrapStatus = 'ready' | 'running' | 'failed' | 'not_configured' | 'disabled' | 'stale';
|
|
14
|
+
|
|
15
|
+
export interface RepoMeshWorktreeBootstrapConfig {
|
|
16
|
+
version: 1;
|
|
17
|
+
enabled?: boolean;
|
|
18
|
+
runOnClone?: boolean;
|
|
19
|
+
required?: boolean;
|
|
20
|
+
commands?: RepoMeshRefineValidationCommandConfig[];
|
|
21
|
+
staleInputs?: string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface WorktreeBootstrapState {
|
|
25
|
+
status: WorktreeBootstrapStatus;
|
|
26
|
+
required: boolean;
|
|
27
|
+
configSource?: string;
|
|
28
|
+
configSourceType?: 'repo_file' | 'mesh_policy' | 'unavailable' | 'invalid';
|
|
29
|
+
startedAt?: string;
|
|
30
|
+
completedAt?: string;
|
|
31
|
+
lastCommand?: string;
|
|
32
|
+
exitCode?: number | null;
|
|
33
|
+
error?: string;
|
|
34
|
+
commandsRun?: Array<Record<string, unknown>>;
|
|
35
|
+
staleInputs?: string[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface WorktreeBootstrapConfigLoadResult {
|
|
39
|
+
config?: RepoMeshWorktreeBootstrapConfig;
|
|
40
|
+
source: string;
|
|
41
|
+
sourceType: 'repo_file' | 'mesh_policy' | 'unavailable' | 'invalid';
|
|
42
|
+
path?: string;
|
|
43
|
+
error?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS = [
|
|
47
|
+
'.adhdev/worktree_bootstrap.json',
|
|
48
|
+
'.adhdev/worktree_bootstrap.yaml',
|
|
49
|
+
'.adhdev/worktree_bootstrap.yml',
|
|
50
|
+
'.adhdev/worktree-bootstrap.json',
|
|
51
|
+
'.adhdev/worktree-bootstrap.yaml',
|
|
52
|
+
'.adhdev/worktree-bootstrap.yml',
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
export const MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA = {
|
|
56
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
57
|
+
title: 'ADHDev Repo Mesh Worktree Bootstrap Config',
|
|
58
|
+
type: 'object',
|
|
59
|
+
additionalProperties: false,
|
|
60
|
+
required: ['version'],
|
|
61
|
+
properties: {
|
|
62
|
+
version: { const: 1 },
|
|
63
|
+
enabled: { type: 'boolean', default: true },
|
|
64
|
+
runOnClone: { type: 'boolean', default: true },
|
|
65
|
+
required: { type: 'boolean', default: true },
|
|
66
|
+
staleInputs: { type: 'array', maxItems: 16, items: { type: 'string', minLength: 1 } },
|
|
67
|
+
commands: {
|
|
68
|
+
type: 'array',
|
|
69
|
+
minItems: 1,
|
|
70
|
+
maxItems: 4,
|
|
71
|
+
items: {
|
|
72
|
+
type: 'object',
|
|
73
|
+
additionalProperties: false,
|
|
74
|
+
required: ['command'],
|
|
75
|
+
properties: {
|
|
76
|
+
command: { type: 'string', minLength: 1 },
|
|
77
|
+
args: { type: 'array', items: { type: 'string' } },
|
|
78
|
+
category: { enum: ['typecheck', 'test', 'lint', 'build', 'custom'] },
|
|
79
|
+
cwd: { type: 'string' },
|
|
80
|
+
timeoutMs: { type: 'number', minimum: 1000, maximum: 600000 },
|
|
81
|
+
outputLimitBytes: { type: 'number', minimum: 1024, maximum: 1048576 },
|
|
82
|
+
env: { type: 'object', additionalProperties: { type: 'string' } },
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
} as const;
|
|
88
|
+
|
|
89
|
+
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
90
|
+
const DEFAULT_OUTPUT_LIMIT_BYTES = 128 * 1024;
|
|
91
|
+
const OUTPUT_SUMMARY_CHARS = 2_000;
|
|
92
|
+
|
|
93
|
+
function parseConfigText(path: string, text: string): unknown {
|
|
94
|
+
if (/\.json$/i.test(path)) return JSON.parse(text);
|
|
95
|
+
return yaml.load(text);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function truncateOutput(value: unknown): string {
|
|
99
|
+
const text = typeof value === 'string' ? value : value == null ? '' : String(value);
|
|
100
|
+
if (text.length <= OUTPUT_SUMMARY_CHARS) return text;
|
|
101
|
+
return `${text.slice(0, OUTPUT_SUMMARY_CHARS)}\n[truncated ${text.length - OUTPUT_SUMMARY_CHARS} chars]`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function validateMeshWorktreeBootstrapConfig(config: unknown, source = 'inline'): {
|
|
105
|
+
valid: boolean;
|
|
106
|
+
errors: string[];
|
|
107
|
+
commands: MeshRefineValidationCommandPlan[];
|
|
108
|
+
rejectedCommands: Array<Record<string, unknown>>;
|
|
109
|
+
} {
|
|
110
|
+
const errors: string[] = [];
|
|
111
|
+
const commands: MeshRefineValidationCommandPlan[] = [];
|
|
112
|
+
const rejectedCommands: Array<Record<string, unknown>> = [];
|
|
113
|
+
if (!isMeshConfigRecord(config)) return { valid: false, errors: ['config must be an object'], commands, rejectedCommands };
|
|
114
|
+
if (config.version !== 1) errors.push('version must be 1');
|
|
115
|
+
if (config.enabled !== undefined && typeof config.enabled !== 'boolean') errors.push('enabled must be a boolean when provided');
|
|
116
|
+
if (config.runOnClone !== undefined && typeof config.runOnClone !== 'boolean') errors.push('runOnClone must be a boolean when provided');
|
|
117
|
+
if (config.required !== undefined && typeof config.required !== 'boolean') errors.push('required must be a boolean when provided');
|
|
118
|
+
if (config.staleInputs !== undefined && (!Array.isArray(config.staleInputs) || !config.staleInputs.every(input => typeof input === 'string' && input.trim()))) {
|
|
119
|
+
errors.push('staleInputs must be an array of non-empty strings when provided');
|
|
120
|
+
}
|
|
121
|
+
if (config.commands !== undefined && !Array.isArray(config.commands)) errors.push('commands must be an array');
|
|
122
|
+
if (Array.isArray(config.commands)) {
|
|
123
|
+
config.commands.forEach((entry, index) => {
|
|
124
|
+
const normalized = normalizeMeshCommandConfig(entry, `${source}:commands[${index}]`);
|
|
125
|
+
if (normalized.command) commands.push(normalized.command);
|
|
126
|
+
if (normalized.rejected) rejectedCommands.push(normalized.rejected);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (config.enabled !== false && config.runOnClone !== false && commands.length === 0) errors.push('commands must contain at least one command when bootstrap is enabled');
|
|
130
|
+
if (rejectedCommands.length) errors.push('one or more bootstrap commands are invalid');
|
|
131
|
+
return { valid: errors.length === 0, errors, commands, rejectedCommands };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function loadMeshWorktreeBootstrapConfig(mesh: any, workspace: string): WorktreeBootstrapConfigLoadResult {
|
|
135
|
+
const inline = mesh?.worktreeBootstrapConfig || mesh?.policy?.worktreeBootstrapConfig || mesh?.policy?.worktreeBootstrap;
|
|
136
|
+
if (inline !== undefined) {
|
|
137
|
+
const validation = validateMeshWorktreeBootstrapConfig(inline, 'mesh.policy.worktreeBootstrapConfig');
|
|
138
|
+
if (!validation.valid) return { source: 'mesh.policy.worktreeBootstrapConfig', sourceType: 'invalid', error: String(validation.rejectedCommands[0]?.reason || validation.errors.join('; ')) };
|
|
139
|
+
return { config: inline as RepoMeshWorktreeBootstrapConfig, source: 'mesh.policy.worktreeBootstrapConfig', sourceType: 'mesh_policy' };
|
|
140
|
+
}
|
|
141
|
+
for (const relative of MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS) {
|
|
142
|
+
const configPath = join(workspace, relative);
|
|
143
|
+
if (!existsSync(configPath)) continue;
|
|
144
|
+
try {
|
|
145
|
+
const parsed = parseConfigText(configPath, readFileSync(configPath, 'utf-8'));
|
|
146
|
+
const validation = validateMeshWorktreeBootstrapConfig(parsed, relative);
|
|
147
|
+
if (!validation.valid) return { source: relative, sourceType: 'invalid', path: configPath, error: String(validation.rejectedCommands[0]?.reason || validation.errors.join('; ')) };
|
|
148
|
+
return { config: parsed as RepoMeshWorktreeBootstrapConfig, source: relative, sourceType: 'repo_file', path: configPath };
|
|
149
|
+
} catch (error: any) {
|
|
150
|
+
return { source: relative, sourceType: 'invalid', path: configPath, error: error?.message || String(error) };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return { source: 'unavailable', sourceType: 'unavailable', error: `No worktree bootstrap config found. Checked: ${MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS.join(', ')}` };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export async function runMeshWorktreeBootstrap(mesh: any, workspace: string): Promise<WorktreeBootstrapState> {
|
|
157
|
+
const loaded = loadMeshWorktreeBootstrapConfig(mesh, workspace);
|
|
158
|
+
if (!loaded.config) {
|
|
159
|
+
return { status: 'not_configured', required: false, configSource: loaded.source, configSourceType: loaded.sourceType, error: loaded.error };
|
|
160
|
+
}
|
|
161
|
+
const required = loaded.config.required !== false;
|
|
162
|
+
if (loaded.config.enabled === false || loaded.config.runOnClone === false) {
|
|
163
|
+
return { status: 'disabled', required, configSource: loaded.path || loaded.source, configSourceType: loaded.sourceType };
|
|
164
|
+
}
|
|
165
|
+
const validation = validateMeshWorktreeBootstrapConfig(loaded.config, loaded.source);
|
|
166
|
+
if (!validation.valid) {
|
|
167
|
+
return { status: 'failed', required, configSource: loaded.path || loaded.source, configSourceType: 'invalid', error: String(validation.rejectedCommands[0]?.reason || validation.errors.join('; ')), commandsRun: [] };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const execFileAsync = promisify(execFile);
|
|
171
|
+
const state: WorktreeBootstrapState = {
|
|
172
|
+
status: 'running',
|
|
173
|
+
required,
|
|
174
|
+
configSource: loaded.path || loaded.source,
|
|
175
|
+
configSourceType: loaded.sourceType,
|
|
176
|
+
startedAt: new Date().toISOString(),
|
|
177
|
+
commandsRun: [],
|
|
178
|
+
staleInputs: loaded.config.staleInputs,
|
|
179
|
+
};
|
|
180
|
+
for (const command of validation.commands) {
|
|
181
|
+
const cwd = command.cwd ? pathResolve(workspace, command.cwd) : workspace;
|
|
182
|
+
const startedAt = Date.now();
|
|
183
|
+
state.lastCommand = command.displayCommand;
|
|
184
|
+
try {
|
|
185
|
+
const result = await execFileAsync(command.command, command.args, {
|
|
186
|
+
cwd,
|
|
187
|
+
encoding: 'utf8',
|
|
188
|
+
timeout: command.timeoutMs || DEFAULT_TIMEOUT_MS,
|
|
189
|
+
maxBuffer: command.outputLimitBytes || DEFAULT_OUTPUT_LIMIT_BYTES,
|
|
190
|
+
env: { ...process.env, CI: process.env.CI || '1', ...(command.env || {}) },
|
|
191
|
+
windowsHide: true,
|
|
192
|
+
});
|
|
193
|
+
state.commandsRun?.push({
|
|
194
|
+
command: command.command,
|
|
195
|
+
args: command.args,
|
|
196
|
+
displayCommand: command.displayCommand,
|
|
197
|
+
category: command.category,
|
|
198
|
+
source: command.source,
|
|
199
|
+
cwd,
|
|
200
|
+
passed: true,
|
|
201
|
+
durationMs: Date.now() - startedAt,
|
|
202
|
+
exitCode: 0,
|
|
203
|
+
stdout: truncateOutput(result.stdout),
|
|
204
|
+
stderr: truncateOutput(result.stderr),
|
|
205
|
+
});
|
|
206
|
+
} catch (error: any) {
|
|
207
|
+
const exitCode = typeof error?.code === 'number' ? error.code : null;
|
|
208
|
+
state.status = 'failed';
|
|
209
|
+
state.exitCode = exitCode;
|
|
210
|
+
state.error = error?.message || String(error);
|
|
211
|
+
state.completedAt = new Date().toISOString();
|
|
212
|
+
state.commandsRun?.push({
|
|
213
|
+
command: command.command,
|
|
214
|
+
args: command.args,
|
|
215
|
+
displayCommand: command.displayCommand,
|
|
216
|
+
category: command.category,
|
|
217
|
+
source: command.source,
|
|
218
|
+
cwd,
|
|
219
|
+
passed: false,
|
|
220
|
+
durationMs: Date.now() - startedAt,
|
|
221
|
+
exitCode,
|
|
222
|
+
signal: typeof error?.signal === 'string' ? error.signal : null,
|
|
223
|
+
timedOut: error?.killed === true || /timed out/i.test(String(error?.message || '')),
|
|
224
|
+
stdout: truncateOutput(error?.stdout),
|
|
225
|
+
stderr: truncateOutput(error?.stderr || error?.message),
|
|
226
|
+
});
|
|
227
|
+
return state;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
state.status = 'ready';
|
|
231
|
+
state.exitCode = 0;
|
|
232
|
+
state.completedAt = new Date().toISOString();
|
|
233
|
+
return state;
|
|
234
|
+
}
|
|
@@ -24,6 +24,13 @@ function normalizeApprovalLabel(value: string): string {
|
|
|
24
24
|
.trim();
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
function isNegativeApprovalLabel(value: string): boolean {
|
|
28
|
+
const label = normalizeApprovalLabel(value);
|
|
29
|
+
return /^(no|deny|reject|cancel|skip|exit|stop)\b/.test(label)
|
|
30
|
+
|| /\bwithout\b/.test(label)
|
|
31
|
+
|| /\bdo not\b/.test(label);
|
|
32
|
+
}
|
|
33
|
+
|
|
27
34
|
export function getApprovalPositiveHints(provider?: Pick<ProviderModule, 'approvalPositiveHints'> | null): string[] {
|
|
28
35
|
const customHints = Array.isArray(provider?.approvalPositiveHints)
|
|
29
36
|
? provider.approvalPositiveHints
|
|
@@ -39,24 +46,24 @@ export function pickApprovalButton(
|
|
|
39
46
|
): { index: number; label: string } {
|
|
40
47
|
const labels = (buttons || []).map((button) => String(button || '').trim()).filter(Boolean);
|
|
41
48
|
if (labels.length === 0) {
|
|
42
|
-
return { index:
|
|
49
|
+
return { index: -1, label: '' };
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
const normalizedButtons = labels.map((label) => normalizeApprovalLabel(label));
|
|
46
53
|
const hints = getApprovalPositiveHints(provider);
|
|
47
54
|
|
|
48
55
|
for (const hint of hints) {
|
|
49
|
-
const exactIndex = normalizedButtons.findIndex((label) => label === hint);
|
|
56
|
+
const exactIndex = normalizedButtons.findIndex((label, index) => label === hint && !isNegativeApprovalLabel(labels[index]));
|
|
50
57
|
if (exactIndex >= 0) return { index: exactIndex, label: labels[exactIndex] };
|
|
51
58
|
|
|
52
|
-
const prefixIndex = normalizedButtons.findIndex((label) => label.startsWith(hint));
|
|
59
|
+
const prefixIndex = normalizedButtons.findIndex((label, index) => label.startsWith(hint) && !isNegativeApprovalLabel(labels[index]));
|
|
53
60
|
if (prefixIndex >= 0) return { index: prefixIndex, label: labels[prefixIndex] };
|
|
54
61
|
|
|
55
|
-
const includeIndex = normalizedButtons.findIndex((label) => label.includes(hint));
|
|
62
|
+
const includeIndex = normalizedButtons.findIndex((label, index) => label.includes(hint) && !isNegativeApprovalLabel(labels[index]));
|
|
56
63
|
if (includeIndex >= 0) return { index: includeIndex, label: labels[includeIndex] };
|
|
57
64
|
}
|
|
58
65
|
|
|
59
|
-
return { index:
|
|
66
|
+
return { index: -1, label: '' };
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
export function formatAutoApprovalMessage(modalMessage?: string, buttonLabel?: string): string {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { ChatMessage } from '../types.js';
|
|
2
2
|
import { flattenContent } from './contracts.js';
|
|
3
3
|
|
|
4
|
+
export const DEFAULT_FINAL_SUMMARY_MAX_CHARS = 4_000;
|
|
5
|
+
|
|
4
6
|
export function extractFinalSummaryFromMessages(
|
|
5
7
|
messages: ChatMessage[] | null | undefined,
|
|
6
|
-
maxChars: number =
|
|
8
|
+
maxChars: number = DEFAULT_FINAL_SUMMARY_MAX_CHARS,
|
|
7
9
|
): string {
|
|
8
10
|
if (!Array.isArray(messages) || messages.length === 0) return '';
|
|
9
11
|
|
|
@@ -18,17 +20,10 @@ export function extractFinalSummaryFromMessages(
|
|
|
18
20
|
}
|
|
19
21
|
}
|
|
20
22
|
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const classification = classifyChatMessageVisibility(msg);
|
|
26
|
-
if (classification.isUserFacing) {
|
|
27
|
-
const text = flattenContent(msg.content).trim();
|
|
28
|
-
if (text) return text.slice(0, maxChars);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
23
|
+
// Completion summaries must describe the assistant/model result. If no
|
|
24
|
+
// user-facing assistant/model message exists yet (for example, only the
|
|
25
|
+
// dispatched user prompt is visible), return empty instead of echoing the
|
|
26
|
+
// prompt as a misleading finalSummary.
|
|
32
27
|
return '';
|
|
33
28
|
}
|
|
34
29
|
|