livedesk 0.1.447 → 0.1.449

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.
@@ -67,6 +67,11 @@ const FAST_PREFLIGHT_CACHE_PATH = join(UNIFIED_CLIENT_STATE_DIR, 'fast-preflight
67
67
  const CLIENT_SLOT_PATH = join(UNIFIED_CLIENT_STATE_DIR, 'device-slot.json');
68
68
  const CLIENT_UPDATE_STATE_PATH = process.env.LIVEDESK_CLIENT_UPDATE_STATE_PATH
69
69
  || join(UNIFIED_CLIENT_STATE_DIR, 'client-update.json');
70
+ // Migration-only compatibility: releases before 0.1.204 could persist these
71
+ // Client-side model options in startup/update commands. Consume them without
72
+ // forwarding so an upgrade starts normally, but never restores that feature.
73
+ const RETIRED_CLIENT_MODEL_SINGLE_OPTIONS = new Set(['--ai', '--no-ai', '--fake-ai']);
74
+ const RETIRED_CLIENT_MODEL_VALUE_OPTION = '--ai-model';
70
75
  let activeAgentProcess = null;
71
76
  let roleRestartRequest = null;
72
77
  let agentRestartRequest = null;
@@ -301,8 +306,8 @@ Options:
301
306
  --version Show the agent version.
302
307
  --help Show this help.
303
308
 
304
- Auto uses C# RemoteFast when supported and falls back to Node for AI assist or
305
- when a packaged RemoteFast runtime is unavailable.
309
+ Auto uses C# RemoteFast when supported and falls back to Node only when a
310
+ packaged RemoteFast runtime is unavailable.
306
311
  Enable Windows auto-start from the connection page when this client signs in.
307
312
  `.trimStart());
308
313
  }
@@ -394,15 +399,30 @@ export function parseLauncherArgs(argv) {
394
399
  let slot = process.env.LIVEDESK_CLIENT_SLOT || process.env.MINDEXEC_REMOTE_SLOT || '';
395
400
  let authPort = normalizePort(process.env.LIVEDESK_CLIENT_RUNTIME_PORT || process.env.LIVEDESK_CLIENT_AUTH_PORT) || DEFAULT_AUTH_CALLBACK_PORT;
396
401
  let command = 'connect';
397
- let nodeOnlyFeature = false;
398
- let fakeThumbnail = false;
402
+ let fakeThumbnail = false;
399
403
  let startupRun = isTruthy(process.env.LIVEDESK_ROLE_TRANSITION);
400
- let openBrowserOnStart = !isTruthy(process.env.LIVEDESK_SKIP_BROWSER_OPEN);
401
- let updateWaitPid = 0;
402
-
403
- for (let index = 0; index < argv.length; index += 1) {
404
- const arg = argv[index];
405
- if (arg && !arg.startsWith('-')) {
404
+ let openBrowserOnStart = !isTruthy(process.env.LIVEDESK_SKIP_BROWSER_OPEN);
405
+ let updateWaitPid = 0;
406
+ let retiredModelOptionsDiscarded = 0;
407
+
408
+ for (let index = 0; index < argv.length; index += 1) {
409
+ const arg = argv[index];
410
+ if (RETIRED_CLIENT_MODEL_SINGLE_OPTIONS.has(arg)) {
411
+ retiredModelOptionsDiscarded += 1;
412
+ continue;
413
+ }
414
+ if (arg === RETIRED_CLIENT_MODEL_VALUE_OPTION) {
415
+ retiredModelOptionsDiscarded += 1;
416
+ if (index + 1 < argv.length && !String(argv[index + 1] || '').startsWith('-')) {
417
+ index += 1;
418
+ }
419
+ continue;
420
+ }
421
+ if (String(arg || '').startsWith(`${RETIRED_CLIENT_MODEL_VALUE_OPTION}=`)) {
422
+ retiredModelOptionsDiscarded += 1;
423
+ continue;
424
+ }
425
+ if (arg && !arg.startsWith('-')) {
406
426
  const lowerArg = arg.toLowerCase();
407
427
  const shortcutSlot = normalizeSlotNumber(arg);
408
428
  if (lowerArg === 'connect') {
@@ -512,11 +532,8 @@ export function parseLauncherArgs(argv) {
512
532
  if (arg === '--version') {
513
533
  version = true;
514
534
  }
515
- if (arg === '--ai' || arg === '--fake-ai' || arg === '--ai-model' || arg === '--no-ai') {
516
- nodeOnlyFeature = true;
517
- }
518
- if (arg === '--fake-thumbnail') {
519
- fakeThumbnail = true;
535
+ if (arg === '--fake-thumbnail') {
536
+ fakeThumbnail = true;
520
537
  }
521
538
  forwarded.push(arg);
522
539
  }
@@ -539,13 +556,13 @@ export function parseLauncherArgs(argv) {
539
556
  deviceId,
540
557
  slot,
541
558
  authPort,
542
- nodeOnlyFeature,
543
- fakeThumbnail,
544
- startupRun,
545
- openBrowserOnStart,
546
- updateWaitPid
547
- };
548
- }
559
+ fakeThumbnail,
560
+ startupRun,
561
+ openBrowserOnStart,
562
+ updateWaitPid,
563
+ retiredModelOptionsDiscarded
564
+ };
565
+ }
549
566
 
550
567
  function normalizePort(value) {
551
568
  const port = Number(String(value || '').trim());
@@ -854,13 +871,15 @@ const CLIENT_UPDATE_SINGLE_FLAGS = new Set([
854
871
  '--no-open',
855
872
  '--exit-on-disconnect',
856
873
  '--exit-on-invalid-pair',
857
- '--once'
874
+ '--once',
875
+ ...RETIRED_CLIENT_MODEL_SINGLE_OPTIONS
858
876
  ]);
859
877
  const CLIENT_UPDATE_VALUE_FLAGS = new Set([
860
878
  '--update-wait-pid',
861
879
  // Pairing credentials stay in the inherited private environment and must
862
880
  // never be copied onto a replacement process command line.
863
- '--pair'
881
+ '--pair',
882
+ RETIRED_CLIENT_MODEL_VALUE_OPTION
864
883
  ]);
865
884
 
866
885
  export function buildClientUpdateRestartArgs(prepared = {}) {
@@ -874,6 +893,9 @@ export function buildClientUpdateRestartArgs(prepared = {}) {
874
893
  if (arg.startsWith('--pair=')) {
875
894
  continue;
876
895
  }
896
+ if (arg.startsWith(`${RETIRED_CLIENT_MODEL_VALUE_OPTION}=`)) {
897
+ continue;
898
+ }
877
899
  if (CLIENT_UPDATE_VALUE_FLAGS.has(arg)) {
878
900
  index += 1;
879
901
  continue;
@@ -4233,6 +4255,18 @@ export function buildFastArgs(args, fakeThumbnail, transport = 'auto', relay = '
4233
4255
  const forwarded = [];
4234
4256
  for (let index = 0; index < args.length; index += 1) {
4235
4257
  const arg = args[index];
4258
+ if (RETIRED_CLIENT_MODEL_SINGLE_OPTIONS.has(arg)) {
4259
+ continue;
4260
+ }
4261
+ if (arg === RETIRED_CLIENT_MODEL_VALUE_OPTION) {
4262
+ if (index + 1 < args.length && !String(args[index + 1] || '').startsWith('-')) {
4263
+ index += 1;
4264
+ }
4265
+ continue;
4266
+ }
4267
+ if (String(arg || '').startsWith(`${RETIRED_CLIENT_MODEL_VALUE_OPTION}=`)) {
4268
+ continue;
4269
+ }
4236
4270
  if (arg === '--transport' || arg === '--relay') {
4237
4271
  index += 1;
4238
4272
  continue;
@@ -4241,8 +4275,8 @@ export function buildFastArgs(args, fakeThumbnail, transport = 'auto', relay = '
4241
4275
  forwarded.push('--fake-frame');
4242
4276
  continue;
4243
4277
  }
4244
- if (arg === '--tasks' || arg === '--no-tasks' || arg === '--no-ai') {
4245
- continue;
4278
+ if (arg === '--tasks' || arg === '--no-tasks') {
4279
+ continue;
4246
4280
  }
4247
4281
  forwarded.push(arg);
4248
4282
  }
@@ -4446,15 +4480,15 @@ function spawnAgent(command, args, env = process.env, onStart = null) {
4446
4480
  });
4447
4481
  }
4448
4482
 
4449
- function shouldUseFast(parsed) {
4450
- if (parsed.engine === 'node') {
4451
- return false;
4483
+ function shouldUseFast(parsed) {
4484
+ if (parsed.engine === 'node') {
4485
+ return false;
4452
4486
  }
4453
4487
  if (parsed.engine === 'fast') {
4454
4488
  return true;
4455
4489
  }
4456
- return !parsed.nodeOnlyFeature;
4457
- }
4490
+ return true;
4491
+ }
4458
4492
 
4459
4493
  function shouldTryFast(parsed, runtime) {
4460
4494
  if (parsed.engine === 'fast') {
@@ -4480,6 +4514,12 @@ export async function runClientRuntime(argv = process.argv.slice(2)) {
4480
4514
  console.warn('[LiveDesk] @livedesk/client is deprecated. Use the unified livedesk package or LiveDesk Desktop.');
4481
4515
  }
4482
4516
  const parsed = parseLauncherArgs(argv);
4517
+ if (parsed.retiredModelOptionsDiscarded > 0) {
4518
+ console.warn(
4519
+ '[LiveDesk Client] Ignored retired Client model-runtime options. '
4520
+ + 'Approved AI computer commands are handled by the Hub Codex Agent.'
4521
+ );
4522
+ }
4483
4523
  if (parsed.updateWaitPid) {
4484
4524
  await waitForProcessExit(parsed.updateWaitPid);
4485
4525
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livedesk/client",
3
- "version": "0.1.202",
3
+ "version": "0.1.204",
4
4
  "description": "LiveDesk local remote client",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,14 +37,13 @@
37
37
  "@livedesk/runtime-core": "0.1.0",
38
38
  "@supabase/supabase-js": "^2.110.0",
39
39
  "node-screenshots": "^0.2.8",
40
- "openai": "^6.42.0",
41
40
  "ws": "^8.18.3"
42
41
  },
43
42
  "optionalDependencies": {
44
- "@livedesk/fast-linux-x64": "0.1.410",
45
- "@livedesk/fast-osx-arm64": "0.1.410",
46
- "@livedesk/fast-osx-x64": "0.1.410",
47
- "@livedesk/fast-win-x64": "0.1.410"
43
+ "@livedesk/fast-linux-x64": "0.1.411",
44
+ "@livedesk/fast-osx-arm64": "0.1.411",
45
+ "@livedesk/fast-osx-x64": "0.1.411",
46
+ "@livedesk/fast-win-x64": "0.1.411"
48
47
  },
49
48
  "publishConfig": {
50
49
  "access": "public"
package/hub/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livedesk/hub",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "description": "LiveDesk local Hub API and browser frame bridge",
5
5
  "type": "module",
6
6
  "main": "src/server.js",
@@ -1,175 +1,123 @@
1
- import os from 'node:os';
2
- import path from 'node:path';
3
- import { AgentSettingsStore, AGENT_PROVIDER_CODEX, AGENT_PROVIDER_OPENCODE_GO } from './agent-settings.js';
4
- import { createOsSecretStore } from './secret-store.js';
5
- import { createOpenCodeGoProvider } from './opencode-go-provider.js';
6
- import { AgentProviderError } from './provider-errors.js';
7
- import { createCodexAgentRuntime } from './codex-agent-runtime.js';
8
-
9
- function maskApiKey(value) {
10
- const key = String(value || '');
11
- return key.length >= 4 ? `••••${key.slice(-4)}` : '';
12
- }
13
-
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
- } = {}) {
31
- const settings = settingsStore || new AgentSettingsStore({ dataDir });
32
- const secrets = secretStore || createOsSecretStore({ dataDir });
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
- let value;
41
- try {
42
- value = publicCodexStatus(await runtime.getStatus());
43
- } catch (error) {
44
- value = publicCodexStatus({ installed: true, authenticated: 'unknown', status: error?.code || 'security-unavailable', detail: error?.message || 'Codex security isolation is unavailable.' });
45
- }
46
- statusCache = { value, expiresAt: Date.now() + 10000 };
47
- return value;
48
- }
49
-
50
- async function publicSettings() {
51
- const current = await settings.get();
52
- const { codexMaxTurns: _codexMaxTurns, ...exposedSettings } = current;
53
- let hasApiKey = false;
54
- let apiKeyPreview = '';
55
- let secretStoreAvailable = true;
56
- if (current.provider === AGENT_PROVIDER_OPENCODE_GO) {
57
- try {
58
- const key = await secrets.getApiKey(AGENT_PROVIDER_OPENCODE_GO);
59
- hasApiKey = Boolean(key);
60
- apiKeyPreview = maskApiKey(key);
61
- } catch {
62
- secretStoreAvailable = false;
63
- }
64
- }
65
- const codex = await getCodexStatus();
66
- const securityStatus = runtime?.getSecurityStatus?.() || {
67
- isolatedCodexHome: false,
68
- restrictedEnvironment: false,
69
- livedeskMcpOnly: false,
70
- selectedDeviceScopeEnforced: false,
71
- failClosed: true,
72
- verified: false,
73
- state: 'unavailable'
74
- };
75
- return {
76
- ...exposedSettings,
77
- hasApiKey,
78
- apiKeyPreview,
79
- secretStoreAvailable,
80
- codexInstallation: codex.installed ? 'installed' : 'not-installed',
81
- codexAuth: codex.authenticated,
82
- codexStatus: codex.status,
83
- codexPath: codex.codexPath,
84
- agentWorkspacePath: current.codexWorkingDirectory,
85
- securityStatus
86
- };
87
- }
88
-
89
- async function legacyApiKey() {
90
- const key = await secrets.getApiKey(AGENT_PROVIDER_OPENCODE_GO);
91
- if (!key) throw new AgentProviderError('agent-api-key-missing', 'OpenCode Go API key is not configured.', { status: 401 });
92
- return key;
93
- }
94
-
95
- async function currentProvider() {
96
- const current = await settings.get();
97
- return { current, active: current.provider === AGENT_PROVIDER_CODEX ? runtime : legacyProvider };
98
- }
99
-
100
- return {
101
- getSettings: publicSettings,
102
- async updateSettings(patch = {}) {
103
- const nextPatch = { ...patch };
104
- const apiKey = typeof nextPatch.apiKey === 'string' ? nextPatch.apiKey.trim() : '';
105
- delete nextPatch.apiKey;
106
- const current = await settings.get();
107
- const requestedProvider = String(nextPatch.provider || current.provider);
108
- if (apiKey) {
109
- if (requestedProvider !== AGENT_PROVIDER_OPENCODE_GO) {
110
- throw new AgentProviderError('agent-codex-does-not-use-api-key', 'Codex authentication is owned by the installed Codex CLI.', { status: 400 });
111
- }
112
- await secrets.setApiKey(AGENT_PROVIDER_OPENCODE_GO, apiKey);
113
- }
114
- await settings.update(nextPatch);
115
- statusCache = { expiresAt: 0, value: null };
116
- return publicSettings();
117
- },
118
- async resetSettings() {
119
- await settings.reset();
120
- statusCache = { expiresAt: 0, value: null };
121
- return publicSettings();
122
- },
123
- async deleteApiKey() {
124
- await secrets.deleteApiKey(AGENT_PROVIDER_OPENCODE_GO);
125
- return publicSettings();
126
- },
127
- async getModels() {
128
- const { current, active } = await currentProvider();
129
- if (current.provider === AGENT_PROVIDER_CODEX) {
130
- return [{ id: '', name: 'Codex default', protocol: 'codex-sdk', available: true, capabilities: { supportsReasoningLevel: true, supportsStreaming: true, supportsTools: true } }];
131
- }
132
- return active.getModels(current, await legacyApiKey());
133
- },
134
- async testConnection() {
135
- const { current, active } = await currentProvider();
136
- if (current.provider === AGENT_PROVIDER_CODEX) {
137
- if (!active) throw new AgentProviderError('codex-sdk-not-installed', 'Codex SDK is not installed.', { status: 503 });
138
- statusCache = { expiresAt: 0, value: null };
139
- return active.testConnection();
140
- }
141
- return active.testConnection(current, await legacyApiKey());
142
- },
143
- async createPlan(input) {
144
- const { current, active } = await currentProvider();
145
- if (!current.enabled) throw new AgentProviderError('agent-ai-disabled', 'Agent AI is disabled in Settings.', { status: 409 });
146
- if (current.provider === AGENT_PROVIDER_CODEX) {
147
- throw new AgentProviderError('agent-codex-run-required', 'Codex runs use the LiveDesk tool loop.', { status: 409 });
148
- }
149
- return active.createPlan(input, current, await legacyApiKey());
150
- },
151
- async createSummary(input) {
152
- const { current, active } = await currentProvider();
153
- if (!current.enabled || !current.generateSummary) throw new AgentProviderError('agent-summary-disabled', 'Agent summaries are disabled in Settings.', { status: 409 });
154
- if (current.provider === AGENT_PROVIDER_CODEX) {
155
- const source = input && typeof input === 'object' ? input : {};
156
- const results = Array.isArray(source.results) ? source.results : [];
157
- 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' };
158
- }
159
- const source = input && typeof input === 'object' ? input : {};
160
- const results = Array.isArray(source.results) ? source.results : [];
161
- const safeResults = current.includeRawDeviceDetails
162
- ? 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) }))
163
- : 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) }));
164
- 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());
165
- },
166
- async startRun(input) {
167
- const current = await settings.get();
168
- if (current.provider !== AGENT_PROVIDER_CODEX || !runtime) throw new AgentProviderError('agent-codex-not-active', 'Codex SDK is not the active Agent provider.', { status: 409 });
169
- return runtime.start(input);
170
- },
171
- getRun(runId) { return runtime?.get(runId) || null; },
172
- cancelRun(runId) { return runtime?.cancel(runId) || null; },
173
- cancelAllRuns() { return runtime?.cancelAll?.() || 0; }
174
- };
175
- }
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+ import { AgentSettingsStore, AGENT_PROVIDER_CODEX } from './agent-settings.js';
4
+ import { AgentProviderError } from './provider-errors.js';
5
+
6
+ function publicCodexStatus(status = {}) {
7
+ return {
8
+ installed: status.installed === true,
9
+ authenticated: ['signed-in', 'not-signed-in'].includes(status.authenticated) ? status.authenticated : 'unknown',
10
+ status: String(status.status || 'unknown').slice(0, 40),
11
+ codexPath: String(status.codexPath || '').slice(0, 500),
12
+ detail: String(status.detail || '').slice(0, 300)
13
+ };
14
+ }
15
+
16
+ export function createAgentManager({
17
+ dataDir = path.join(os.homedir(), '.livedesk'),
18
+ settingsStore,
19
+ codexRuntime
20
+ } = {}) {
21
+ const settings = settingsStore || new AgentSettingsStore({ dataDir });
22
+ const runtime = codexRuntime;
23
+ let statusCache = { expiresAt: 0, value: null };
24
+
25
+ async function getCodexStatus() {
26
+ if (!runtime) return { installed: false, authenticated: 'unknown', status: 'unavailable', codexPath: '' };
27
+ if (statusCache.value && statusCache.expiresAt > Date.now()) return statusCache.value;
28
+ let value;
29
+ try {
30
+ value = publicCodexStatus(await runtime.getStatus());
31
+ } catch (error) {
32
+ value = publicCodexStatus({
33
+ installed: true,
34
+ authenticated: 'unknown',
35
+ status: error?.code || 'security-unavailable',
36
+ detail: error?.message || 'Codex security isolation is unavailable.'
37
+ });
38
+ }
39
+ statusCache = { value, expiresAt: Date.now() + 10000 };
40
+ return value;
41
+ }
42
+
43
+ async function publicSettings() {
44
+ const current = await settings.get();
45
+ const { codexMaxTurns: _codexMaxTurns, ...exposedSettings } = current;
46
+ const codex = await getCodexStatus();
47
+ const securityStatus = runtime?.getSecurityStatus?.() || {
48
+ isolatedCodexHome: false,
49
+ restrictedEnvironment: false,
50
+ livedeskMcpOnly: false,
51
+ selectedDeviceScopeEnforced: false,
52
+ failClosed: true,
53
+ verified: false,
54
+ state: 'unavailable'
55
+ };
56
+ return {
57
+ ...exposedSettings,
58
+ provider: AGENT_PROVIDER_CODEX,
59
+ codexInstallation: codex.installed ? 'installed' : 'not-installed',
60
+ codexAuth: codex.authenticated,
61
+ codexStatus: codex.status,
62
+ codexPath: codex.codexPath,
63
+ agentWorkspacePath: current.codexWorkingDirectory,
64
+ securityStatus
65
+ };
66
+ }
67
+
68
+ return {
69
+ getSettings: publicSettings,
70
+ async updateSettings(patch = {}) {
71
+ const nextPatch = { ...patch };
72
+ const requestedProvider = String(nextPatch.provider || AGENT_PROVIDER_CODEX).trim().toLowerCase();
73
+ const suppliedLegacyKey = typeof nextPatch.apiKey === 'string' && nextPatch.apiKey.trim().length > 0;
74
+ delete nextPatch.provider;
75
+ delete nextPatch.apiKey;
76
+ if (requestedProvider !== AGENT_PROVIDER_CODEX || suppliedLegacyKey) {
77
+ throw new AgentProviderError(
78
+ 'agent-provider-retired',
79
+ 'LiveDesk uses the Hub-hosted Codex Agent and does not configure alternate model providers.',
80
+ { status: 400 }
81
+ );
82
+ }
83
+ await settings.update({ ...nextPatch, provider: AGENT_PROVIDER_CODEX });
84
+ statusCache = { expiresAt: 0, value: null };
85
+ return publicSettings();
86
+ },
87
+ async resetSettings() {
88
+ await settings.reset();
89
+ statusCache = { expiresAt: 0, value: null };
90
+ return publicSettings();
91
+ },
92
+ // Kept as a harmless compatibility endpoint for an older cached web UI.
93
+ // No credential is read, written, or returned by the current Hub.
94
+ async deleteApiKey() {
95
+ return publicSettings();
96
+ },
97
+ async testConnection() {
98
+ if (!runtime) throw new AgentProviderError('codex-sdk-not-installed', 'Codex SDK is not installed.', { status: 503 });
99
+ statusCache = { expiresAt: 0, value: null };
100
+ return runtime.testConnection();
101
+ },
102
+ async createPlan() {
103
+ throw new AgentProviderError('agent-codex-run-required', 'Codex Agent requests use the LiveDesk tool loop.', { status: 409 });
104
+ },
105
+ async createSummary(input) {
106
+ const source = input && typeof input === 'object' ? input : {};
107
+ const results = Array.isArray(source.results) ? source.results : [];
108
+ return {
109
+ summary: String(results.length ? `${source.completed || 0} completed, ${source.failed || 0} failed.` : 'LiveDesk Agent run completed.').slice(0, 1200)
110
+ };
111
+ },
112
+ async startRun(input) {
113
+ const current = await settings.get();
114
+ if (current.provider !== AGENT_PROVIDER_CODEX || !runtime) {
115
+ throw new AgentProviderError('agent-codex-not-active', 'Codex SDK is not the active Agent runtime.', { status: 409 });
116
+ }
117
+ return runtime.start(input);
118
+ },
119
+ getRun(runId) { return runtime?.get(runId) || null; },
120
+ cancelRun(runId) { return runtime?.cancel(runId) || null; },
121
+ cancelAllRuns() { return runtime?.cancelAll?.() || 0; }
122
+ };
123
+ }