openmeld 0.3.210 → 0.3.212

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.
Files changed (77) hide show
  1. package/dist/{add-me-membership-BgDujS4W.js → add-me-membership-BwPpVKtA.js} +2 -2
  2. package/dist/{add-me-membership-BgDujS4W.js.map → add-me-membership-BwPpVKtA.js.map} +1 -1
  3. package/dist/agents-Y-UJ74By.js +4153 -0
  4. package/dist/agents-Y-UJ74By.js.map +1 -0
  5. package/dist/{api-client-foundation--H_ib5Vh.js → api-client-foundation-CrQN8_-N.js} +1 -1
  6. package/dist/{api-client-foundation-CQsXMqnr.js → api-client-foundation-DjbtuE52.js} +3 -3
  7. package/dist/{api-client-foundation-CQsXMqnr.js.map → api-client-foundation-DjbtuE52.js.map} +1 -1
  8. package/dist/{auth-session-CVxflepT.js → auth-session-Dqz9X4F0.js} +42 -21
  9. package/dist/auth-session-Dqz9X4F0.js.map +1 -0
  10. package/dist/auth-session-bRzDGlXy.js +4 -0
  11. package/dist/{command-DNIZzJuu.js → command-jgHbP21v.js} +10 -9
  12. package/dist/{command-DNIZzJuu.js.map → command-jgHbP21v.js.map} +1 -1
  13. package/dist/{computer-connection-TgsnU0K9.js → computer-connection-DvqsgYls.js} +30 -3
  14. package/dist/computer-connection-DvqsgYls.js.map +1 -0
  15. package/dist/{computer-connection-review-DqJTijzX.js → computer-connection-review-CzSpaHE0.js} +6 -6
  16. package/dist/{computer-connection-review-DqJTijzX.js.map → computer-connection-review-CzSpaHE0.js.map} +1 -1
  17. package/dist/computer-disconnect-DNM7jvda.js +47 -0
  18. package/dist/computer-disconnect-DNM7jvda.js.map +1 -0
  19. package/dist/{computer-setup-continuation-DFUxl9kf.js → computer-setup-continuation-BqdhIAjT.js} +4 -4
  20. package/dist/{computer-setup-continuation-DFUxl9kf.js.map → computer-setup-continuation-BqdhIAjT.js.map} +1 -1
  21. package/dist/{control-plane-D9jDcbMZ.js → control-plane-PxntAyDq.js} +3 -547
  22. package/dist/control-plane-PxntAyDq.js.map +1 -0
  23. package/dist/{daemon-service-runtime-D7cb3-Ov.js → daemon-service-runtime-CWTlAhZY.js} +291 -45
  24. package/dist/daemon-service-runtime-CWTlAhZY.js.map +1 -0
  25. package/dist/{openmeld-tools-update-coordinator-i1zATb9g.js → daemon-service-update-coordinator-DcjOzGu1.js} +23099 -22988
  26. package/dist/daemon-service-update-coordinator-DcjOzGu1.js.map +1 -0
  27. package/dist/dist--pb6JxqG.js +4 -0
  28. package/dist/{dist-Ccbpt6rT.js → dist-jsWwS864.js} +1435 -1206
  29. package/dist/dist-jsWwS864.js.map +1 -0
  30. package/dist/{elapsed-time-B2UGqUlR.js → elapsed-time-iFOSSUJo.js} +45 -78
  31. package/dist/elapsed-time-iFOSSUJo.js.map +1 -0
  32. package/dist/{installation-inventory-DJiJh9nP.js → installation-inventory-B0cbVLun.js} +3 -3
  33. package/dist/{installation-inventory-DJiJh9nP.js.map → installation-inventory-B0cbVLun.js.map} +1 -1
  34. package/dist/local-components-H3SkJMC1.js +567 -0
  35. package/dist/local-components-H3SkJMC1.js.map +1 -0
  36. package/dist/{openmeld-BqTI30JS.js → openmeld-B4gQCCex.js} +244 -154
  37. package/dist/openmeld-B4gQCCex.js.map +1 -0
  38. package/dist/{openmeld-maintenance-worker-DeOu5U2Q.js → openmeld-maintenance-worker-DtY_H7Bv.js} +5 -5
  39. package/dist/{openmeld-maintenance-worker-DeOu5U2Q.js.map → openmeld-maintenance-worker-DtY_H7Bv.js.map} +1 -1
  40. package/dist/openmeld-tools-update-join-DS_EIL5q.js +54 -0
  41. package/dist/openmeld-tools-update-join-DS_EIL5q.js.map +1 -0
  42. package/dist/openmeld-tools-update-process-frame-CbZPpc7D.js +93 -0
  43. package/dist/openmeld-tools-update-process-frame-CbZPpc7D.js.map +1 -0
  44. package/dist/openmeld-tools-update-process-frame-CcPLrSsw.js +4 -0
  45. package/dist/openmeld.js +7 -3
  46. package/dist/openmeld.js.map +1 -1
  47. package/dist/{prepare-session-executor-BSTToACx.js → prepare-session-executor-CWKqfuse.js} +11 -9
  48. package/dist/{prepare-session-executor-BSTToACx.js.map → prepare-session-executor-CWKqfuse.js.map} +1 -1
  49. package/dist/{runtime-transport-BrzIQru0.js → runtime-transport-BQAmcHwF.js} +16 -2
  50. package/dist/runtime-transport-BQAmcHwF.js.map +1 -0
  51. package/dist/{agents-DdYL--Rj.js → service-CTG3_X0o.js} +476 -4582
  52. package/dist/service-CTG3_X0o.js.map +1 -0
  53. package/dist/{service-account-authorization-BM7a5V4I.js → service-account-authorization-Dq4LcAVa.js} +3 -3
  54. package/dist/{service-account-authorization-BM7a5V4I.js.map → service-account-authorization-Dq4LcAVa.js.map} +1 -1
  55. package/dist/service-account-control-BTR_X7QM.js +145 -0
  56. package/dist/service-account-control-BTR_X7QM.js.map +1 -0
  57. package/dist/{service-control-capabilities-BL8hTCE6.js → service-control-capabilities-C5SxnBg2.js} +2 -2
  58. package/dist/{service-control-capabilities-BL8hTCE6.js.map → service-control-capabilities-C5SxnBg2.js.map} +1 -1
  59. package/dist/{service-control-capabilities--ktpSDhp.js → service-control-capabilities-ChRe8hkw.js} +1 -1
  60. package/dist/{upgrade-JMsVlRjL.js → upgrade-DAVEeo8B.js} +36 -17
  61. package/dist/upgrade-DAVEeo8B.js.map +1 -0
  62. package/dist/upgrade-DKXu-1wO.js +4 -0
  63. package/package.json +1 -1
  64. package/dist/agents-DdYL--Rj.js.map +0 -1
  65. package/dist/auth-session-CVxflepT.js.map +0 -1
  66. package/dist/auth-session-LTSqq_Tb.js +0 -4
  67. package/dist/computer-connection-TgsnU0K9.js.map +0 -1
  68. package/dist/control-plane-D9jDcbMZ.js.map +0 -1
  69. package/dist/daemon-service-runtime-D7cb3-Ov.js.map +0 -1
  70. package/dist/dist-Ccbpt6rT.js.map +0 -1
  71. package/dist/dist-Cs3dH2Of.js +0 -4
  72. package/dist/elapsed-time-B2UGqUlR.js.map +0 -1
  73. package/dist/openmeld-BqTI30JS.js.map +0 -1
  74. package/dist/openmeld-tools-update-coordinator-i1zATb9g.js.map +0 -1
  75. package/dist/runtime-transport-BrzIQru0.js.map +0 -1
  76. package/dist/upgrade-C5r5IT_8.js +0 -4
  77. package/dist/upgrade-JMsVlRjL.js.map +0 -1
