@nexus-ai-fs/tui 0.9.18

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 (193) hide show
  1. package/README.md +30 -0
  2. package/package.json +48 -0
  3. package/src/app.tsx +349 -0
  4. package/src/index.tsx +137 -0
  5. package/src/opentui-env.d.ts +61 -0
  6. package/src/panels/access/access-panel.tsx +597 -0
  7. package/src/panels/access/alert-list.tsx +77 -0
  8. package/src/panels/access/constraint-creator.tsx +128 -0
  9. package/src/panels/access/constraint-list.tsx +72 -0
  10. package/src/panels/access/credential-list.tsx +68 -0
  11. package/src/panels/access/delegation-chain-view.tsx +110 -0
  12. package/src/panels/access/delegation-completer.tsx +120 -0
  13. package/src/panels/access/delegation-creator.tsx +237 -0
  14. package/src/panels/access/delegation-list.tsx +74 -0
  15. package/src/panels/access/fraud-score-view.tsx +94 -0
  16. package/src/panels/access/manifest-creator.tsx +167 -0
  17. package/src/panels/access/manifest-list.tsx +105 -0
  18. package/src/panels/access/namespace-config-view.tsx +525 -0
  19. package/src/panels/access/permission-checker.tsx +231 -0
  20. package/src/panels/agents/agent-status-view.tsx +196 -0
  21. package/src/panels/agents/agents-panel.tsx +493 -0
  22. package/src/panels/agents/delegation-list.tsx +154 -0
  23. package/src/panels/agents/inbox-view.tsx +96 -0
  24. package/src/panels/agents/trajectories-tab.tsx +40 -0
  25. package/src/panels/api-console/api-console-panel.tsx +189 -0
  26. package/src/panels/api-console/codegen-viewer.tsx +36 -0
  27. package/src/panels/api-console/codegen.ts +112 -0
  28. package/src/panels/api-console/endpoint-list.tsx +57 -0
  29. package/src/panels/api-console/request-builder.tsx +69 -0
  30. package/src/panels/api-console/response-viewer.tsx +54 -0
  31. package/src/panels/connectors/available-tab.tsx +357 -0
  32. package/src/panels/connectors/connector-row.tsx +121 -0
  33. package/src/panels/connectors/connectors-panel.tsx +88 -0
  34. package/src/panels/connectors/error-parser.ts +116 -0
  35. package/src/panels/connectors/mounted-tab.tsx +179 -0
  36. package/src/panels/connectors/skills-tab.tsx +235 -0
  37. package/src/panels/connectors/template-generator.ts +211 -0
  38. package/src/panels/connectors/write-tab.tsx +514 -0
  39. package/src/panels/events/audit-tab.tsx +69 -0
  40. package/src/panels/events/audit-trail.tsx +75 -0
  41. package/src/panels/events/connector-detail.tsx +49 -0
  42. package/src/panels/events/connector-list.tsx +73 -0
  43. package/src/panels/events/connectors-tab.tsx +92 -0
  44. package/src/panels/events/event-replay.tsx +80 -0
  45. package/src/panels/events/events-panel.tsx +414 -0
  46. package/src/panels/events/events-tab.tsx +212 -0
  47. package/src/panels/events/lock-list.tsx +54 -0
  48. package/src/panels/events/locks-tab.tsx +103 -0
  49. package/src/panels/events/mcl-replay.tsx +77 -0
  50. package/src/panels/events/mcl-tab.tsx +83 -0
  51. package/src/panels/events/operations-tab-wrapper.tsx +62 -0
  52. package/src/panels/events/operations-tab.tsx +41 -0
  53. package/src/panels/events/replay-tab.tsx +76 -0
  54. package/src/panels/events/secrets-audit.tsx +64 -0
  55. package/src/panels/events/secrets-tab.tsx +75 -0
  56. package/src/panels/events/subscription-list.tsx +54 -0
  57. package/src/panels/events/subscriptions-tab.tsx +82 -0
  58. package/src/panels/files/file-aspects.tsx +93 -0
  59. package/src/panels/files/file-editor.tsx +160 -0
  60. package/src/panels/files/file-explorer-keybindings.ts +468 -0
  61. package/src/panels/files/file-explorer-panel.tsx +545 -0
  62. package/src/panels/files/file-lineage.tsx +163 -0
  63. package/src/panels/files/file-list-item.tsx +28 -0
  64. package/src/panels/files/file-metadata.tsx +62 -0
  65. package/src/panels/files/file-preview.tsx +108 -0
  66. package/src/panels/files/file-schema.tsx +89 -0
  67. package/src/panels/files/file-tree-node.tsx +44 -0
  68. package/src/panels/files/file-tree.tsx +169 -0
  69. package/src/panels/files/share-links-tab.tsx +33 -0
  70. package/src/panels/files/uploads-tab.tsx +45 -0
  71. package/src/panels/payments/approval-list.tsx +83 -0
  72. package/src/panels/payments/balance-card.tsx +43 -0
  73. package/src/panels/payments/budget-card.tsx +70 -0
  74. package/src/panels/payments/payments-panel.tsx +451 -0
  75. package/src/panels/payments/policy-list.tsx +64 -0
  76. package/src/panels/payments/reservation-list.tsx +78 -0
  77. package/src/panels/payments/transaction-list.tsx +103 -0
  78. package/src/panels/payments/transfer-form.tsx +109 -0
  79. package/src/panels/search/column-search.tsx +79 -0
  80. package/src/panels/search/knowledge-view.tsx +100 -0
  81. package/src/panels/search/memory-list.tsx +197 -0
  82. package/src/panels/search/playbook-list.tsx +77 -0
  83. package/src/panels/search/rlm-answer-view.tsx +105 -0
  84. package/src/panels/search/search-panel.tsx +405 -0
  85. package/src/panels/search/search-results.tsx +116 -0
  86. package/src/panels/stack/stack-panel.tsx +474 -0
  87. package/src/panels/versions/conflicts-tab.tsx +59 -0
  88. package/src/panels/versions/entry-detail.tsx +89 -0
  89. package/src/panels/versions/transaction-actions.tsx +34 -0
  90. package/src/panels/versions/transaction-list.tsx +90 -0
  91. package/src/panels/versions/versions-panel.tsx +276 -0
  92. package/src/panels/workflows/execution-list.tsx +102 -0
  93. package/src/panels/workflows/scheduler-view.tsx +135 -0
  94. package/src/panels/workflows/workflow-list.tsx +88 -0
  95. package/src/panels/workflows/workflows-panel.tsx +295 -0
  96. package/src/panels/zones/brick-detail.tsx +136 -0
  97. package/src/panels/zones/brick-list.tsx +56 -0
  98. package/src/panels/zones/cache-tab.tsx +118 -0
  99. package/src/panels/zones/drift-view.tsx +97 -0
  100. package/src/panels/zones/mcp-mounts-tab.tsx +38 -0
  101. package/src/panels/zones/memories-tab.tsx +37 -0
  102. package/src/panels/zones/reindex-status.tsx +84 -0
  103. package/src/panels/zones/workspaces-tab.tsx +37 -0
  104. package/src/panels/zones/zone-list.tsx +73 -0
  105. package/src/panels/zones/zones-panel.tsx +559 -0
  106. package/src/services/command-runner.ts +303 -0
  107. package/src/shared/accessibility-announcements.ts +44 -0
  108. package/src/shared/action-registry.ts +466 -0
  109. package/src/shared/brick-states.ts +91 -0
  110. package/src/shared/command-palette.ts +35 -0
  111. package/src/shared/components/announcement-bar.tsx +30 -0
  112. package/src/shared/components/app-confirm-dialog.tsx +29 -0
  113. package/src/shared/components/breadcrumb.tsx +21 -0
  114. package/src/shared/components/brick-gate.tsx +60 -0
  115. package/src/shared/components/command-output.tsx +95 -0
  116. package/src/shared/components/command-palette.tsx +97 -0
  117. package/src/shared/components/confirm-dialog.tsx +61 -0
  118. package/src/shared/components/diff-viewer.tsx +219 -0
  119. package/src/shared/components/empty-state.tsx +36 -0
  120. package/src/shared/components/error-bar.tsx +60 -0
  121. package/src/shared/components/error-boundary.tsx +53 -0
  122. package/src/shared/components/help-overlay.tsx +99 -0
  123. package/src/shared/components/identity-switcher.tsx +168 -0
  124. package/src/shared/components/loading-indicator.tsx +40 -0
  125. package/src/shared/components/pagination-bar.tsx +68 -0
  126. package/src/shared/components/pre-connection-screen.tsx +398 -0
  127. package/src/shared/components/scroll-indicator.tsx +46 -0
  128. package/src/shared/components/side-nav-utils.ts +68 -0
  129. package/src/shared/components/side-nav.tsx +287 -0
  130. package/src/shared/components/spinner.tsx +26 -0
  131. package/src/shared/components/status-bar.tsx +117 -0
  132. package/src/shared/components/styled-text.tsx +72 -0
  133. package/src/shared/components/sub-tab-bar-utils.ts +100 -0
  134. package/src/shared/components/sub-tab-bar.tsx +40 -0
  135. package/src/shared/components/tab-bar-utils.ts +36 -0
  136. package/src/shared/components/tab-bar.tsx +50 -0
  137. package/src/shared/components/text-input.tsx +73 -0
  138. package/src/shared/components/tooltip.tsx +53 -0
  139. package/src/shared/components/virtual-list.tsx +93 -0
  140. package/src/shared/components/welcome-screen.tsx +111 -0
  141. package/src/shared/hooks/use-api.ts +10 -0
  142. package/src/shared/hooks/use-brick-available.ts +42 -0
  143. package/src/shared/hooks/use-confirm.ts +66 -0
  144. package/src/shared/hooks/use-connection-state.ts +67 -0
  145. package/src/shared/hooks/use-copy.ts +31 -0
  146. package/src/shared/hooks/use-fresh-server.ts +62 -0
  147. package/src/shared/hooks/use-keyboard.ts +58 -0
  148. package/src/shared/hooks/use-list-navigation.ts +106 -0
  149. package/src/shared/hooks/use-swr.ts +117 -0
  150. package/src/shared/hooks/use-tab-fallback.ts +32 -0
  151. package/src/shared/hooks/use-text-input.ts +113 -0
  152. package/src/shared/hooks/use-visible-tabs.ts +61 -0
  153. package/src/shared/lib/circular-buffer.ts +82 -0
  154. package/src/shared/lib/clipboard.ts +14 -0
  155. package/src/shared/nav-items.ts +73 -0
  156. package/src/shared/navigation.ts +110 -0
  157. package/src/shared/status-breadcrumb.ts +74 -0
  158. package/src/shared/syntax-style.ts +3 -0
  159. package/src/shared/tab-visibility.ts +15 -0
  160. package/src/shared/text-style.ts +23 -0
  161. package/src/shared/theme.ts +179 -0
  162. package/src/shared/utils/format-size.ts +20 -0
  163. package/src/shared/utils/format-text.ts +10 -0
  164. package/src/shared/utils/format-time.ts +72 -0
  165. package/src/shared/utils/lru-cache.ts +75 -0
  166. package/src/stores/access-store-types.ts +154 -0
  167. package/src/stores/access-store.ts +674 -0
  168. package/src/stores/agents-store.ts +404 -0
  169. package/src/stores/announcement-store.ts +46 -0
  170. package/src/stores/api-console-store.ts +476 -0
  171. package/src/stores/connectors-store.ts +434 -0
  172. package/src/stores/create-api-action.ts +140 -0
  173. package/src/stores/delegation-store.ts +300 -0
  174. package/src/stores/error-store.ts +102 -0
  175. package/src/stores/events-store.ts +163 -0
  176. package/src/stores/files-store.ts +630 -0
  177. package/src/stores/first-run-store.ts +34 -0
  178. package/src/stores/global-store.ts +255 -0
  179. package/src/stores/infra-store.ts +461 -0
  180. package/src/stores/knowledge-store.ts +358 -0
  181. package/src/stores/lineage-store.ts +126 -0
  182. package/src/stores/mcp-store.ts +147 -0
  183. package/src/stores/payments-store.ts +545 -0
  184. package/src/stores/search-store-types.ts +155 -0
  185. package/src/stores/search-store.ts +656 -0
  186. package/src/stores/share-link-store.ts +151 -0
  187. package/src/stores/stack-store.ts +352 -0
  188. package/src/stores/ui-store.ts +161 -0
  189. package/src/stores/upload-store.ts +131 -0
  190. package/src/stores/versions-store.ts +355 -0
  191. package/src/stores/workflows-store.ts +402 -0
  192. package/src/stores/workspace-store.ts +185 -0
  193. package/src/stores/zones-store.ts +378 -0
