livedesk 0.1.204 → 0.1.206

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/hub/package.json CHANGED
@@ -15,6 +15,7 @@
15
15
  },
16
16
  "dependencies": {
17
17
  "@ffmpeg-installer/ffmpeg": "^1.1.0",
18
+ "@openai/codex-sdk": "0.144.5",
18
19
  "cors": "^2.8.5",
19
20
  "express": "^4.21.2",
20
21
  "ffmpeg-static": "^5.3.0",
@@ -0,0 +1,18 @@
1
+ function normalizeIds(value) {
2
+ const source = Array.isArray(value) ? value : [];
3
+ return [...new Set(source.map(item => String(item || '').trim()).filter(Boolean))].slice(0, 500);
4
+ }
5
+
6
+ export function createAgentDeviceScope(allowedDeviceIds, fallbackDeviceIds = []) {
7
+ const requested = normalizeIds(allowedDeviceIds);
8
+ const fallback = normalizeIds(fallbackDeviceIds);
9
+ return new Set(requested.length > 0 ? requested : fallback);
10
+ }
11
+
12
+ export function selectAgentDeviceIds({ allowedDeviceIds, requestedDeviceIds, connectedDeviceIds }) {
13
+ const allowed = allowedDeviceIds instanceof Set ? allowedDeviceIds : new Set(normalizeIds(allowedDeviceIds));
14
+ const requested = normalizeIds(requestedDeviceIds);
15
+ const connected = new Set(normalizeIds(connectedDeviceIds));
16
+ const candidates = requested.length > 0 ? requested : [...allowed];
17
+ return candidates.filter(deviceId => allowed.has(deviceId) && connected.has(deviceId));
18
+ }
@@ -1,44 +1,84 @@
1
1
  import os from 'node:os';
2
2
  import path from 'node:path';
3
- import { AgentSettingsStore } from './agent-settings.js';
3
+ import { AgentSettingsStore, AGENT_PROVIDER_CODEX, AGENT_PROVIDER_OPENCODE_GO } from './agent-settings.js';
4
4
  import { createOsSecretStore } from './secret-store.js';
5
5
  import { createOpenCodeGoProvider } from './opencode-go-provider.js';
6
+ import { AgentProviderError } from './provider-errors.js';
7
+ import { createCodexAgentRuntime } from './codex-agent-runtime.js';
6
8
 
7
9
  function maskApiKey(value) {
8
10
  const key = String(value || '');
9
11
  return key.length >= 4 ? `••••${key.slice(-4)}` : '';
10
12
  }
11
13
 
12
- export function createAgentManager({ dataDir = path.join(os.homedir(), '.livedesk'), settingsStore, secretStore, provider } = {}) {
14
+ function publicCodexStatus(status = {}) {
15
+ return {
16
+ installed: status.installed === true,
17
+ authenticated: ['signed-in', 'not-signed-in'].includes(status.authenticated) ? status.authenticated : 'unknown',
18
+ status: String(status.status || 'unknown').slice(0, 40),
19
+ codexPath: String(status.codexPath || '').slice(0, 500),
20
+ detail: String(status.detail || '').slice(0, 300)
21
+ };
22
+ }
23
+
24
+ export function createAgentManager({
25
+ dataDir = path.join(os.homedir(), '.livedesk'),
26
+ settingsStore,
27
+ secretStore,
28
+ provider,
29
+ codexRuntime
30
+ } = {}) {
13
31
  const settings = settingsStore || new AgentSettingsStore({ dataDir });
14
32
  const secrets = secretStore || createOsSecretStore({ dataDir });
15
- const agentProvider = provider || createOpenCodeGoProvider();
33
+ const legacyProvider = provider || createOpenCodeGoProvider();
34
+ const runtime = codexRuntime;
35
+ let statusCache = { expiresAt: 0, value: null };
36
+
37
+ async function getCodexStatus() {
38
+ if (!runtime) return { installed: false, authenticated: 'unknown', status: 'unavailable', codexPath: '' };
39
+ if (statusCache.value && statusCache.expiresAt > Date.now()) return statusCache.value;
40
+ const value = publicCodexStatus(await runtime.getStatus());
41
+ statusCache = { value, expiresAt: Date.now() + 10000 };
42
+ return value;
43
+ }
16
44
 
17
45
  async function publicSettings() {
18
46
  const current = await settings.get();
19
47
  let hasApiKey = false;
20
48
  let apiKeyPreview = '';
21
49
  let secretStoreAvailable = true;
22
- try {
23
- const key = await secrets.getApiKey(current.provider);
24
- hasApiKey = Boolean(key);
25
- apiKeyPreview = maskApiKey(key);
26
- } catch (error) {
27
- secretStoreAvailable = false;
50
+ if (current.provider === AGENT_PROVIDER_OPENCODE_GO) {
51
+ try {
52
+ const key = await secrets.getApiKey(AGENT_PROVIDER_OPENCODE_GO);
53
+ hasApiKey = Boolean(key);
54
+ apiKeyPreview = maskApiKey(key);
55
+ } catch {
56
+ secretStoreAvailable = false;
57
+ }
28
58
  }
29
- return { ...current, hasApiKey, apiKeyPreview, secretStoreAvailable };
59
+ const codex = await getCodexStatus();
60
+ return {
61
+ ...current,
62
+ hasApiKey,
63
+ apiKeyPreview,
64
+ secretStoreAvailable,
65
+ codexInstallation: codex.installed ? 'installed' : 'not-installed',
66
+ codexAuth: codex.authenticated,
67
+ codexStatus: codex.status,
68
+ codexPath: codex.codexPath,
69
+ agentWorkspacePath: current.codexWorkingDirectory
70
+ };
71
+ }
72
+
73
+ async function legacyApiKey() {
74
+ const key = await secrets.getApiKey(AGENT_PROVIDER_OPENCODE_GO);
75
+ if (!key) throw new AgentProviderError('agent-api-key-missing', 'OpenCode Go API key is not configured.', { status: 401 });
76
+ return key;
30
77
  }
31
78
 
32
- async function apiKeyForCurrentSettings() {
79
+ async function currentProvider() {
33
80
  const current = await settings.get();
34
- const apiKey = await secrets.getApiKey(current.provider);
35
- if (!apiKey) {
36
- const error = new Error('OpenCode Go API key is not configured.');
37
- error.code = 'agent-api-key-missing';
38
- error.status = 401;
39
- throw error;
40
- }
41
- return { current, apiKey };
81
+ return { current, active: current.provider === AGENT_PROVIDER_CODEX ? runtime : legacyProvider };
42
82
  }
43
83
 
44
84
  return {
@@ -48,84 +88,71 @@ export function createAgentManager({ dataDir = path.join(os.homedir(), '.livedes
48
88
  const apiKey = typeof nextPatch.apiKey === 'string' ? nextPatch.apiKey.trim() : '';
49
89
  delete nextPatch.apiKey;
50
90
  const current = await settings.get();
51
- if (apiKey) await secrets.setApiKey(current.provider, apiKey);
91
+ const requestedProvider = String(nextPatch.provider || current.provider);
92
+ if (apiKey) {
93
+ if (requestedProvider !== AGENT_PROVIDER_OPENCODE_GO) {
94
+ throw new AgentProviderError('agent-codex-does-not-use-api-key', 'Codex authentication is owned by the installed Codex CLI.', { status: 400 });
95
+ }
96
+ await secrets.setApiKey(AGENT_PROVIDER_OPENCODE_GO, apiKey);
97
+ }
52
98
  await settings.update(nextPatch);
99
+ statusCache = { expiresAt: 0, value: null };
53
100
  return publicSettings();
54
101
  },
55
102
  async resetSettings() {
56
103
  await settings.reset();
104
+ statusCache = { expiresAt: 0, value: null };
57
105
  return publicSettings();
58
106
  },
59
107
  async deleteApiKey() {
60
- const current = await settings.get();
61
- await secrets.deleteApiKey(current.provider);
108
+ await secrets.deleteApiKey(AGENT_PROVIDER_OPENCODE_GO);
62
109
  return publicSettings();
63
110
  },
64
111
  async getModels() {
65
- const { current, apiKey } = await apiKeyForCurrentSettings();
66
- return agentProvider.getModels(current, apiKey);
112
+ const { current, active } = await currentProvider();
113
+ if (current.provider === AGENT_PROVIDER_CODEX) {
114
+ return [{ id: '', name: 'Codex default', protocol: 'codex-sdk', available: true, capabilities: { supportsReasoningLevel: true, supportsStreaming: true, supportsTools: true } }];
115
+ }
116
+ return active.getModels(current, await legacyApiKey());
67
117
  },
68
118
  async testConnection() {
69
- const { current, apiKey } = await apiKeyForCurrentSettings();
70
- return agentProvider.testConnection(current, apiKey);
119
+ const { current, active } = await currentProvider();
120
+ if (current.provider === AGENT_PROVIDER_CODEX) {
121
+ if (!active) throw new AgentProviderError('codex-sdk-not-installed', 'Codex SDK is not installed.', { status: 503 });
122
+ statusCache = { expiresAt: 0, value: null };
123
+ return active.testConnection();
124
+ }
125
+ return active.testConnection(current, await legacyApiKey());
71
126
  },
72
127
  async createPlan(input) {
73
- const current = await settings.get();
74
- if (!current.enabled) {
75
- const error = new Error('Agent AI is disabled in Settings.');
76
- error.code = 'agent-ai-disabled';
77
- error.status = 409;
78
- throw error;
79
- }
80
- const apiKey = await secrets.getApiKey(current.provider);
81
- if (!apiKey) {
82
- const error = new Error('OpenCode Go API key is not configured.');
83
- error.code = 'agent-api-key-missing';
84
- error.status = 401;
85
- throw error;
128
+ const { current, active } = await currentProvider();
129
+ if (!current.enabled) throw new AgentProviderError('agent-ai-disabled', 'Agent AI is disabled in Settings.', { status: 409 });
130
+ if (current.provider === AGENT_PROVIDER_CODEX) {
131
+ throw new AgentProviderError('agent-codex-run-required', 'Codex runs use the LiveDesk tool loop.', { status: 409 });
86
132
  }
87
- return agentProvider.createPlan(input, current, apiKey);
133
+ return active.createPlan(input, current, await legacyApiKey());
88
134
  },
89
135
  async createSummary(input) {
90
- const current = await settings.get();
91
- if (!current.enabled || !current.generateSummary) {
92
- const error = new Error('Agent summaries are disabled in Settings.');
93
- error.code = 'agent-summary-disabled';
94
- error.status = 409;
95
- throw error;
96
- }
97
- const apiKey = await secrets.getApiKey(current.provider);
98
- if (!apiKey) {
99
- const error = new Error('OpenCode Go API key is not configured.');
100
- error.code = 'agent-api-key-missing';
101
- error.status = 401;
102
- throw error;
136
+ const { current, active } = await currentProvider();
137
+ if (!current.enabled || !current.generateSummary) throw new AgentProviderError('agent-summary-disabled', 'Agent summaries are disabled in Settings.', { status: 409 });
138
+ if (current.provider === AGENT_PROVIDER_CODEX) {
139
+ const source = input && typeof input === 'object' ? input : {};
140
+ const results = Array.isArray(source.results) ? source.results : [];
141
+ return { summary: String(results.length ? `${source.completed || 0} completed, ${source.failed || 0} failed.` : 'LiveDesk Agent run completed.').slice(0, 1200), modelId: current.codexModelId || 'Codex default' };
103
142
  }
104
143
  const source = input && typeof input === 'object' ? input : {};
105
144
  const results = Array.isArray(source.results) ? source.results : [];
106
145
  const safeResults = current.includeRawDeviceDetails
107
- ? results.map(result => ({
108
- deviceName: String(result?.deviceName || '').slice(0, 120),
109
- status: String(result?.status || '').slice(0, 40),
110
- result: String(result?.result || '').slice(0, 1200),
111
- error: String(result?.error || '').slice(0, 500)
112
- }))
113
- : results.map(result => ({
114
- deviceName: String(result?.deviceName || '').slice(0, 120),
115
- status: String(result?.status || '').slice(0, 40),
116
- hasResult: Boolean(result?.result),
117
- hasError: Boolean(result?.error)
118
- }));
119
- return agentProvider.createSummary({
120
- instruction: String(source.instruction || '').slice(0, 500),
121
- operation: String(source.operation || '').slice(0, 80),
122
- summary: {
123
- total: Number(source.total || results.length),
124
- completed: Number(source.completed || 0),
125
- failed: Number(source.failed || 0),
126
- results: safeResults
127
- }
128
- }, current, apiKey);
129
- }
146
+ ? results.map(result => ({ deviceName: String(result?.deviceName || '').slice(0, 120), status: String(result?.status || '').slice(0, 40), result: String(result?.result || '').slice(0, 1200), error: String(result?.error || '').slice(0, 500) }))
147
+ : results.map(result => ({ deviceName: String(result?.deviceName || '').slice(0, 120), status: String(result?.status || '').slice(0, 40), hasResult: Boolean(result?.result), hasError: Boolean(result?.error) }));
148
+ return active.createSummary({ instruction: String(source.instruction || '').slice(0, 500), operation: String(source.operation || '').slice(0, 80), summary: { total: Number(source.total || results.length), completed: Number(source.completed || 0), failed: Number(source.failed || 0), results: safeResults } }, current, await legacyApiKey());
149
+ },
150
+ async startRun(input) {
151
+ const current = await settings.get();
152
+ if (current.provider !== AGENT_PROVIDER_CODEX || !runtime) throw new AgentProviderError('agent-codex-not-active', 'Codex SDK is not the active Agent provider.', { status: 409 });
153
+ return runtime.start(input);
154
+ },
155
+ getRun(runId) { return runtime?.get(runId) || null; },
156
+ cancelRun(runId) { return runtime?.cancel(runId) || null; }
130
157
  };
131
158
  }
@@ -2,12 +2,33 @@ import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
2
2
  import os from 'node:os';
3
3
  import path from 'node:path';
4
4
 
5
- export const AGENT_PROVIDER = 'opencode-go';
5
+ export const AGENT_PROVIDER_CODEX = 'codex';
6
+ export const AGENT_PROVIDER_OPENCODE_GO = 'opencode-go';
7
+ export const DEFAULT_AGENT_PROVIDER = AGENT_PROVIDER_CODEX;
6
8
  export const DEFAULT_AGENT_BASE_URL = 'https://opencode.ai/zen/go/v1';
7
-
9
+ export const DEFAULT_CODEX_MODEL_ID = '';
10
+ export const DEFAULT_AGENT_WORKSPACE = path.join(os.homedir(), '.livedesk', 'agent-workspace');
8
11
  export const DEFAULT_AGENT_SETTINGS = Object.freeze({
9
- enabled: false,
10
- provider: AGENT_PROVIDER,
12
+ settingsVersion: 2,
13
+ enabled: true,
14
+ provider: DEFAULT_AGENT_PROVIDER,
15
+ codexModelId: DEFAULT_CODEX_MODEL_ID,
16
+ codexReasoningEffort: 'medium',
17
+ codexSandboxMode: 'read-only',
18
+ codexApprovalPolicy: 'never',
19
+ codexWorkingDirectory: DEFAULT_AGENT_WORKSPACE,
20
+ codexTaskTimeoutMs: 600000,
21
+ codexMaxTurns: 12,
22
+ codexMaxToolCalls: 20,
23
+ codexResumeSessions: true,
24
+ codexShowDetailedEvents: true,
25
+ codexNetworkAccessEnabled: false,
26
+ codexWebSearchMode: 'disabled',
27
+ deterministicParserFirst: true,
28
+ generateSummary: true,
29
+ fallbackSummary: true,
30
+ includeRawDeviceDetails: false,
31
+ maxConcurrentRequests: 2,
11
32
  baseUrl: DEFAULT_AGENT_BASE_URL,
12
33
  defaultModelId: 'deepseek-v4-flash',
13
34
  planModelId: '',
@@ -22,15 +43,15 @@ export const DEFAULT_AGENT_SETTINGS = Object.freeze({
22
43
  summaryTimeoutMs: 60000,
23
44
  retryCount: 1,
24
45
  retryDelayMs: 1000,
25
- maxConcurrentRequests: 2,
26
- streamSummary: false,
27
- deterministicParserFirst: true,
28
- generateSummary: true,
29
- fallbackSummary: true,
30
- includeRawDeviceDetails: false
46
+ streamSummary: false
31
47
  });
32
48
 
33
- const REASONING_LEVELS = new Set(['auto', 'low', 'medium', 'high']);
49
+ const PROVIDERS = new Set([AGENT_PROVIDER_CODEX, AGENT_PROVIDER_OPENCODE_GO]);
50
+ const REASONING_EFFORTS = new Set(['minimal', 'low', 'medium', 'high', 'xhigh']);
51
+ const SANDBOX_MODES = new Set(['read-only', 'workspace-write', 'danger-full-access']);
52
+ const APPROVAL_POLICIES = new Set(['never', 'on-request', 'on-failure', 'untrusted']);
53
+ const WEB_SEARCH_MODES = new Set(['disabled', 'cached', 'live']);
54
+ const LEGACY_REASONING_LEVELS = new Set(['auto', 'low', 'medium', 'high']);
34
55
 
35
56
  export class AgentSettingsError extends Error {
36
57
  constructor(code, message) {
@@ -57,13 +78,17 @@ function stringValue(value, fallback, maxLength = 240) {
57
78
  return value.trim().slice(0, maxLength);
58
79
  }
59
80
 
81
+ function enumValue(value, allowed, fallback, code) {
82
+ const normalized = stringValue(value, fallback, 80).toLowerCase();
83
+ if (!allowed.has(normalized)) throw new AgentSettingsError(code, `Invalid agent setting: ${normalized}.`);
84
+ return normalized;
85
+ }
86
+
60
87
  function normalizeBaseUrl(value) {
61
- const candidate = stringValue(value, DEFAULT_AGENT_BASE_URL, 500).replace(/\/+$/, '');
88
+ const candidate = stringValue(value, DEFAULT_AGENT_SETTINGS.baseUrl, 500).replace(/\/+$/, '');
62
89
  try {
63
90
  const parsed = new URL(candidate);
64
- if (!['http:', 'https:'].includes(parsed.protocol) || !parsed.hostname) {
65
- throw new Error('invalid protocol');
66
- }
91
+ if (!['http:', 'https:'].includes(parsed.protocol) || !parsed.hostname) throw new Error('invalid protocol');
67
92
  parsed.hash = '';
68
93
  parsed.search = '';
69
94
  return parsed.toString().replace(/\/+$/, '');
@@ -72,19 +97,45 @@ function normalizeBaseUrl(value) {
72
97
  }
73
98
  }
74
99
 
100
+ function normalizeWorkingDirectory(value) {
101
+ // The agent runtime owns this directory. Do not allow a browser/API caller
102
+ // to move Codex into a user project or an arbitrary filesystem root.
103
+ return DEFAULT_AGENT_WORKSPACE;
104
+ }
105
+
75
106
  export function normalizeAgentSettings(value = {}) {
76
107
  const source = value && typeof value === 'object' && !Array.isArray(value) ? value : {};
77
- const provider = stringValue(source.provider, DEFAULT_AGENT_SETTINGS.provider, 40);
78
- if (provider !== AGENT_PROVIDER) {
79
- throw new AgentSettingsError('agent-unsupported-provider', 'Only OpenCode Go is supported by this LiveDesk build.');
80
- }
81
- const reasoningLevel = stringValue(source.reasoningLevel, DEFAULT_AGENT_SETTINGS.reasoningLevel, 20).toLowerCase();
82
- if (!REASONING_LEVELS.has(reasoningLevel)) {
83
- throw new AgentSettingsError('agent-invalid-reasoning-level', 'Reasoning level is invalid.');
84
- }
108
+ // The pre-Codex settings file was written by LiveDesk itself and had no
109
+ // migration marker. Keep the legacy values but move that first-run config
110
+ // to the new Codex default.
111
+ const migratingLegacy = Number(source.settingsVersion || 0) < 2 && source.provider === AGENT_PROVIDER_OPENCODE_GO;
112
+ const provider = migratingLegacy
113
+ ? DEFAULT_AGENT_PROVIDER
114
+ : enumValue(source.provider, PROVIDERS, DEFAULT_AGENT_PROVIDER, 'agent-unsupported-provider');
115
+ const reasoningLevel = enumValue(source.reasoningLevel, LEGACY_REASONING_LEVELS, DEFAULT_AGENT_SETTINGS.reasoningLevel, 'agent-invalid-reasoning-level');
85
116
  return {
117
+ settingsVersion: 2,
86
118
  enabled: booleanValue(source.enabled, DEFAULT_AGENT_SETTINGS.enabled),
87
- provider: AGENT_PROVIDER,
119
+ provider,
120
+ codexModelId: stringValue(source.codexModelId, DEFAULT_AGENT_SETTINGS.codexModelId, 160),
121
+ codexReasoningEffort: enumValue(source.codexReasoningEffort, REASONING_EFFORTS, DEFAULT_AGENT_SETTINGS.codexReasoningEffort, 'agent-invalid-reasoning-effort'),
122
+ // These are safety settings, not user-controlled execution toggles. They
123
+ // are accepted for compatibility but always normalized to the safe floor.
124
+ codexSandboxMode: 'read-only',
125
+ codexApprovalPolicy: 'never',
126
+ codexWorkingDirectory: normalizeWorkingDirectory(source.codexWorkingDirectory),
127
+ codexTaskTimeoutMs: numberValue(source.codexTaskTimeoutMs, 30000, 1800000, DEFAULT_AGENT_SETTINGS.codexTaskTimeoutMs, true),
128
+ codexMaxTurns: numberValue(source.codexMaxTurns, 1, 40, DEFAULT_AGENT_SETTINGS.codexMaxTurns, true),
129
+ codexMaxToolCalls: numberValue(source.codexMaxToolCalls, 1, 100, DEFAULT_AGENT_SETTINGS.codexMaxToolCalls, true),
130
+ codexResumeSessions: booleanValue(source.codexResumeSessions, DEFAULT_AGENT_SETTINGS.codexResumeSessions),
131
+ codexShowDetailedEvents: booleanValue(source.codexShowDetailedEvents, DEFAULT_AGENT_SETTINGS.codexShowDetailedEvents),
132
+ codexNetworkAccessEnabled: false,
133
+ codexWebSearchMode: 'disabled',
134
+ deterministicParserFirst: booleanValue(source.deterministicParserFirst, DEFAULT_AGENT_SETTINGS.deterministicParserFirst),
135
+ generateSummary: booleanValue(source.generateSummary, DEFAULT_AGENT_SETTINGS.generateSummary),
136
+ fallbackSummary: booleanValue(source.fallbackSummary, DEFAULT_AGENT_SETTINGS.fallbackSummary),
137
+ includeRawDeviceDetails: booleanValue(source.includeRawDeviceDetails, DEFAULT_AGENT_SETTINGS.includeRawDeviceDetails),
138
+ maxConcurrentRequests: numberValue(source.maxConcurrentRequests, 1, 8, DEFAULT_AGENT_SETTINGS.maxConcurrentRequests, true),
88
139
  baseUrl: normalizeBaseUrl(source.baseUrl ?? DEFAULT_AGENT_SETTINGS.baseUrl),
89
140
  defaultModelId: stringValue(source.defaultModelId, DEFAULT_AGENT_SETTINGS.defaultModelId, 160),
90
141
  planModelId: stringValue(source.planModelId, DEFAULT_AGENT_SETTINGS.planModelId, 160),
@@ -99,12 +150,7 @@ export function normalizeAgentSettings(value = {}) {
99
150
  summaryTimeoutMs: numberValue(source.summaryTimeoutMs, 5000, 180000, DEFAULT_AGENT_SETTINGS.summaryTimeoutMs, true),
100
151
  retryCount: numberValue(source.retryCount, 0, 4, DEFAULT_AGENT_SETTINGS.retryCount, true),
101
152
  retryDelayMs: numberValue(source.retryDelayMs, 100, 10000, DEFAULT_AGENT_SETTINGS.retryDelayMs, true),
102
- maxConcurrentRequests: numberValue(source.maxConcurrentRequests, 1, 8, DEFAULT_AGENT_SETTINGS.maxConcurrentRequests, true),
103
- streamSummary: booleanValue(source.streamSummary, DEFAULT_AGENT_SETTINGS.streamSummary),
104
- deterministicParserFirst: booleanValue(source.deterministicParserFirst, DEFAULT_AGENT_SETTINGS.deterministicParserFirst),
105
- generateSummary: booleanValue(source.generateSummary, DEFAULT_AGENT_SETTINGS.generateSummary),
106
- fallbackSummary: booleanValue(source.fallbackSummary, DEFAULT_AGENT_SETTINGS.fallbackSummary),
107
- includeRawDeviceDetails: booleanValue(source.includeRawDeviceDetails, DEFAULT_AGENT_SETTINGS.includeRawDeviceDetails)
153
+ streamSummary: booleanValue(source.streamSummary, DEFAULT_AGENT_SETTINGS.streamSummary)
108
154
  };
109
155
  }
110
156
 
@@ -126,12 +172,8 @@ export class AgentSettingsStore {
126
172
  try {
127
173
  const raw = await readFile(this.filePath, 'utf8');
128
174
  this.settings = normalizeAgentSettings(JSON.parse(raw));
129
- } catch (error) {
130
- if (error?.code !== 'ENOENT') {
131
- this.settings = normalizeAgentSettings(DEFAULT_AGENT_SETTINGS);
132
- } else {
133
- this.settings = normalizeAgentSettings(DEFAULT_AGENT_SETTINGS);
134
- }
175
+ } catch {
176
+ this.settings = normalizeAgentSettings(DEFAULT_AGENT_SETTINGS);
135
177
  }
136
178
  return { ...this.settings };
137
179
  }
@@ -151,4 +193,3 @@ export class AgentSettingsStore {
151
193
  return { ...next };
152
194
  }
153
195
  }
154
-