@@ -0,0 +1,4153 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { t as isLoopbackHostname } from "./loopback-hostname-DHQPKq8N.js";
4
+ import { Cn as listAgentTargetStates, Fn as parseAgentEnabledOption, In as sortAgentRegistrations, Ln as repairBuiltinControllerInstall, Mn as updateAgentRegistrations, Nn as normalizeAgentId, On as removeAgentCustomTarget, Pn as normalizeAgentIds, Ra as resolveRuntimeContext, Sn as listAgentRegistrations, Tn as readAgentRuntimeConfigHealth, Xt as DAEMON_SERVICE_WAKEABILITY_ADVISORY_ANOMALIES, _o as compareAgentControllerRefsForDisplay, bn as createRegistrationFromCatalogTarget, jn as updateAgentCustomTarget, kn as resolveAgentTargetState, pa as toErrorMessage, si as buildAgentControllerRef, ui as createAgentControllerInterfaceDisplayRows, xn as listAgentCustomTargets, yn as addAgentCustomTarget } from "./daemon-service-update-coordinator-DcjOzGu1.js";
5
+ import { $n as runInteractivePrompt, A as buildDaemonStartNeedsAttentionCard, B as resolveDaemonServiceParticipationStatus, F as emitCliJsonEnvelope, Jn as renderInfoCard, Qn as isReturnKeypress, Zn as promptTextEntry, _ as buildDaemonBackgroundStartFailure, a as runDaemonBackgroundStartForDecision, dt as collectLocalAgentControllerInventory, ht as resolveAgentControllerInterfaceSnapshot, j as runDaemonStartDecisionPrompt, k as buildDaemonStartDeferredCard, l as runDaemonReinstall, qn as buildTextInfoCardLines, s as runDaemonInstall, v as buildSystemServiceStartupFailureReportFromVerdict } from "./service-CTG3_X0o.js";
6
+ import { Vt as readDaemonServiceContract } from "./daemon-service-runtime-CWTlAhZY.js";
7
+ import { Ft as buildBuiltinAgentSelectionExamples, Gt as createPresenter, It as getBuiltinAgentSelectionOptionalArguments, Kt as sanitizeTerminalDisplayText, Lt as getBuiltinAgentSelectionRequiredArguments, Pt as buildBuiltinAgentSelectionCommand, Wt as formatAgentOverviewPayload, g as canUseInteractivePrompts, i as formatDaemonInstallFailedMessage, p as runDaemonInstallDecisionPrompt, zt as validateBuiltinAgentSelectionInput } from "./elapsed-time-iFOSSUJo.js";
8
+ import { i as normalizeGatewayBaseUrlOrNull, l as DEFAULT_RUNTIME_AGENT_TRANSPORT_MODE } from "./base-url-C6sHRNGu.js";
9
+ import { c as formatOpenMeldCliTextBlock, i as formatInlineOpenMeldCliCommands, o as formatOpenMeldCliCommands } from "./openmeld-environment-target-NtJYJe1D.js";
10
+ import { r as waitForOpenMeldToolsUpdateCompletion } from "./openmeld-tools-update-join-DS_EIL5q.js";
11
+ import { isAbsolute } from "node:path";
12
+ import pc from "picocolors";
13
+ import { cancel, isCancel, select } from "@clack/prompts";
14
+ //#region src/system/local-service-participation-gate.ts
15
+ function shouldRequireDaemonServiceParticipation(localDemand) {
16
+ return localDemand?.decision === "keep_running";
17
+ }
18
+ async function runDaemonServiceParticipationGate(input) {
19
+ const forceRefresh = input.forceRefresh === true;
20
+ if (!shouldRunDaemonServiceParticipationGate({
21
+ ...input,
22
+ forceRefresh
23
+ })) return { status: "aligned" };
24
+ const initialStatus = await resolveDaemonServiceParticipationStatus();
25
+ if (isReadyAdvisoryServiceDrift(initialStatus)) {
26
+ const codePrefix = resolveDaemonServiceParticipationCodePrefix(input.intent);
27
+ if (input.autoRepair === true) return await runDaemonServiceParticipationRefresh({
28
+ input,
29
+ codePrefix,
30
+ expectedVersion: initialStatus.expectedVersion,
31
+ scheduleBackgroundUpdate: hasServiceVersionAdvisory(initialStatus)
32
+ });
33
+ }
34
+ if (isRunningAdvisoryOnlyServiceDrift(initialStatus)) {
35
+ const accountIssue = await resolveDaemonServiceAccountAlignmentIssue(input);
36
+ if (!accountIssue) return { status: "aligned" };
37
+ const codePrefix = resolveDaemonServiceParticipationCodePrefix(input.intent);
38
+ if (input.autoRepair === true) return await runDaemonServiceParticipationRefresh({
39
+ input,
40
+ codePrefix,
41
+ expectedVersion: initialStatus.expectedVersion,
42
+ accountIssue
43
+ });
44
+ renderDaemonStartNeedsAttention({
45
+ presenter: input.presenter,
46
+ codePrefix,
47
+ summary: accountIssue.summary
48
+ });
49
+ input.presenter.line({
50
+ code: `${codePrefix}.account_alignment_failed`,
51
+ text: accountIssue.summary,
52
+ data: { reasonCode: accountIssue.reasonCode }
53
+ });
54
+ return { status: "needs_attention" };
55
+ }
56
+ if (forceRefresh && initialStatus.kind === "ready") return await runDaemonServiceParticipationRefresh({
57
+ input,
58
+ codePrefix: resolveDaemonServiceParticipationCodePrefix(input.intent),
59
+ expectedVersion: initialStatus.expectedVersion
60
+ });
61
+ if (initialStatus.kind === "ready") {
62
+ const accountIssue = await resolveDaemonServiceAccountAlignmentIssue(input);
63
+ if (!accountIssue) return { status: "aligned" };
64
+ const codePrefix = resolveDaemonServiceParticipationCodePrefix(input.intent);
65
+ if (input.autoRepair === true) return await runDaemonServiceParticipationRefresh({
66
+ input,
67
+ codePrefix,
68
+ expectedVersion: initialStatus.expectedVersion,
69
+ accountIssue
70
+ });
71
+ renderDaemonStartNeedsAttention({
72
+ presenter: input.presenter,
73
+ codePrefix,
74
+ summary: accountIssue.summary
75
+ });
76
+ input.presenter.line({
77
+ code: `${codePrefix}.account_alignment_failed`,
78
+ text: accountIssue.summary,
79
+ data: { reasonCode: accountIssue.reasonCode }
80
+ });
81
+ return { status: "needs_attention" };
82
+ }
83
+ const codePrefix = resolveDaemonServiceParticipationCodePrefix(input.intent);
84
+ const installStageResult = await runDaemonServiceParticipationInstallStage({
85
+ input,
86
+ codePrefix,
87
+ status: initialStatus
88
+ });
89
+ if (installStageResult.result) return installStageResult.result;
90
+ return await runDaemonServiceParticipationActionStage({
91
+ input,
92
+ codePrefix,
93
+ status: installStageResult.status
94
+ });
95
+ }
96
+ function isRunningAdvisoryOnlyServiceDrift(status) {
97
+ return status.kind === "needs_repair" && status.runtimeStatus === "running" && status.anomalyCodes.length > 0 && status.anomalyCodes.every((code) => DAEMON_SERVICE_WAKEABILITY_ADVISORY_ANOMALIES.has(code));
98
+ }
99
+ function isReadyAdvisoryServiceDrift(status) {
100
+ return status.kind === "ready" && Array.isArray(status.advisoryAnomalyCodes) && status.advisoryAnomalyCodes.length > 0;
101
+ }
102
+ function hasServiceVersionAdvisory(status) {
103
+ return Boolean(status.advisoryAnomalyCodes?.some((code) => code === "version_outdated" || code === "bundle_version_drift"));
104
+ }
105
+ function shouldRunDaemonServiceParticipationGate(input) {
106
+ const canActWithoutPrompt = input.autoRepair === true && (input.resolvedView === "human" || input.resolvedView === "agent" && (input.forcePrompt === true || input.forceRefresh === true));
107
+ return (input.resolvedView === "human" && (input.allowPrompt || input.autoRepair === true) || canActWithoutPrompt) && (input.forcePrompt === true || input.forceRefresh === true || shouldRequireDaemonServiceParticipation(input.localDemand));
108
+ }
109
+ async function runDaemonServiceParticipationInstallStage(input) {
110
+ if (input.status.kind !== "missing" && input.status.kind !== "outdated") return { status: input.status };
111
+ const installableStatus = input.status;
112
+ if (input.input.autoRepair === true) {
113
+ try {
114
+ await runDaemonServiceParticipationInstallAction({
115
+ input: input.input,
116
+ status: installableStatus
117
+ });
118
+ } catch {
119
+ renderDaemonStartNeedsAttention({
120
+ presenter: input.input.presenter,
121
+ codePrefix: input.codePrefix,
122
+ summary: formatDaemonInstallFailedMessage(installableStatus.kind)
123
+ });
124
+ return {
125
+ status: installableStatus,
126
+ result: { status: "needs_attention" }
127
+ };
128
+ }
129
+ return await resolveDaemonServiceParticipationStatusAfterInstall(input);
130
+ }
131
+ const installResult = await runDaemonInstallDecisionPrompt({
132
+ kind: installableStatus.kind,
133
+ ...installableStatus.kind === "outdated" ? { installedVersion: installableStatus.installedVersion } : {},
134
+ expectedVersion: installableStatus.expectedVersion,
135
+ emitLine: (text) => {
136
+ input.input.presenter.line({
137
+ code: `${input.codePrefix}.install`,
138
+ text
139
+ });
140
+ },
141
+ installService: async () => {
142
+ await runDaemonServiceParticipationInstallAction({
143
+ input: input.input,
144
+ status: installableStatus
145
+ });
146
+ }
147
+ });
148
+ if (installResult === "cancelled") return {
149
+ status: installableStatus,
150
+ result: { status: "cancelled" }
151
+ };
152
+ if (installResult === "failed") return {
153
+ status: installableStatus,
154
+ result: { status: "needs_attention" }
155
+ };
156
+ if (installResult === "not_now" || !shouldStartAfterDaemonInstall(input.input.intent)) return {
157
+ status: installableStatus,
158
+ result: { status: "declined" }
159
+ };
160
+ return await resolveDaemonServiceParticipationStatusAfterInstall(input);
161
+ }
162
+ async function runDaemonServiceParticipationInstallAction(input) {
163
+ if (input.status.kind === "outdated") {
164
+ input.input.serviceProgress?.detail({
165
+ code: "daemon.participation.refresh.updating",
166
+ text: "Updating OpenMeld Service..."
167
+ });
168
+ await runDaemonServiceSafeRefresh({
169
+ openMeldProfileId: input.input.openMeldProfileId,
170
+ gatewayUrl: input.input.gatewayUrl,
171
+ suppressHumanResultCard: input.input.suppressRefreshHumanResultCard,
172
+ view: input.input.view
173
+ });
174
+ return;
175
+ }
176
+ await runDaemonInstall({
177
+ ...input.input.gatewayUrl ? { gatewayUrl: input.input.gatewayUrl } : {},
178
+ ...input.input.openMeldProfileId ? { profile: input.input.openMeldProfileId } : {},
179
+ view: input.input.view
180
+ }, {
181
+ suppressHumanOutput: shouldSuppressHumanInstallOutput(input.input.intent),
182
+ ...input.input.serviceProgress ? { serviceProgress: input.input.serviceProgress } : {}
183
+ });
184
+ }
185
+ async function resolveDaemonServiceParticipationStatusAfterInstall(input) {
186
+ const refreshedStatus = await resolveDaemonServiceParticipationStatus();
187
+ if (refreshedStatus.kind === "ready") return {
188
+ status: refreshedStatus,
189
+ result: { status: "aligned" }
190
+ };
191
+ if (refreshedStatus.kind === "missing" || refreshedStatus.kind === "outdated") {
192
+ renderDaemonStartNeedsAttention({
193
+ presenter: input.input.presenter,
194
+ codePrefix: input.codePrefix,
195
+ summary: refreshedStatus.kind === "missing" ? formatDaemonInstallFailedMessage("missing") : formatDaemonInstallFailedMessage("outdated")
196
+ });
197
+ return {
198
+ status: refreshedStatus,
199
+ result: { status: "needs_attention" }
200
+ };
201
+ }
202
+ return { status: refreshedStatus };
203
+ }
204
+ async function runDaemonServiceSafeRefresh(input) {
205
+ return await runDaemonReinstall({
206
+ agentOverviewHandled: true,
207
+ ...input.scheduleBackgroundUpdate ? { displayAction: "update" } : {},
208
+ ...input.gatewayUrl ? { gatewayUrl: input.gatewayUrl } : {},
209
+ ...input.openMeldProfileId ? { profile: input.openMeldProfileId } : {},
210
+ view: input.view
211
+ }, {
212
+ skipConfirm: true,
213
+ startAfterInstallMode: "always",
214
+ suppressAgentOverview: true,
215
+ ...input.suppressHumanResultCard !== void 0 ? { suppressHumanResultCard: input.suppressHumanResultCard } : {},
216
+ suppressInstallOutput: true,
217
+ ...input.forceReinstall === true ? { forceReinstall: true } : {}
218
+ });
219
+ }
220
+ async function runDaemonBackgroundStartForParticipation(input) {
221
+ return await runDaemonBackgroundStartForDecision({
222
+ gatewayUrl: input.gatewayUrl,
223
+ ...input.openMeldProfileId ? { profile: input.openMeldProfileId } : {},
224
+ ...input.serviceProgress ? { serviceProgress: input.serviceProgress } : {},
225
+ ...input.suppressStartSpinner === true ? { suppressStartSpinner: true } : {},
226
+ view: input.view
227
+ });
228
+ }
229
+ async function runDaemonServiceParticipationActionStage(input) {
230
+ if (input.status.kind === "needs_repair") return await runDaemonServiceParticipationRefresh({
231
+ input: input.input,
232
+ codePrefix: input.codePrefix,
233
+ expectedVersion: input.status.expectedVersion
234
+ });
235
+ if (input.status.kind !== "not_running") return { status: "aligned" };
236
+ if (input.input.autoRepair === true) return await mapDaemonBackgroundStartResult(await runDaemonBackgroundStartForParticipation({
237
+ openMeldProfileId: input.input.openMeldProfileId,
238
+ gatewayUrl: input.input.gatewayUrl,
239
+ serviceProgress: input.input.serviceProgress,
240
+ suppressStartSpinner: input.input.suppressStartSpinner,
241
+ view: input.input.view
242
+ }), {
243
+ expectedVersion: input.status.expectedVersion,
244
+ presenter: input.input.presenter,
245
+ codePrefix: input.codePrefix
246
+ });
247
+ return await mapDaemonBackgroundStartResult(await runDaemonStartDecisionPrompt({ startService: async () => await runDaemonBackgroundStartForParticipation({
248
+ openMeldProfileId: input.input.openMeldProfileId,
249
+ gatewayUrl: input.input.gatewayUrl,
250
+ serviceProgress: input.input.serviceProgress,
251
+ suppressStartSpinner: input.input.suppressStartSpinner,
252
+ view: input.input.view
253
+ }) }), {
254
+ expectedVersion: input.status.expectedVersion,
255
+ presenter: input.input.presenter,
256
+ codePrefix: input.codePrefix
257
+ });
258
+ }
259
+ async function runDaemonServiceParticipationRefresh(input) {
260
+ emitServiceAccountRefreshNotice(input);
261
+ try {
262
+ input.input.serviceProgress?.detail({
263
+ code: "daemon.participation.refresh.updating",
264
+ text: "Updating OpenMeld Service..."
265
+ });
266
+ const refreshOutcome = await runDaemonServiceSafeRefresh({
267
+ openMeldProfileId: input.input.openMeldProfileId,
268
+ gatewayUrl: input.input.gatewayUrl,
269
+ suppressHumanResultCard: input.input.suppressRefreshHumanResultCard,
270
+ view: input.input.view,
271
+ forceReinstall: shouldForceDaemonServiceReinstall({
272
+ accountIssue: input.accountIssue,
273
+ forceRefresh: input.input.forceRefresh
274
+ }),
275
+ ...input.scheduleBackgroundUpdate === void 0 ? {} : { scheduleBackgroundUpdate: input.scheduleBackgroundUpdate }
276
+ });
277
+ if (isCancelledDaemonServiceRefresh(refreshOutcome)) return { status: "cancelled" };
278
+ if (isStoppedDaemonServiceRefresh(refreshOutcome)) {
279
+ const startMapped = await mapDaemonBackgroundStartResult(await runDaemonBackgroundStartForParticipation({
280
+ openMeldProfileId: input.input.openMeldProfileId,
281
+ gatewayUrl: input.input.gatewayUrl,
282
+ serviceProgress: input.input.serviceProgress,
283
+ suppressStartSpinner: input.input.suppressStartSpinner,
284
+ view: input.input.view
285
+ }), {
286
+ expectedVersion: input.expectedVersion,
287
+ presenter: input.input.presenter,
288
+ codePrefix: input.codePrefix
289
+ });
290
+ if (startMapped.status !== "aligned") return startMapped;
291
+ const accountIssueAfterStart = await resolveDaemonServiceAccountAlignmentIssue(input.input);
292
+ if (accountIssueAfterStart) {
293
+ renderDaemonStartNeedsAttention({
294
+ presenter: input.input.presenter,
295
+ codePrefix: input.codePrefix,
296
+ summary: accountIssueAfterStart.summary
297
+ });
298
+ input.input.presenter.line({
299
+ code: `${input.codePrefix}.account_alignment_failed`,
300
+ text: accountIssueAfterStart.summary,
301
+ data: { reasonCode: accountIssueAfterStart.reasonCode }
302
+ });
303
+ return { status: "needs_attention" };
304
+ }
305
+ return startMapped;
306
+ }
307
+ const scheduled = await completeScheduledServiceRefresh({
308
+ outcome: refreshOutcome,
309
+ presenter: input.input.presenter,
310
+ codePrefix: input.codePrefix
311
+ });
312
+ if (!refreshOutcome.ok && (refreshOutcome.result === "start_failed" || refreshOutcome.result === "start_blocked") && (refreshOutcome.startFailureVerdict?.jobDiagnostics || refreshOutcome.startFailureVerdict?.localMachineIssue)) return renderRefreshStartFailure({
313
+ codePrefix: input.codePrefix,
314
+ presenter: input.input.presenter,
315
+ verdict: refreshOutcome.startFailureVerdict
316
+ });
317
+ if (!(scheduled || refreshOutcome.ok && (refreshOutcome.result === "already_healthy" || refreshOutcome.result === "completed") && refreshOutcome.serviceState === "running")) throw new Error(`OpenMeld Service refresh did not complete: ${refreshOutcome.result}`);
318
+ } catch (error) {
319
+ renderRefreshFailureIfVisible({
320
+ codePrefix: input.codePrefix,
321
+ presenter: input.input.presenter,
322
+ summary: "OpenMeld Service was repaired, but it is not running yet. Local Wake work will keep waiting until OpenMeld Service starts. Run `openmeld service start`.",
323
+ suppressed: input.input.suppressRefreshHumanResultCard === true
324
+ });
325
+ return {
326
+ errorMessage: toErrorMessage(error),
327
+ status: "needs_attention"
328
+ };
329
+ }
330
+ const refreshedStatus = await resolveDaemonServiceParticipationStatus({ expectedVersion: input.expectedVersion });
331
+ const accountIssue = await resolveDaemonServiceAccountAlignmentIssue(input.input);
332
+ if (refreshedStatus.kind === "ready" && !accountIssue) return { status: "aligned" };
333
+ renderDaemonStartNeedsAttention({
334
+ presenter: input.input.presenter,
335
+ codePrefix: input.codePrefix,
336
+ summary: accountIssue?.summary ?? summarizeDaemonServiceParticipationStatus(refreshedStatus)
337
+ });
338
+ if (accountIssue) input.input.presenter.line({
339
+ code: `${input.codePrefix}.account_alignment_failed`,
340
+ text: accountIssue.summary,
341
+ data: { reasonCode: accountIssue.reasonCode }
342
+ });
343
+ return { status: "needs_attention" };
344
+ }
345
+ function emitServiceAccountRefreshNotice(input) {
346
+ if (!input.accountIssue) return;
347
+ input.input.presenter.line({
348
+ code: `${input.codePrefix}.account_alignment_refresh`,
349
+ text: input.accountIssue.summary,
350
+ data: { reasonCode: input.accountIssue.reasonCode }
351
+ });
352
+ }
353
+ async function completeScheduledServiceRefresh(input) {
354
+ if (!(input.outcome.ok && input.outcome.result === "scheduled")) return false;
355
+ await waitForOpenMeldToolsUpdateCompletion({
356
+ operationId: input.outcome.jobId,
357
+ onWaiting: (operation) => {
358
+ input.presenter.line({
359
+ code: `${input.codePrefix}.update.waiting`,
360
+ text: "OpenMeld is finishing its background update. This command will continue automatically when it finishes.",
361
+ data: {
362
+ operationId: operation.operationId,
363
+ phase: operation.phase,
364
+ targetVersion: operation.targetVersion
365
+ }
366
+ });
367
+ }
368
+ });
369
+ return true;
370
+ }
371
+ function renderRefreshFailureIfVisible(input) {
372
+ if (input.suppressed) return;
373
+ renderDaemonStartNeedsAttention({
374
+ autoRepairAttempted: true,
375
+ codePrefix: input.codePrefix,
376
+ presenter: input.presenter,
377
+ summary: input.summary
378
+ });
379
+ }
380
+ function shouldForceDaemonServiceReinstall(input) {
381
+ return input.accountIssue !== void 0 || input.forceRefresh === true;
382
+ }
383
+ function renderRefreshStartFailure(input) {
384
+ const report = buildSystemServiceStartupFailureReportFromVerdict(input.verdict, { autoRepairAttempted: true });
385
+ renderInfoCard({
386
+ presenter: input.presenter,
387
+ title: report.title,
388
+ codePrefix: `${input.codePrefix}.failed`,
389
+ rows: report.rows
390
+ });
391
+ const { outcome: _outcome, ...failure } = buildDaemonBackgroundStartFailure(input.verdict);
392
+ return {
393
+ ...failure,
394
+ status: "needs_attention"
395
+ };
396
+ }
397
+ async function resolveDaemonServiceAccountAlignmentIssue(input) {
398
+ const expectedProfileId = normalizeOptionalText$1(input.openMeldProfileId);
399
+ const expectedOwnerUserId = normalizeOptionalText$1(input.expectedOwnerUserId);
400
+ const expectedGatewayUrl = normalizeGatewayBaseUrlOrNull(input.gatewayUrl);
401
+ if (!(expectedProfileId || expectedOwnerUserId || expectedGatewayUrl)) return null;
402
+ const contract = await readDaemonServiceContract().catch(() => null);
403
+ if (!contract) return {
404
+ reasonCode: "service_contract.missing",
405
+ summary: "OpenMeld Service is running, but this setup could not verify the service account contract for this computer."
406
+ };
407
+ const contractOwnerUserId = normalizeOptionalText$1(contract.ownerUserId);
408
+ if (expectedOwnerUserId && contractOwnerUserId && contractOwnerUserId !== expectedOwnerUserId) return {
409
+ reasonCode: "service_contract.owner_mismatch",
410
+ summary: "OpenMeld Service is running for a different OpenMeld account. This setup will reconnect OpenMeld Service to the current Web account."
411
+ };
412
+ if (expectedProfileId && contract.profileId && contract.profileId !== expectedProfileId) return {
413
+ reasonCode: "service_contract.profile_mismatch",
414
+ summary: "OpenMeld Service is running for a different OpenMeld account. This setup will reconnect OpenMeld Service to the current Web account."
415
+ };
416
+ if (expectedOwnerUserId && expectedProfileId && !(contractOwnerUserId || contract.profileId)) return {
417
+ reasonCode: "service_contract.owner_unverified",
418
+ summary: "OpenMeld Service is running, but this setup could not verify that OpenMeld Service belongs to the current Web account. This setup will reconnect OpenMeld Service to the current Web account."
419
+ };
420
+ const contractGatewayUrl = normalizeGatewayBaseUrlOrNull(contract.gatewayUrl);
421
+ if (expectedGatewayUrl && contractGatewayUrl && contractGatewayUrl !== expectedGatewayUrl) return {
422
+ reasonCode: "service_contract.gateway_mismatch",
423
+ summary: "OpenMeld Service is running for a different OpenMeld gateway. This setup will reconnect OpenMeld Service to the current Web account."
424
+ };
425
+ return null;
426
+ }
427
+ function normalizeOptionalText$1(value) {
428
+ const normalized = String(value ?? "").trim();
429
+ return normalized.length > 0 ? normalized : null;
430
+ }
431
+ function resolveDaemonServiceParticipationCodePrefix(intent) {
432
+ switch (intent) {
433
+ case "start": return "start.service";
434
+ case "space_create_join": return "space.create_join.service";
435
+ case "space_join": return "space.join.service";
436
+ case "space_add_members": return "space.add_members.service";
437
+ default: throw new Error(`Unknown local service participation intent: ${intent}`);
438
+ }
439
+ }
440
+ function isCancelledDaemonServiceRefresh(outcome) {
441
+ return !outcome.ok && outcome.result === "cancelled";
442
+ }
443
+ function isStoppedDaemonServiceRefresh(outcome) {
444
+ return outcome.ok && (outcome.result === "already_healthy" || outcome.result === "completed" || outcome.result === "stopped_safe") && outcome.serviceState === "stopped";
445
+ }
446
+ function shouldStartAfterDaemonInstall(_intent) {
447
+ return true;
448
+ }
449
+ function shouldSuppressHumanInstallOutput(intent) {
450
+ return intent !== "start";
451
+ }
452
+ async function mapDaemonBackgroundStartResult(result, input) {
453
+ if (typeof result === "object") return {
454
+ status: "needs_attention",
455
+ errorMessage: result.errorMessage,
456
+ ...result.localMachineIssue ? { localMachineIssue: result.localMachineIssue } : {}
457
+ };
458
+ if (result === "started") {
459
+ const refreshedStatus = await resolveDaemonServiceParticipationStatus({ expectedVersion: input.expectedVersion });
460
+ if (refreshedStatus.kind === "ready") return { status: "aligned" };
461
+ renderDaemonStartNeedsAttention({
462
+ presenter: input.presenter,
463
+ codePrefix: input.codePrefix,
464
+ summary: summarizeDaemonServiceParticipationStatus(refreshedStatus)
465
+ });
466
+ return { status: "needs_attention" };
467
+ }
468
+ if (result === "deferred_with_card") return { status: "declined" };
469
+ if (result === "cancelled") return { status: "cancelled" };
470
+ if (result === "not_now") {
471
+ renderDaemonStartDeferred({
472
+ presenter: input.presenter,
473
+ codePrefix: input.codePrefix
474
+ });
475
+ return { status: "declined" };
476
+ }
477
+ if (result === "failed") {
478
+ renderDaemonStartNeedsAttention({
479
+ presenter: input.presenter,
480
+ codePrefix: input.codePrefix,
481
+ summary: "OpenMeld could not finish starting OpenMeld Service."
482
+ });
483
+ return { status: "needs_attention" };
484
+ }
485
+ return { status: "needs_attention" };
486
+ }
487
+ function summarizeDaemonServiceParticipationStatus(status) {
488
+ switch (status.kind) {
489
+ case "missing": return formatDaemonInstallFailedMessage("missing");
490
+ case "outdated": return formatDaemonInstallFailedMessage("outdated");
491
+ case "needs_repair": return status.detail;
492
+ case "not_running": return "OpenMeld could not finish starting OpenMeld Service.";
493
+ case "ready": return "OpenMeld Service is running.";
494
+ default: return "OpenMeld Service still needs attention on this device.";
495
+ }
496
+ }
497
+ function renderDaemonStartDeferred(input) {
498
+ const card = buildDaemonStartDeferredCard();
499
+ renderInfoCard({
500
+ presenter: input.presenter,
501
+ title: card.title,
502
+ codePrefix: `${input.codePrefix}.deferred`,
503
+ rows: card.rows
504
+ });
505
+ }
506
+ function renderDaemonStartNeedsAttention(input) {
507
+ const card = buildDaemonStartNeedsAttentionCard({
508
+ summary: input.summary,
509
+ ...input.nextStep ? { nextStep: input.nextStep } : {},
510
+ ...input.detail ? { detail: input.detail } : {},
511
+ ...input.autoRepairAttempted ? { autoRepairAttempted: input.autoRepairAttempted } : {}
512
+ });
513
+ renderInfoCard({
514
+ presenter: input.presenter,
515
+ title: card.title,
516
+ codePrefix: `${input.codePrefix}.failed`,
517
+ rows: card.rows
518
+ });
519
+ }
520
+ //#endregion
521
+ //#region src/space/web-url.ts
522
+ function buildSpaceWebUrl(baseUrl, organizationSlug, spaceId, options) {
523
+ const url = new URL(resolveGatewayWebOrigin(baseUrl));
524
+ const orgSlug = requireOrganizationSlug(organizationSlug);
525
+ url.pathname = `/o/${encodeURIComponent(orgSlug)}/spaces/${encodeURIComponent(spaceId)}`;
526
+ url.search = "";
527
+ const profileId = normalizeOptionalText(options?.profileId);
528
+ if (profileId) url.searchParams.set("profileId", profileId);
529
+ url.hash = "";
530
+ return url.toString();
531
+ }
532
+ function resolveGatewayWebOrigin(baseUrl) {
533
+ const url = new URL(baseUrl);
534
+ if (isLoopbackHostname(url.hostname) && url.port === LOCAL_GATEWAY_PORT) url.port = LOCAL_WEB_PORT;
535
+ const pair = OPENMELD_ORIGIN_HOST_PAIRS.find(({ gateway }) => gateway === url.hostname);
536
+ if (pair) {
537
+ url.hostname = pair.web;
538
+ url.port = "";
539
+ url.protocol = "https:";
540
+ }
541
+ url.pathname = "";
542
+ url.search = "";
543
+ url.hash = "";
544
+ return url.origin;
545
+ }
546
+ function resolveSpaceWebGatewayOrigin(spaceUrl) {
547
+ const url = new URL(spaceUrl.toString());
548
+ const pair = OPENMELD_ORIGIN_HOST_PAIRS.find(({ web }) => web === url.hostname);
549
+ if (pair) {
550
+ if (url.protocol !== "https:" || url.port.length > 0) throw new Error(TRUSTED_SPACE_WEB_ORIGIN_ERROR);
551
+ url.hostname = pair.gateway;
552
+ } else if (isLoopbackHostname(url.hostname) && (url.protocol === "http:" || url.protocol === "https:") && url.port === LOCAL_WEB_PORT) url.port = LOCAL_GATEWAY_PORT;
553
+ else throw new Error(TRUSTED_SPACE_WEB_ORIGIN_ERROR);
554
+ url.pathname = "";
555
+ url.search = "";
556
+ url.hash = "";
557
+ return url.origin;
558
+ }
559
+ function normalizeOptionalText(value) {
560
+ const normalized = String(value ?? "").trim();
561
+ return normalized.length > 0 ? normalized : null;
562
+ }
563
+ function requireOrganizationSlug(value) {
564
+ const normalized = normalizeOptionalText(value);
565
+ if (!normalized) throw new Error("Active organization slug is required to build a Space URL.");
566
+ return normalized;
567
+ }
568
+ const OPENMELD_ORIGIN_HOST_PAIRS = [
569
+ {
570
+ gateway: "gateway.openmeld.ai",
571
+ web: "openmeld.ai"
572
+ },
573
+ {
574
+ gateway: "gateway.staging.openmeld.ai",
575
+ web: "staging.openmeld.ai"
576
+ },
577
+ {
578
+ gateway: "gateway.openmeld.invalid",
579
+ web: "openmeld.invalid"
580
+ }
581
+ ];
582
+ const LOCAL_GATEWAY_PORT = "8080";
583
+ const LOCAL_WEB_PORT = "3000";
584
+ const TRUSTED_SPACE_WEB_ORIGIN_ERROR = "Space URL must use a trusted OpenMeld Web origin. For a custom local environment, pass --gateway-url explicitly.";
585
+ //#endregion
586
+ //#region src/runtime/retry-delay.ts
587
+ const RETRY_DELAY_SCHEDULE_MS = [
588
+ 250,
589
+ 500,
590
+ 1e3,
591
+ 2e3
592
+ ];
593
+ const RETRY_DELAY_CAP_MS = 2e3;
594
+ function computeRetryDelayMs(attempt) {
595
+ const index = Math.max(0, Math.min(attempt - 1, RETRY_DELAY_SCHEDULE_MS.length - 1));
596
+ return RETRY_DELAY_SCHEDULE_MS[index] ?? RETRY_DELAY_CAP_MS;
597
+ }
598
+ //#endregion
599
+ //#region src/agents/agent-overview.ts
600
+ function emitAgentsAgentOverview(input) {
601
+ input.presenter.data({
602
+ code: `agents.${input.command}.agent_overview`,
603
+ payload: formatAgentOverviewPayload(buildAgentsAgentOverviewPayload(input.command))
604
+ });
605
+ }
606
+ function buildBuiltinAgentSelectionOverviewPayload(command) {
607
+ return {
608
+ commands: buildBuiltinAgentSelectionExamples({
609
+ command,
610
+ includeViewFlag: true
611
+ }),
612
+ optionalArguments: getBuiltinAgentSelectionOptionalArguments(command),
613
+ requiredArguments: getBuiltinAgentSelectionRequiredArguments(command)
614
+ };
615
+ }
616
+ function buildAgentsAgentOverviewPayload(command) {
617
+ switch (command) {
618
+ case "menu": return {
619
+ summary: "Manage local OpenMeld-connected agents, agent visibility in Space, and custom agent targets.",
620
+ requiredArguments: [],
621
+ optionalArguments: [],
622
+ commands: [
623
+ "openmeld agents --view agent",
624
+ "openmeld agents detect --view agent",
625
+ "openmeld agents list --view agent",
626
+ ...buildBuiltinAgentSelectionExamples({
627
+ command: "enable",
628
+ includeViewFlag: true
629
+ }),
630
+ "openmeld agents manage --view agent"
631
+ ],
632
+ stages: ["Inspect available agents and current registrations.", "Choose an explicit subcommand for detect, list, enable, disable, show, config, or custom targets."],
633
+ notes: ["Agent view does not open the human interactive agents menu.", "Use explicit subcommands to keep automation deterministic."],
634
+ humanActionRequired: false
635
+ };
636
+ case "manage": return {
637
+ summary: "Review connected agent IDs and decide whether to inspect, enable, or disable them with explicit commands.",
638
+ requiredArguments: [],
639
+ optionalArguments: [],
640
+ commands: [
641
+ "openmeld agents list --view agent",
642
+ "openmeld agents show <agent-id> --view agent",
643
+ ...buildBuiltinAgentSelectionExamples({
644
+ command: "repair",
645
+ includeViewFlag: true
646
+ }),
647
+ "openmeld agents config <agent-id> --enabled true|false --view agent",
648
+ ...buildBuiltinAgentSelectionExamples({
649
+ command: "disable",
650
+ includeViewFlag: true
651
+ })
652
+ ],
653
+ stages: ["List connected agent IDs.", "Inspect one agent or change enabled state with explicit subcommands."],
654
+ notes: ["The manage menu is interactive in human view only."],
655
+ humanActionRequired: true
656
+ };
657
+ case "detect": return {
658
+ summary: "Detect supported local AI tools on this computer and report which ones are available to OpenMeld now.",
659
+ requiredArguments: [],
660
+ optionalArguments: [],
661
+ commands: ["openmeld agents detect --view agent"],
662
+ stages: ["Scan supported builtin agent targets.", "Return detected agents and whether each one is immediately selectable."],
663
+ notes: [],
664
+ humanActionRequired: false
665
+ };
666
+ case "list": return {
667
+ summary: "List currently registered OpenMeld agent IDs that can wake and reply in Space.",
668
+ requiredArguments: [],
669
+ optionalArguments: [],
670
+ commands: ["openmeld agents list --view agent"],
671
+ stages: ["Load agent registrations from your local OpenMeld config.", "Return registered agents and enabled state."],
672
+ notes: [],
673
+ humanActionRequired: false
674
+ };
675
+ case "repair": return {
676
+ summary: "Repair one or more builtin local agents so OpenMeld can start them again on this computer.",
677
+ ...buildBuiltinAgentSelectionOverviewPayload("repair"),
678
+ stages: ["Resolve a fresh launch contract for each requested builtin agent.", "Persist the repaired machine launch contract into OpenMeld runtime state."],
679
+ notes: ["Repair does not enable or disable agents. It refreshes how OpenMeld starts them on this computer."],
680
+ humanActionRequired: false
681
+ };
682
+ case "show": return {
683
+ summary: "Show one registered agent ID, its current OpenMeld config, and how OpenMeld will wake it.",
684
+ requiredArguments: ["<agent-id>"],
685
+ optionalArguments: [],
686
+ commands: ["openmeld agents show <agent-id> --view agent"],
687
+ stages: ["Resolve the requested agent ID.", "Return the current registration and effective runtime config."],
688
+ notes: [],
689
+ humanActionRequired: false
690
+ };
691
+ case "enable": return {
692
+ summary: "Enable one or more detected agents so new Space @mentions can wake them.",
693
+ ...buildBuiltinAgentSelectionOverviewPayload("enable"),
694
+ stages: [
695
+ "Resolve selectable detected agents.",
696
+ "Register or re-enable them in your local OpenMeld config.",
697
+ "Return per-agent enable summary."
698
+ ],
699
+ notes: ["Use `openmeld agents detect --view agent` first if you need to discover selectable ids."],
700
+ humanActionRequired: false
701
+ };
702
+ case "disable": return {
703
+ summary: "Disable one or more registered agents so new Space @mentions stop waking them.",
704
+ ...buildBuiltinAgentSelectionOverviewPayload("disable"),
705
+ stages: [
706
+ "Resolve requested registered agent ids.",
707
+ "Update enabled=false in your local OpenMeld config.",
708
+ "Return per-agent disable summary."
709
+ ],
710
+ notes: [],
711
+ humanActionRequired: false
712
+ };
713
+ case "config": return {
714
+ summary: "Update a registered agent's OpenMeld configuration with explicit flags.",
715
+ requiredArguments: ["<agent-id>", "--enabled true|false"],
716
+ optionalArguments: [],
717
+ commands: ["openmeld agents config <agent-id> --enabled true --view agent", "openmeld agents config <agent-id> --enabled false --view agent"],
718
+ stages: [
719
+ "Resolve the requested registered agent id.",
720
+ "Apply explicit config updates.",
721
+ "Return the updated effective config."
722
+ ],
723
+ notes: [],
724
+ humanActionRequired: false
725
+ };
726
+ case "custom_list": return {
727
+ summary: "List custom agent targets stored in your local OpenMeld config and whether they are currently registered.",
728
+ requiredArguments: [],
729
+ optionalArguments: [],
730
+ commands: ["openmeld agents custom list --view agent"],
731
+ stages: ["Load configured custom targets.", "Project whether each target is registered and enabled."],
732
+ notes: [],
733
+ humanActionRequired: false
734
+ };
735
+ case "custom_add": return {
736
+ summary: "Add a custom agent target to your local OpenMeld config with an absolute directory path.",
737
+ requiredArguments: ["--name <name>", "--dir <absolute-path>"],
738
+ optionalArguments: ["--id <id>"],
739
+ commands: ["openmeld agents custom add --name <name> --dir <absolute-path> --view agent", "openmeld agents custom add --id <id> --name <name> --dir <absolute-path> --view agent"],
740
+ stages: [
741
+ "Validate explicit custom target input.",
742
+ "Save the target in your local OpenMeld config.",
743
+ "Return the created custom target."
744
+ ],
745
+ notes: [],
746
+ humanActionRequired: false
747
+ };
748
+ case "custom_update": return {
749
+ summary: "Update a custom agent target while keeping its ID stable.",
750
+ requiredArguments: ["--id <id>", "one update: --name <name> | --dir <absolute-path>"],
751
+ optionalArguments: [],
752
+ commands: ["openmeld agents custom update --id <id> --name <name> --view agent", "openmeld agents custom update --id <id> --dir <absolute-path> --view agent"],
753
+ stages: [
754
+ "Resolve the custom target id.",
755
+ "Apply explicit updates.",
756
+ "Return the updated target and changed fields."
757
+ ],
758
+ notes: [],
759
+ humanActionRequired: false
760
+ };
761
+ case "custom_remove": return {
762
+ summary: "Remove a custom agent target that is not currently registered.",
763
+ requiredArguments: ["--id <id>"],
764
+ optionalArguments: [],
765
+ commands: ["openmeld agents custom remove --id <id> --view agent"],
766
+ stages: [
767
+ "Resolve the custom target id.",
768
+ "Verify it is not currently registered.",
769
+ "Remove it from your local OpenMeld config and report the result."
770
+ ],
771
+ notes: ["Install records are kept after removal; clean them up with OpenMeld skills commands when needed."],
772
+ humanActionRequired: false
773
+ };
774
+ default: return assertNever(command);
775
+ }
776
+ }
777
+ function assertNever(value) {
778
+ throw new Error(`unsupported agents agent overview command: ${String(value)}`);
779
+ }
780
+ //#endregion
781
+ //#region src/ui/visible-window.ts
782
+ function clampNumber(value, min, max) {
783
+ if (!Number.isFinite(value)) return min;
784
+ if (value < min) return min;
785
+ if (value > max) return max;
786
+ return value;
787
+ }
788
+ function resolveAnchoredVisibleWindow(input) {
789
+ if (input.itemCount <= input.maxVisible) return {
790
+ start: 0,
791
+ end: input.itemCount
792
+ };
793
+ const start = clampNumber((input.anchorIndex === null ? 0 : clampNumber(input.anchorIndex, 0, input.itemCount - 1)) - Math.floor(input.maxVisible / 2), 0, input.itemCount - input.maxVisible);
794
+ return {
795
+ start,
796
+ end: Math.min(input.itemCount, start + input.maxVisible)
797
+ };
798
+ }
799
+ function resolveOffsetVisibleWindow(input) {
800
+ if (input.itemCount <= input.maxVisible) return {
801
+ start: 0,
802
+ end: input.itemCount
803
+ };
804
+ const maxStart = Math.max(0, input.itemCount - input.maxVisible);
805
+ const start = resolveVisibleWindowOffset(input.offset, maxStart);
806
+ return {
807
+ start,
808
+ end: Math.min(input.itemCount, start + input.maxVisible)
809
+ };
810
+ }
811
+ function resolveVisibleWindowOffset(offset, maxStart) {
812
+ if (!Number.isFinite(offset)) return offset > 0 ? maxStart : 0;
813
+ return clampNumber(offset, 0, maxStart);
814
+ }
815
+ //#endregion
816
+ //#region src/ui/search-select.ts
817
+ const PROMPT_SYMBOL_ACTIVE = pc.green("◇");
818
+ const PROMPT_SYMBOL_CANCELLED = pc.red("■");
819
+ const CURSOR_SYMBOL = pc.cyan("❯");
820
+ const BAR_SYMBOL = pc.dim("│");
821
+ const SINGLE_SELECTED_SYMBOL = pc.green("●");
822
+ const SINGLE_UNSELECTED_SYMBOL = pc.dim("○");
823
+ const MULTI_SELECTED_SYMBOL = pc.green("■");
824
+ const MULTI_UNSELECTED_SYMBOL = pc.dim("□");
825
+ const EMPTY_MATCH_LABEL = "No matches found";
826
+ const DEFAULT_MAX_VISIBLE = 8;
827
+ const PRINTABLE_SEARCH_INPUT_REGEX = /^[\x20-\x7E]+$/;
828
+ function createSearchPromptState(input) {
829
+ const items = input.items.map(sanitizeSearchPromptItem);
830
+ const baseState = {
831
+ mode: input.mode,
832
+ message: sanitizeTerminalDisplayText(input.message, "single-line"),
833
+ noticeLines: (input.noticeLines ?? []).map((line) => sanitizeTerminalDisplayText(line, "single-line")),
834
+ items,
835
+ footerActions: (input.footerActions ?? []).map(sanitizeSearchPromptFooterAction),
836
+ footerActionsResolver: input.footerActionsResolver ?? null,
837
+ workbench: input.workbench === true,
838
+ allowBack: input.allowBack === true,
839
+ backLabel: sanitizeTerminalDisplayText(input.backLabel ?? "Back", "single-line"),
840
+ emptyLabel: sanitizeTerminalDisplayText(input.emptyLabel ?? EMPTY_MATCH_LABEL, "single-line"),
841
+ maxVisible: normalizeMaxVisible(input.maxVisible),
842
+ required: input.required === true,
843
+ readOnly: input.readOnly === true,
844
+ summaryCard: input.summaryCard === void 0 ? null : sanitizeSearchPromptSummaryCard(input.summaryCard),
845
+ selectedValues: dedupeValues(input.initialSelectedValues ?? []),
846
+ query: "",
847
+ activeRowIndex: 0
848
+ };
849
+ return syncSearchPromptState({
850
+ ...baseState,
851
+ activeRowIndex: resolveInitialSearchPromptActiveRowIndex(baseState)
852
+ });
853
+ }
854
+ function sanitizeSearchPromptFooterAction(action) {
855
+ return {
856
+ ...action,
857
+ label: sanitizeTerminalDisplayText(action.label, "single-line"),
858
+ ...typeof action.icon === "string" ? { icon: sanitizeTerminalDisplayText(action.icon, "single-line") } : {},
859
+ ...typeof action.hint === "string" ? { hint: sanitizeTerminalDisplayText(action.hint, "single-line") } : {}
860
+ };
861
+ }
862
+ function sanitizeSearchPromptItem(item) {
863
+ const normalizedItem = {
864
+ ...item,
865
+ label: sanitizeTerminalDisplayText(item.label, "single-line"),
866
+ ...typeof item.detail === "string" ? { detail: sanitizeTerminalDisplayText(item.detail, "single-line") } : {},
867
+ ...typeof item.hint === "string" ? { hint: sanitizeTerminalDisplayText(item.hint, "single-line") } : {},
868
+ ...typeof item.searchText === "string" ? { searchText: sanitizeTerminalDisplayText(item.searchText, "multi-line") } : {}
869
+ };
870
+ return {
871
+ ...normalizedItem,
872
+ searchKey: buildSearchKey(normalizedItem)
873
+ };
874
+ }
875
+ function sanitizeSearchPromptSummaryCard(card) {
876
+ return {
877
+ ...card,
878
+ title: sanitizeTerminalDisplayText(card.title, "single-line"),
879
+ rows: card.rows.map((row) => ({
880
+ label: sanitizeTerminalDisplayText(row.label, "single-line"),
881
+ value: sanitizeTerminalDisplayText(row.value, "single-line")
882
+ }))
883
+ };
884
+ }
885
+ function getSearchPromptViewModel(state) {
886
+ const filteredIndexes = getFilteredItemIndexes(state);
887
+ const footerActions = resolveSearchPromptFooterActions(state);
888
+ const rows = buildSearchPromptRows({
889
+ allowBack: state.allowBack,
890
+ filteredIndexes,
891
+ footerActions,
892
+ workbench: state.workbench
893
+ });
894
+ const activeRow = rows[state.activeRowIndex] ?? null;
895
+ const activeItemFilteredIndex = resolveSearchPromptActiveItemFilteredIndex({
896
+ activeRow,
897
+ filteredIndexes,
898
+ workbench: state.workbench
899
+ });
900
+ const visibleWindow = resolveAnchoredVisibleWindow({
901
+ itemCount: filteredIndexes.length,
902
+ anchorIndex: activeItemFilteredIndex,
903
+ maxVisible: state.maxVisible
904
+ });
905
+ return {
906
+ filteredIndexes,
907
+ rows,
908
+ activeRow,
909
+ activeItemFilteredIndex,
910
+ visibleRows: buildSearchPromptVisibleRows({
911
+ activeRow,
912
+ filteredIndexes,
913
+ footerActions,
914
+ state,
915
+ visibleWindow
916
+ }),
917
+ hiddenBefore: visibleWindow.start,
918
+ hiddenAfter: Math.max(0, filteredIndexes.length - visibleWindow.end)
919
+ };
920
+ }
921
+ function resolveSearchPromptActiveItemFilteredIndex(input) {
922
+ if (input.activeRow?.kind === "item") return input.activeRow.filteredIndex;
923
+ if (input.filteredIndexes.length === 0) return null;
924
+ return input.filteredIndexes.length - 1;
925
+ }
926
+ function updateSearchPromptState(state, action) {
927
+ switch (action.type) {
928
+ case "move": return moveSearchPromptCursor(state, action.delta);
929
+ case "query-append":
930
+ if (action.text.length === 0) return state;
931
+ return syncSearchPromptState({
932
+ ...state,
933
+ query: `${state.query}${action.text}`
934
+ });
935
+ case "query-backspace":
936
+ if (state.query.length === 0) return state;
937
+ return syncSearchPromptState({
938
+ ...state,
939
+ query: state.query.slice(0, -1)
940
+ });
941
+ case "query-clear":
942
+ if (state.query.length === 0) return state;
943
+ return syncSearchPromptState({
944
+ ...state,
945
+ query: ""
946
+ });
947
+ case "set-query": return syncSearchPromptState({
948
+ ...state,
949
+ query: action.query
950
+ });
951
+ case "toggle-active": return toggleSearchPromptActiveSelection(state);
952
+ case "set-selected": return syncSearchPromptState({
953
+ ...state,
954
+ selectedValues: dedupeValues(action.selectedValues)
955
+ });
956
+ default: return state;
957
+ }
958
+ }
959
+ function getSearchPromptSelection(state) {
960
+ const viewModel = getSearchPromptViewModel(state);
961
+ if (viewModel.activeRow?.kind === "back") return state.workbench ? {
962
+ status: "back",
963
+ selectedValues: state.selectedValues
964
+ } : { status: "back" };
965
+ if (viewModel.activeRow?.kind === "action") {
966
+ const action = resolveSearchPromptFooterActions(state)[viewModel.activeRow.actionIndex];
967
+ if (!action || action.disabled === true) return null;
968
+ return {
969
+ status: "action",
970
+ action: action.value,
971
+ selectedValues: state.selectedValues
972
+ };
973
+ }
974
+ if (state.readOnly) return null;
975
+ if (state.workbench) return null;
976
+ if (state.mode === "single") {
977
+ const activeItem = getActiveItem(state);
978
+ if (!activeItem || activeItem.disabled === true) return null;
979
+ return {
980
+ status: "submitted",
981
+ value: activeItem.value
982
+ };
983
+ }
984
+ if (state.required && state.selectedValues.length === 0) return null;
985
+ return {
986
+ status: "submitted",
987
+ value: state.selectedValues
988
+ };
989
+ }
990
+ function renderSearchPromptLines(state, status = "active") {
991
+ const lines = [`${status === "cancelled" ? PROMPT_SYMBOL_CANCELLED : PROMPT_SYMBOL_ACTIVE} ${pc.bold(state.message)}`];
992
+ if (status === "submitted") {
993
+ lines.push(`${BAR_SYMBOL} ${pc.dim(buildSubmittedSearchPromptSummary(state) ?? "Done")}`);
994
+ return lines;
995
+ }
996
+ if (status === "cancelled") {
997
+ lines.push(`${BAR_SYMBOL} ${pc.strikethrough(pc.dim("Cancelled"))}`);
998
+ return lines;
999
+ }
1000
+ lines.push(...renderActiveSearchPromptLines(state));
1001
+ lines.push(pc.dim("└"));
1002
+ return lines;
1003
+ }
1004
+ function renderActiveSearchPromptLines(state) {
1005
+ const viewModel = getSearchPromptViewModel(state);
1006
+ const lines = [];
1007
+ if (state.summaryCard) {
1008
+ lines.push(`${BAR_SYMBOL}`);
1009
+ lines.push(...renderSearchPromptSummaryCardLines(state.summaryCard));
1010
+ lines.push(`${BAR_SYMBOL}`);
1011
+ }
1012
+ if (state.noticeLines.length > 0) {
1013
+ lines.push(`${BAR_SYMBOL}`);
1014
+ lines.push(...renderSearchPromptNoticeCardLines(state.noticeLines));
1015
+ lines.push(`${BAR_SYMBOL}`);
1016
+ }
1017
+ lines.push(`${BAR_SYMBOL} ${pc.dim("Search:")} ${state.query}${pc.inverse(" ")}`, renderSearchPromptHintLine({
1018
+ mode: state.mode,
1019
+ allowBack: state.allowBack,
1020
+ readOnly: state.readOnly,
1021
+ workbench: state.workbench
1022
+ }));
1023
+ lines.push(`${BAR_SYMBOL}`);
1024
+ lines.push(...renderSearchPromptBodyLines(state, viewModel));
1025
+ lines.push(...renderSearchPromptOverflowLines(viewModel));
1026
+ if (state.workbench) {
1027
+ lines.push(...renderSearchPromptSelectionSummaryLines(state));
1028
+ lines.push(...renderSearchPromptWorkbenchMenuLines(viewModel));
1029
+ } else {
1030
+ lines.push(...renderSearchPromptActionLines(viewModel));
1031
+ lines.push(...renderSearchPromptBackLines(state, viewModel));
1032
+ lines.push(...renderSearchPromptSelectionSummaryLines(state));
1033
+ }
1034
+ return lines;
1035
+ }
1036
+ function renderSearchPromptBodyLines(state, viewModel) {
1037
+ const itemRows = viewModel.visibleRows.filter((row) => row.kind === "item");
1038
+ if (itemRows.length === 0) return [`${BAR_SYMBOL} ${pc.dim(state.emptyLabel)}`];
1039
+ return itemRows.flatMap((row) => {
1040
+ const lines = [renderSearchPromptItemLine(row, {
1041
+ mode: state.mode,
1042
+ readOnly: state.readOnly
1043
+ })];
1044
+ if (row.item.detail) lines.push(`${BAR_SYMBOL} ${pc.dim(row.item.detail)}`);
1045
+ return lines;
1046
+ });
1047
+ }
1048
+ function renderSearchPromptOverflowLines(viewModel) {
1049
+ if (viewModel.hiddenBefore === 0 && viewModel.hiddenAfter === 0) return [];
1050
+ const hiddenLabels = [];
1051
+ if (viewModel.hiddenBefore > 0) hiddenLabels.push(`↑ ${String(viewModel.hiddenBefore)} more`);
1052
+ if (viewModel.hiddenAfter > 0) hiddenLabels.push(`↓ ${String(viewModel.hiddenAfter)} more`);
1053
+ return [`${BAR_SYMBOL} ${pc.dim(hiddenLabels.join(" "))}`];
1054
+ }
1055
+ function renderSearchPromptActionLines(viewModel) {
1056
+ const actionRows = viewModel.visibleRows.filter((row) => row.kind === "action");
1057
+ if (actionRows.length === 0) return [];
1058
+ return renderSearchPromptActionBlockLines(actionRows);
1059
+ }
1060
+ function renderSearchPromptWorkbenchMenuLines(viewModel) {
1061
+ const actionRows = viewModel.visibleRows.filter((row) => row.kind === "action");
1062
+ const backRow = viewModel.visibleRows.find((row) => row.kind === "back");
1063
+ if (actionRows.length === 0 && !backRow) return [];
1064
+ const lines = renderSearchPromptActionBlockLines(actionRows);
1065
+ if (backRow) {
1066
+ if (actionRows.length > 0) lines.push(`${BAR_SYMBOL}`);
1067
+ lines.push(renderSearchPromptBackLine(backRow, { workbench: true }));
1068
+ }
1069
+ return lines;
1070
+ }
1071
+ function renderSearchPromptBackLines(state, viewModel) {
1072
+ if (!state.allowBack || state.workbench) return [];
1073
+ const backRow = viewModel.visibleRows.find((row) => row.kind === "back");
1074
+ if (!backRow) return [];
1075
+ return [`${BAR_SYMBOL}`, renderSearchPromptBackLine(backRow)];
1076
+ }
1077
+ function renderSearchPromptSelectionSummaryLines(state) {
1078
+ if (state.mode !== "multiple" || state.readOnly) return [];
1079
+ const lines = [`${BAR_SYMBOL}`, `${BAR_SYMBOL} ${pc.green("Selected:")} ${buildMultiselectSummary(state)}`];
1080
+ if (!state.workbench && (!state.required || state.selectedValues.length > 0)) lines.push(`${BAR_SYMBOL} ${pc.dim("Press ")}${pc.green("`Enter`")}${pc.dim(" to continue.")}`);
1081
+ return lines;
1082
+ }
1083
+ function handleSearchPromptKeypress(input) {
1084
+ const { state, str, key } = input;
1085
+ if (!key) return { type: "ignore" };
1086
+ if (key.ctrl === true && key.name === "c") return {
1087
+ type: "result",
1088
+ result: buildSearchPromptDismissResult({
1089
+ state,
1090
+ status: "cancelled"
1091
+ }),
1092
+ status: "cancelled"
1093
+ };
1094
+ if (key.name === "escape") return handleSearchPromptEscapeKeypress(state);
1095
+ if (isReturnKeypress({
1096
+ key,
1097
+ str
1098
+ })) return handleSearchPromptReturnKeypress(state);
1099
+ const moveDelta = resolveSearchPromptMoveDelta(key.name);
1100
+ if (moveDelta !== null) return {
1101
+ type: "state",
1102
+ state: updateSearchPromptState(state, {
1103
+ type: "move",
1104
+ delta: moveDelta
1105
+ })
1106
+ };
1107
+ if (key.name === "space" && state.mode === "multiple") return {
1108
+ type: "state",
1109
+ state: updateSearchPromptState(state, { type: "toggle-active" })
1110
+ };
1111
+ if (key.name === "backspace") return {
1112
+ type: "state",
1113
+ state: updateSearchPromptState(state, { type: "query-backspace" })
1114
+ };
1115
+ if (key.ctrl === true && key.name === "u") return {
1116
+ type: "state",
1117
+ state: updateSearchPromptState(state, { type: "query-clear" })
1118
+ };
1119
+ const printableInput = normalizePrintableSearchInput(str, state.mode);
1120
+ if (printableInput !== null) return {
1121
+ type: "state",
1122
+ state: updateSearchPromptState(state, {
1123
+ type: "query-append",
1124
+ text: printableInput
1125
+ })
1126
+ };
1127
+ return { type: "ignore" };
1128
+ }
1129
+ function buildSearchPromptDismissResult(input) {
1130
+ if (input.state.workbench) {
1131
+ if (input.status === "back") return {
1132
+ status: "back",
1133
+ selectedValues: input.state.selectedValues
1134
+ };
1135
+ return {
1136
+ status: "cancelled",
1137
+ selectedValues: input.state.selectedValues
1138
+ };
1139
+ }
1140
+ if (input.status === "back") return { status: "back" };
1141
+ return { status: "cancelled" };
1142
+ }
1143
+ function handleSearchPromptEscapeKeypress(state) {
1144
+ return {
1145
+ type: "result",
1146
+ result: buildSearchPromptDismissResult({
1147
+ state,
1148
+ status: state.allowBack ? "back" : "cancelled"
1149
+ }),
1150
+ status: state.allowBack ? "submitted" : "cancelled",
1151
+ state: state.allowBack ? focusSearchPromptBackRow(state) : state
1152
+ };
1153
+ }
1154
+ function handleSearchPromptReturnKeypress(state) {
1155
+ if (state.workbench && getSearchPromptViewModel(state).activeRow?.kind === "item") return {
1156
+ type: "state",
1157
+ state: updateSearchPromptState(state, { type: "toggle-active" })
1158
+ };
1159
+ const result = getSearchPromptSelection(state);
1160
+ if (!result) return { type: "ignore" };
1161
+ return {
1162
+ type: "result",
1163
+ result,
1164
+ status: "submitted"
1165
+ };
1166
+ }
1167
+ function promptSearchSelect(input, streams) {
1168
+ return runInteractiveSearchPrompt(createSearchPromptState({
1169
+ ...input,
1170
+ mode: "single"
1171
+ }), streams).then((result) => result);
1172
+ }
1173
+ function promptSearchMultiselect(input, streams) {
1174
+ return runInteractiveSearchPrompt(createSearchPromptState({
1175
+ ...input,
1176
+ mode: "multiple"
1177
+ }), streams).then((result) => result);
1178
+ }
1179
+ function promptSearchMultiselectWorkbench(input) {
1180
+ return runInteractiveSearchPrompt(createSearchPromptState({
1181
+ ...input,
1182
+ mode: "multiple",
1183
+ workbench: true
1184
+ })).then((result) => result);
1185
+ }
1186
+ function buildSearchKey(item) {
1187
+ return [
1188
+ item.searchText,
1189
+ item.label,
1190
+ item.detail,
1191
+ item.hint,
1192
+ String(item.value)
1193
+ ].filter((value) => typeof value === "string").join("\n").trim().toLowerCase();
1194
+ }
1195
+ function getFilteredItemIndexes(state) {
1196
+ const query = state.query.trim().toLowerCase();
1197
+ if (query.length === 0) return state.items.map((_item, index) => index);
1198
+ return state.items.flatMap((item, index) => item.searchKey.includes(query) ? [index] : []);
1199
+ }
1200
+ function syncSearchPromptState(state) {
1201
+ const viewModel = getSearchPromptViewModelInternal(state);
1202
+ const activeRowIndex = viewModel.rows.length === 0 ? 0 : clampNumber(state.activeRowIndex, 0, viewModel.rows.length - 1);
1203
+ return {
1204
+ ...state,
1205
+ activeRowIndex
1206
+ };
1207
+ }
1208
+ function getSearchPromptViewModelInternal(state) {
1209
+ const filteredIndexes = getFilteredItemIndexes(state);
1210
+ const footerActions = resolveSearchPromptFooterActions(state);
1211
+ return {
1212
+ filteredIndexes,
1213
+ rows: buildSearchPromptRows({
1214
+ allowBack: state.allowBack,
1215
+ filteredIndexes,
1216
+ footerActions,
1217
+ workbench: state.workbench
1218
+ })
1219
+ };
1220
+ }
1221
+ function buildSearchPromptRows(input) {
1222
+ const itemRows = input.filteredIndexes.map((itemIndex, filteredIndex) => ({
1223
+ kind: "item",
1224
+ filteredIndex,
1225
+ itemIndex
1226
+ }));
1227
+ const actionRows = input.footerActions.map((_action, actionIndex) => ({
1228
+ kind: "action",
1229
+ actionIndex
1230
+ }));
1231
+ const rows = input.workbench ? [...itemRows, ...actionRows] : [...itemRows, ...actionRows];
1232
+ if (input.allowBack) rows.push({ kind: "back" });
1233
+ return rows;
1234
+ }
1235
+ function buildSearchPromptVisibleRows(input) {
1236
+ const itemRows = input.filteredIndexes.slice(input.visibleWindow.start, input.visibleWindow.end).map((itemIndex, offset) => {
1237
+ const filteredIndex = input.visibleWindow.start + offset;
1238
+ const item = input.state.items[itemIndex];
1239
+ if (!item) throw new Error("search prompt item is missing");
1240
+ return {
1241
+ kind: "item",
1242
+ filteredIndex,
1243
+ itemIndex,
1244
+ item,
1245
+ active: input.activeRow?.kind === "item" && input.activeRow.filteredIndex === filteredIndex,
1246
+ selected: isItemSelected(input.state, item.value)
1247
+ };
1248
+ });
1249
+ const actionRows = input.footerActions.map((action, actionIndex) => ({
1250
+ kind: "action",
1251
+ action,
1252
+ actionIndex,
1253
+ active: input.activeRow?.kind === "action" && input.activeRow.actionIndex === actionIndex
1254
+ }));
1255
+ const rows = [...itemRows, ...actionRows];
1256
+ if (input.state.allowBack) rows.push({
1257
+ kind: "back",
1258
+ active: input.activeRow?.kind === "back",
1259
+ label: input.state.backLabel
1260
+ });
1261
+ return rows;
1262
+ }
1263
+ function resolveInitialSearchPromptActiveRowIndex(state) {
1264
+ if (!state.workbench) return 0;
1265
+ if (getFilteredItemIndexes(state).length > 0) return 0;
1266
+ return 0;
1267
+ }
1268
+ function moveSearchPromptCursor(state, delta) {
1269
+ const viewModel = getSearchPromptViewModelInternal(state);
1270
+ if (viewModel.rows.length === 0 || delta === 0) return state;
1271
+ return {
1272
+ ...state,
1273
+ activeRowIndex: wrapNumber(state.activeRowIndex + delta, 0, viewModel.rows.length - 1)
1274
+ };
1275
+ }
1276
+ function toggleSearchPromptActiveSelection(state) {
1277
+ if (state.mode !== "multiple") return state;
1278
+ const activeItem = getActiveItem(state);
1279
+ if (!activeItem || activeItem.disabled === true) return state;
1280
+ const selectedValues = new Set(state.selectedValues);
1281
+ if (selectedValues.has(activeItem.value)) selectedValues.delete(activeItem.value);
1282
+ else selectedValues.add(activeItem.value);
1283
+ return {
1284
+ ...state,
1285
+ selectedValues: Array.from(selectedValues)
1286
+ };
1287
+ }
1288
+ function getActiveItem(state) {
1289
+ const viewModel = getSearchPromptViewModel(state);
1290
+ if (viewModel.activeRow?.kind !== "item") return null;
1291
+ return state.items[viewModel.activeRow.itemIndex] ?? null;
1292
+ }
1293
+ function isItemSelected(state, value) {
1294
+ return state.selectedValues.includes(value);
1295
+ }
1296
+ function renderSearchPromptItemLine(row, input) {
1297
+ const prefix = row.active ? CURSOR_SYMBOL : " ";
1298
+ if (input.readOnly) {
1299
+ const hint = row.item.hint ? pc.dim(` (${row.item.hint})`) : "";
1300
+ return `${BAR_SYMBOL} ${prefix} ${row.item.label}${hint}`;
1301
+ }
1302
+ const marker = resolveSearchPromptItemMarker({
1303
+ mode: input.mode,
1304
+ active: row.active,
1305
+ selected: row.selected
1306
+ });
1307
+ const label = row.item.label;
1308
+ const hint = row.item.hint ? pc.dim(` (${row.item.hint})`) : "";
1309
+ const renderedLabel = row.item.disabled === true ? pc.dim(label) : label;
1310
+ return `${BAR_SYMBOL} ${prefix} ${marker} ${renderedLabel}${hint}`;
1311
+ }
1312
+ function renderSearchPromptActionLine(row) {
1313
+ const prefix = row.active ? CURSOR_SYMBOL : " ";
1314
+ const icon = typeof row.action.icon === "string" && row.action.icon.length > 0 ? `${row.action.disabled === true || row.action.icon === "·" ? pc.dim(row.action.icon) : pc.cyan(row.action.icon)} ` : "";
1315
+ let label = pc.bold(row.action.label);
1316
+ if (row.action.accent === true) label = pc.green(row.action.label);
1317
+ if (row.action.disabled === true) label = pc.dim(row.action.label);
1318
+ const hint = row.action.hint ? pc.dim(` (${row.action.hint})`) : "";
1319
+ return `${BAR_SYMBOL} ${prefix} ${icon}${label}${hint}`;
1320
+ }
1321
+ function renderSearchPromptActionBlockLines(actionRows) {
1322
+ const lines = [`${BAR_SYMBOL}`];
1323
+ for (const row of actionRows) {
1324
+ if (row.action.separatorBefore === true) lines.push(`${BAR_SYMBOL}`);
1325
+ lines.push(renderSearchPromptActionLine(row));
1326
+ }
1327
+ return lines;
1328
+ }
1329
+ function resolveSearchPromptItemMarker(input) {
1330
+ if (input.mode === "multiple") return input.selected ? MULTI_SELECTED_SYMBOL : MULTI_UNSELECTED_SYMBOL;
1331
+ return input.active ? SINGLE_SELECTED_SYMBOL : SINGLE_UNSELECTED_SYMBOL;
1332
+ }
1333
+ function renderSearchPromptBackLine(row, input) {
1334
+ const prefix = row.active ? CURSOR_SYMBOL : " ";
1335
+ if (input?.workbench === true) return `${BAR_SYMBOL} ${prefix} ${row.label}`;
1336
+ return `${BAR_SYMBOL} ${prefix} ${row.label}`;
1337
+ }
1338
+ function buildSubmittedSearchPromptSummary(state) {
1339
+ const viewModel = getSearchPromptViewModel(state);
1340
+ if (viewModel.activeRow?.kind === "back") return state.backLabel;
1341
+ if (viewModel.activeRow?.kind === "action") return resolveSearchPromptFooterActions(state)[viewModel.activeRow.actionIndex]?.label ?? null;
1342
+ if (state.mode === "single") return getActiveItem(state)?.label ?? null;
1343
+ return buildMultiselectSummary(state);
1344
+ }
1345
+ function buildMultiselectSummary(state) {
1346
+ const labels = state.items.filter((item) => isItemSelected(state, item.value)).map((item) => item.label);
1347
+ if (labels.length === 0) return "(none)";
1348
+ if (labels.length <= 3) return labels.join(", ");
1349
+ return `${labels.slice(0, 3).join(", ")} +${String(labels.length - 3)} more`;
1350
+ }
1351
+ function focusSearchPromptBackRow(state) {
1352
+ const backRowIndex = getSearchPromptViewModel(state).rows.findIndex((row) => row.kind === "back");
1353
+ if (backRowIndex < 0) return state;
1354
+ return {
1355
+ ...state,
1356
+ activeRowIndex: backRowIndex
1357
+ };
1358
+ }
1359
+ function resolveSearchPromptMoveDelta(name) {
1360
+ if (name === "up") return -1;
1361
+ if (name === "down") return 1;
1362
+ return null;
1363
+ }
1364
+ function renderSearchPromptHintLine(input) {
1365
+ if (input.readOnly) return `${BAR_SYMBOL} ${pc.dim(input.allowBack ? "↑↓ browse, type to filter, Esc back" : "↑↓ browse, type to filter")}`;
1366
+ if (input.workbench) return `${BAR_SYMBOL} ${pc.dim("↑↓ move, Press ")}${pc.green("`Space`")}${pc.dim(" or ")}${pc.green("`Enter`")}${pc.dim(input.allowBack ? " to select, choose an action to continue, Esc back" : " to select, choose an action to continue")}`;
1367
+ if (input.mode === "multiple") return `${BAR_SYMBOL} ${pc.dim("↑↓ move, Press ")}${pc.green("`Space`")}${pc.dim(" to select, Press ")}${pc.green("`Enter`")}${pc.dim(input.allowBack ? " to confirm, Esc back" : " to confirm")}`;
1368
+ return `${BAR_SYMBOL} ${pc.dim("↑↓ move, Press ")}${pc.green("`Enter`")}${pc.dim(input.allowBack ? " to select, Esc back" : " to select")}`;
1369
+ }
1370
+ function renderSearchPromptSummaryCardLines(card) {
1371
+ return buildTextInfoCardLines({
1372
+ ...card,
1373
+ sanitize: true
1374
+ }).map((line) => `${BAR_SYMBOL} ${line}`);
1375
+ }
1376
+ function renderSearchPromptNoticeCardLines(noticeLines) {
1377
+ if (noticeLines.length === 0) return [];
1378
+ return buildTextInfoCardLines({
1379
+ title: pc.yellow("⚠️ Notification"),
1380
+ rows: noticeLines.map((line) => ({
1381
+ label: "",
1382
+ value: pc.yellow(`⚠️ ${line}`)
1383
+ })),
1384
+ compact: true,
1385
+ minContentWidth: 48
1386
+ }).map((line) => `${BAR_SYMBOL} ${line}`);
1387
+ }
1388
+ function normalizeMaxVisible(value) {
1389
+ const normalized = typeof value === "number" ? Math.trunc(value) : NaN;
1390
+ return Number.isFinite(normalized) && normalized > 0 ? normalized : DEFAULT_MAX_VISIBLE;
1391
+ }
1392
+ function wrapNumber(value, min, max) {
1393
+ if (value < min) return max;
1394
+ if (value > max) return min;
1395
+ return value;
1396
+ }
1397
+ function dedupeValues(values) {
1398
+ return Array.from(new Set(values));
1399
+ }
1400
+ function runInteractiveSearchPrompt(initialState, streams) {
1401
+ return runInteractivePrompt({
1402
+ initialState,
1403
+ fallbackResult: buildSearchPromptFallbackResult(initialState),
1404
+ renderLines: (state, status) => renderSearchPromptLines(state, status),
1405
+ handleKeypress: ({ state, str, key }) => handleSearchPromptKeypress({
1406
+ state,
1407
+ str,
1408
+ key
1409
+ })
1410
+ }, streams);
1411
+ }
1412
+ function buildSearchPromptFallbackResult(state) {
1413
+ return buildSearchPromptDismissResult({
1414
+ state,
1415
+ status: state.allowBack ? "back" : "cancelled"
1416
+ });
1417
+ }
1418
+ function resolveSearchPromptFooterActions(state) {
1419
+ if (state.footerActionsResolver === null) return state.footerActions;
1420
+ return state.footerActionsResolver({ selectedValues: state.selectedValues }).map(sanitizeSearchPromptFooterAction);
1421
+ }
1422
+ function isPrintableSearchInput(value, mode) {
1423
+ if (typeof value !== "string" || value.length === 0) return false;
1424
+ if (mode === "multiple" && value === " ") return false;
1425
+ return PRINTABLE_SEARCH_INPUT_REGEX.test(value);
1426
+ }
1427
+ function normalizePrintableSearchInput(value, mode) {
1428
+ return typeof value === "string" && isPrintableSearchInput(value, mode) ? value : null;
1429
+ }
1430
+ //#endregion
1431
+ //#region src/ui/transport-mode-display.ts
1432
+ const DISPLAY_NOT_SET_UP = "Not set up";
1433
+ function formatTransportModeDisplay(value) {
1434
+ const normalized = String(value ?? "").trim();
1435
+ if (!normalized || normalized === "not configured") return DISPLAY_NOT_SET_UP;
1436
+ if (normalized === "local-cli") return "Local CLI";
1437
+ if (normalized === "codex-app-server") return "Codex App Server";
1438
+ if (normalized === "acp") return "Agent Client Protocol (ACP)";
1439
+ return normalized;
1440
+ }
1441
+ //#endregion
1442
+ //#region src/commands/agents-text.ts
1443
+ const formatAgentsCopy = (text) => formatOpenMeldCliTextBlock(text);
1444
+ function formatAgentMention(agentId) {
1445
+ return pc.bold(pc.cyan(`@${agentId}`));
1446
+ }
1447
+ function formatAgentsToneText(tone, value) {
1448
+ if (tone === "success") return pc.green(value);
1449
+ if (tone === "warning") return pc.yellow(value);
1450
+ return pc.cyan(value);
1451
+ }
1452
+ function formatStatusTextForHuman(value) {
1453
+ if (value.length === 0) return value;
1454
+ const normalized = value.replaceAll("_", " ");
1455
+ return normalized[0]?.toUpperCase() + normalized.slice(1);
1456
+ }
1457
+ function normalizeOptionalString(value) {
1458
+ if (typeof value !== "string") return null;
1459
+ const trimmed = value.trim();
1460
+ return trimmed.length > 0 ? trimmed : null;
1461
+ }
1462
+ //#endregion
1463
+ //#region src/commands/agents-cards.ts
1464
+ const AGENTS_CARD_MIN_CONTENT_WIDTH = 34;
1465
+ function renderAgentsInfoCard(input) {
1466
+ renderInfoCard({
1467
+ presenter: input.presenter,
1468
+ title: input.title,
1469
+ codePrefix: input.codePrefix,
1470
+ rows: input.rows,
1471
+ minContentWidth: input.minContentWidth ?? AGENTS_CARD_MIN_CONTENT_WIDTH,
1472
+ horizontalPadding: 1,
1473
+ compact: true
1474
+ });
1475
+ }
1476
+ function formatAgentListItem(input) {
1477
+ const parts = [
1478
+ formatAgentMention(input.agentId),
1479
+ pc.dim("·"),
1480
+ input.displayName
1481
+ ];
1482
+ if (input.detail) parts.push(pc.dim(input.detail));
1483
+ return parts.join(" ");
1484
+ }
1485
+ function buildReadyAgentCardRow(agentId, displayName) {
1486
+ return {
1487
+ label: "",
1488
+ value: formatAgentListItem({
1489
+ agentId,
1490
+ displayName
1491
+ })
1492
+ };
1493
+ }
1494
+ function buildStatusAgentCardRow(input) {
1495
+ return {
1496
+ label: "",
1497
+ value: formatAgentListItem({
1498
+ agentId: input.agentId,
1499
+ displayName: input.displayName,
1500
+ detail: input.detail
1501
+ })
1502
+ };
1503
+ }
1504
+ function buildTurnedOffAgentCardRow(agentId, displayName) {
1505
+ return {
1506
+ label: "",
1507
+ value: formatAgentListItem({
1508
+ agentId,
1509
+ displayName
1510
+ })
1511
+ };
1512
+ }
1513
+ function buildReadyCountValue(count, noun) {
1514
+ return `${formatAgentsToneText("success", pc.bold(String(count)))} ${pc.dim(noun)}`;
1515
+ }
1516
+ function buildNotReadyCountValue(count, noun) {
1517
+ return `${formatAgentsToneText("warning", pc.bold(String(count)))} ${pc.dim(noun)}`;
1518
+ }
1519
+ //#endregion
1520
+ //#region src/commands/agents-descriptions.ts
1521
+ function normalizeDescriptionLines(description) {
1522
+ return (Array.isArray(description) ? description : [description]).map((line) => line.trim()).filter((line) => line.length > 0);
1523
+ }
1524
+ function buildInteractiveMenuMessage(input) {
1525
+ return [
1526
+ input.title,
1527
+ ...normalizeDescriptionLines(input.description).map((line) => pc.dim(formatAgentsCopy(line))),
1528
+ ""
1529
+ ].join("\n");
1530
+ }
1531
+ function emitInteractiveDescription(description) {
1532
+ const lines = normalizeDescriptionLines(description);
1533
+ if (lines.length === 0) return;
1534
+ process.stdout.write(`${lines.map((line) => pc.dim(formatAgentsCopy(line))).join("\n")}\n`);
1535
+ }
1536
+ function emitHumanSectionDescription(presenter, input) {
1537
+ if (presenter.view !== "human") return;
1538
+ const lines = normalizeDescriptionLines(input.description);
1539
+ for (const [index, line] of lines.entries()) presenter.line({
1540
+ code: `${input.codePrefix}.description.${String(index)}`,
1541
+ text: pc.dim(formatAgentsCopy(line))
1542
+ });
1543
+ }
1544
+ function emitHumanSectionHeader(presenter, input) {
1545
+ if (presenter.view !== "human") return;
1546
+ presenter.line({
1547
+ code: `${input.codePrefix}.spacer`,
1548
+ text: ""
1549
+ });
1550
+ presenter.line({
1551
+ code: `${input.codePrefix}.title`,
1552
+ text: input.title
1553
+ });
1554
+ }
1555
+ //#endregion
1556
+ //#region src/commands/agents-summary-cards.ts
1557
+ const AGENTS_ENABLE_RESULT_DESCRIPTION = "These agents can now reply when someone mentions them in a Space from this computer.";
1558
+ const AGENTS_DISABLE_RESULT_DESCRIPTION = "These agents will stop replying to new mentions in Spaces.";
1559
+ function emitEnableSummaryCardsForHuman(input) {
1560
+ emitHumanSectionDescription(input.presenter, {
1561
+ codePrefix: "agents.enable",
1562
+ description: AGENTS_ENABLE_RESULT_DESCRIPTION
1563
+ });
1564
+ renderAgentsInfoCard({
1565
+ presenter: input.presenter,
1566
+ codePrefix: "agents.enable.summary.card",
1567
+ title: "Agents Turned On",
1568
+ rows: buildEnableSummaryRows(input.summary),
1569
+ minContentWidth: 38
1570
+ });
1571
+ const detailCards = buildEnableDetailCards(input.summary, input.targetStateById);
1572
+ for (const card of detailCards) {
1573
+ input.presenter.line({
1574
+ code: `agents.enable.${card.codeSuffix}.spacer`,
1575
+ text: ""
1576
+ });
1577
+ renderAgentsInfoCard({
1578
+ presenter: input.presenter,
1579
+ codePrefix: `agents.enable.${card.codeSuffix}.card`,
1580
+ title: card.title,
1581
+ rows: card.rows,
1582
+ minContentWidth: 30
1583
+ });
1584
+ }
1585
+ }
1586
+ function buildEnableSummaryRows(summary) {
1587
+ const rows = [{
1588
+ label: "Status",
1589
+ value: buildEnableSummaryStatus(summary)
1590
+ }];
1591
+ if (summary.registered.length > 0) rows.push({
1592
+ label: "On now",
1593
+ value: String(summary.registered.length)
1594
+ });
1595
+ if (summary.reenabled.length > 0) rows.push({
1596
+ label: "Back on",
1597
+ value: String(summary.reenabled.length)
1598
+ });
1599
+ if (summary.skipped.length > 0) rows.push({
1600
+ label: "Already on",
1601
+ value: String(summary.skipped.length)
1602
+ });
1603
+ if (summary.errors.length > 0) rows.push({
1604
+ label: "Needs attention",
1605
+ value: String(summary.errors.length)
1606
+ });
1607
+ return rows;
1608
+ }
1609
+ function buildEnableSummaryStatus(summary) {
1610
+ const readyCount = summary.registered.length + summary.reenabled.length;
1611
+ const reviewCount = summary.skipped.length + summary.errors.length;
1612
+ if (readyCount > 0 && reviewCount === 0) {
1613
+ if (summary.allMode) return readyCount === 1 ? `1 detected agent is ${formatAgentsToneText("success", "available now")}` : `All detected available agents are ${formatAgentsToneText("success", "on now")}`;
1614
+ return readyCount === 1 ? `1 selected agent is ${formatAgentsToneText("success", "available now")}` : `${readyCount} selected agents are ${formatAgentsToneText("success", "available now")}`;
1615
+ }
1616
+ if (readyCount > 0) return readyCount === 1 ? `1 agent is ${formatAgentsToneText("success", "available now")} ${pc.dim("· review the rest below")}` : `${readyCount} agents are ${formatAgentsToneText("success", "available now")} ${pc.dim("· review the rest below")}`;
1617
+ if (summary.skipped.length > 0 && summary.errors.length === 0) return formatAgentsToneText("info", "Already on");
1618
+ return formatAgentsToneText("warning", "OpenMeld could not turn these on");
1619
+ }
1620
+ function buildEnableDetailCards(summary, targetStateById) {
1621
+ const cards = [];
1622
+ if (summary.registered.length > 0) cards.push({
1623
+ codeSuffix: "enabled_now",
1624
+ title: `Turned On Now (${summary.registered.length})`,
1625
+ rows: summary.registered.map((agentId) => buildReadyAgentCardRow(agentId, resolveEnableResultDisplayName(agentId, targetStateById)))
1626
+ });
1627
+ if (summary.reenabled.length > 0) cards.push({
1628
+ codeSuffix: "reenabled",
1629
+ title: `Turned Back On (${summary.reenabled.length})`,
1630
+ rows: summary.reenabled.map((agentId) => buildReadyAgentCardRow(agentId, resolveEnableResultDisplayName(agentId, targetStateById)))
1631
+ });
1632
+ if (summary.skipped.length > 0) cards.push({
1633
+ codeSuffix: "already_enabled",
1634
+ title: `Already On (${summary.skipped.length})`,
1635
+ rows: summary.skipped.map((item) => ({
1636
+ label: resolveEnableResultDisplayName(item.agentId, targetStateById),
1637
+ value: formatEnableSkippedReasonForHuman(item.reason)
1638
+ }))
1639
+ });
1640
+ if (summary.errors.length > 0) cards.push({
1641
+ codeSuffix: "needs_attention",
1642
+ title: `Needs Attention (${summary.errors.length})`,
1643
+ rows: summary.errors.map((item) => ({
1644
+ label: resolveEnableResultDisplayName(item.agentId, targetStateById),
1645
+ value: formatEnableErrorReasonForHuman(item)
1646
+ }))
1647
+ });
1648
+ return cards;
1649
+ }
1650
+ function resolveEnableResultDisplayName(agentId, targetStateById) {
1651
+ return targetStateById.get(agentId)?.displayName ?? agentId;
1652
+ }
1653
+ function formatEnableSkippedReasonForHuman(reason) {
1654
+ if (reason === "already_enabled") return "Already on in OpenMeld";
1655
+ return formatStatusTextForHuman(reason);
1656
+ }
1657
+ function formatEnableErrorReasonForHuman(input) {
1658
+ if (input.code === "controller.launch.choice_required" && input.installChoices && input.installChoices.length > 0) {
1659
+ const choiceSummary = input.installChoices.map((installChoice) => `${installChoice.installId} (${installChoice.label} · ${installChoice.detail})`).join("; ");
1660
+ const nextStepSummary = (input.nextSteps ?? []).join(" or ");
1661
+ return [
1662
+ input.summary ?? "Needs your choice on this Mac before OpenMeld can continue.",
1663
+ `Choices: ${choiceSummary}`,
1664
+ nextStepSummary ? `Next: ${nextStepSummary}` : null
1665
+ ].filter((line) => Boolean(line)).join(" ");
1666
+ }
1667
+ if (input.reason === "not found") return "Not found on this computer";
1668
+ if (input.reason === "will be supported soon") return "Found on this computer, but OpenMeld support is coming soon";
1669
+ if (input.reason === "custom target is not runnable yet") return "Custom targets cannot be enabled yet";
1670
+ if (input.reason === "not selectable in current runtime") return "Not available in this runtime";
1671
+ if (input.reason === "unknown built-in target id") return "Not in the built-in support list";
1672
+ if (input.reason === "needs repair") return "Needs repair on this computer before OpenMeld can start it";
1673
+ if (input.reason === "needs your choice") return "Needs your choice on this Mac before OpenMeld can use it";
1674
+ if (input.code === "agents.config_write_failed") return "OpenMeld could not save this change";
1675
+ return formatStatusTextForHuman(input.reason);
1676
+ }
1677
+ function buildRepairSummaryRows(summary) {
1678
+ const rows = [{
1679
+ label: "Status",
1680
+ value: buildRepairSummaryStatus(summary)
1681
+ }];
1682
+ if (summary.repaired.length > 0) rows.push({
1683
+ label: "Repaired",
1684
+ value: String(summary.repaired.length)
1685
+ });
1686
+ if (summary.skipped.length > 0) rows.push({
1687
+ label: "Already available",
1688
+ value: String(summary.skipped.length)
1689
+ });
1690
+ if (summary.errors.length > 0) rows.push({
1691
+ label: "Needs attention",
1692
+ value: String(summary.errors.length)
1693
+ });
1694
+ return rows;
1695
+ }
1696
+ function buildRepairSummaryStatus(summary) {
1697
+ if (summary.repaired.length > 0 && summary.errors.length === 0) {
1698
+ if (summary.skipped.length === 0) return summary.repaired.length === 1 ? `1 agent is ${formatAgentsToneText("success", "available now")}` : `${String(summary.repaired.length)} agents are ${formatAgentsToneText("success", "available now")}`;
1699
+ return `${String(summary.repaired.length)} agents are ${formatAgentsToneText("success", "available now")} ${pc.dim("· review the rest below")}`;
1700
+ }
1701
+ if (summary.skipped.length > 0 && summary.errors.length === 0) return formatAgentsToneText("info", "Already available");
1702
+ return formatAgentsToneText("warning", "OpenMeld could not repair all selected agents");
1703
+ }
1704
+ function buildRepairDetailCards(summary, targetStateById) {
1705
+ const cards = [];
1706
+ if (summary.repaired.length > 0) cards.push({
1707
+ codeSuffix: "repaired",
1708
+ title: `Available Now (${summary.repaired.length})`,
1709
+ rows: summary.repaired.map((agentId) => buildReadyAgentCardRow(agentId, resolveEnableResultDisplayName(agentId, targetStateById)))
1710
+ });
1711
+ if (summary.skipped.length > 0) cards.push({
1712
+ codeSuffix: "already_ready",
1713
+ title: `Already Available (${summary.skipped.length})`,
1714
+ rows: summary.skipped.map((item) => ({
1715
+ label: resolveEnableResultDisplayName(item.agentId, targetStateById),
1716
+ value: item.reason === "already_ready" ? "Already available on this computer" : formatStatusTextForHuman(item.reason)
1717
+ }))
1718
+ });
1719
+ if (summary.errors.length > 0) cards.push({
1720
+ codeSuffix: "needs_attention",
1721
+ title: `Needs Attention (${summary.errors.length})`,
1722
+ rows: summary.errors.map((item) => ({
1723
+ label: resolveEnableResultDisplayName(item.agentId, targetStateById),
1724
+ value: formatRepairErrorReasonForHuman(item)
1725
+ }))
1726
+ });
1727
+ return cards;
1728
+ }
1729
+ function formatRepairErrorReasonForHuman(input) {
1730
+ if (input.reason === "agent is not registered") return "This agent is not connected in OpenMeld yet";
1731
+ if (input.reason === "custom target is not runnable yet") return "Custom targets cannot be repaired yet";
1732
+ if (input.reason === "unknown built-in target id") return "Not in the built-in support list";
1733
+ return formatEnableErrorReasonForHuman(input);
1734
+ }
1735
+ function emitDisableSummaryCardsForHuman(input) {
1736
+ emitHumanSectionDescription(input.presenter, {
1737
+ codePrefix: "agents.disable",
1738
+ description: AGENTS_DISABLE_RESULT_DESCRIPTION
1739
+ });
1740
+ renderAgentsInfoCard({
1741
+ presenter: input.presenter,
1742
+ codePrefix: "agents.disable.summary.card",
1743
+ title: "Agents Turned Off",
1744
+ rows: buildDisableSummaryRows(input.summary),
1745
+ minContentWidth: 38
1746
+ });
1747
+ const detailCards = buildDisableDetailCards(input.summary, input.displayNameById);
1748
+ for (const card of detailCards) {
1749
+ input.presenter.line({
1750
+ code: `agents.disable.${card.codeSuffix}.spacer`,
1751
+ text: ""
1752
+ });
1753
+ renderAgentsInfoCard({
1754
+ presenter: input.presenter,
1755
+ codePrefix: `agents.disable.${card.codeSuffix}.card`,
1756
+ title: card.title,
1757
+ rows: card.rows,
1758
+ minContentWidth: 30
1759
+ });
1760
+ }
1761
+ }
1762
+ function buildDisableSummaryRows(summary) {
1763
+ const rows = [{
1764
+ label: "Status",
1765
+ value: buildDisableSummaryStatus(summary)
1766
+ }];
1767
+ if (summary.disabled.length > 0) rows.push({
1768
+ label: "Off now",
1769
+ value: String(summary.disabled.length)
1770
+ });
1771
+ if (summary.skipped.length > 0) rows.push({
1772
+ label: "Already off",
1773
+ value: String(summary.skipped.length)
1774
+ });
1775
+ if (summary.errors.length > 0) rows.push({
1776
+ label: "Needs attention",
1777
+ value: String(summary.errors.length)
1778
+ });
1779
+ return rows;
1780
+ }
1781
+ function buildDisableSummaryStatus(summary) {
1782
+ if (summary.disabled.length > 0 && summary.skipped.length === 0) return summary.disabled.length === 1 ? `1 agent ${formatAgentsToneText("warning", "turned off")}` : `${String(summary.disabled.length)} agents ${formatAgentsToneText("warning", "turned off")}`;
1783
+ if (summary.disabled.length > 0) return summary.disabled.length === 1 ? `1 agent ${formatAgentsToneText("warning", "turned off")} ${pc.dim("· review the rest below")}` : `${String(summary.disabled.length)} agents ${formatAgentsToneText("warning", "turned off")} ${pc.dim("· review the rest below")}`;
1784
+ if (summary.skipped.length > 0 && summary.errors.length === 0) return formatAgentsToneText("info", "Already off");
1785
+ return formatAgentsToneText("warning", "OpenMeld could not turn these off");
1786
+ }
1787
+ function buildDisableDetailCards(summary, displayNameById) {
1788
+ const cards = [];
1789
+ if (summary.disabled.length > 0) cards.push({
1790
+ codeSuffix: "turned_off",
1791
+ title: `Turned Off (${summary.disabled.length})`,
1792
+ rows: summary.disabled.map((agentId) => buildTurnedOffAgentCardRow(agentId, displayNameById.get(agentId) ?? agentId))
1793
+ });
1794
+ if (summary.skipped.length > 0) cards.push({
1795
+ codeSuffix: "already_off",
1796
+ title: `Already Off (${summary.skipped.length})`,
1797
+ rows: summary.skipped.map((item) => ({
1798
+ label: displayNameById.get(item.agentId) ?? item.agentId,
1799
+ value: formatDisableSkippedReasonForHuman(item.reason)
1800
+ }))
1801
+ });
1802
+ if (summary.errors.length > 0) cards.push({
1803
+ codeSuffix: "needs_attention",
1804
+ title: `Needs Attention (${summary.errors.length})`,
1805
+ rows: summary.errors.map((item) => ({
1806
+ label: displayNameById.get(item.agentId) ?? item.agentId,
1807
+ value: formatDisableErrorReasonForHuman(item)
1808
+ }))
1809
+ });
1810
+ return cards;
1811
+ }
1812
+ function formatDisableSkippedReasonForHuman(reason) {
1813
+ if (reason === "already_disabled") return "Already off in OpenMeld";
1814
+ return formatStatusTextForHuman(reason);
1815
+ }
1816
+ function formatDisableErrorReasonForHuman(input) {
1817
+ if (input.reason === "agent is not registered") return "Not currently connected to OpenMeld";
1818
+ if (input.code === "agents.config_write_failed") return "OpenMeld could not save this change";
1819
+ return formatStatusTextForHuman(input.reason);
1820
+ }
1821
+ //#endregion
1822
+ //#region src/commands/agents.ts
1823
+ function isBuiltinEnableChoiceCandidate(value) {
1824
+ return value?.targetKind === "builtin";
1825
+ }
1826
+ function hasAgentRegistration(value) {
1827
+ return value?.registration !== null && value?.registration !== void 0;
1828
+ }
1829
+ const AGENTS_MENU_CHOICE = {
1830
+ quickEnable: "quick_enable",
1831
+ manage: "manage",
1832
+ detect: "detect",
1833
+ custom: "custom",
1834
+ exit: "exit"
1835
+ };
1836
+ const AGENTS_MANAGE_AGENT_ACTION_CHOICE = {
1837
+ showDetails: "show_details",
1838
+ chooseInstall: "choose_install",
1839
+ repair: "repair",
1840
+ toggleEnabled: "toggle_enabled",
1841
+ back: "back"
1842
+ };
1843
+ const CUSTOM_AGENTS_COMING_SOON_OPTION_ID = "__custom_agents_coming_soon__";
1844
+ const NO_REGISTERED_AGENTS_MANAGE_MESSAGE = "No agents are set up on this computer yet. Run `openmeld agents detect`, then turn on the ones you want OpenMeld to use.";
1845
+ const NO_ACTIONABLE_AGENTS_ENABLE_MESSAGE = "No more agents are available to enable. Run `openmeld agents detect` to scan this computer again.";
1846
+ const AGENTS_MENU_DESCRIPTION = "Enable agents on this computer so people in a Space can mention them.";
1847
+ const AGENTS_ENABLE_DESCRIPTION = "Choose which new agents OpenMeld should turn on for Space from this computer.";
1848
+ const AGENTS_SELECT_REGISTERED_DESCRIPTION = "Choose an agent on this computer to review or change.";
1849
+ const AGENTS_MENU_COMMANDS = [
1850
+ ...buildBuiltinAgentSelectionExamples({ command: "enable" }),
1851
+ ...buildBuiltinAgentSelectionExamples({ command: "repair" }),
1852
+ "openmeld agents detect",
1853
+ "openmeld agents list",
1854
+ "openmeld agents manage",
1855
+ ...buildBuiltinAgentSelectionExamples({ command: "disable" }),
1856
+ "openmeld agents show <agent-id>",
1857
+ "openmeld agents config <agent-id> --enabled true|false",
1858
+ "openmeld agents custom list",
1859
+ "openmeld agents custom add --name <name> --dir <dir> [--id <id>]",
1860
+ "openmeld agents custom update --id <id> [--name <name>] [--dir <dir>]",
1861
+ "openmeld agents custom remove --id <id>"
1862
+ ];
1863
+ const AGENTS_MANAGE_RESULT_COMMANDS = [
1864
+ "openmeld agents list",
1865
+ "openmeld agents show <agent-id>",
1866
+ ...buildBuiltinAgentSelectionExamples({ command: "repair" }),
1867
+ "openmeld agents config <agent-id> --enabled true|false",
1868
+ ...buildBuiltinAgentSelectionExamples({ command: "disable" })
1869
+ ];
1870
+ function formatBuiltinAgentSelectionAlternatives(command) {
1871
+ return buildBuiltinAgentSelectionExamples({ command }).filter((example) => !example.includes(" --install ")).map((example) => `\`${example}\``).join(" or ");
1872
+ }
1873
+ function buildBuiltinAgentMenuActions(command) {
1874
+ switch (command) {
1875
+ case "enable": return [
1876
+ {
1877
+ command: buildBuiltinAgentSelectionCommand({
1878
+ all: true,
1879
+ command
1880
+ }),
1881
+ description: "Enable every detected agent OpenMeld can use on this computer."
1882
+ },
1883
+ {
1884
+ command: buildBuiltinAgentSelectionCommand({
1885
+ agentIds: ["<id...>"],
1886
+ command
1887
+ }),
1888
+ description: "Enable selected agents so OpenMeld can use them."
1889
+ },
1890
+ {
1891
+ command: buildBuiltinAgentSelectionCommand({
1892
+ agentIds: ["<id>"],
1893
+ command,
1894
+ installId: "<install-id>"
1895
+ }),
1896
+ description: "Enable one agent with the exact local install OpenMeld should use."
1897
+ }
1898
+ ];
1899
+ case "repair": return [
1900
+ {
1901
+ command: buildBuiltinAgentSelectionCommand({
1902
+ all: true,
1903
+ command
1904
+ }),
1905
+ description: "Repair every connected builtin agent on this computer that needs attention."
1906
+ },
1907
+ {
1908
+ command: buildBuiltinAgentSelectionCommand({
1909
+ agentIds: ["<id...>"],
1910
+ command
1911
+ }),
1912
+ description: "Refresh how OpenMeld starts selected agents on this computer."
1913
+ },
1914
+ {
1915
+ command: buildBuiltinAgentSelectionCommand({
1916
+ agentIds: ["<id>"],
1917
+ command,
1918
+ installId: "<install-id>"
1919
+ }),
1920
+ description: "Repair one agent with the exact local install to use."
1921
+ }
1922
+ ];
1923
+ case "disable": return [{
1924
+ command: buildBuiltinAgentSelectionCommand({
1925
+ all: true,
1926
+ command
1927
+ }),
1928
+ description: "Disable every enabled agent for new mentions in Spaces."
1929
+ }, {
1930
+ command: buildBuiltinAgentSelectionCommand({
1931
+ agentIds: ["<id...>"],
1932
+ command
1933
+ }),
1934
+ description: "Disable selected agents so new mentions in Spaces stop waking them."
1935
+ }];
1936
+ default: throw new Error(`unsupported builtin agent selection command: ${command}`);
1937
+ }
1938
+ }
1939
+ function formatAgentViewLaunchStatus(status) {
1940
+ return status === "ready" ? "available" : status;
1941
+ }
1942
+ function buildQuickEnableOptionHint(actionableBuiltinCount) {
1943
+ return actionableBuiltinCount === 1 ? "1 new agent can be turned on now" : `${String(actionableBuiltinCount)} new agents can be turned on now`;
1944
+ }
1945
+ function buildManageOptionHint(registeredCount) {
1946
+ if (registeredCount === 0) return "No agents are set up on this computer yet";
1947
+ if (registeredCount === 1) return "Review 1 agent on this computer";
1948
+ return `Review ${String(registeredCount)} agents on this computer`;
1949
+ }
1950
+ function buildDetectedAgentsSubtitle(detectedAgentsCount) {
1951
+ return detectedAgentsCount === 1 ? "1 agent found on this computer" : `${String(detectedAgentsCount)} agents found on this computer`;
1952
+ }
1953
+ function buildEnabledAgentsSubtitle(enabledAgentsCount) {
1954
+ return enabledAgentsCount === 1 ? "1 agent is live for Space" : `${String(enabledAgentsCount)} agents are live for Space`;
1955
+ }
1956
+ async function runAgents(input) {
1957
+ const runtime = await resolveRuntimeContext({
1958
+ interactive: input.interactive,
1959
+ view: input.view,
1960
+ profile: input.profile
1961
+ });
1962
+ const presenter = createPresenter(runtime);
1963
+ await emitAgentsConfigParseFallbackSignal({
1964
+ presenter,
1965
+ view: runtime.resolvedView
1966
+ });
1967
+ emitAgentsOverviewIfAgent({
1968
+ presenter,
1969
+ resolvedView: runtime.resolvedView,
1970
+ command: "menu"
1971
+ });
1972
+ const payload = await buildAgentsMenuPayload();
1973
+ if (runtime.resolvedView === "agent") {
1974
+ presenter.data({
1975
+ code: "agents.menu.result",
1976
+ payload
1977
+ });
1978
+ return;
1979
+ }
1980
+ if (canUseInteractivePrompts(runtime)) {
1981
+ await runInteractiveAgentsMenu({
1982
+ profile: input.profile,
1983
+ view: runtime.resolvedView
1984
+ });
1985
+ return;
1986
+ }
1987
+ presenter.line({
1988
+ code: "agents.menu.header",
1989
+ text: "OpenMeld agents commands:"
1990
+ });
1991
+ for (const command of formatOpenMeldCliCommands(payload.commands)) presenter.line({
1992
+ code: "agents.menu.command",
1993
+ text: `- ${command}`
1994
+ });
1995
+ for (const note of payload.notes) presenter.line({
1996
+ code: "agents.menu.note",
1997
+ text: `- ${formatAgentsCopy(note)}`
1998
+ });
1999
+ }
2000
+ async function runAgentsManage(input) {
2001
+ const runtime = await resolveRuntimeContext({
2002
+ view: input.view,
2003
+ profile: input.profile
2004
+ });
2005
+ const presenter = createPresenter(runtime);
2006
+ await emitAgentsConfigParseFallbackSignal({
2007
+ presenter,
2008
+ view: runtime.resolvedView
2009
+ });
2010
+ emitAgentsOverviewIfAgent({
2011
+ presenter,
2012
+ resolvedView: runtime.resolvedView,
2013
+ command: "manage"
2014
+ });
2015
+ if (runtime.resolvedView === "agent") {
2016
+ presenter.data({
2017
+ code: "agents.manage.result",
2018
+ payload: {
2019
+ interactive: true,
2020
+ supportedView: "human",
2021
+ commands: [...AGENTS_MANAGE_RESULT_COMMANDS]
2022
+ }
2023
+ });
2024
+ return;
2025
+ }
2026
+ if (!canUseInteractivePrompts(runtime)) {
2027
+ presenter.line({
2028
+ code: "agents.manage.non_interactive",
2029
+ text: formatInlineOpenMeldCliCommands("This menu is interactive. In scripts or non-interactive terminals, use `openmeld agents list`, `openmeld agents show <agent-id>`, `openmeld agents config <agent-id> --enabled true|false`, or `openmeld agents disable --agent <id...>`.")
2030
+ });
2031
+ return;
2032
+ }
2033
+ if (await runInteractiveAgentsManageMenu({
2034
+ view: "human",
2035
+ profile: input.profile
2036
+ }) === "cancelled") cancel("Cancelled.");
2037
+ }
2038
+ async function runInteractiveAgentsMenu(input) {
2039
+ while (true) {
2040
+ const { enableCandidates, registrations } = await resolveAgentsChoiceState();
2041
+ const actionableBuiltinCount = enableCandidates.filter((candidate) => canSelectAgentForNewLocalAgentSetup(candidate) && !candidate.registered).length;
2042
+ const registeredCount = registrations.length;
2043
+ const choice = await select({
2044
+ message: buildInteractiveMenuMessage({
2045
+ title: "OpenMeld agents:",
2046
+ description: AGENTS_MENU_DESCRIPTION
2047
+ }),
2048
+ options: [
2049
+ ...actionableBuiltinCount > 0 ? [{
2050
+ value: AGENTS_MENU_CHOICE.quickEnable,
2051
+ label: "Enable available agents",
2052
+ hint: buildQuickEnableOptionHint(actionableBuiltinCount)
2053
+ }] : [],
2054
+ {
2055
+ value: AGENTS_MENU_CHOICE.manage,
2056
+ label: "Review Agents on This Device",
2057
+ hint: buildManageOptionHint(registeredCount)
2058
+ },
2059
+ {
2060
+ value: AGENTS_MENU_CHOICE.detect,
2061
+ label: "Find Agents on This Device",
2062
+ hint: "Check which supported AI tools OpenMeld can see here"
2063
+ },
2064
+ {
2065
+ value: AGENTS_MENU_CHOICE.exit,
2066
+ label: "Exit"
2067
+ }
2068
+ ]
2069
+ });
2070
+ if (isCancel(choice)) {
2071
+ cancel("Cancelled.");
2072
+ return;
2073
+ }
2074
+ if (choice === AGENTS_MENU_CHOICE.exit) return;
2075
+ if (await runInteractiveAgentsMenuChoice({
2076
+ input,
2077
+ choice
2078
+ }) === "cancelled") {
2079
+ cancel("Cancelled.");
2080
+ return;
2081
+ }
2082
+ }
2083
+ }
2084
+ async function runInteractiveAgentsMenuChoice(input) {
2085
+ switch (input.choice) {
2086
+ case AGENTS_MENU_CHOICE.detect:
2087
+ await runAgentsDetect(input.input);
2088
+ return "continue";
2089
+ case AGENTS_MENU_CHOICE.quickEnable: return runInteractiveAgentsEnable(input.input);
2090
+ case AGENTS_MENU_CHOICE.manage: return runInteractiveAgentsManageMenu(input.input);
2091
+ case AGENTS_MENU_CHOICE.custom: return runInteractiveCustomAgentsMenu(input.input);
2092
+ default: return "continue";
2093
+ }
2094
+ }
2095
+ async function buildAgentsMenuPayload() {
2096
+ const { enableCandidates, registrations } = await resolveAgentsChoiceState();
2097
+ const registeredAgents = registrations.map((registration) => ({
2098
+ agentId: registration.agentId,
2099
+ displayName: registration.displayName,
2100
+ enabled: registration.enabled
2101
+ }));
2102
+ return {
2103
+ commands: formatOpenMeldCliCommands([...AGENTS_MENU_COMMANDS]),
2104
+ notes: [
2105
+ "Agents are AI tools on this computer. When you enable one, people in a Space can mention `@agent-id` to wake it and get a reply.",
2106
+ "Use `openmeld agents detect` to look for supported AI tools on this computer.",
2107
+ `Use ${formatBuiltinAgentSelectionAlternatives("enable")} to enable the agents that are available.`,
2108
+ `If OpenMeld can see an agent but cannot start it here, use ${formatBuiltinAgentSelectionAlternatives("repair")}.`,
2109
+ "Use `openmeld agents manage` to review enabled agents and change them later.",
2110
+ "Manage custom agents with `openmeld agents custom`."
2111
+ ].map(formatAgentsCopy),
2112
+ actions: [
2113
+ ...buildBuiltinAgentMenuActions("enable"),
2114
+ ...buildBuiltinAgentMenuActions("repair"),
2115
+ {
2116
+ command: "openmeld agents detect",
2117
+ description: "Look for supported AI tools on this computer."
2118
+ },
2119
+ {
2120
+ command: "openmeld agents list",
2121
+ description: "List the agents people can mention in a Space."
2122
+ },
2123
+ {
2124
+ command: "openmeld agents manage",
2125
+ description: "Review enabled agents and change their settings."
2126
+ },
2127
+ ...buildBuiltinAgentMenuActions("disable"),
2128
+ {
2129
+ command: "openmeld agents show <agent-id>",
2130
+ description: "Show one agent's current OpenMeld settings."
2131
+ },
2132
+ {
2133
+ command: "openmeld agents config <agent-id> --enabled true|false",
2134
+ description: "Enable or disable one agent for Space mentions."
2135
+ },
2136
+ {
2137
+ command: "openmeld agents custom list",
2138
+ description: "List custom agent targets stored in the local OpenMeld config."
2139
+ },
2140
+ {
2141
+ command: "openmeld agents custom add --name <name> --dir <dir> [--id <id>]",
2142
+ description: "Add a custom agent target to the local OpenMeld config."
2143
+ },
2144
+ {
2145
+ command: "openmeld agents custom update --id <id> [--name <name>] [--dir <dir>]",
2146
+ description: "Update a custom agent target while keeping its id stable."
2147
+ },
2148
+ {
2149
+ command: "openmeld agents custom remove --id <id>",
2150
+ description: "Remove a custom agent target that is not currently connected."
2151
+ }
2152
+ ].map((action) => ({
2153
+ ...action,
2154
+ command: formatOpenMeldCliCommands([action.command])[0] ?? action.command
2155
+ })),
2156
+ selection: {
2157
+ enableCandidates: enableCandidates.map((candidate) => ({
2158
+ agentId: candidate.agentId,
2159
+ displayName: candidate.displayName,
2160
+ targetKind: candidate.targetKind,
2161
+ detected: candidate.detected,
2162
+ lifecycleStatus: candidate.lifecycleStatus,
2163
+ registered: candidate.registered,
2164
+ enabled: candidate.enabled,
2165
+ selectable: candidate.selectable,
2166
+ selectableReason: candidate.selectableReason,
2167
+ launchStatus: formatAgentViewLaunchStatus(candidate.launchStatus),
2168
+ launchStatusDetail: candidate.launchStatusDetail,
2169
+ defaultTransportMode: candidate.defaultTransportMode ?? null,
2170
+ transportModes: candidate.transportModes,
2171
+ status: resolveEnableCandidateStatus(candidate),
2172
+ ...candidate.customDir ? { customDir: candidate.customDir } : {}
2173
+ })),
2174
+ selectableAgentIds: enableCandidates.filter(canSelectAgentForNewLocalAgentSetup).map((candidate) => candidate.agentId),
2175
+ registeredAgents,
2176
+ disableCandidates: registeredAgents.map((agent) => ({
2177
+ agentId: agent.agentId,
2178
+ displayName: agent.displayName,
2179
+ enabled: agent.enabled
2180
+ })),
2181
+ customAgents: {
2182
+ supported: false,
2183
+ message: formatAgentsCopy("Custom agents are not available in this list yet. Use `openmeld agents custom` for now."),
2184
+ commands: formatOpenMeldCliCommands([
2185
+ "openmeld agents custom list",
2186
+ "openmeld agents custom add --name <name> --dir <dir> [--id <id>]",
2187
+ "openmeld agents custom update --id <id> [--name <name>] [--dir <dir>]",
2188
+ "openmeld agents custom remove --id <id>"
2189
+ ])
2190
+ }
2191
+ }
2192
+ };
2193
+ }
2194
+ async function resolveAgentsChoiceState() {
2195
+ const enableCandidates = await listAgentTargetStates();
2196
+ return {
2197
+ registrations: enableCandidates.flatMap((candidate) => candidate.registration ? [candidate.registration] : []).sort((left, right) => compareAgentControllerRefsForDisplay(left.agentId, right.agentId)),
2198
+ enableCandidates
2199
+ };
2200
+ }
2201
+ function resolveEnableCandidateStatus(candidate) {
2202
+ if (candidate.targetKind === "custom") return "custom_coming_soon";
2203
+ if (!candidate.detected) return "not_detected";
2204
+ if (candidate.lifecycleStatus === "suspended") return "detected_suspended";
2205
+ if (candidate.launchStatus === "needs_choice") return "detected_needs_choice";
2206
+ if (candidate.launchStatus === "needs_repair") return "detected_needs_repair";
2207
+ if (!candidate.selectable) return "detected_not_supported_yet";
2208
+ return "detected_supported";
2209
+ }
2210
+ function canSelectAgentForNewLocalAgentSetup(candidate) {
2211
+ return candidate.targetKind === "builtin" && candidate.lifecycleStatus !== "suspended" && candidate.selectable;
2212
+ }
2213
+ function buildUnifiedEnableTargetHint(candidate) {
2214
+ if (candidate.enabled) return "Already turned on in OpenMeld";
2215
+ if (!candidate.detected) return "Not found on this computer";
2216
+ if (candidate.lifecycleStatus === "suspended") return "Suspended for new OpenMeld profiles";
2217
+ if (candidate.launchStatus === "needs_choice") return "Needs your choice on this Mac before OpenMeld can turn it on";
2218
+ if (!candidate.selectable && candidate.selectableReason === "not_supported_yet") return "Found on this computer, but OpenMeld doesn't support it yet";
2219
+ if (!candidate.selectable && candidate.selectableReason === "needs_repair") return "Needs repair before OpenMeld can start it here";
2220
+ if (!candidate.selectable && candidate.selectableReason) return formatSelectableReasonForHuman(candidate.selectableReason);
2221
+ return "Available to enable now";
2222
+ }
2223
+ function formatSelectableReason(reason) {
2224
+ if (reason === "not_detected") return "not found";
2225
+ if (reason === "not_supported_yet") return "will be supported soon";
2226
+ if (reason === "custom_coming_soon") return "coming soon";
2227
+ if (reason === "needs_choice") return "needs your choice";
2228
+ if (reason === "needs_repair") return "needs repair";
2229
+ if (reason === "suspended") return "suspended";
2230
+ return reason;
2231
+ }
2232
+ function formatSelectableReasonForHuman(reason) {
2233
+ if (reason === "not_detected") return "Not found on this computer";
2234
+ if (reason === "not_supported_yet") return "Found on this computer, but OpenMeld doesn't support it yet";
2235
+ if (reason === "custom_coming_soon") return "Custom agent support is coming soon";
2236
+ if (reason === "needs_choice") return "Needs your choice on this Mac before OpenMeld can use it";
2237
+ if (reason === "needs_repair") return "Needs repair on this computer before OpenMeld can start it";
2238
+ if (reason === "suspended") return "Suspended for new OpenMeld profiles";
2239
+ return formatStatusTextForHuman(reason);
2240
+ }
2241
+ function resolveEnableBlockedReason(state) {
2242
+ if (!state) return "unknown built-in target id";
2243
+ if (state.targetKind === "custom") return "custom target is not runnable yet";
2244
+ if (!state.detected) return "not found";
2245
+ if (state.lifecycleStatus === "suspended") return "suspended for new OpenMeld profiles";
2246
+ if (state.launchStatus === "needs_choice") return "needs your choice";
2247
+ if (!state.selectable && state.selectableReason === "needs_repair") return "needs repair";
2248
+ if (!state.selectable && state.selectableReason) return formatSelectableReason(state.selectableReason);
2249
+ return "not selectable in current runtime";
2250
+ }
2251
+ function buildRegisteredAgentHint(state) {
2252
+ const activationHint = state.registration.enabled ? "Turned on" : "Turned off for new @mentions";
2253
+ if (state.launchStatus === "needs_choice") return buildMetadataDetailLine([activationHint, "Needs your choice on this Mac"]);
2254
+ if (state.launchStatus === "needs_repair") return buildMetadataDetailLine([activationHint, "Needs repair on this computer"]);
2255
+ return buildMetadataDetailLine([activationHint, state.registration.enabled ? "Live for Space @mentions" : null]);
2256
+ }
2257
+ async function runInteractiveAgentsEnable(input) {
2258
+ const { enableCandidates } = await resolveAgentsChoiceState();
2259
+ const actionableCandidates = enableCandidates.filter((candidate) => candidate.targetKind === "builtin").filter((candidate) => canSelectAgentForNewLocalAgentSetup(candidate) && !candidate.registered);
2260
+ if (actionableCandidates.length === 0) {
2261
+ process.stderr.write(`${formatAgentsCopy(NO_ACTIONABLE_AGENTS_ENABLE_MESSAGE)}\n`);
2262
+ return "completed";
2263
+ }
2264
+ const selectedResult = await promptEnableAgentIdsFromUnifiedBuiltins({ candidates: actionableCandidates });
2265
+ if (selectedResult.status === "cancelled") return "cancelled";
2266
+ if (selectedResult.status !== "selected") return "completed";
2267
+ await runAgentsEnable({
2268
+ agent: selectedResult.value,
2269
+ profile: input.profile,
2270
+ view: input.view
2271
+ });
2272
+ return "completed";
2273
+ }
2274
+ async function runInteractiveCustomAgentsMenu(input) {
2275
+ while (true) {
2276
+ const outcome = await handleInteractiveCustomAgentsMenuChoice({
2277
+ input,
2278
+ choiceResult: await promptInteractiveCustomAgentsMenuChoice()
2279
+ });
2280
+ if (outcome === "continue") continue;
2281
+ return outcome;
2282
+ }
2283
+ }
2284
+ async function runInteractiveAgentsManageMenu(input) {
2285
+ if ((await listRegisteredAgentStates()).length === 0) {
2286
+ process.stderr.write(`${formatAgentsCopy(NO_REGISTERED_AGENTS_MANAGE_MESSAGE)}\n`);
2287
+ return "completed";
2288
+ }
2289
+ while (true) {
2290
+ const selectedAgentResult = await promptSelectRegisteredAgent({
2291
+ message: "Choose an agent",
2292
+ emptyMessage: NO_REGISTERED_AGENTS_MANAGE_MESSAGE
2293
+ });
2294
+ if (selectedAgentResult.status === "cancelled") return "cancelled";
2295
+ if (selectedAgentResult.status !== "selected") return "completed";
2296
+ const selectedState = selectedAgentResult.value;
2297
+ const actionResult = await promptInteractiveAgentsManageAgentAction({
2298
+ agentId: selectedState.agentId,
2299
+ displayName: selectedState.displayName,
2300
+ enabled: selectedState.enabled,
2301
+ launchStatus: selectedState.launchStatus
2302
+ });
2303
+ if (actionResult.status === "cancelled") return "cancelled";
2304
+ if (actionResult.status !== "selected" || actionResult.value === AGENTS_MANAGE_AGENT_ACTION_CHOICE.back) continue;
2305
+ if (actionResult.value === AGENTS_MANAGE_AGENT_ACTION_CHOICE.showDetails) {
2306
+ await runAgentsShow({
2307
+ agentId: selectedState.agentId,
2308
+ profile: input.profile,
2309
+ view: input.view
2310
+ });
2311
+ continue;
2312
+ }
2313
+ if (actionResult.value === AGENTS_MANAGE_AGENT_ACTION_CHOICE.repair) {
2314
+ await runAgentsRepair({
2315
+ agent: [selectedState.agentId],
2316
+ profile: input.profile,
2317
+ view: input.view
2318
+ });
2319
+ continue;
2320
+ }
2321
+ if (actionResult.value === AGENTS_MANAGE_AGENT_ACTION_CHOICE.chooseInstall) {
2322
+ await runAgentsRepair({
2323
+ agent: [selectedState.agentId],
2324
+ changeInstall: true,
2325
+ profile: input.profile,
2326
+ view: input.view
2327
+ });
2328
+ continue;
2329
+ }
2330
+ await runAgentsConfig({
2331
+ agentId: selectedState.agentId,
2332
+ enabled: selectedState.enabled ? "false" : "true",
2333
+ profile: input.profile,
2334
+ view: input.view
2335
+ });
2336
+ }
2337
+ }
2338
+ async function handleInteractiveCustomAgentsMenuChoice(input) {
2339
+ if (input.choiceResult.status === "cancelled") return "cancelled";
2340
+ if (input.choiceResult.status !== "selected") return "completed";
2341
+ switch (input.choiceResult.value) {
2342
+ case "back": return "completed";
2343
+ case "list":
2344
+ await runAgentsCustomList({
2345
+ profile: input.input.profile,
2346
+ view: input.input.view
2347
+ });
2348
+ return "continue";
2349
+ case "add": return await runInteractiveCustomAgentsAddFlow(input.input) === "cancelled" ? "cancelled" : "continue";
2350
+ case "remove": return await runInteractiveCustomAgentsRemoveFlow(input.input) === "cancelled" ? "cancelled" : "continue";
2351
+ case "edit": return await runInteractiveCustomAgentsEditFlow(input.input) === "cancelled" ? "cancelled" : "continue";
2352
+ default: return "completed";
2353
+ }
2354
+ }
2355
+ async function promptInteractiveAgentsManageAgentAction(input) {
2356
+ const action = await select({
2357
+ message: buildInteractiveMenuMessage({
2358
+ title: `Agent: ${input.agentId}`,
2359
+ description: `When on, @${input.agentId} in Space can wake ${input.displayName}.`
2360
+ }),
2361
+ options: [
2362
+ {
2363
+ value: AGENTS_MANAGE_AGENT_ACTION_CHOICE.showDetails,
2364
+ label: "View Setup"
2365
+ },
2366
+ ...input.launchStatus === "needs_choice" ? [{
2367
+ value: AGENTS_MANAGE_AGENT_ACTION_CHOICE.chooseInstall,
2368
+ label: "Choose install on This Device",
2369
+ hint: "Pick which local install OpenMeld should use"
2370
+ }] : [],
2371
+ ...input.launchStatus === "needs_repair" ? [{
2372
+ value: AGENTS_MANAGE_AGENT_ACTION_CHOICE.repair,
2373
+ label: "Repair on This Device",
2374
+ hint: "Refresh how OpenMeld starts it locally"
2375
+ }] : [],
2376
+ ...input.launchStatus === "ready" ? [{
2377
+ value: AGENTS_MANAGE_AGENT_ACTION_CHOICE.chooseInstall,
2378
+ label: "Change install on This Device",
2379
+ hint: "Pick a different local install for future replies"
2380
+ }] : [],
2381
+ {
2382
+ value: AGENTS_MANAGE_AGENT_ACTION_CHOICE.toggleEnabled,
2383
+ label: input.enabled ? "Turn Off for Space @mentions" : "Turn On for Space @mentions",
2384
+ hint: input.enabled ? "Stop waking it for new @mentions" : "Allow @mentions to wake it again"
2385
+ },
2386
+ {
2387
+ value: AGENTS_MANAGE_AGENT_ACTION_CHOICE.back,
2388
+ label: "Back to Agents"
2389
+ }
2390
+ ]
2391
+ });
2392
+ if (isCancel(action)) return { status: "cancelled" };
2393
+ return {
2394
+ status: "selected",
2395
+ value: action
2396
+ };
2397
+ }
2398
+ async function promptInteractiveCustomAgentsMenuChoice() {
2399
+ const choice = await select({
2400
+ message: "Custom Targets:",
2401
+ options: [
2402
+ {
2403
+ value: "add",
2404
+ label: "Add custom target",
2405
+ hint: "Create a new custom agent target"
2406
+ },
2407
+ {
2408
+ value: "edit",
2409
+ label: "Edit custom target",
2410
+ hint: "Update name or dir (id remains stable)"
2411
+ },
2412
+ {
2413
+ value: "remove",
2414
+ label: "Remove custom target",
2415
+ hint: "Blocked when target is currently registered"
2416
+ },
2417
+ {
2418
+ value: "list",
2419
+ label: "List custom targets",
2420
+ hint: "Show custom targets and current status"
2421
+ },
2422
+ {
2423
+ value: "back",
2424
+ label: "Back"
2425
+ }
2426
+ ]
2427
+ });
2428
+ if (isCancel(choice)) return { status: "cancelled" };
2429
+ return {
2430
+ status: "selected",
2431
+ value: choice
2432
+ };
2433
+ }
2434
+ async function runInteractiveCustomAgentsAddFlow(input) {
2435
+ let namePlaceholder = "e.g. OpenMeld Agent Executor";
2436
+ let dirPlaceholder = "/absolute/path/to/skills";
2437
+ let idPlaceholder = "leave empty to auto-generate from dir";
2438
+ while (true) {
2439
+ const nameResult = await promptRequiredInteractiveText({
2440
+ message: "Custom target name",
2441
+ placeholder: namePlaceholder,
2442
+ requiredMessage: "Custom target name is required."
2443
+ });
2444
+ if (nameResult.status === "cancelled") return "cancelled";
2445
+ if (nameResult.status !== "selected") return "completed";
2446
+ namePlaceholder = nameResult.value;
2447
+ const addDetails = await promptInteractiveCustomAgentAddDetails({
2448
+ dirPlaceholder,
2449
+ idPlaceholder
2450
+ });
2451
+ if (addDetails.status === "cancelled") return "cancelled";
2452
+ if (addDetails.status !== "submitted") continue;
2453
+ dirPlaceholder = addDetails.value.dir;
2454
+ if (addDetails.value.idInput) idPlaceholder = addDetails.value.idInput;
2455
+ await runAgentsCustomAdd({
2456
+ name: nameResult.value,
2457
+ dir: addDetails.value.dir,
2458
+ ...addDetails.value.id ? { id: addDetails.value.id } : {},
2459
+ profile: input.profile,
2460
+ view: input.view
2461
+ });
2462
+ return "completed";
2463
+ }
2464
+ }
2465
+ async function runInteractiveCustomAgentsRemoveFlow(input) {
2466
+ const selectedTargetResult = await promptSelectCustomTarget({
2467
+ message: "Select custom target to remove",
2468
+ emptyMessage: "No custom targets available. Add a custom target with `openmeld agents custom add --name <name> --dir <dir>`."
2469
+ });
2470
+ if (selectedTargetResult.status === "cancelled") return "cancelled";
2471
+ if (selectedTargetResult.status !== "selected") return "completed";
2472
+ await runAgentsCustomRemove({
2473
+ id: selectedTargetResult.value.id,
2474
+ profile: input.profile,
2475
+ view: input.view
2476
+ });
2477
+ return "completed";
2478
+ }
2479
+ async function runInteractiveCustomAgentsEditFlow(input) {
2480
+ while (true) {
2481
+ const selectedTargetResult = await promptSelectCustomTarget({
2482
+ message: "Select custom target to edit",
2483
+ emptyMessage: "No custom targets available. Add a custom target with `openmeld agents custom add --name <name> --dir <dir>`."
2484
+ });
2485
+ if (selectedTargetResult.status === "cancelled") return "cancelled";
2486
+ if (selectedTargetResult.status !== "selected") return "completed";
2487
+ const updateRequest = await promptInteractiveCustomAgentUpdate(selectedTargetResult.value);
2488
+ if (updateRequest.status === "cancelled") return "cancelled";
2489
+ if (updateRequest.status !== "submitted") continue;
2490
+ await runAgentsCustomUpdate({
2491
+ id: updateRequest.value.id,
2492
+ ..."name" in updateRequest.value ? { name: updateRequest.value.name } : { dir: updateRequest.value.dir },
2493
+ profile: input.profile,
2494
+ view: input.view
2495
+ });
2496
+ return "completed";
2497
+ }
2498
+ }
2499
+ async function promptInteractiveCustomAgentAddDetails(input) {
2500
+ while (true) {
2501
+ const dirResult = await promptRequiredInteractiveText({
2502
+ message: "Custom target absolute dir",
2503
+ placeholder: input.dirPlaceholder,
2504
+ requiredMessage: "Custom target absolute dir is required.",
2505
+ validate: (value) => isAbsolute(value) ? null : "Custom target absolute dir must be an absolute path."
2506
+ });
2507
+ if (dirResult.status === "cancelled" || dirResult.status === "back") return dirResult;
2508
+ if (dirResult.status !== "selected") continue;
2509
+ const idResult = await promptInteractiveText({
2510
+ message: "Custom target id (optional)",
2511
+ placeholder: input.idPlaceholder,
2512
+ allowEmpty: true
2513
+ });
2514
+ if (idResult.status === "cancelled") return idResult;
2515
+ if (idResult.status === "back") {
2516
+ input.dirPlaceholder = dirResult.value;
2517
+ continue;
2518
+ }
2519
+ if (idResult.status !== "selected") continue;
2520
+ return {
2521
+ status: "submitted",
2522
+ value: {
2523
+ dir: dirResult.value,
2524
+ id: normalizeOptionalString(idResult.value) ?? void 0,
2525
+ idInput: normalizeOptionalString(idResult.value) ?? void 0
2526
+ }
2527
+ };
2528
+ }
2529
+ }
2530
+ async function promptInteractiveCustomAgentUpdate(selectedTarget) {
2531
+ while (true) {
2532
+ const field = await promptInteractiveCustomAgentEditField();
2533
+ if (field.status === "cancelled" || field.status === "back") return field;
2534
+ if (field.status !== "selected") continue;
2535
+ const valueResult = await promptInteractiveCustomAgentEditValue({
2536
+ field: field.value,
2537
+ selectedTarget
2538
+ });
2539
+ if (valueResult.status === "cancelled") return valueResult;
2540
+ if (valueResult.status === "back") continue;
2541
+ if (valueResult.status !== "selected") continue;
2542
+ return {
2543
+ status: "submitted",
2544
+ value: field.value === "name" ? {
2545
+ id: selectedTarget.id,
2546
+ name: valueResult.value
2547
+ } : {
2548
+ id: selectedTarget.id,
2549
+ dir: valueResult.value
2550
+ }
2551
+ };
2552
+ }
2553
+ }
2554
+ async function promptInteractiveCustomAgentEditField() {
2555
+ const field = await select({
2556
+ message: "Edit field",
2557
+ options: [
2558
+ {
2559
+ value: "name",
2560
+ label: "Name"
2561
+ },
2562
+ {
2563
+ value: "dir",
2564
+ label: "Dir"
2565
+ },
2566
+ {
2567
+ value: "back",
2568
+ label: "Back"
2569
+ }
2570
+ ]
2571
+ });
2572
+ if (isCancel(field)) return { status: "cancelled" };
2573
+ if (field === "back") return { status: "back" };
2574
+ return {
2575
+ status: "selected",
2576
+ value: field
2577
+ };
2578
+ }
2579
+ function promptInteractiveCustomAgentEditValue(input) {
2580
+ return input.field === "name" ? promptRequiredInteractiveText({
2581
+ message: "New name",
2582
+ placeholder: input.selectedTarget.name,
2583
+ requiredMessage: "Custom target name is required."
2584
+ }) : promptRequiredInteractiveText({
2585
+ message: "New absolute dir",
2586
+ placeholder: input.selectedTarget.dir,
2587
+ requiredMessage: "Custom target absolute dir is required.",
2588
+ validate: (nextDir) => isAbsolute(nextDir) ? null : "Custom target absolute dir must be an absolute path."
2589
+ });
2590
+ }
2591
+ async function promptInteractiveText(input) {
2592
+ const value = await promptTextEntry({
2593
+ message: input.message,
2594
+ ...input.placeholder ? { placeholder: input.placeholder } : {},
2595
+ allowBack: true,
2596
+ backLabel: "Back",
2597
+ allowEmpty: input.allowEmpty === true,
2598
+ requiredMessage: "Value is required."
2599
+ });
2600
+ if (value.status === "cancelled") return { status: "cancelled" };
2601
+ if (value.status === "back") return { status: "back" };
2602
+ return {
2603
+ status: "selected",
2604
+ value: value.value
2605
+ };
2606
+ }
2607
+ async function promptRequiredInteractiveText(input) {
2608
+ while (true) {
2609
+ const valueResult = await promptInteractiveText({
2610
+ message: input.message,
2611
+ ...input.placeholder ? { placeholder: input.placeholder } : {}
2612
+ });
2613
+ if (valueResult.status === "cancelled" || valueResult.status === "back") return valueResult;
2614
+ if (valueResult.status !== "selected") continue;
2615
+ const normalizedValue = normalizeOptionalString(valueResult.value);
2616
+ if (!normalizedValue) {
2617
+ process.stderr.write(`${input.requiredMessage}\n`);
2618
+ continue;
2619
+ }
2620
+ const validationMessage = input.validate?.(normalizedValue) ?? null;
2621
+ if (validationMessage) {
2622
+ process.stderr.write(`${validationMessage}\n`);
2623
+ continue;
2624
+ }
2625
+ return {
2626
+ status: "selected",
2627
+ value: normalizedValue
2628
+ };
2629
+ }
2630
+ }
2631
+ async function promptSelectCustomTarget(input) {
2632
+ const customTargets = await listAgentCustomTargets();
2633
+ if (customTargets.length === 0) {
2634
+ process.stderr.write(`${formatAgentsCopy(input.emptyMessage)}\n`);
2635
+ return { status: "empty" };
2636
+ }
2637
+ const choice = await promptSearchSelect({
2638
+ message: input.message,
2639
+ items: customTargets.map(buildCustomTargetSearchItem),
2640
+ maxVisible: 8,
2641
+ allowBack: true,
2642
+ backLabel: "Back"
2643
+ });
2644
+ if (choice.status === "cancelled") return { status: "cancelled" };
2645
+ if (choice.status === "back") return { status: "back" };
2646
+ const id = normalizeAgentId(choice.value);
2647
+ const selectedTarget = customTargets.find((target) => target.id === id);
2648
+ if (!selectedTarget) return { status: "empty" };
2649
+ return {
2650
+ status: "selected",
2651
+ value: selectedTarget
2652
+ };
2653
+ }
2654
+ async function promptEnableAgentIdsFromUnifiedBuiltins(input) {
2655
+ const selectableCandidates = input.candidates.filter((candidate) => canSelectAgentForNewLocalAgentSetup(candidate) && !candidate.enabled);
2656
+ if (selectableCandidates.length === 0) {
2657
+ process.stderr.write(`${formatAgentsCopy(NO_ACTIONABLE_AGENTS_ENABLE_MESSAGE)}\n`);
2658
+ return { status: "empty" };
2659
+ }
2660
+ emitInteractiveDescription(AGENTS_ENABLE_DESCRIPTION);
2661
+ const selected = await promptSearchMultiselect({
2662
+ message: "Choose agents to enable",
2663
+ items: [...selectableCandidates.map(buildUnifiedEnableSearchItem), {
2664
+ value: CUSTOM_AGENTS_COMING_SOON_OPTION_ID,
2665
+ label: "Custom agents (coming soon)",
2666
+ detail: formatInlineOpenMeldCliCommands("Use `openmeld agents custom` commands for custom targets for now."),
2667
+ searchText: "Custom agents (coming soon) Use openmeld agents custom commands for custom targets for now",
2668
+ disabled: true
2669
+ }],
2670
+ required: true,
2671
+ maxVisible: 8,
2672
+ allowBack: true,
2673
+ backLabel: "Back"
2674
+ });
2675
+ if (selected.status === "cancelled") return { status: "cancelled" };
2676
+ if (selected.status === "back") return { status: "back" };
2677
+ const agentIds = normalizeAgentIds(selected.value.sort()).filter((agentId) => agentId !== CUSTOM_AGENTS_COMING_SOON_OPTION_ID);
2678
+ if (agentIds.length === 0) return { status: "empty" };
2679
+ return {
2680
+ status: "selected",
2681
+ value: agentIds
2682
+ };
2683
+ }
2684
+ async function listRegisteredAgentStates() {
2685
+ return (await listAgentTargetStates()).filter((state) => Boolean(state.registration));
2686
+ }
2687
+ async function promptSelectRegisteredAgent(input) {
2688
+ const registeredStates = await listRegisteredAgentStates();
2689
+ if (registeredStates.length === 0) {
2690
+ process.stderr.write(`${formatAgentsCopy(input.emptyMessage)}\n`);
2691
+ return { status: "empty" };
2692
+ }
2693
+ emitInteractiveDescription(AGENTS_SELECT_REGISTERED_DESCRIPTION);
2694
+ const choice = await promptSearchSelect({
2695
+ message: input.message,
2696
+ items: registeredStates.map(buildRegisteredAgentSearchItem),
2697
+ maxVisible: 8,
2698
+ allowBack: true,
2699
+ backLabel: "Back"
2700
+ });
2701
+ if (choice.status === "cancelled") return { status: "cancelled" };
2702
+ if (choice.status === "back") return { status: "back" };
2703
+ const agentId = normalizeAgentId(choice.value);
2704
+ if (!agentId) return { status: "empty" };
2705
+ const selectedState = registeredStates.find((state) => state.agentId === agentId);
2706
+ if (!selectedState) return { status: "empty" };
2707
+ return {
2708
+ status: "selected",
2709
+ value: selectedState
2710
+ };
2711
+ }
2712
+ function buildCustomTargetSearchItem(target) {
2713
+ return {
2714
+ value: target.id,
2715
+ label: target.name,
2716
+ detail: buildMetadataDetailLine([`ID ${target.id}`, target.dir]),
2717
+ searchText: [
2718
+ target.name,
2719
+ target.id,
2720
+ target.dir
2721
+ ].join(" ")
2722
+ };
2723
+ }
2724
+ function buildUnifiedEnableSearchItem(candidate) {
2725
+ const statusHint = buildUnifiedEnableTargetHint(candidate);
2726
+ return {
2727
+ value: candidate.agentId,
2728
+ label: candidate.displayName,
2729
+ detail: buildMetadataDetailLine([`ID ${candidate.agentId}`, statusHint]),
2730
+ searchText: [
2731
+ candidate.displayName,
2732
+ candidate.agentId,
2733
+ statusHint,
2734
+ candidate.selectableReason ?? ""
2735
+ ].join(" "),
2736
+ disabled: !canSelectAgentForNewLocalAgentSetup(candidate) || candidate.enabled
2737
+ };
2738
+ }
2739
+ function buildRegisteredAgentSearchItem(state) {
2740
+ const enabledHint = buildRegisteredAgentHint(state);
2741
+ return {
2742
+ value: state.agentId,
2743
+ label: state.agentId,
2744
+ detail: buildMetadataDetailLine([state.displayName, enabledHint]),
2745
+ searchText: [
2746
+ state.displayName,
2747
+ state.agentId,
2748
+ enabledHint,
2749
+ "space wake mention reply"
2750
+ ].join(" ")
2751
+ };
2752
+ }
2753
+ function buildMetadataDetailLine(parts) {
2754
+ return parts.map((part) => String(part ?? "").trim()).filter((part) => part.length > 0).join(" · ");
2755
+ }
2756
+ async function runAgentsDetect(input) {
2757
+ const runtime = await resolveRuntimeContext({
2758
+ view: input.view,
2759
+ profile: input.profile
2760
+ });
2761
+ const inventory = await collectLocalAgentControllerInventory();
2762
+ const detectedAgents = inventory.capabilities.filter((capability) => capability.detected && (capability.lifecycleStatus !== "suspended" || capability.enabled)).map((capability) => ({
2763
+ id: capability.agentId,
2764
+ blockerReasonCodes: capability.blockerReasonCodes,
2765
+ displayName: capability.displayName,
2766
+ launchability: capability.launchability,
2767
+ launchStatus: formatAgentViewLaunchStatus(capability.launchStatus),
2768
+ launchStatusDetail: capability.launchStatusDetail,
2769
+ lifecycleStatus: capability.lifecycleStatus,
2770
+ selectable: capability.selectable
2771
+ }));
2772
+ const payload = {
2773
+ detectedAgents,
2774
+ count: detectedAgents.length,
2775
+ inventorySource: "local_runtime_inventory",
2776
+ source: "builtin_detect"
2777
+ };
2778
+ if (input.json === true) {
2779
+ emitCliJsonEnvelope({
2780
+ schemaVersion: 1,
2781
+ type: "agents.detect",
2782
+ ...payload
2783
+ });
2784
+ return;
2785
+ }
2786
+ const presenter = createPresenter(runtime);
2787
+ await emitAgentsConfigParseFallbackSignal({
2788
+ presenter,
2789
+ view: runtime.resolvedView
2790
+ });
2791
+ emitAgentsOverviewIfAgent({
2792
+ presenter,
2793
+ resolvedView: runtime.resolvedView,
2794
+ command: "detect"
2795
+ });
2796
+ if (runtime.resolvedView === "agent") {
2797
+ presenter.data({
2798
+ code: "agents.detect.result",
2799
+ payload
2800
+ });
2801
+ return;
2802
+ }
2803
+ const readyAgents = detectedAgents.filter((agent) => agent.lifecycleStatus !== "suspended" && agent.launchability === "launchable");
2804
+ const needsChoiceAgents = detectedAgents.filter((agent) => agent.lifecycleStatus !== "suspended" && agent.launchability === "needs_choice");
2805
+ const needsRepairAgents = detectedAgents.filter((agent) => agent.lifecycleStatus !== "suspended" && agent.launchability === "needs_repair");
2806
+ const comingSoonAgents = detectedAgents.filter((agent) => agent.lifecycleStatus !== "suspended" && agent.launchability === "not_supported" && !agent.selectable);
2807
+ const suspendedAgents = detectedAgents.filter((agent) => agent.lifecycleStatus === "suspended");
2808
+ const notDetectedAgents = inventory.capabilities.filter((capability) => !capability.detected);
2809
+ if (detectedAgents.length === 0) {
2810
+ renderAgentsInfoCard({
2811
+ presenter,
2812
+ codePrefix: "agents.detect.empty.card",
2813
+ title: "Agents Found Here",
2814
+ rows: [{
2815
+ label: "Found",
2816
+ value: pc.dim("No supported AI agents yet")
2817
+ }, {
2818
+ label: "Next",
2819
+ value: formatInlineOpenMeldCliCommands("Open or install the AI tool you want OpenMeld to use, then run `openmeld agents detect` again.")
2820
+ }],
2821
+ minContentWidth: 36
2822
+ });
2823
+ return;
2824
+ }
2825
+ renderAgentsInfoCard({
2826
+ presenter,
2827
+ codePrefix: "agents.detect.summary.card",
2828
+ title: "Agents Found Here",
2829
+ rows: [
2830
+ {
2831
+ label: "Found",
2832
+ value: buildDetectedAgentsSubtitle(detectedAgents.length)
2833
+ },
2834
+ {
2835
+ label: "Available now",
2836
+ value: buildReadyCountValue(readyAgents.length, "live for Space")
2837
+ },
2838
+ {
2839
+ label: "Needs choice",
2840
+ value: buildNotReadyCountValue(needsChoiceAgents.length, "found, needs your choice")
2841
+ },
2842
+ {
2843
+ label: "Needs repair",
2844
+ value: buildNotReadyCountValue(needsRepairAgents.length, "found, needs repair")
2845
+ },
2846
+ {
2847
+ label: "Coming soon",
2848
+ value: buildNotReadyCountValue(comingSoonAgents.length, "found, not supported yet")
2849
+ },
2850
+ {
2851
+ label: "Suspended",
2852
+ value: buildNotReadyCountValue(suspendedAgents.length, "already configured, not offered for new profiles")
2853
+ },
2854
+ {
2855
+ label: "Not found",
2856
+ value: buildNotReadyCountValue(notDetectedAgents.length, "not found on this computer")
2857
+ }
2858
+ ],
2859
+ minContentWidth: 42
2860
+ });
2861
+ if (readyAgents.length > 0) {
2862
+ presenter.line({
2863
+ code: "agents.detect.ready.spacer",
2864
+ text: ""
2865
+ });
2866
+ renderAgentsInfoCard({
2867
+ presenter,
2868
+ codePrefix: "agents.detect.ready.card",
2869
+ title: `Available Now (${readyAgents.length})`,
2870
+ rows: readyAgents.map((agent) => buildReadyAgentCardRow(agent.id, agent.displayName)),
2871
+ minContentWidth: 30
2872
+ });
2873
+ }
2874
+ if (needsRepairAgents.length > 0) {
2875
+ presenter.line({
2876
+ code: "agents.detect.needs_repair.spacer",
2877
+ text: ""
2878
+ });
2879
+ renderAgentsInfoCard({
2880
+ presenter,
2881
+ codePrefix: "agents.detect.needs_repair.card",
2882
+ title: `Needs Repair (${needsRepairAgents.length})`,
2883
+ rows: needsRepairAgents.map((agent) => buildStatusAgentCardRow({
2884
+ agentId: agent.id,
2885
+ displayName: agent.displayName,
2886
+ detail: agent.launchStatusDetail ?? "Repair on this computer before OpenMeld can start it"
2887
+ })),
2888
+ minContentWidth: 36
2889
+ });
2890
+ }
2891
+ if (needsChoiceAgents.length > 0) {
2892
+ presenter.line({
2893
+ code: "agents.detect.needs_choice.spacer",
2894
+ text: ""
2895
+ });
2896
+ renderAgentsInfoCard({
2897
+ presenter,
2898
+ codePrefix: "agents.detect.needs_choice.card",
2899
+ title: `Needs Your Choice (${needsChoiceAgents.length})`,
2900
+ rows: needsChoiceAgents.map((agent) => buildStatusAgentCardRow({
2901
+ agentId: agent.id,
2902
+ displayName: agent.displayName,
2903
+ detail: "Choose which install OpenMeld should use on this Mac"
2904
+ })),
2905
+ minContentWidth: 36
2906
+ });
2907
+ }
2908
+ if (comingSoonAgents.length > 0) {
2909
+ presenter.line({
2910
+ code: "agents.detect.coming_soon.spacer",
2911
+ text: ""
2912
+ });
2913
+ renderAgentsInfoCard({
2914
+ presenter,
2915
+ codePrefix: "agents.detect.coming_soon.card",
2916
+ title: `Coming Soon (${comingSoonAgents.length})`,
2917
+ rows: comingSoonAgents.map((agent) => buildStatusAgentCardRow({
2918
+ agentId: agent.id,
2919
+ displayName: agent.displayName,
2920
+ detail: "OpenMeld found it, but builtin runtime support is not available yet"
2921
+ })),
2922
+ minContentWidth: 36
2923
+ });
2924
+ }
2925
+ if (suspendedAgents.length > 0) {
2926
+ presenter.line({
2927
+ code: "agents.detect.suspended.spacer",
2928
+ text: ""
2929
+ });
2930
+ renderAgentsInfoCard({
2931
+ presenter,
2932
+ codePrefix: "agents.detect.suspended.card",
2933
+ title: `Suspended (${suspendedAgents.length})`,
2934
+ rows: suspendedAgents.map((agent) => buildStatusAgentCardRow({
2935
+ agentId: agent.id,
2936
+ displayName: agent.displayName,
2937
+ detail: "Already configured here, but OpenMeld no longer offers it for new profiles"
2938
+ })),
2939
+ minContentWidth: 46
2940
+ });
2941
+ }
2942
+ }
2943
+ async function runAgentsList(input) {
2944
+ const runtime = await resolveRuntimeContext({
2945
+ view: input.view,
2946
+ profile: input.profile
2947
+ });
2948
+ const states = await listAgentTargetStates();
2949
+ const registrations = states.filter(hasAgentRegistration).map((state) => buildAgentRegistrationAgentView(state)).sort((left, right) => compareAgentControllerRefsForDisplay(left.agentId, right.agentId));
2950
+ const payload = {
2951
+ registrations,
2952
+ count: registrations.length
2953
+ };
2954
+ if (input.json === true) {
2955
+ emitCliJsonEnvelope({
2956
+ schemaVersion: 1,
2957
+ type: "agents.list",
2958
+ ...payload
2959
+ });
2960
+ return;
2961
+ }
2962
+ const presenter = createPresenter(runtime);
2963
+ await emitAgentsConfigParseFallbackSignal({
2964
+ presenter,
2965
+ view: runtime.resolvedView
2966
+ });
2967
+ emitAgentsOverviewIfAgent({
2968
+ presenter,
2969
+ resolvedView: runtime.resolvedView,
2970
+ command: "list"
2971
+ });
2972
+ if (runtime.resolvedView === "agent") {
2973
+ presenter.data({
2974
+ code: "agents.list.result",
2975
+ payload
2976
+ });
2977
+ return;
2978
+ }
2979
+ const enabledStates = states.filter((state) => state.registration?.enabled);
2980
+ const readyEnabledStates = enabledStates.filter((state) => state.launchStatus === "ready");
2981
+ const needsChoiceEnabledStates = enabledStates.filter((state) => state.launchStatus === "needs_choice");
2982
+ const needsRepairEnabledStates = enabledStates.filter((state) => state.launchStatus === "needs_repair");
2983
+ if (readyEnabledStates.length === 0 && needsChoiceEnabledStates.length === 0 && needsRepairEnabledStates.length === 0) {
2984
+ renderAgentsInfoCard({
2985
+ presenter,
2986
+ codePrefix: "agents.list.empty.card",
2987
+ title: "Agents Available for Space",
2988
+ rows: [{
2989
+ label: "Live now",
2990
+ value: pc.dim("No agents are live yet")
2991
+ }, {
2992
+ label: "Next",
2993
+ value: formatInlineOpenMeldCliCommands("Run `openmeld agents detect`, then enable the agents you want OpenMeld to use.")
2994
+ }],
2995
+ minContentWidth: 38
2996
+ });
2997
+ return;
2998
+ }
2999
+ renderAgentsInfoCard({
3000
+ presenter,
3001
+ codePrefix: "agents.list.summary.card",
3002
+ title: "Agents Available for Space",
3003
+ rows: [
3004
+ {
3005
+ label: "Live now",
3006
+ value: buildEnabledAgentsSubtitle(readyEnabledStates.length)
3007
+ },
3008
+ {
3009
+ label: "Needs choice",
3010
+ value: String(needsChoiceEnabledStates.length)
3011
+ },
3012
+ {
3013
+ label: "Needs repair",
3014
+ value: String(needsRepairEnabledStates.length)
3015
+ },
3016
+ {
3017
+ label: "Mention as",
3018
+ value: `${formatAgentMention("agent-id")} ${pc.dim("in Space")}`
3019
+ }
3020
+ ],
3021
+ minContentWidth: 38
3022
+ });
3023
+ presenter.line({
3024
+ code: "agents.list.ready.spacer",
3025
+ text: ""
3026
+ });
3027
+ renderAgentsInfoCard({
3028
+ presenter,
3029
+ codePrefix: "agents.list.ready.card",
3030
+ title: `Available Agents (${readyEnabledStates.length})`,
3031
+ rows: readyEnabledStates.map((state) => buildReadyAgentCardRow(state.agentId, state.displayName)),
3032
+ minContentWidth: 30
3033
+ });
3034
+ if (needsRepairEnabledStates.length > 0) {
3035
+ presenter.line({
3036
+ code: "agents.list.needs_repair.spacer",
3037
+ text: ""
3038
+ });
3039
+ renderAgentsInfoCard({
3040
+ presenter,
3041
+ codePrefix: "agents.list.needs_repair.card",
3042
+ title: `Needs Repair (${needsRepairEnabledStates.length})`,
3043
+ rows: needsRepairEnabledStates.map((state) => buildStatusAgentCardRow({
3044
+ agentId: state.agentId,
3045
+ displayName: state.displayName,
3046
+ detail: "Connected in OpenMeld, but OpenMeld can't start it on this computer yet"
3047
+ })),
3048
+ minContentWidth: 36
3049
+ });
3050
+ }
3051
+ if (needsChoiceEnabledStates.length > 0) {
3052
+ presenter.line({
3053
+ code: "agents.list.needs_choice.spacer",
3054
+ text: ""
3055
+ });
3056
+ renderAgentsInfoCard({
3057
+ presenter,
3058
+ codePrefix: "agents.list.needs_choice.card",
3059
+ title: `Needs Your Choice (${needsChoiceEnabledStates.length})`,
3060
+ rows: needsChoiceEnabledStates.map((state) => buildStatusAgentCardRow({
3061
+ agentId: state.agentId,
3062
+ displayName: state.displayName,
3063
+ detail: "Connected in OpenMeld, but OpenMeld still needs one install choice on this Mac"
3064
+ })),
3065
+ minContentWidth: 36
3066
+ });
3067
+ }
3068
+ }
3069
+ async function runAgentsShow(input) {
3070
+ const runtime = await resolveRuntimeContext({
3071
+ view: input.view,
3072
+ profile: input.profile
3073
+ });
3074
+ const presenter = createPresenter(runtime);
3075
+ await emitAgentsConfigParseFallbackSignal({
3076
+ presenter,
3077
+ view: runtime.resolvedView
3078
+ });
3079
+ emitAgentsOverviewIfAgent({
3080
+ presenter,
3081
+ resolvedView: runtime.resolvedView,
3082
+ command: "show"
3083
+ });
3084
+ const agentId = normalizeAgentId(input.agentId ?? "");
3085
+ if (!agentId) throw new Error("agents.invalid_agent_id: provide a non-empty <agent-id> for show");
3086
+ const state = await resolveAgentTargetState(agentId);
3087
+ if (!hasAgentRegistration(state)) throw new Error(`agents.not_registered: agent is not registered: ${agentId}`);
3088
+ const registration = state.registration;
3089
+ let agentControllerInterface = null;
3090
+ if (state.targetKind === "builtin" && state.enabled) {
3091
+ const builtinState = state;
3092
+ agentControllerInterface = await resolveAgentControllerInterfaceSnapshot({
3093
+ agentControllerRef: buildAgentControllerRef({
3094
+ id: builtinState.agentId,
3095
+ kind: "builtin"
3096
+ }),
3097
+ state: builtinState
3098
+ });
3099
+ }
3100
+ const payload = {
3101
+ agent: buildAgentRegistrationAgentView(state),
3102
+ agentControllerInterface: input.json === true || runtime.resolvedView === "agent" ? projectLegacyAgentControllerInterface(agentControllerInterface) : agentControllerInterface,
3103
+ installChoices: state.installChoices,
3104
+ launchStatus: formatAgentViewLaunchStatus(state.launchStatus),
3105
+ launchStatusDetail: state.launchStatusDetail,
3106
+ effectiveConfig: buildEffectiveConfig(state)
3107
+ };
3108
+ if (input.json === true) {
3109
+ emitCliJsonEnvelope({
3110
+ schemaVersion: 1,
3111
+ type: "agents.show",
3112
+ ...payload
3113
+ });
3114
+ return;
3115
+ }
3116
+ if (runtime.resolvedView === "agent") {
3117
+ presenter.data({
3118
+ code: "agents.show.result",
3119
+ payload
3120
+ });
3121
+ return;
3122
+ }
3123
+ renderAgentsInfoCard({
3124
+ presenter,
3125
+ codePrefix: "agents.show.card",
3126
+ title: "Agent in OpenMeld",
3127
+ rows: [
3128
+ {
3129
+ label: "Agent",
3130
+ value: formatAgentMention(registration.agentId)
3131
+ },
3132
+ {
3133
+ label: "App",
3134
+ value: registration.displayName
3135
+ },
3136
+ {
3137
+ label: "Status",
3138
+ value: buildAgentsShowStatusValue({
3139
+ enabled: registration.enabled,
3140
+ launchStatus: state.launchStatus
3141
+ })
3142
+ },
3143
+ {
3144
+ label: "Wake",
3145
+ value: buildAgentsShowWakeValue({
3146
+ agentId: registration.agentId,
3147
+ enabled: registration.enabled,
3148
+ launchStatus: state.launchStatus
3149
+ })
3150
+ },
3151
+ {
3152
+ label: "Connection Type",
3153
+ value: `${resolveTransportModeForDisplay(state)} ${pc.dim("on this computer")}`
3154
+ },
3155
+ ...buildAgentControllerInterfaceRows(agentControllerInterface),
3156
+ ...state.launchStatusDetail ? [{
3157
+ label: "Why",
3158
+ value: state.launchStatusDetail
3159
+ }] : [],
3160
+ ...state.launchStatus === "needs_choice" ? [{
3161
+ label: "Next",
3162
+ value: formatInlineOpenMeldCliCommands(`Run \`openmeld agents repair --agent ${registration.agentId}\` and choose one install`)
3163
+ }] : [],
3164
+ ...state.launchStatus === "needs_repair" ? [{
3165
+ label: "Next",
3166
+ value: formatInlineOpenMeldCliCommands(`Run \`openmeld agents repair --agent ${registration.agentId}\``)
3167
+ }] : []
3168
+ ],
3169
+ minContentWidth: 34
3170
+ });
3171
+ }
3172
+ function projectLegacyAgentControllerInterface(snapshot) {
3173
+ if (!snapshot?.usageLimits) return snapshot;
3174
+ const { usageLimits: _usageLimits, ...legacySnapshot } = snapshot;
3175
+ return legacySnapshot;
3176
+ }
3177
+ function createSilentAgentsPresenter(runtime) {
3178
+ return {
3179
+ view: runtime.resolvedView,
3180
+ event: () => void 0,
3181
+ line: () => void 0,
3182
+ data: () => void 0
3183
+ };
3184
+ }
3185
+ async function runAgentsEnable(input) {
3186
+ const runtime = await resolveRuntimeContext({
3187
+ interactive: input.interactive,
3188
+ view: input.view,
3189
+ profile: input.profile
3190
+ });
3191
+ const presenter = input.json === true ? createSilentAgentsPresenter(runtime) : createPresenter(runtime);
3192
+ await emitAgentsConfigParseFallbackSignal({
3193
+ presenter,
3194
+ view: runtime.resolvedView
3195
+ });
3196
+ emitAgentsOverviewIfAgent({
3197
+ presenter,
3198
+ resolvedView: runtime.resolvedView,
3199
+ command: "enable",
3200
+ handled: input.presentation === "handoff"
3201
+ });
3202
+ const explicitIds = normalizeAgentIds(input.agent ?? []);
3203
+ const explicitInstallId = normalizeOptionalString(input.install);
3204
+ validateBuiltinAgentSelectionInput({
3205
+ all: input.all,
3206
+ command: "enable",
3207
+ explicitIds,
3208
+ explicitInstallId
3209
+ });
3210
+ const targetStates = await listAgentTargetStates();
3211
+ const targetStateById = new Map(targetStates.map((state) => [state.agentId, state]));
3212
+ const selectableBuiltinIds = new Set(targetStates.filter(canSelectAgentForNewLocalAgentSetup).map((state) => state.agentId));
3213
+ const requested = input.all ? [...selectableBuiltinIds].sort((left, right) => left.localeCompare(right)) : explicitIds;
3214
+ const summary = {
3215
+ requested,
3216
+ registered: [],
3217
+ reenabled: [],
3218
+ skipped: [],
3219
+ errors: [],
3220
+ allMode: Boolean(input.all)
3221
+ };
3222
+ const launchSelections = await collectEnableLaunchActionSelections({
3223
+ explicitInstallId,
3224
+ interactiveChoiceAllowed: input.json === true ? false : resolveInteractiveChoiceAllowed({
3225
+ all: input.all,
3226
+ runtime
3227
+ }),
3228
+ requested,
3229
+ summary,
3230
+ targetStateById
3231
+ });
3232
+ if (launchSelections.status === "cancelled") {
3233
+ cancel("Cancelled.");
3234
+ return {
3235
+ status: "cancelled",
3236
+ summary
3237
+ };
3238
+ }
3239
+ if (launchSelections.status === "back") return {
3240
+ status: "back",
3241
+ summary
3242
+ };
3243
+ await updateAgentRegistrations((current) => applyEnableSelectionUpdates({
3244
+ current,
3245
+ requested: requested.filter((agentId) => !launchSelections.blockedAgentIds.has(agentId)),
3246
+ selectedLaunchContracts: launchSelections.selectedLaunchContracts,
3247
+ selectableBuiltinIds,
3248
+ summary,
3249
+ targetStateById
3250
+ }));
3251
+ if (input.json === true) {
3252
+ emitCliJsonEnvelope({
3253
+ schemaVersion: 1,
3254
+ type: "agents.enable",
3255
+ ...summary
3256
+ });
3257
+ return {
3258
+ status: "completed",
3259
+ summary
3260
+ };
3261
+ }
3262
+ if (runtime.resolvedView === "agent") {
3263
+ presenter.data({
3264
+ code: "agents.enable.result",
3265
+ payload: summary
3266
+ });
3267
+ return {
3268
+ status: "completed",
3269
+ summary
3270
+ };
3271
+ }
3272
+ if (input.presentation !== "handoff") emitEnableSummaryCardsForHuman({
3273
+ presenter,
3274
+ summary,
3275
+ targetStateById
3276
+ });
3277
+ return {
3278
+ status: "completed",
3279
+ summary
3280
+ };
3281
+ }
3282
+ async function runAgentsRepair(input) {
3283
+ const runtime = await resolveRuntimeContext({
3284
+ interactive: input.interactive,
3285
+ view: input.view,
3286
+ profile: input.profile
3287
+ });
3288
+ const presenter = createPresenter(runtime);
3289
+ await emitAgentsConfigParseFallbackSignal({
3290
+ presenter,
3291
+ view: runtime.resolvedView
3292
+ });
3293
+ emitAgentsOverviewIfAgent({
3294
+ presenter,
3295
+ resolvedView: runtime.resolvedView,
3296
+ command: "repair",
3297
+ handled: input.presentation === "handoff"
3298
+ });
3299
+ const explicitIds = normalizeAgentIds(input.agent ?? []);
3300
+ const explicitInstallId = normalizeOptionalString(input.install);
3301
+ validateBuiltinAgentSelectionInput({
3302
+ all: input.all,
3303
+ command: "repair",
3304
+ explicitIds,
3305
+ explicitInstallId
3306
+ });
3307
+ const targetStates = await listAgentTargetStates();
3308
+ const targetStateById = new Map(targetStates.map((state) => [state.agentId, state]));
3309
+ const requested = input.all ? targetStates.filter((state) => state.targetKind === "builtin" && state.registered && (state.launchStatus === "needs_choice" || state.launchStatus === "needs_repair")).map((state) => state.agentId) : explicitIds;
3310
+ const summary = {
3311
+ requested,
3312
+ repaired: [],
3313
+ skipped: [],
3314
+ errors: [],
3315
+ allMode: Boolean(input.all)
3316
+ };
3317
+ const repairedLaunchContracts = /* @__PURE__ */ new Map();
3318
+ const repairOutcome = await collectRepairLaunchActions({
3319
+ allowReadyForInstallChange: input.changeInstall === true || explicitInstallId !== null,
3320
+ explicitInstallId,
3321
+ interactiveChoiceAllowed: resolveInteractiveChoiceAllowed({
3322
+ all: input.all,
3323
+ runtime
3324
+ }),
3325
+ repairedLaunchContracts,
3326
+ requested,
3327
+ summary,
3328
+ targetStateById
3329
+ });
3330
+ if (repairOutcome === "cancelled") {
3331
+ cancel("Cancelled.");
3332
+ return {
3333
+ status: "cancelled",
3334
+ summary
3335
+ };
3336
+ }
3337
+ if (repairOutcome === "back") return {
3338
+ status: "back",
3339
+ summary
3340
+ };
3341
+ if (repairedLaunchContracts.size > 0) await updateAgentRegistrations((current) => {
3342
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
3343
+ return sortAgentRegistrations(current.map((registration) => {
3344
+ const launchContract = repairedLaunchContracts.get(registration.agentId);
3345
+ if (!launchContract) return registration;
3346
+ return {
3347
+ ...registration,
3348
+ launchContract,
3349
+ updatedAt: nowIso
3350
+ };
3351
+ }));
3352
+ });
3353
+ if (runtime.resolvedView === "agent") {
3354
+ presenter.data({
3355
+ code: "agents.repair.result",
3356
+ payload: summary
3357
+ });
3358
+ return {
3359
+ status: "completed",
3360
+ summary
3361
+ };
3362
+ }
3363
+ if (input.presentation !== "handoff") {
3364
+ renderAgentsInfoCard({
3365
+ presenter,
3366
+ codePrefix: "agents.repair.summary.card",
3367
+ title: "Agents Repaired",
3368
+ rows: buildRepairSummaryRows(summary),
3369
+ minContentWidth: 38
3370
+ });
3371
+ for (const card of buildRepairDetailCards(summary, targetStateById)) {
3372
+ presenter.line({
3373
+ code: `agents.repair.${card.codeSuffix}.spacer`,
3374
+ text: ""
3375
+ });
3376
+ renderAgentsInfoCard({
3377
+ presenter,
3378
+ codePrefix: `agents.repair.${card.codeSuffix}.card`,
3379
+ title: card.title,
3380
+ rows: card.rows,
3381
+ minContentWidth: 32
3382
+ });
3383
+ }
3384
+ }
3385
+ return {
3386
+ status: "completed",
3387
+ summary
3388
+ };
3389
+ }
3390
+ function buildAgentsShowStatusValue(input) {
3391
+ if (input.launchStatus === "needs_choice") return formatAgentsToneText("warning", "Needs your choice on this Mac");
3392
+ if (input.launchStatus === "needs_repair") return formatAgentsToneText("warning", "Needs repair on this computer");
3393
+ if (input.enabled) return formatAgentsToneText("success", "Available in Space");
3394
+ return formatAgentsToneText("warning", "Turned off in Space");
3395
+ }
3396
+ function buildAgentsShowWakeValue(input) {
3397
+ const mention = formatAgentMention(input.agentId);
3398
+ if (input.launchStatus === "needs_choice") return `New ${mention} mentions will not wake it until you choose one install on this Mac`;
3399
+ if (input.launchStatus === "needs_repair") return `New ${mention} mentions will not wake it until you repair it`;
3400
+ if (input.enabled) return `${mention} can wake it`;
3401
+ return `New ${mention} mentions stay off`;
3402
+ }
3403
+ function buildAgentControllerInterfaceRows(snapshot) {
3404
+ if (!snapshot) return [];
3405
+ return createAgentControllerInterfaceDisplayRows(snapshot);
3406
+ }
3407
+ async function promptBuiltinAgentInstallChoice(input) {
3408
+ const choice = await select({
3409
+ message: buildInteractiveMenuMessage({
3410
+ title: `Choose the ${input.displayName} install OpenMeld should use`,
3411
+ description: `We found more than one ${input.displayName} install on this Mac. Pick the one OpenMeld should launch for replies.`
3412
+ }),
3413
+ options: [...input.installChoices.map((installChoice) => ({
3414
+ value: installChoice.installId,
3415
+ label: installChoice.label,
3416
+ hint: installChoice.detail
3417
+ })), {
3418
+ value: "__back__",
3419
+ label: "Back"
3420
+ }]
3421
+ });
3422
+ if (isCancel(choice)) return { status: "cancelled" };
3423
+ if (choice === "__back__") return { status: "back" };
3424
+ return {
3425
+ status: "selected",
3426
+ value: String(choice)
3427
+ };
3428
+ }
3429
+ function buildInstallChoiceNextSteps(input) {
3430
+ return input.installChoices.map((installChoice) => buildBuiltinAgentSelectionCommand({
3431
+ agentIds: [input.agentId],
3432
+ command: input.command,
3433
+ installId: installChoice.installId
3434
+ }));
3435
+ }
3436
+ function createInstallChoiceError(input) {
3437
+ return {
3438
+ agentId: input.agentId,
3439
+ code: "controller.launch.choice_required",
3440
+ humanActionRequired: true,
3441
+ installChoices: input.installChoices,
3442
+ nextSteps: buildInstallChoiceNextSteps({
3443
+ agentId: input.agentId,
3444
+ command: input.command,
3445
+ installChoices: input.installChoices
3446
+ }),
3447
+ reason: input.reason,
3448
+ summary: input.summary ?? "OpenMeld needs one install choice on this Mac before it can continue."
3449
+ };
3450
+ }
3451
+ async function resolveBuiltinLaunchAction(input) {
3452
+ if (!(input.command === "repair" || Boolean(input.explicitInstallId) || input.state.launchStatus === "needs_choice" || input.state.launchStatus === "needs_repair")) return { status: "not_needed" };
3453
+ let repairResult = await repairBuiltinControllerInstall({
3454
+ agentId: input.state.agentId,
3455
+ detected: input.state.detected,
3456
+ displayName: input.state.displayName,
3457
+ installId: input.explicitInstallId,
3458
+ runtimeSupported: input.state.runtimeSupported
3459
+ });
3460
+ if (!repairResult.ok && repairResult.errorCode === "controller.launch.choice_required" && input.interactiveChoiceAllowed) {
3461
+ const installChoice = await promptBuiltinAgentInstallChoice({
3462
+ displayName: input.state.displayName,
3463
+ installChoices: repairResult.installChoices
3464
+ });
3465
+ if (installChoice.status === "cancelled") return { status: "cancelled" };
3466
+ if (installChoice.status === "back" || installChoice.status === "empty") return { status: "back" };
3467
+ repairResult = await repairBuiltinControllerInstall({
3468
+ agentId: input.state.agentId,
3469
+ detected: input.state.detected,
3470
+ displayName: input.state.displayName,
3471
+ installId: installChoice.value,
3472
+ runtimeSupported: input.state.runtimeSupported
3473
+ });
3474
+ }
3475
+ if (!repairResult.ok) return {
3476
+ error: repairResult.errorCode === "controller.launch.choice_required" ? createInstallChoiceError({
3477
+ agentId: input.state.agentId,
3478
+ command: input.command,
3479
+ installChoices: repairResult.installChoices,
3480
+ reason: repairResult.errorMessage
3481
+ }) : {
3482
+ agentId: input.state.agentId,
3483
+ code: repairResult.errorCode,
3484
+ reason: repairResult.errorMessage
3485
+ },
3486
+ status: "blocked"
3487
+ };
3488
+ return {
3489
+ launchContract: repairResult.launchContract,
3490
+ status: "resolved"
3491
+ };
3492
+ }
3493
+ function resolveInteractiveChoiceAllowed(input) {
3494
+ return input.runtime.resolvedView === "human" && canUseInteractivePrompts(input.runtime) && input.all !== true;
3495
+ }
3496
+ async function collectEnableLaunchActionSelections(input) {
3497
+ const blockedAgentIds = /* @__PURE__ */ new Set();
3498
+ const selectedLaunchContracts = /* @__PURE__ */ new Map();
3499
+ for (const requestedId of input.requested) {
3500
+ const state = input.targetStateById.get(requestedId) ?? null;
3501
+ if (!isBuiltinEnableChoiceCandidate(state)) continue;
3502
+ const launchAction = await resolveBuiltinLaunchAction({
3503
+ command: "enable",
3504
+ explicitInstallId: input.explicitInstallId,
3505
+ interactiveChoiceAllowed: input.interactiveChoiceAllowed,
3506
+ state
3507
+ });
3508
+ if (launchAction.status === "not_needed") continue;
3509
+ if (launchAction.status === "cancelled" || launchAction.status === "back") return { status: launchAction.status };
3510
+ if (launchAction.status === "blocked") {
3511
+ blockedAgentIds.add(state.agentId);
3512
+ input.summary.errors.push(launchAction.error);
3513
+ continue;
3514
+ }
3515
+ selectedLaunchContracts.set(state.agentId, launchAction.launchContract);
3516
+ }
3517
+ return {
3518
+ blockedAgentIds,
3519
+ selectedLaunchContracts,
3520
+ status: "completed"
3521
+ };
3522
+ }
3523
+ async function collectRepairLaunchActions(input) {
3524
+ for (const requestedId of input.requested) {
3525
+ const candidate = resolveRepairCandidate({
3526
+ allowReadyForInstallChange: input.allowReadyForInstallChange,
3527
+ requestedId,
3528
+ targetStateById: input.targetStateById
3529
+ });
3530
+ if (!candidate.ok) {
3531
+ input.summary.errors.push(candidate.error);
3532
+ continue;
3533
+ }
3534
+ if (!(input.allowReadyForInstallChange || input.explicitInstallId) && candidate.state.launchStatus === "ready" && candidate.state.registration?.launchContract) {
3535
+ input.summary.skipped.push({
3536
+ agentId: requestedId,
3537
+ reason: "already_ready"
3538
+ });
3539
+ continue;
3540
+ }
3541
+ const launchAction = await resolveBuiltinLaunchAction({
3542
+ command: "repair",
3543
+ explicitInstallId: input.explicitInstallId,
3544
+ interactiveChoiceAllowed: input.interactiveChoiceAllowed,
3545
+ state: candidate.state
3546
+ });
3547
+ if (launchAction.status === "cancelled" || launchAction.status === "back") return launchAction.status;
3548
+ if (launchAction.status === "blocked") {
3549
+ input.summary.errors.push(launchAction.error);
3550
+ continue;
3551
+ }
3552
+ if (launchAction.status === "not_needed") continue;
3553
+ input.repairedLaunchContracts.set(requestedId, launchAction.launchContract);
3554
+ input.summary.repaired.push(requestedId);
3555
+ }
3556
+ return "completed";
3557
+ }
3558
+ function applyEnableSelectionUpdates(input) {
3559
+ const next = [...input.current];
3560
+ const existingById = new Map(next.map((registration, index) => [registration.agentId, index]));
3561
+ for (const requestedId of input.requested) applySingleEnableSelection({
3562
+ existingById,
3563
+ next,
3564
+ requestedId,
3565
+ selectedLaunchContracts: input.selectedLaunchContracts,
3566
+ selectableBuiltinIds: input.selectableBuiltinIds,
3567
+ summary: input.summary,
3568
+ targetStateById: input.targetStateById
3569
+ });
3570
+ return sortAgentRegistrations(next);
3571
+ }
3572
+ function applySingleEnableSelection(input) {
3573
+ if (!input.selectableBuiltinIds.has(input.requestedId)) {
3574
+ const blockedState = input.targetStateById.get(input.requestedId) ?? null;
3575
+ input.summary.errors.push({
3576
+ agentId: input.requestedId,
3577
+ code: "agents.invalid_agent_id",
3578
+ reason: resolveEnableBlockedReason(blockedState)
3579
+ });
3580
+ return;
3581
+ }
3582
+ const targetState = input.targetStateById.get(input.requestedId);
3583
+ if (targetState?.targetKind !== "builtin") {
3584
+ input.summary.errors.push({
3585
+ agentId: input.requestedId,
3586
+ code: "agents.config_write_failed",
3587
+ reason: "selectable built-in target missing from catalog"
3588
+ });
3589
+ return;
3590
+ }
3591
+ const selectedLaunchContract = input.selectedLaunchContracts.get(targetState.agentId) ?? null;
3592
+ const resolvedLaunchContract = selectedLaunchContract ?? targetState.launchContract ?? void 0;
3593
+ if (targetState.launchStatus === "needs_choice" && !selectedLaunchContract) {
3594
+ input.summary.errors.push(createInstallChoiceError({
3595
+ agentId: targetState.agentId,
3596
+ command: "enable",
3597
+ installChoices: targetState.installChoices,
3598
+ reason: targetState.launchStatusDetail ?? "OpenMeld needs one install choice on this Mac before it can turn this on."
3599
+ }));
3600
+ return;
3601
+ }
3602
+ const existingIndex = input.existingById.get(targetState.agentId);
3603
+ if (existingIndex === void 0) {
3604
+ input.next.push(createRegistrationFromCatalogTarget({
3605
+ ...targetState,
3606
+ ...resolvedLaunchContract ? { launchContract: resolvedLaunchContract } : {}
3607
+ }));
3608
+ input.existingById.set(targetState.agentId, input.next.length - 1);
3609
+ input.summary.registered.push(targetState.agentId);
3610
+ return;
3611
+ }
3612
+ const existing = input.next[existingIndex];
3613
+ if (!existing) {
3614
+ input.summary.errors.push({
3615
+ agentId: targetState.agentId,
3616
+ code: "agents.config_write_failed",
3617
+ reason: "registration index missing"
3618
+ });
3619
+ return;
3620
+ }
3621
+ if (existing.enabled && selectedLaunchContract) {
3622
+ input.next[existingIndex] = applyTargetRuntimeDefaults({
3623
+ registration: {
3624
+ ...existing,
3625
+ launchContract: selectedLaunchContract,
3626
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3627
+ },
3628
+ targetState
3629
+ });
3630
+ input.summary.reenabled.push(targetState.agentId);
3631
+ return;
3632
+ }
3633
+ if (existing.enabled) {
3634
+ input.summary.skipped.push({
3635
+ agentId: targetState.agentId,
3636
+ reason: "already_enabled"
3637
+ });
3638
+ return;
3639
+ }
3640
+ input.next[existingIndex] = applyTargetRuntimeDefaults({
3641
+ registration: {
3642
+ ...existing,
3643
+ enabled: true,
3644
+ ...resolvedLaunchContract ? { launchContract: resolvedLaunchContract } : {},
3645
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3646
+ },
3647
+ targetState
3648
+ });
3649
+ input.summary.reenabled.push(targetState.agentId);
3650
+ }
3651
+ function applyTargetRuntimeDefaults(input) {
3652
+ return {
3653
+ ...input.registration,
3654
+ ...input.targetState.defaultStreamingMode ? { streamingMode: input.targetState.defaultStreamingMode } : {},
3655
+ ...input.targetState.defaultTransportMode ? { transportMode: input.targetState.defaultTransportMode } : {}
3656
+ };
3657
+ }
3658
+ function resolveRepairCandidate(input) {
3659
+ const state = input.targetStateById.get(input.requestedId) ?? null;
3660
+ if (!isBuiltinEnableChoiceCandidate(state)) {
3661
+ if (!state) return {
3662
+ error: {
3663
+ agentId: input.requestedId,
3664
+ code: "agents.invalid_agent_id",
3665
+ reason: "unknown built-in target id"
3666
+ },
3667
+ ok: false
3668
+ };
3669
+ return {
3670
+ error: {
3671
+ agentId: input.requestedId,
3672
+ code: "agents.invalid_agent_id",
3673
+ reason: "custom target is not runnable yet"
3674
+ },
3675
+ ok: false
3676
+ };
3677
+ }
3678
+ if (!state.registered) return {
3679
+ error: {
3680
+ agentId: input.requestedId,
3681
+ code: "agents.not_registered",
3682
+ reason: "agent is not registered"
3683
+ },
3684
+ ok: false
3685
+ };
3686
+ if (input.allowReadyForInstallChange !== true && !(state.launchStatus === "needs_choice" || state.launchStatus === "needs_repair")) return {
3687
+ error: {
3688
+ agentId: input.requestedId,
3689
+ code: "controller.launch.needs_repair",
3690
+ reason: "already available"
3691
+ },
3692
+ ok: false
3693
+ };
3694
+ return {
3695
+ ok: true,
3696
+ state
3697
+ };
3698
+ }
3699
+ async function runAgentsDisable(input) {
3700
+ const runtime = await resolveRuntimeContext({
3701
+ view: input.view,
3702
+ profile: input.profile
3703
+ });
3704
+ const presenter = input.json === true ? createSilentAgentsPresenter(runtime) : createPresenter(runtime);
3705
+ await emitAgentsConfigParseFallbackSignal({
3706
+ presenter,
3707
+ view: runtime.resolvedView
3708
+ });
3709
+ emitAgentsOverviewIfAgent({
3710
+ presenter,
3711
+ resolvedView: runtime.resolvedView,
3712
+ command: "disable"
3713
+ });
3714
+ const explicitIds = normalizeAgentIds(input.agent ?? []);
3715
+ validateBuiltinAgentSelectionInput({
3716
+ all: input.all,
3717
+ command: "disable",
3718
+ explicitIds,
3719
+ explicitInstallId: null
3720
+ });
3721
+ const registrations = await listAgentRegistrations();
3722
+ const displayNameById = new Map(registrations.map((registration) => [registration.agentId, registration.displayName]));
3723
+ const requested = input.all ? registrations.map((registration) => registration.agentId) : explicitIds;
3724
+ const summary = {
3725
+ requested,
3726
+ disabled: [],
3727
+ skipped: [],
3728
+ errors: [],
3729
+ allMode: Boolean(input.all)
3730
+ };
3731
+ await updateAgentRegistrations((current) => {
3732
+ const next = [...current];
3733
+ const byId = new Map(next.map((registration, index) => [registration.agentId, index]));
3734
+ for (const agentId of requested) {
3735
+ const index = byId.get(agentId);
3736
+ if (index === void 0) {
3737
+ summary.errors.push({
3738
+ agentId,
3739
+ code: "agents.not_registered",
3740
+ reason: "agent is not registered"
3741
+ });
3742
+ continue;
3743
+ }
3744
+ const registration = next[index];
3745
+ if (!registration) {
3746
+ summary.errors.push({
3747
+ agentId,
3748
+ code: "agents.config_write_failed",
3749
+ reason: "registration index missing"
3750
+ });
3751
+ continue;
3752
+ }
3753
+ if (!registration.enabled) {
3754
+ summary.skipped.push({
3755
+ agentId,
3756
+ reason: "already_disabled"
3757
+ });
3758
+ continue;
3759
+ }
3760
+ next[index] = {
3761
+ ...registration,
3762
+ enabled: false,
3763
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3764
+ };
3765
+ summary.disabled.push(agentId);
3766
+ }
3767
+ return sortAgentRegistrations(next);
3768
+ });
3769
+ if (input.json === true) {
3770
+ emitCliJsonEnvelope({
3771
+ schemaVersion: 1,
3772
+ type: "agents.disable",
3773
+ ...summary
3774
+ });
3775
+ return;
3776
+ }
3777
+ if (runtime.resolvedView === "agent") {
3778
+ presenter.data({
3779
+ code: "agents.disable.result",
3780
+ payload: summary
3781
+ });
3782
+ return;
3783
+ }
3784
+ emitDisableSummaryCardsForHuman({
3785
+ presenter,
3786
+ summary,
3787
+ displayNameById
3788
+ });
3789
+ }
3790
+ async function runAgentsConfig(input) {
3791
+ const runtime = await resolveRuntimeContext({
3792
+ view: input.view,
3793
+ profile: input.profile
3794
+ });
3795
+ const presenter = createPresenter(runtime);
3796
+ await emitAgentsConfigParseFallbackSignal({
3797
+ presenter,
3798
+ view: runtime.resolvedView
3799
+ });
3800
+ emitAgentsOverviewIfAgent({
3801
+ presenter,
3802
+ resolvedView: runtime.resolvedView,
3803
+ command: "config"
3804
+ });
3805
+ const agentId = normalizeAgentId(input.agentId ?? "");
3806
+ if (!agentId) throw new Error("agents.invalid_agent_id: provide a non-empty <agent-id> for config");
3807
+ const enabled = parseAgentEnabledOption(input.enabled);
3808
+ if (enabled === void 0) throw new Error("agents.config_invalid: provide at least one option (--enabled)");
3809
+ const updatedFields = [];
3810
+ const effectiveConfig = (await updateAgentRegistrations((current) => {
3811
+ const index = current.findIndex((item) => item.agentId === agentId);
3812
+ if (index < 0) throw new Error(`agents.not_registered: agent is not registered: ${agentId}`);
3813
+ const next = [...current];
3814
+ const selected = next[index];
3815
+ if (!selected) throw new Error("agents.config_write_failed: registration index missing");
3816
+ const existing = { ...selected };
3817
+ if (enabled !== void 0) {
3818
+ existing.enabled = enabled;
3819
+ updatedFields.push("enabled");
3820
+ }
3821
+ existing.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3822
+ next[index] = existing;
3823
+ return sortAgentRegistrations(next);
3824
+ })).find((item) => item.agentId === agentId) ?? null;
3825
+ if (!effectiveConfig) throw new Error("agents.config_write_failed: config update returned empty");
3826
+ const payload = {
3827
+ agentId,
3828
+ updatedFields,
3829
+ effectiveConfig
3830
+ };
3831
+ if (runtime.resolvedView === "agent") {
3832
+ presenter.data({
3833
+ code: "agents.config.result",
3834
+ payload
3835
+ });
3836
+ return;
3837
+ }
3838
+ renderAgentsInfoCard({
3839
+ presenter,
3840
+ codePrefix: "agents.config.result.card",
3841
+ title: "Agent Updated",
3842
+ rows: [
3843
+ {
3844
+ label: "Agent",
3845
+ value: formatAgentMention(effectiveConfig.agentId)
3846
+ },
3847
+ {
3848
+ label: "App",
3849
+ value: effectiveConfig.displayName
3850
+ },
3851
+ {
3852
+ label: "Status",
3853
+ value: enabled ? formatAgentsToneText("success", "Available for new @mentions") : formatAgentsToneText("warning", "Turned off for new @mentions")
3854
+ },
3855
+ {
3856
+ label: "Wake",
3857
+ value: enabled ? `New ${formatAgentMention(effectiveConfig.agentId)} mentions can wake it` : `New ${formatAgentMention(effectiveConfig.agentId)} mentions stay off`
3858
+ }
3859
+ ],
3860
+ minContentWidth: 34
3861
+ });
3862
+ }
3863
+ async function runAgentsCustomList(input) {
3864
+ const runtime = await resolveRuntimeContext({
3865
+ view: input.view,
3866
+ profile: input.profile
3867
+ });
3868
+ const presenter = createPresenter(runtime);
3869
+ await emitAgentsConfigParseFallbackSignal({
3870
+ presenter,
3871
+ view: runtime.resolvedView
3872
+ });
3873
+ emitAgentsOverviewIfAgent({
3874
+ presenter,
3875
+ resolvedView: runtime.resolvedView,
3876
+ command: "custom_list"
3877
+ });
3878
+ const [targets, states] = await Promise.all([listAgentCustomTargets(), listAgentTargetStates()]);
3879
+ const customStateById = new Map(states.filter((state) => state.targetKind === "custom").map((state) => [state.agentId, state]));
3880
+ const payloadTargets = targets.map((target) => {
3881
+ const state = customStateById.get(target.id);
3882
+ return {
3883
+ id: target.id,
3884
+ name: target.name,
3885
+ dir: target.dir,
3886
+ registered: state?.registered ?? false,
3887
+ enabled: state?.enabled ?? false
3888
+ };
3889
+ });
3890
+ const payload = {
3891
+ targets: payloadTargets,
3892
+ count: payloadTargets.length
3893
+ };
3894
+ if (runtime.resolvedView === "agent") {
3895
+ presenter.data({
3896
+ code: "agents.custom.list.result",
3897
+ payload
3898
+ });
3899
+ return;
3900
+ }
3901
+ emitHumanSectionHeader(presenter, {
3902
+ codePrefix: "agents.custom.list",
3903
+ title: "Custom agents"
3904
+ });
3905
+ if (payloadTargets.length === 0) {
3906
+ presenter.line({
3907
+ code: "agents.custom.list.empty",
3908
+ text: "No custom agents configured."
3909
+ });
3910
+ presenter.line({
3911
+ code: "agents.custom.list.hint",
3912
+ text: formatInlineOpenMeldCliCommands("Tip: run `openmeld agents custom add --name <name> --dir <dir>`.")
3913
+ });
3914
+ return;
3915
+ }
3916
+ presenter.line({
3917
+ code: "agents.custom.list.subtitle",
3918
+ text: `Configured custom agents: ${payloadTargets.length}`
3919
+ });
3920
+ for (const target of payloadTargets) presenter.line({
3921
+ code: "agents.custom.list.item",
3922
+ text: `- ${target.name} (${target.id}) • dir=${target.dir} • registered=${target.registered ? "true" : "false"} • enabled=${target.enabled ? "true" : "false"}`
3923
+ });
3924
+ }
3925
+ async function runAgentsCustomAdd(input) {
3926
+ const runtime = await resolveRuntimeContext({
3927
+ view: input.view,
3928
+ profile: input.profile
3929
+ });
3930
+ const presenter = createPresenter(runtime);
3931
+ await emitAgentsConfigParseFallbackSignal({
3932
+ presenter,
3933
+ view: runtime.resolvedView
3934
+ });
3935
+ emitAgentsOverviewIfAgent({
3936
+ presenter,
3937
+ resolvedView: runtime.resolvedView,
3938
+ command: "custom_add"
3939
+ });
3940
+ const name = normalizeOptionalString(input.name);
3941
+ const dir = normalizeOptionalString(input.dir);
3942
+ if (!(name && dir)) throw new Error("agents.custom.invalid_argument: provide --name <name> and --dir <dir>");
3943
+ if (!isAbsolute(dir)) throw new Error("agents.custom.invalid_argument: --dir must be an absolute path");
3944
+ const id = normalizeOptionalString(input.id) ?? void 0;
3945
+ const target = await addAgentCustomTarget({
3946
+ name,
3947
+ dir,
3948
+ ...id ? { id } : {}
3949
+ });
3950
+ const payload = { target };
3951
+ if (runtime.resolvedView === "agent") {
3952
+ presenter.data({
3953
+ code: "agents.custom.add.result",
3954
+ payload
3955
+ });
3956
+ return;
3957
+ }
3958
+ emitHumanSectionHeader(presenter, {
3959
+ codePrefix: "agents.custom.add",
3960
+ title: "Custom Agent Added"
3961
+ });
3962
+ presenter.line({
3963
+ code: "agents.custom.add.result",
3964
+ text: `${target.name} (${target.id}) -> ${target.dir}`
3965
+ });
3966
+ }
3967
+ async function runAgentsCustomUpdate(input) {
3968
+ const runtime = await resolveRuntimeContext({
3969
+ view: input.view,
3970
+ profile: input.profile
3971
+ });
3972
+ const presenter = createPresenter(runtime);
3973
+ await emitAgentsConfigParseFallbackSignal({
3974
+ presenter,
3975
+ view: runtime.resolvedView
3976
+ });
3977
+ emitAgentsOverviewIfAgent({
3978
+ presenter,
3979
+ resolvedView: runtime.resolvedView,
3980
+ command: "custom_update"
3981
+ });
3982
+ const id = normalizeAgentId(input.id ?? "");
3983
+ if (!id) throw new Error("agents.custom.invalid_argument: provide --id <id>");
3984
+ const name = normalizeOptionalString(input.name) ?? void 0;
3985
+ const dir = normalizeOptionalString(input.dir) ?? void 0;
3986
+ if (name === void 0 && dir === void 0) throw new Error("agents.custom.invalid_argument: provide at least one option (--name/--dir)");
3987
+ if (dir && !isAbsolute(dir)) throw new Error("agents.custom.invalid_argument: --dir must be an absolute path");
3988
+ const updated = await updateAgentCustomTarget({
3989
+ id,
3990
+ ...name ? { name } : {},
3991
+ ...dir ? { dir } : {}
3992
+ });
3993
+ const payload = {
3994
+ target: updated.target,
3995
+ updatedFields: updated.updatedFields
3996
+ };
3997
+ if (runtime.resolvedView === "agent") {
3998
+ presenter.data({
3999
+ code: "agents.custom.update.result",
4000
+ payload
4001
+ });
4002
+ return;
4003
+ }
4004
+ emitHumanSectionHeader(presenter, {
4005
+ codePrefix: "agents.custom.update",
4006
+ title: "Custom Agent Updated"
4007
+ });
4008
+ const fields = updated.updatedFields.length > 0 ? updated.updatedFields.join(", ") : "no field changed";
4009
+ presenter.line({
4010
+ code: "agents.custom.update.result",
4011
+ text: `${updated.target.id}: ${fields}`
4012
+ });
4013
+ }
4014
+ async function runAgentsCustomRemove(input) {
4015
+ const runtime = await resolveRuntimeContext({
4016
+ view: input.view,
4017
+ profile: input.profile
4018
+ });
4019
+ const presenter = createPresenter(runtime);
4020
+ await emitAgentsConfigParseFallbackSignal({
4021
+ presenter,
4022
+ view: runtime.resolvedView
4023
+ });
4024
+ emitAgentsOverviewIfAgent({
4025
+ presenter,
4026
+ resolvedView: runtime.resolvedView,
4027
+ command: "custom_remove"
4028
+ });
4029
+ const id = normalizeAgentId(input.id ?? "");
4030
+ if (!id) throw new Error("agents.custom.invalid_argument: provide --id <id>");
4031
+ if ((await listAgentRegistrations()).some((registration) => registration.agentId === id)) throw new Error(`agents.custom.in_use: target is currently registered: ${id}`);
4032
+ const removed = await removeAgentCustomTarget({ id });
4033
+ const payload = {
4034
+ targetId: id,
4035
+ removed,
4036
+ installsPolicy: "kept"
4037
+ };
4038
+ if (runtime.resolvedView === "agent") {
4039
+ presenter.data({
4040
+ code: "agents.custom.remove.result",
4041
+ payload
4042
+ });
4043
+ return;
4044
+ }
4045
+ emitHumanSectionHeader(presenter, {
4046
+ codePrefix: "agents.custom.remove",
4047
+ title: "Custom Agent Removed"
4048
+ });
4049
+ presenter.line({
4050
+ code: "agents.custom.remove.result",
4051
+ text: `${removed.name} (${removed.id}) removed`
4052
+ });
4053
+ presenter.line({
4054
+ code: "agents.custom.remove.note",
4055
+ text: formatInlineOpenMeldCliCommands("Installs records are kept; cleanup via `openmeld skills uninstall`.")
4056
+ });
4057
+ }
4058
+ function buildAgentRegistrationAgentView(state) {
4059
+ const transportProjection = resolveTransportModeProjection(state);
4060
+ return {
4061
+ ...state.registration,
4062
+ lifecycleStatus: state.lifecycleStatus,
4063
+ configuredTransportMode: transportProjection.configuredTransportMode,
4064
+ configuredTransportModeStatus: transportProjection.configuredTransportModeStatus,
4065
+ defaultTransportMode: transportProjection.defaultTransportMode,
4066
+ effectiveConfig: buildEffectiveConfig(state),
4067
+ effectiveTransportMode: transportProjection.effectiveTransportMode,
4068
+ supportedTransportModes: transportProjection.supportedTransportModes,
4069
+ transportModeSource: transportProjection.transportModeSource
4070
+ };
4071
+ }
4072
+ function buildEffectiveConfig(state) {
4073
+ const transportProjection = resolveTransportModeProjection(state);
4074
+ return {
4075
+ configuredTransportMode: transportProjection.configuredTransportMode,
4076
+ configuredTransportModeStatus: transportProjection.configuredTransportModeStatus,
4077
+ defaultTransportMode: transportProjection.defaultTransportMode,
4078
+ enabled: state.registration.enabled,
4079
+ effectiveTransportMode: transportProjection.effectiveTransportMode,
4080
+ supportedTransportModes: transportProjection.supportedTransportModes,
4081
+ transportMode: transportProjection.effectiveTransportMode,
4082
+ transportModeSource: transportProjection.transportModeSource
4083
+ };
4084
+ }
4085
+ function emitAgentsOverviewIfAgent(input) {
4086
+ if (input.resolvedView !== "agent" || input.handled) return;
4087
+ emitAgentsAgentOverview({
4088
+ presenter: input.presenter,
4089
+ command: input.command
4090
+ });
4091
+ }
4092
+ function resolveTransportModeForDisplay(state) {
4093
+ return formatTransportModeDisplay(resolveTransportModeProjection(state).effectiveTransportMode);
4094
+ }
4095
+ function resolveTransportModeProjection(state) {
4096
+ const supportedTransportModes = state.transportModes.map((transportMode) => transportMode.mode);
4097
+ const configuredTransportMode = state.registration.transportMode ?? null;
4098
+ let configuredTransportModeStatus;
4099
+ if (configuredTransportMode === null) configuredTransportModeStatus = "missing";
4100
+ else if (supportedTransportModes.length === 0 || supportedTransportModes.includes(configuredTransportMode)) configuredTransportModeStatus = "supported";
4101
+ else configuredTransportModeStatus = "unsupported";
4102
+ if (configuredTransportModeStatus === "supported" && configuredTransportMode) return {
4103
+ configuredTransportMode,
4104
+ configuredTransportModeStatus,
4105
+ defaultTransportMode: state.defaultTransportMode ?? null,
4106
+ effectiveTransportMode: configuredTransportMode,
4107
+ supportedTransportModes,
4108
+ transportModeSource: "registration"
4109
+ };
4110
+ const registryTransportMode = state.defaultTransportMode ?? supportedTransportModes[0] ?? null;
4111
+ if (registryTransportMode) return {
4112
+ configuredTransportMode,
4113
+ configuredTransportModeStatus,
4114
+ defaultTransportMode: state.defaultTransportMode ?? null,
4115
+ effectiveTransportMode: registryTransportMode,
4116
+ supportedTransportModes,
4117
+ transportModeSource: "registry_default"
4118
+ };
4119
+ return {
4120
+ configuredTransportMode,
4121
+ configuredTransportModeStatus,
4122
+ defaultTransportMode: null,
4123
+ effectiveTransportMode: DEFAULT_RUNTIME_AGENT_TRANSPORT_MODE,
4124
+ supportedTransportModes,
4125
+ transportModeSource: "global_default"
4126
+ };
4127
+ }
4128
+ async function emitAgentsConfigParseFallbackSignal(input) {
4129
+ const health = await readAgentRuntimeConfigHealth();
4130
+ if (!health.parseFallback) return;
4131
+ const reason = health.reason ?? "unknown";
4132
+ const data = {
4133
+ reason,
4134
+ strategy: "fallback_to_empty_config"
4135
+ };
4136
+ if (input.view === "agent") {
4137
+ input.presenter.line({
4138
+ code: "agents.config_parse_fallback",
4139
+ text: `runtime config parse fallback applied (${reason})`,
4140
+ data
4141
+ });
4142
+ return;
4143
+ }
4144
+ input.presenter.line({
4145
+ code: "agents.config_parse_fallback",
4146
+ text: `warning: runtime config parse fallback applied (${reason})`,
4147
+ data
4148
+ });
4149
+ }
4150
+ //#endregion
4151
+ export { resolveGatewayWebOrigin as C, buildSpaceWebUrl as S, runDaemonServiceParticipationGate as T, promptSearchSelect as _, runAgentsCustomRemove as a, resolveOffsetVisibleWindow as b, runAgentsDisable as c, runAgentsManage as d, runAgentsRepair as f, promptSearchMultiselectWorkbench as g, promptSearchMultiselect as h, runAgentsCustomList as i, runAgentsEnable as l, formatTransportModeDisplay as m, runAgentsConfig as n, runAgentsCustomUpdate as o, runAgentsShow as p, runAgentsCustomAdd as r, runAgentsDetect as s, runAgents as t, runAgentsList as u, clampNumber as v, resolveSpaceWebGatewayOrigin as w, computeRetryDelayMs as x, resolveAnchoredVisibleWindow as y };
4152
+
4153
+ //# sourceMappingURL=agents-Y-UJ74By.js.map