@@ -0,0 +1,404 @@
1
+ /**
2
+ * Zustand store for the Agents panel: status, delegations, inbox, identity.
3
+ */
4
+
5
+ import { create } from "zustand";
6
+ import type { FetchClient } from "@nexus-ai-fs/api-client";
7
+ import { createApiAction, categorizeError } from "./create-api-action.js";
8
+ import { useErrorStore } from "./error-store.js";
9
+ import { useUiStore } from "./ui-store.js";
10
+ export type { DelegationItem } from "./delegation-store.js";
11
+ import type { DelegationItem } from "./delegation-store.js";
12
+
13
+ // =============================================================================
14
+ // Types
15
+ // =============================================================================
16
+
17
+ export type AgentPhase =
18
+ | "warming"
19
+ | "ready"
20
+ | "active"
21
+ | "thinking"
22
+ | "idle"
23
+ | "suspended"
24
+ | "evicted";
25
+
26
+ export interface AgentCondition {
27
+ readonly type: string;
28
+ readonly status: string;
29
+ readonly reason: string;
30
+ readonly message: string;
31
+ readonly last_transition: string;
32
+ }
33
+
34
+ export interface AgentResourceUsage {
35
+ readonly tokens_used: number;
36
+ readonly storage_used_mb: number;
37
+ readonly context_usage_pct: number;
38
+ }
39
+
40
+ export interface AgentStatus {
41
+ readonly agent_id: string;
42
+ readonly phase: AgentPhase;
43
+ readonly observed_generation: number;
44
+ readonly conditions: readonly AgentCondition[];
45
+ readonly resource_usage: AgentResourceUsage;
46
+ readonly last_heartbeat: string | null;
47
+ readonly last_activity: string | null;
48
+ readonly inbox_depth: number;
49
+ readonly context_usage_pct: number;
50
+ }
51
+
52
+ export interface AgentSpec {
53
+ readonly agent_type: string;
54
+ readonly capabilities: readonly string[];
55
+ readonly qos_class: string;
56
+ readonly zone_affinity: string | null;
57
+ readonly spec_generation: number;
58
+ }
59
+
60
+ export interface AgentIdentity {
61
+ readonly agent_id: string;
62
+ readonly key_id: string;
63
+ readonly did: string;
64
+ readonly algorithm: string;
65
+ readonly public_key_hex: string;
66
+ readonly created_at: string | null;
67
+ readonly expires_at: string | null;
68
+ }
69
+
70
+ // DelegationItem re-exported from delegation-store.ts (canonical source)
71
+
72
+ export interface InboxMessage {
73
+ readonly filename: string;
74
+ }
75
+
76
+ export interface AgentListItem {
77
+ readonly agent_id: string;
78
+ readonly owner_id: string;
79
+ readonly zone_id: string | null;
80
+ readonly name: string | null;
81
+ readonly state: string;
82
+ readonly generation: number;
83
+ }
84
+
85
+ export interface TrajectoryItem {
86
+ readonly trace_id: string;
87
+ readonly agent_id: string;
88
+ readonly status: string;
89
+ readonly started_at: string | null;
90
+ readonly completed_at: string | null;
91
+ readonly step_count: number;
92
+ }
93
+
94
+ export type AgentTab = "status" | "delegations" | "inbox" | "trajectories";
95
+
96
+ // =============================================================================
97
+ // Store
98
+ // =============================================================================
99
+
100
+ export interface AgentsState {
101
+ // Agent list (we track agents we've queried)
102
+ readonly knownAgents: readonly string[];
103
+ readonly selectedAgentId: string | null;
104
+ readonly selectedAgentIndex: number;
105
+
106
+ // Fetched agent list
107
+ readonly agents: readonly AgentListItem[];
108
+ readonly agentsLoading: boolean;
109
+
110
+ // Detail tabs
111
+ readonly activeTab: AgentTab;
112
+
113
+ // Status
114
+ readonly agentStatus: AgentStatus | null;
115
+ readonly agentSpec: AgentSpec | null;
116
+ readonly agentIdentity: AgentIdentity | null;
117
+ readonly statusLoading: boolean;
118
+
119
+ // Trust score
120
+ readonly trustScore: number | null;
121
+ readonly trustScoreLoading: boolean;
122
+
123
+ // Reputation
124
+ readonly reputation: unknown | null;
125
+ readonly reputationLoading: boolean;
126
+
127
+ // Delegations
128
+ readonly delegations: readonly DelegationItem[];
129
+ readonly delegationsLoading: boolean;
130
+ readonly selectedDelegationIndex: number;
131
+
132
+ // Inbox
133
+ readonly inboxMessages: readonly InboxMessage[];
134
+ readonly inboxCount: number;
135
+ readonly processedMessages: readonly InboxMessage[];
136
+ readonly deadLetterMessages: readonly InboxMessage[];
137
+ readonly inboxLoading: boolean;
138
+
139
+ // Permissions
140
+ readonly agentPermissions: readonly { readonly relation: string; readonly object_type: string; readonly object_id: string }[];
141
+
142
+ // Trajectories
143
+ readonly trajectories: readonly TrajectoryItem[];
144
+ readonly trajectoriesLoading: boolean;
145
+
146
+ // Error
147
+ readonly error: string | null;
148
+
149
+ // Actions
150
+ readonly setSelectedAgentId: (id: string) => void;
151
+ readonly setSelectedAgentIndex: (index: number) => void;
152
+ readonly setActiveTab: (tab: AgentTab) => void;
153
+ readonly addKnownAgent: (id: string) => void;
154
+ readonly fetchAgents: (zoneId: string, client: FetchClient) => Promise<void>;
155
+ readonly fetchAgentStatus: (agentId: string, client: FetchClient) => Promise<void>;
156
+ readonly fetchAgentSpec: (agentId: string, client: FetchClient) => Promise<void>;
157
+ readonly fetchAgentIdentity: (agentId: string, client: FetchClient) => Promise<void>;
158
+ readonly fetchDelegations: (agentId: string, client: FetchClient) => Promise<void>;
159
+ readonly fetchInbox: (agentId: string, client: FetchClient) => Promise<void>;
160
+ readonly fetchTrajectories: (agentId: string, client: FetchClient) => Promise<void>;
161
+ readonly revokeDelegation: (delegationId: string, client: FetchClient) => Promise<void>;
162
+ readonly warmupAgent: (agentId: string, client: FetchClient) => Promise<void>;
163
+ readonly evictAgent: (agentId: string, client: FetchClient) => Promise<void>;
164
+ readonly verifyAgent: (agentId: string, client: FetchClient) => Promise<void>;
165
+ readonly fetchTrustScore: (agentId: string, client: FetchClient) => Promise<void>;
166
+ readonly fetchAgentReputation: (agentId: string, client: FetchClient) => Promise<void>;
167
+ readonly setSelectedDelegationIndex: (index: number) => void;
168
+ }
169
+
170
+ const SOURCE = "agents";
171
+
172
+ export const useAgentsStore = create<AgentsState>((set, get) => ({
173
+ knownAgents: [],
174
+ selectedAgentId: null,
175
+ selectedAgentIndex: 0,
176
+ agents: [],
177
+ agentsLoading: false,
178
+ activeTab: "status",
179
+ agentStatus: null,
180
+ agentSpec: null,
181
+ agentIdentity: null,
182
+ statusLoading: false,
183
+ trustScore: null,
184
+ trustScoreLoading: false,
185
+ reputation: null,
186
+ reputationLoading: false,
187
+ delegations: [],
188
+ delegationsLoading: false,
189
+ selectedDelegationIndex: 0,
190
+ inboxMessages: [],
191
+ processedMessages: [],
192
+ deadLetterMessages: [],
193
+ inboxCount: 0,
194
+ inboxLoading: false,
195
+ agentPermissions: [],
196
+ trajectories: [],
197
+ trajectoriesLoading: false,
198
+ error: null,
199
+
200
+ setSelectedAgentId: (id) => {
201
+ set({ selectedAgentId: id, error: null });
202
+ },
203
+
204
+ setSelectedAgentIndex: (index) => {
205
+ set({ selectedAgentIndex: index });
206
+ },
207
+
208
+ setActiveTab: (tab) => {
209
+ set({ activeTab: tab });
210
+ },
211
+
212
+ addKnownAgent: (id) => {
213
+ const { knownAgents } = get();
214
+ if (knownAgents.includes(id)) return;
215
+ set({ knownAgents: [...knownAgents, id] });
216
+ },
217
+
218
+ // =========================================================================
219
+ // Actions migrated to createApiAction (Decision 5A)
220
+ // =========================================================================
221
+
222
+ fetchAgentStatus: createApiAction<AgentsState, [string, FetchClient]>(set, {
223
+ loadingKey: "statusLoading",
224
+ source: SOURCE,
225
+ action: async (agentId, client) => {
226
+ const response = await client.get<Omit<AgentStatus, "agent_id">>(
227
+ `/api/v2/agents/${encodeURIComponent(agentId)}/status`,
228
+ );
229
+ return { agentStatus: { ...response, agent_id: agentId } };
230
+ },
231
+ }),
232
+
233
+ fetchAgents: createApiAction<AgentsState, [string, FetchClient]>(set, {
234
+ loadingKey: "agentsLoading",
235
+ source: SOURCE,
236
+ action: async (zoneId, client) => {
237
+ const response = await client.get<{
238
+ readonly agents: readonly AgentListItem[];
239
+ }>(`/api/v2/agents?zone_id=${encodeURIComponent(zoneId)}&limit=50&offset=0`);
240
+ return { agents: response.agents };
241
+ },
242
+ }),
243
+
244
+ fetchDelegations: createApiAction<AgentsState, [string, FetchClient]>(set, {
245
+ loadingKey: "delegationsLoading",
246
+ source: SOURCE,
247
+ action: async (agentId, client) => {
248
+ const agentParam = agentId ? `&agent_id=${encodeURIComponent(agentId)}` : "";
249
+ const response = await client.get<{
250
+ readonly delegations: readonly DelegationItem[];
251
+ readonly total: number;
252
+ }>(`/api/v2/agents/delegate?limit=50&offset=0${agentParam}`);
253
+ return { delegations: response.delegations, selectedDelegationIndex: 0 };
254
+ },
255
+ }),
256
+
257
+ fetchInbox: createApiAction<AgentsState, [string, FetchClient]>(set, {
258
+ loadingKey: "inboxLoading",
259
+ source: SOURCE,
260
+ action: async (agentId, client) => {
261
+ const encodedId = encodeURIComponent(agentId);
262
+ type InboxResp = { readonly messages: readonly InboxMessage[]; readonly total: number };
263
+ type FilesResp = { readonly items: readonly { readonly name: string }[] };
264
+
265
+ // Fetch inbox via IPC endpoint, processed/dead_letter via files API
266
+ const listFolder = (folder: string): Promise<readonly InboxMessage[]> =>
267
+ client.get<FilesResp>(
268
+ `/api/v2/files/list?path=${encodeURIComponent(`/agents/${agentId}/${folder}`)}&limit=100`,
269
+ ).then((r) => r.items.filter((f) => f.name.endsWith(".json")).map((f) => ({ filename: f.name })))
270
+ .catch(() => []);
271
+
272
+ const [inboxResp, processedMsgs, deadLetterMsgs] = await Promise.all([
273
+ client.get<InboxResp>(`/api/v2/ipc/inbox/${encodedId}`)
274
+ .catch(() => ({ messages: [] as InboxMessage[], total: 0 })),
275
+ listFolder("processed"),
276
+ listFolder("dead_letter"),
277
+ ]);
278
+ return {
279
+ inboxMessages: inboxResp.messages,
280
+ inboxCount: inboxResp.total,
281
+ processedMessages: processedMsgs,
282
+ deadLetterMessages: deadLetterMsgs,
283
+ };
284
+ },
285
+ }),
286
+
287
+ fetchTrajectories: createApiAction<AgentsState, [string, FetchClient]>(set, {
288
+ loadingKey: "trajectoriesLoading",
289
+ source: SOURCE,
290
+ action: async (agentId, client) => {
291
+ const response = await client.get<{
292
+ readonly trajectories: readonly TrajectoryItem[];
293
+ }>(`/api/v2/trajectories?agent_id=${encodeURIComponent(agentId)}&limit=20`);
294
+ return { trajectories: response.trajectories ?? [] };
295
+ },
296
+ }),
297
+
298
+ fetchTrustScore: createApiAction<AgentsState, [string, FetchClient]>(set, {
299
+ loadingKey: "trustScoreLoading",
300
+ source: SOURCE,
301
+ action: async (agentId, client) => {
302
+ const response = await client.get<{ readonly trust_score: number }>(
303
+ `/api/v2/agents/${encodeURIComponent(agentId)}/trust-score`,
304
+ );
305
+ return { trustScore: response.trust_score };
306
+ },
307
+ }),
308
+
309
+ fetchAgentReputation: createApiAction<AgentsState, [string, FetchClient]>(set, {
310
+ loadingKey: "reputationLoading",
311
+ source: SOURCE,
312
+ action: async (agentId, client) => {
313
+ const response = await client.get<unknown>(
314
+ `/api/v2/agents/${encodeURIComponent(agentId)}/reputation`,
315
+ );
316
+ return { reputation: response };
317
+ },
318
+ }),
319
+
320
+ // =========================================================================
321
+ // Actions without loading keys — inline but with error store integration
322
+ // =========================================================================
323
+
324
+ fetchAgentSpec: async (agentId, client) => {
325
+ try {
326
+ const response = await client.get<AgentSpec>(
327
+ `/api/v2/agents/${encodeURIComponent(agentId)}/spec`,
328
+ );
329
+ set({ agentSpec: response, error: null });
330
+ useUiStore.getState().markDataUpdated("agents");
331
+ } catch (err) {
332
+ const message = err instanceof Error ? err.message : "Failed to fetch agent spec";
333
+ set({ error: message });
334
+ useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
335
+ }
336
+ },
337
+
338
+ fetchAgentIdentity: async (agentId, client) => {
339
+ try {
340
+ const response = await client.get<AgentIdentity>(
341
+ `/api/v2/agents/${encodeURIComponent(agentId)}/identity`,
342
+ );
343
+ set({ agentIdentity: response, error: null });
344
+ useUiStore.getState().markDataUpdated("agents");
345
+ } catch (err) {
346
+ const message = err instanceof Error ? err.message : "Failed to fetch agent identity";
347
+ set({ error: message });
348
+ useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
349
+ }
350
+ },
351
+
352
+ revokeDelegation: async (delegationId, client) => {
353
+ try {
354
+ await client.delete(`/api/v2/agents/delegate/${encodeURIComponent(delegationId)}`);
355
+ set((state) => ({
356
+ delegations: state.delegations.map((d) =>
357
+ d.delegation_id === delegationId ? { ...d, status: "revoked" as const } : d,
358
+ ),
359
+ error: null,
360
+ }));
361
+ } catch (err) {
362
+ const message = err instanceof Error ? err.message : "Failed to revoke delegation";
363
+ set({ error: message });
364
+ useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
365
+ }
366
+ },
367
+
368
+ warmupAgent: async (agentId, client) => {
369
+ set({ error: null });
370
+ try {
371
+ await client.post(`/api/v2/agents/${encodeURIComponent(agentId)}/warmup`, {});
372
+ } catch (err) {
373
+ const message = err instanceof Error ? err.message : "Failed to warmup agent";
374
+ set({ error: message });
375
+ useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
376
+ }
377
+ },
378
+
379
+ evictAgent: async (agentId, client) => {
380
+ set({ error: null });
381
+ try {
382
+ await client.post(`/api/v2/agents/${encodeURIComponent(agentId)}/evict`, {});
383
+ } catch (err) {
384
+ const message = err instanceof Error ? err.message : "Failed to evict agent";
385
+ set({ error: message });
386
+ useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
387
+ }
388
+ },
389
+
390
+ verifyAgent: async (agentId, client) => {
391
+ set({ error: null });
392
+ try {
393
+ await client.post(`/api/v2/agents/${encodeURIComponent(agentId)}/verify`, {});
394
+ } catch (err) {
395
+ const message = err instanceof Error ? err.message : "Failed to verify agent";
396
+ set({ error: message });
397
+ useErrorStore.getState().pushError({ message, category: categorizeError(message), source: SOURCE });
398
+ }
399
+ },
400
+
401
+ setSelectedDelegationIndex: (index) => {
402
+ set({ selectedDelegationIndex: index });
403
+ },
404
+ }));
@@ -0,0 +1,46 @@
1
+ import { create } from "zustand";
2
+ import {
3
+ normalizeAnnouncementMessage,
4
+ type AnnouncementLevel,
5
+ } from "../shared/accessibility-announcements.js";
6
+
7
+ export interface AnnouncementState {
8
+ readonly message: string | null;
9
+ readonly level: AnnouncementLevel;
10
+ readonly sequence: number;
11
+ readonly announce: (message: string, level?: AnnouncementLevel) => void;
12
+ readonly clear: () => void;
13
+ }
14
+
15
+ function emitAnnouncementToStderr(message: string): void {
16
+ if (process.env.NEXUS_TUI_SCREEN_READER_STDERR !== "1") return;
17
+ try {
18
+ process.stderr.write(`${message}\n`);
19
+ } catch {
20
+ // Best-effort transport only.
21
+ }
22
+ }
23
+
24
+ export const useAnnouncementStore = create<AnnouncementState>((set) => ({
25
+ message: null,
26
+ level: "info",
27
+ sequence: 0,
28
+
29
+ announce: (message, level = "info") => {
30
+ const normalized = normalizeAnnouncementMessage(message);
31
+ if (!normalized) return;
32
+ emitAnnouncementToStderr(normalized);
33
+ set((state) => ({
34
+ message: normalized,
35
+ level,
36
+ sequence: state.sequence + 1,
37
+ }));
38
+ },
39
+
40
+ clear: () => {
41
+ set((state) => ({
42
+ ...state,
43
+ message: null,
44
+ }));
45
+ },
46
+ }));