zidane-ui 9.1.4 → 9.1.6

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 (251) hide show
  1. package/dist/adapters/server-extensions-host.js +1 -1
  2. package/dist/adapters/server-host.js +2 -1
  3. package/dist/adapters/server-machines-host.js +1 -1
  4. package/dist/adapters/server-session-host.js +87 -13
  5. package/dist/adapters/server-settings-host.js +1 -1
  6. package/dist/appearance/AppearanceProvider.js +2 -2
  7. package/dist/appearance/headless/index.js +1 -1
  8. package/dist/approvals/headless/index.js +2 -2
  9. package/dist/chat/HostTranscript.d.ts +5 -1
  10. package/dist/chat/HostTranscript.js +16 -4
  11. package/dist/chat/RunEstimateChip.d.ts +5 -3
  12. package/dist/chat/RunEstimateChip.js +33 -3
  13. package/dist/chat/Transcript.js +1 -1
  14. package/dist/chat/composer/ConversationBadges.js +16 -10
  15. package/dist/chat/composer/composer-bus.d.ts +2 -2
  16. package/dist/chat/composer/composer-bus.js +2 -2
  17. package/dist/chat/composer/use-composer-stores.js +1 -1
  18. package/dist/chat/composer/use-run-meter.js +1 -1
  19. package/dist/chat/headless/composer/conversation-badges.d.ts +8 -3
  20. package/dist/chat/headless/composer/conversation-badges.js +26 -11
  21. package/dist/chat/headless/composer/index.js +2 -2
  22. package/dist/chat/headless/composer/labels-chrome.d.ts +3 -1
  23. package/dist/chat/headless/composer/labels-chrome.js +3 -1
  24. package/dist/chat/headless/index.d.ts +6 -3
  25. package/dist/chat/headless/index.js +16 -13
  26. package/dist/chat/headless/labels.d.ts +54 -1
  27. package/dist/chat/headless/labels.js +62 -2
  28. package/dist/chat/headless/secrets.d.ts +32 -0
  29. package/dist/chat/headless/secrets.js +43 -0
  30. package/dist/chat/headless/session-list.d.ts +45 -0
  31. package/dist/chat/headless/sidebar/estimates.js +13 -1
  32. package/dist/chat/headless/sidebar/history.d.ts +71 -0
  33. package/dist/chat/headless/sidebar/history.js +174 -0
  34. package/dist/chat/headless/sidebar/index.d.ts +3 -2
  35. package/dist/chat/headless/sidebar/index.js +2 -1
  36. package/dist/chat/headless/sidebar/labels.d.ts +50 -0
  37. package/dist/chat/headless/sidebar/labels.js +1 -0
  38. package/dist/chat/headless/sidebar/query.d.ts +2 -2
  39. package/dist/chat/headless/sidebar/query.js +6 -3
  40. package/dist/chat/headless/sidebar/types.d.ts +7 -1
  41. package/dist/chat/headless/sidebar/view.js +2 -0
  42. package/dist/chat/headless/transcript/auth-failure.d.ts +67 -0
  43. package/dist/chat/headless/transcript/auth-failure.js +87 -0
  44. package/dist/chat/headless/transcript/index.d.ts +2 -1
  45. package/dist/chat/headless/transcript/index.js +2 -1
  46. package/dist/chat/headless/transcript/nesting.d.ts +3 -3
  47. package/dist/chat/headless/transcript/nesting.js +12 -4
  48. package/dist/chat/index.d.ts +4 -2
  49. package/dist/chat/index.js +3 -1
  50. package/dist/chat/interactions/InboxPanel.d.ts +6 -2
  51. package/dist/chat/interactions/InboxPanel.js +82 -44
  52. package/dist/chat/interactions/PlanCard.d.ts +2 -2
  53. package/dist/chat/interactions/PlanCard.js +1 -1
  54. package/dist/chat/interactions/QuestionField.d.ts +8 -3
  55. package/dist/chat/interactions/QuestionField.js +89 -8
  56. package/dist/chat/interactions/QuestionForm.d.ts +3 -1
  57. package/dist/chat/interactions/QuestionForm.js +48 -10
  58. package/dist/chat/interactions/SecretInput.d.ts +32 -0
  59. package/dist/chat/interactions/SecretInput.js +72 -0
  60. package/dist/chat/interactions/card-ui.d.ts +5 -3
  61. package/dist/chat/interactions/card-ui.js +5 -3
  62. package/dist/chat/interactions/index.d.ts +2 -1
  63. package/dist/chat/interactions/index.js +2 -1
  64. package/dist/chat/sidebar/SessionSidebar.js +2 -0
  65. package/dist/chat/sidebar/SidebarFilterMenu.d.ts +7 -2
  66. package/dist/chat/sidebar/SidebarFilterMenu.js +26 -2
  67. package/dist/chat/sidebar/SidebarSessionRow.d.ts +3 -1
  68. package/dist/chat/sidebar/SidebarSessionRow.js +8 -3
  69. package/dist/chat/sidebar/sidebar-live.js +8 -4
  70. package/dist/chat/transcript/AuthFailureCard.d.ts +34 -0
  71. package/dist/chat/transcript/AuthFailureCard.js +358 -0
  72. package/dist/chat/transcript/InteractionToolBody.js +12 -2
  73. package/dist/chat/transcript/actions.d.ts +24 -1
  74. package/dist/chat/transcript/index.d.ts +3 -2
  75. package/dist/chat/transcript/index.js +2 -1
  76. package/dist/chat/transcript/notices.d.ts +2 -1
  77. package/dist/chat/transcript/notices.js +13 -1
  78. package/dist/chat/transcript/script-input.d.ts +1 -1
  79. package/dist/chat/transcript/shell-block.js +1 -1
  80. package/dist/components/HowItWorks.d.ts +8 -1
  81. package/dist/components/HowItWorks.js +2 -2
  82. package/dist/components/RowActionsMenu.d.ts +6 -1
  83. package/dist/components/RowActionsMenu.js +4 -0
  84. package/dist/core.css +24 -0
  85. package/dist/dist/{index-WlE93Q6d.d.ts → index-DqCio7Nd.d.ts} +36 -3
  86. package/dist/dist/index-f-XWIoYo.d.ts +1 -0
  87. package/dist/dist/index.d.ts +6 -0
  88. package/dist/dist/sandbox-B4OW6oNx.d.ts +2 -0
  89. package/dist/dist/timeout-B5pO5I5a.d.ts +30 -0
  90. package/dist/extensions/ExtensionSettingsPage.js +1 -1
  91. package/dist/extensions/ExtensionSettingsSidebar.js +1 -1
  92. package/dist/extensions/headless/fixture-host.js +1 -1
  93. package/dist/extensions/headless/index.js +4 -4
  94. package/dist/extensions/hooks.js +1 -1
  95. package/dist/files/DiffsTab.js +1 -1
  96. package/dist/files/EditorColumn.js +1 -1
  97. package/dist/files/MediaDiff.js +1 -1
  98. package/dist/files/SearchPanel.js +121 -53
  99. package/dist/files/headless/name-match.js +29 -14
  100. package/dist/files/useEntryActions.js +1 -1
  101. package/dist/files/useOpenFiles.js +1 -1
  102. package/dist/git/WherePicker.js +2 -2
  103. package/dist/git/headless/environment.d.ts +6 -1
  104. package/dist/git/headless/environment.js +5 -2
  105. package/dist/git/headless/labels.d.ts +2 -0
  106. package/dist/git/headless/labels.js +2 -0
  107. package/dist/git/index.d.ts +2 -1
  108. package/dist/git/index.js +2 -1
  109. package/dist/git/useStartFromPanel.d.ts +22 -0
  110. package/dist/git/useStartFromPanel.js +91 -0
  111. package/dist/keybindings/KeyCommands.js +1 -1
  112. package/dist/keybindings/KeybindingsProvider.js +1 -1
  113. package/dist/keybindings/headless/catalog.d.ts +1 -1
  114. package/dist/keybindings/headless/catalog.js +14 -2
  115. package/dist/keybindings/headless/index.js +2 -2
  116. package/dist/keybindings/headless/key-scope.d.ts +4 -2
  117. package/dist/keybindings/headless/key-scope.js +17 -2
  118. package/dist/keybindings/headless/overrides.js +1 -1
  119. package/dist/machines/HostSyncPanel.js +1 -1
  120. package/dist/machines/SyncOutcomeBadge.js +1 -1
  121. package/dist/machines/headless/index.js +1 -1
  122. package/dist/onboarding/demos/features.d.ts +9 -1
  123. package/dist/onboarding/demos/features.js +107 -2
  124. package/dist/onboarding/demos/how-it-works.d.ts +5 -1
  125. package/dist/onboarding/demos/how-it-works.js +120 -2
  126. package/dist/onboarding/demos/index.js +1 -1
  127. package/dist/onboarding/headless/labels.d.ts +17 -0
  128. package/dist/onboarding/headless/labels.js +17 -0
  129. package/dist/onboarding/index.js +4 -4
  130. package/dist/onboarding/marks.d.ts +1 -2
  131. package/dist/onboarding/marks.js +4 -12
  132. package/dist/onboarding/steps/ConnectorsStep.js +1 -1
  133. package/dist/onboarding/steps/ReadyStep.js +1 -1
  134. package/dist/onboarding/steps/SkillsStep.js +1 -1
  135. package/dist/onboarding/steps/connect/ProviderPanel.js +1 -1
  136. package/dist/onboarding/tutorials.d.ts +3 -1
  137. package/dist/onboarding/tutorials.js +2 -1
  138. package/dist/palette/app-sources.js +1 -1
  139. package/dist/palette/headless/app-sources.js +10 -0
  140. package/dist/palette/headless/labels.js +2 -0
  141. package/dist/session/ArtifactsPanel.js +1 -1
  142. package/dist/session/PanelAttention.d.ts +14 -6
  143. package/dist/session/PanelAttention.js +35 -24
  144. package/dist/session/PlanTracker.d.ts +54 -0
  145. package/dist/session/PlanTracker.js +413 -0
  146. package/dist/session/ProjectHome.d.ts +15 -2
  147. package/dist/session/ProjectHome.js +158 -85
  148. package/dist/session/RemindersPanel.d.ts +25 -0
  149. package/dist/session/RemindersPanel.js +526 -0
  150. package/dist/session/SecretsPanel.d.ts +19 -0
  151. package/dist/session/SecretsPanel.js +241 -0
  152. package/dist/session/SessionProvider.d.ts +26 -5
  153. package/dist/session/SessionProvider.js +51 -6
  154. package/dist/session/StepStatusIcon.d.ts +24 -0
  155. package/dist/session/StepStatusIcon.js +50 -0
  156. package/dist/session/TodoList.d.ts +4 -3
  157. package/dist/session/TodoList.js +6 -5
  158. package/dist/session/headless/fixture-host.d.ts +32 -22
  159. package/dist/session/headless/fixture-host.js +86 -51
  160. package/dist/session/headless/fixture-plans.d.ts +57 -0
  161. package/dist/session/headless/fixture-plans.js +141 -0
  162. package/dist/session/headless/fixture-schedules.d.ts +49 -0
  163. package/dist/session/headless/fixture-schedules.js +133 -0
  164. package/dist/session/headless/home-layout.d.ts +10 -2
  165. package/dist/session/headless/home-layout.js +7 -2
  166. package/dist/session/headless/host.d.ts +85 -13
  167. package/dist/session/headless/index.d.ts +5 -3
  168. package/dist/session/headless/index.js +5 -3
  169. package/dist/session/headless/labels.d.ts +95 -12
  170. package/dist/session/headless/labels.js +67 -18
  171. package/dist/session/headless/plans.d.ts +53 -0
  172. package/dist/session/headless/plans.js +78 -0
  173. package/dist/session/headless/reminder-labels.d.ts +63 -0
  174. package/dist/session/headless/reminder-labels.js +84 -0
  175. package/dist/session/headless/schedules.d.ts +93 -0
  176. package/dist/session/headless/schedules.js +190 -0
  177. package/dist/session/headless/secrets.d.ts +28 -0
  178. package/dist/session/headless/secrets.js +48 -0
  179. package/dist/session/index.d.ts +7 -5
  180. package/dist/session/index.js +6 -4
  181. package/dist/session/testing/contract.d.ts +18 -5
  182. package/dist/session/testing/contract.js +351 -43
  183. package/dist/session/testing/index.d.ts +4 -2
  184. package/dist/session/testing/index.js +4 -2
  185. package/dist/settings/headless/catalog.js +17 -1
  186. package/dist/settings/headless/index.d.ts +2 -2
  187. package/dist/settings/headless/index.js +3 -3
  188. package/dist/settings/headless/labels.d.ts +0 -2
  189. package/dist/settings/headless/labels.js +0 -2
  190. package/dist/settings/headless/layout.js +7 -1
  191. package/dist/settings/headless/providers.d.ts +17 -1
  192. package/dist/settings/headless/providers.js +19 -1
  193. package/dist/settings/headless/search.d.ts +1 -1
  194. package/dist/settings/rows.d.ts +1 -1
  195. package/dist/settings/rows.js +2 -2
  196. package/dist/settings/sections/appearance/AppearanceSection.js +2 -2
  197. package/dist/settings/sections/claude-import/ClaudeImportDialog.js +1 -1
  198. package/dist/settings/sections/editor/EditorSection.js +1 -1
  199. package/dist/settings/sections/environments/EnvironmentsSection.js +1 -1
  200. package/dist/settings/sections/extensions/ChangeOriginDialog.js +1 -1
  201. package/dist/settings/sections/extensions/CreateExtensionDialog.js +1 -1
  202. package/dist/settings/sections/extensions/ExtensionsSection.js +1 -1
  203. package/dist/settings/sections/host-catalog/HostCatalogSection.js +1 -1
  204. package/dist/settings/sections/instructions/InstructionsSection.js +1 -1
  205. package/dist/settings/sections/keybindings/labels.d.ts +2 -0
  206. package/dist/settings/sections/keybindings/labels.js +2 -0
  207. package/dist/settings/sections/keybindings/rows.js +8 -0
  208. package/dist/settings/sections/mcp/McpSection.js +1 -1
  209. package/dist/settings/sections/mcp/McpServerRow.js +1 -1
  210. package/dist/settings/sections/models/ModelRows.js +2 -2
  211. package/dist/settings/sections/providers/ProvidersSection.js +209 -201
  212. package/dist/settings/sections/providers/ServerProvidersSection.js +1 -1
  213. package/dist/settings/sections/shared/catalog.js +1 -1
  214. package/dist/settings/sections/skills/SkillList.js +1 -1
  215. package/dist/settings/sections/skills/SkillsSection.js +2 -2
  216. package/dist/styles.css +59 -1
  217. package/dist/terminal/HostTerminalSurface.js +2 -2
  218. package/dist/workspace/PanelContentView.d.ts +22 -4
  219. package/dist/workspace/PanelContentView.js +112 -16
  220. package/dist/workspace/PanelGlyph.d.ts +21 -0
  221. package/dist/workspace/PanelGlyph.js +33 -0
  222. package/dist/workspace/PanelHome.js +5 -3
  223. package/dist/workspace/PanelStrip.js +11 -4
  224. package/dist/workspace/PanelToolbar.d.ts +32 -0
  225. package/dist/workspace/PanelToolbar.js +194 -0
  226. package/dist/workspace/WorkspaceProvider.d.ts +3 -1
  227. package/dist/workspace/WorkspaceProvider.js +6 -2
  228. package/dist/workspace/extension-tabs.js +5 -2
  229. package/dist/workspace/headless/host.d.ts +4 -0
  230. package/dist/workspace/headless/index.d.ts +3 -1
  231. package/dist/workspace/headless/index.js +4 -2
  232. package/dist/workspace/headless/labels.d.ts +10 -0
  233. package/dist/workspace/headless/labels.js +11 -1
  234. package/dist/workspace/headless/pages.d.ts +15 -0
  235. package/dist/workspace/headless/pages.js +49 -0
  236. package/dist/workspace/headless/tab-mark.d.ts +14 -0
  237. package/dist/workspace/headless/tab-mark.js +27 -0
  238. package/dist/workspace/headless/view.d.ts +7 -1
  239. package/dist/workspace/index.d.ts +3 -2
  240. package/dist/workspace/index.js +3 -2
  241. package/dist/workspace/testing/index.js +6 -0
  242. package/dist/workspace/workspace-key-commands.js +3 -3
  243. package/package.json +2 -2
  244. package/src/styles/motion.css +13 -0
  245. package/src/styles/onboarding.css +0 -1
  246. package/src/styles/primitives.css +10 -0
  247. package/src/styles/workspace.css +17 -0
  248. package/dist/session/TodosPanel.d.ts +0 -19
  249. package/dist/session/TodosPanel.js +0 -251
  250. package/dist/session/headless/todos.d.ts +0 -22
  251. package/dist/session/headless/todos.js +0 -38
@@ -1,46 +1,23 @@
1
1
  import { createArtifactsState, groupArtifactRows } from "./artifacts.js";
2
2
  import { createQueueState } from "./queue.js";
3
3
  import { createRunningWorkState } from "./running-work.js";
4
- import { createTodosState } from "./todos.js";
4
+ import { createSchedulesState } from "./schedules.js";
5
+ import { createPlansState } from "./plans.js";
6
+ import { createSecretsState } from "./secrets.js";
7
+ import { createFixtureScheduleBackend } from "./fixture-schedules.js";
8
+ import { createFixturePlansBackend } from "./fixture-plans.js";
9
+ import { isSecretName } from "zidane/chat/pure";
5
10
  //#region src/session/headless/fixture-host.ts
6
11
  /**
7
- * Counts todo items by status.
8
- *
9
- * @param items - The todo list.
10
- */
11
- function tallyTodos(items) {
12
- const tally = {
13
- pending: 0,
14
- in_progress: 0,
15
- completed: 0,
16
- cancelled: 0
17
- };
18
- for (const item of items) tally[item.status]++;
19
- return tally;
20
- }
21
- /**
22
- * A todo snapshot of a list, as a backend broadcasts it.
23
- *
24
- * @param items - The live list.
25
- * @param archive - The last non-empty list (shown read-only when the live one is empty).
26
- */
27
- function todosSnapshotOf(items, archive = []) {
28
- return {
29
- runId: "fixture",
30
- todos: [...items],
31
- archive: [...archive],
32
- inProgress: items.find((t) => t.status === "in_progress") ?? null,
33
- tally: tallyTodos(items)
34
- };
35
- }
36
- /**
37
12
  * Creates a {@link FixtureSessionHost} over the fixture's data.
38
13
  *
39
14
  * @param fixture - The data per session.
40
15
  */
41
16
  function createFixtureSessionHost(fixture = {}) {
42
- const todoLists = new Map(Object.entries(fixture.todos ?? {}).map(([id, items]) => [id, [...items]]));
43
- const archives = /* @__PURE__ */ new Map();
17
+ const planBackend = createFixturePlansBackend({
18
+ ...fixture.plans ? { plans: fixture.plans } : {},
19
+ ...fixture.todos ? { todos: fixture.todos } : {}
20
+ });
44
21
  const queues = new Map(Object.entries(fixture.queue ?? {}).map(([id, entries]) => [id, [...entries]]));
45
22
  const artifacts = new Map(Object.entries(fixture.artifacts ?? {}).map(([id, rows]) => [id, [...rows]]));
46
23
  const work = new Map(Object.entries(fixture.work ?? {}).map(([id, list]) => [id, [...list]]));
@@ -94,10 +71,23 @@ function createFixtureSessionHost(fixture = {}) {
94
71
  };
95
72
  const sides = fixture.sides ? new Map(Object.entries(fixture.sides)) : null;
96
73
  let sideSeq = 0;
97
- const snapshotOf = (sessionId) => {
98
- const items = todoLists.get(sessionId);
99
- return items ? todosSnapshotOf(items, archives.get(sessionId)) : null;
74
+ const secretLists = new Map(Object.entries(fixture.secrets ?? {}).map(([id, list]) => [id, [...list]]));
75
+ const secretsState = createSecretsState(async (sessionId) => secretLists.get(sessionId) ?? []);
76
+ const writeSecrets = (sessionId, entries) => {
77
+ secretLists.set(sessionId, [...entries]);
78
+ secretsState.wakeAll();
100
79
  };
80
+ const secretsSaveError = fixture.secretsSaveError;
81
+ const scheduleBackend = createFixtureScheduleBackend({
82
+ ...fixture.schedules ? { schedules: fixture.schedules } : {},
83
+ ...fixture.scheduleFires ? { fires: fixture.scheduleFires } : {},
84
+ ...fixture.schedulesEnabled === false ? { enabled: false } : {}
85
+ });
86
+ const schedulesState = createSchedulesState({
87
+ status: async () => scheduleBackend.status(),
88
+ list: async () => scheduleBackend.list()
89
+ });
90
+ scheduleBackend.subscribe(() => schedulesState.wake());
101
91
  const items = (sessionId) => (queues.get(sessionId) ?? []).map((e) => ({
102
92
  id: e.id,
103
93
  text: e.text,
@@ -106,13 +96,11 @@ function createFixtureSessionHost(fixture = {}) {
106
96
  ...a.name ? { name: a.name } : {}
107
97
  })) } : {}
108
98
  }));
109
- const todos = createTodosState(async (sessionId) => snapshotOf(sessionId));
110
- const writeTodos = (sessionId, next) => {
111
- const prev = todoLists.get(sessionId) ?? [];
112
- if (next.length === 0 && prev.length > 0) archives.set(sessionId, prev);
113
- todoLists.set(sessionId, [...next]);
114
- todos.push(sessionId, snapshotOf(sessionId));
115
- };
99
+ const plans = createPlansState(async (sessionId) => planBackend.snapshot(sessionId));
100
+ planBackend.subscribe((sessionId) => {
101
+ const snap = planBackend.snapshot(sessionId);
102
+ if (snap) plans.push(sessionId, snap);
103
+ });
116
104
  const writeQueue = (sessionId, entries) => {
117
105
  queues.set(sessionId, entries);
118
106
  queue.replace(sessionId, items(sessionId));
@@ -132,12 +120,14 @@ function createFixtureSessionHost(fixture = {}) {
132
120
  });
133
121
  const artifactsState = createArtifactsState(async (sessionId) => groupArtifactRows(artifacts.get(sessionId) ?? [], fixture.kindLabel));
134
122
  return {
135
- todos: {
136
- snapshot: todos.snapshot,
137
- refresh: todos.refresh,
138
- async set(sessionId, next) {
139
- writeTodos(sessionId, next);
140
- return true;
123
+ plans: {
124
+ snapshot: plans.snapshot,
125
+ refresh: plans.refresh,
126
+ async edit(sessionId, planId, op) {
127
+ planBackend.edit(sessionId, planId, op);
128
+ },
129
+ async control(sessionId, planId, action) {
130
+ planBackend.control(sessionId, planId, action);
141
131
  },
142
132
  async retry() {}
143
133
  },
@@ -270,9 +260,49 @@ function createFixtureSessionHost(fixture = {}) {
270
260
  for (const [parentId, id] of sides) if (id === sideId) sides.delete(parentId);
271
261
  }
272
262
  } } : {},
263
+ ...fixture.secrets ? { secrets: {
264
+ entries: secretsState.entries,
265
+ refresh: secretsState.refresh,
266
+ async save(sessionId, input) {
267
+ if (secretsSaveError) throw new Error(secretsSaveError);
268
+ if (!isSecretName(input.name)) throw new Error(`Invalid secret name: ${input.name}`);
269
+ const createdAt = Date.now();
270
+ const entry = {
271
+ name: input.name,
272
+ createdAt,
273
+ expiresAt: fixtureSecretExpiry(input.ttl, createdAt),
274
+ sessionId
275
+ };
276
+ writeSecrets(sessionId, [...(secretLists.get(sessionId) ?? []).filter((e) => e.name !== input.name), entry]);
277
+ return entry;
278
+ },
279
+ async revoke(sessionId, name) {
280
+ const before = secretLists.get(sessionId) ?? [];
281
+ const after = before.filter((e) => e.name !== name);
282
+ writeSecrets(sessionId, after);
283
+ return after.length < before.length;
284
+ }
285
+ } } : {},
286
+ ...fixture.schedules ? { schedules: {
287
+ status: schedulesState.status,
288
+ all: schedulesState.all,
289
+ forSession: schedulesState.forSession,
290
+ refresh: schedulesState.refresh,
291
+ ensure: schedulesState.ensure,
292
+ create: async (input) => scheduleBackend.create(input),
293
+ update: async (id, patch) => scheduleBackend.update(id, patch),
294
+ remove: async (id) => scheduleBackend.remove(id),
295
+ runNow: async (id) => scheduleBackend.runNow(id),
296
+ fires: async (id, limit) => scheduleBackend.fires(id, limit)
297
+ } } : {},
273
298
  control: {
299
+ setSchedules: (list) => scheduleBackend.replace(list),
300
+ setSchedulesEnabled: (enabled) => scheduleBackend.setEnabled(enabled),
301
+ setSecrets: writeSecrets,
274
302
  setWork: writeWork,
275
- setTodos: writeTodos,
303
+ writeTodos: planBackend.writeTodos,
304
+ endRun: planBackend.endRun,
305
+ setPlans: planBackend.setPlans,
276
306
  setQueue: (sessionId, entries) => writeQueue(sessionId, [...entries]),
277
307
  recordArtifact(sessionId, row) {
278
308
  artifacts.set(sessionId, [row, ...artifacts.get(sessionId) ?? []]);
@@ -281,5 +311,10 @@ function createFixtureSessionHost(fixture = {}) {
281
311
  }
282
312
  };
283
313
  }
314
+ const HOUR = 36e5;
315
+ /** When a secret saved at `now` with `ttl` expires, as the backend computes it. */
316
+ function fixtureSecretExpiry(ttl, now) {
317
+ return ttl === "never" ? null : now + (ttl === "1h" ? HOUR : ttl === "24h" ? 24 * HOUR : 168 * HOUR);
318
+ }
284
319
  //#endregion
285
- export { createFixtureSessionHost, tallyTodos, todosSnapshotOf };
320
+ export { createFixtureSessionHost };
@@ -0,0 +1,57 @@
1
+ import { PlanControlAction } from "./host.js";
2
+ import { TodoTallyCounts } from "./plans.js";
3
+ import { Plan, PlanEditOp, PlanView, PlansSnapshot, TodoItem, TodoStatus } from "zidane/chat";
4
+ //#region src/session/headless/fixture-plans.d.ts
5
+ /** What a fixture plans backend starts with, per session id. */
6
+ interface FixturePlansSeed {
7
+ /** Each session's plans, oldest first (`possiblyInterrupted` shows an applying plan with no live run). */
8
+ plans?: Readonly<Record<string, readonly PlanView[]>>;
9
+ /** Each session's task list as the agent wrote it (`todowrite`), applied over `plans`: an applying plan. */
10
+ todos?: Readonly<Record<string, readonly TodoItem[]>>;
11
+ }
12
+ /** A plans backend in memory. */
13
+ interface FixturePlansBackend {
14
+ /** A session's snapshot, `null` for a session it does not hold. */
15
+ snapshot(sessionId: string): PlansSnapshot | null;
16
+ /** A user edit (`plans.edit`). Throws the gate's reason. */
17
+ edit(sessionId: string, planId: string, op: PlanEditOp): PlansSnapshot;
18
+ /** A stop or drop (`plans.control`). Throws the gate's reason. */
19
+ control(sessionId: string, planId: string, action: PlanControlAction): PlansSnapshot;
20
+ /** The agent's `todowrite`: updates the applying plan, starts one, or resumes a stopped one with the same step ids. */
21
+ writeTodos(sessionId: string, items: readonly TodoItem[]): void;
22
+ /** The session's run ended: the plan it applied stops. */
23
+ endRun(sessionId: string, outcome?: 'completed' | 'aborted' | 'error'): void;
24
+ /** Replaces a session's plans. */
25
+ setPlans(sessionId: string, plans: readonly PlanView[]): void;
26
+ /** Calls `listener` with a session's id after each change. Returns the unsubscribe. */
27
+ subscribe(listener: (sessionId: string) => void): () => void;
28
+ }
29
+ /**
30
+ * The snapshot of a session's plans, as the host broadcasts it (every run live: nothing reads as interrupted).
31
+ *
32
+ * @param plans - The plans, oldest first.
33
+ */
34
+ declare function plansSnapshotOf(plans: readonly PlanView[]): PlansSnapshot;
35
+ /**
36
+ * A session-scope plan over a task list, for fixtures and tests.
37
+ *
38
+ * @param items - The steps, as todo items.
39
+ * @param plan - Fields over the defaults (an applying adhoc plan `plan-1`, untitled).
40
+ */
41
+ declare function planOf(items: readonly TodoItem[], plan?: Partial<Plan>): Plan;
42
+ /**
43
+ * Counts todo items by status.
44
+ *
45
+ * @param items - The todo list.
46
+ */
47
+ declare function tallyTodos(items: readonly {
48
+ status: TodoStatus;
49
+ }[]): TodoTallyCounts;
50
+ /**
51
+ * Creates a {@link FixturePlansBackend}.
52
+ *
53
+ * @param seed - The plans and task lists it starts with.
54
+ */
55
+ declare function createFixturePlansBackend(seed?: FixturePlansSeed): FixturePlansBackend;
56
+ //#endregion
57
+ export { FixturePlansBackend, FixturePlansSeed, createFixturePlansBackend, planOf, plansSnapshotOf, tallyTodos };
@@ -0,0 +1,141 @@
1
+ import { SESSION_SCOPE, activePlanSummary, applyPlanEvent } from "zidane/chat/pure";
2
+ //#region src/session/headless/fixture-plans.ts
3
+ const bagOf = (plans) => ({
4
+ v: 1,
5
+ plans: [...plans],
6
+ seen: []
7
+ });
8
+ /**
9
+ * The snapshot of a session's plans, as the host broadcasts it (every run live: nothing reads as interrupted).
10
+ *
11
+ * @param plans - The plans, oldest first.
12
+ */
13
+ function plansSnapshotOf(plans) {
14
+ return {
15
+ plans: [...plans],
16
+ active: activePlanSummary(bagOf(plans))
17
+ };
18
+ }
19
+ /**
20
+ * A session-scope plan over a task list, for fixtures and tests.
21
+ *
22
+ * @param items - The steps, as todo items.
23
+ * @param plan - Fields over the defaults (an applying adhoc plan `plan-1`, untitled).
24
+ */
25
+ function planOf(items, plan = {}) {
26
+ return {
27
+ id: "plan-1",
28
+ scope: SESSION_SCOPE,
29
+ kind: "adhoc",
30
+ state: "applying",
31
+ title: "",
32
+ revision: 1,
33
+ createdAt: 1,
34
+ updatedAt: 1,
35
+ steps: items.map((item) => ({
36
+ id: item.id,
37
+ title: item.content,
38
+ status: item.status,
39
+ updatedAt: 1
40
+ })),
41
+ ...plan
42
+ };
43
+ }
44
+ /**
45
+ * Counts todo items by status.
46
+ *
47
+ * @param items - The todo list.
48
+ */
49
+ function tallyTodos(items) {
50
+ const tally = {
51
+ pending: 0,
52
+ in_progress: 0,
53
+ completed: 0,
54
+ cancelled: 0
55
+ };
56
+ for (const item of items) tally[item.status]++;
57
+ return tally;
58
+ }
59
+ /**
60
+ * Creates a {@link FixturePlansBackend}.
61
+ *
62
+ * @param seed - The plans and task lists it starts with.
63
+ */
64
+ function createFixturePlansBackend(seed = {}) {
65
+ const bags = new Map(Object.entries(seed.plans ?? {}).map(([id, plans]) => [id, bagOf(plans)]));
66
+ const runs = /* @__PURE__ */ new Map();
67
+ const listeners = /* @__PURE__ */ new Set();
68
+ let seq = 0;
69
+ const runId = (sessionId) => `fixture-run-${sessionId}-${runs.get(sessionId) ?? 0}`;
70
+ const apply = (sessionId, event) => {
71
+ const result = applyPlanEvent(bags.get(sessionId) ?? bagOf([]), event);
72
+ if (result.error) throw new Error(result.error);
73
+ if (result.changed || !bags.has(sessionId)) {
74
+ bags.set(sessionId, result.bag);
75
+ for (const listener of listeners) listener(sessionId);
76
+ }
77
+ return result.bag;
78
+ };
79
+ const writeTodos = (sessionId, items) => {
80
+ apply(sessionId, {
81
+ type: "write",
82
+ id: `write:${++seq}`,
83
+ at: Date.now(),
84
+ scope: SESSION_SCOPE,
85
+ runId: runId(sessionId),
86
+ items: items.map((item) => ({
87
+ id: item.id,
88
+ content: item.content,
89
+ status: item.status
90
+ }))
91
+ });
92
+ };
93
+ for (const [sessionId, items] of Object.entries(seed.todos ?? {})) writeTodos(sessionId, items);
94
+ return {
95
+ snapshot(sessionId) {
96
+ const bag = bags.get(sessionId);
97
+ return bag ? plansSnapshotOf(bag.plans) : null;
98
+ },
99
+ edit(sessionId, planId, op) {
100
+ return plansSnapshotOf(apply(sessionId, {
101
+ type: "edit",
102
+ id: `edit:${++seq}`,
103
+ at: Date.now(),
104
+ planId,
105
+ op
106
+ }).plans);
107
+ },
108
+ control(sessionId, planId, action) {
109
+ const event = {
110
+ type: "control",
111
+ id: `control:${++seq}`,
112
+ at: Date.now(),
113
+ planId,
114
+ action
115
+ };
116
+ return plansSnapshotOf(apply(sessionId, event).plans);
117
+ },
118
+ writeTodos,
119
+ endRun(sessionId, outcome = "completed") {
120
+ const id = runId(sessionId);
121
+ runs.set(sessionId, (runs.get(sessionId) ?? 0) + 1);
122
+ apply(sessionId, {
123
+ type: "runEnd",
124
+ id: `runEnd:${id}`,
125
+ at: Date.now(),
126
+ runId: id,
127
+ outcome
128
+ });
129
+ },
130
+ setPlans(sessionId, plans) {
131
+ bags.set(sessionId, bagOf(plans));
132
+ for (const listener of listeners) listener(sessionId);
133
+ },
134
+ subscribe(listener) {
135
+ listeners.add(listener);
136
+ return () => listeners.delete(listener);
137
+ }
138
+ };
139
+ }
140
+ //#endregion
141
+ export { createFixturePlansBackend, planOf, plansSnapshotOf, tallyTodos };
@@ -0,0 +1,49 @@
1
+ import { SchedulesStatus } from "./host.js";
2
+ import { Schedule, ScheduleFire } from "zidane/chat/pure";
3
+ //#region src/session/headless/fixture-schedules.d.ts
4
+ /** A scripted reminders backend. */
5
+ interface FixtureScheduleBackend {
6
+ /** `schedules.status`. */
7
+ status(): SchedulesStatus;
8
+ /** `schedules.list`: every schedule, or those whose prompt goes to `sessionId`. */
9
+ list(filter?: {
10
+ sessionId?: string;
11
+ }): Schedule[];
12
+ /** `schedules.create`, as the person (`createdBy: user`). */
13
+ create(input: unknown): Schedule;
14
+ /** `schedules.update`. */
15
+ update(id: string, patch: unknown): Schedule;
16
+ /** `schedules.delete`: `false` when it was gone. */
17
+ remove(id: string): boolean;
18
+ /** `schedules.runNow`: records a fire now; the next time does not move. */
19
+ runNow(id: string): ScheduleFire;
20
+ /** `schedules.fires`: newest first. */
21
+ fires(id: string, limit?: number): ScheduleFire[];
22
+ /** Replaces the stored schedules from outside (the agent set one, one fired). */
23
+ replace(list: readonly Schedule[]): void;
24
+ /** Turns the setting on or off. */
25
+ setEnabled(enabled: boolean): void;
26
+ /** Hears every change (`schedules:changed`). Returns the unsubscribe. */
27
+ subscribe(onChange: (scheduleId?: string) => void): () => void;
28
+ }
29
+ /** What {@link createFixtureScheduleBackend} starts with. */
30
+ interface FixtureScheduleOptions {
31
+ /** The stored schedules, oldest first. */
32
+ schedules?: readonly Schedule[];
33
+ /** Each schedule's fires, newest first. */
34
+ fires?: Readonly<Record<string, readonly ScheduleFire[]>>;
35
+ /** `Settings.schedules`. @default true */
36
+ enabled?: boolean;
37
+ /** The clock. @default Date.now */
38
+ now?: () => number;
39
+ }
40
+ /** The message zidane's backend answers while the setting is off. */
41
+ declare const SCHEDULES_OFF_MESSAGE = "Reminders are off. Turn them on in Settings > Sessions > Reminders.";
42
+ /**
43
+ * Creates a {@link FixtureScheduleBackend}.
44
+ *
45
+ * @param options - The starting schedules, fires, setting and clock.
46
+ */
47
+ declare function createFixtureScheduleBackend(options?: FixtureScheduleOptions): FixtureScheduleBackend;
48
+ //#endregion
49
+ export { FixtureScheduleBackend, FixtureScheduleOptions, SCHEDULES_OFF_MESSAGE, createFixtureScheduleBackend };
@@ -0,0 +1,133 @@
1
+ import { defaultCatchUp, nextFireAfter, scheduleTargetSession, validateScheduleInput, validateSchedulePatch } from "zidane/chat/pure";
2
+ //#region src/session/headless/fixture-schedules.ts
3
+ /**
4
+ * Reminders (zidane's schedules) held in memory, as zidane's backend keeps
5
+ * them: the fixture session host's `schedules`, and the scripted backend an
6
+ * app's own `SessionHost` test serves its transport from. Methods answer
7
+ * like the wire methods (`schedules.status`, `.list`, `.create`, `.update`,
8
+ * `.delete`, `.runNow`, `.fires`): they throw the backend's message while
9
+ * the setting is off, and zidane's `ScheduleInputError` for a bad input.
10
+ */
11
+ /** The message zidane's backend answers while the setting is off. */
12
+ const SCHEDULES_OFF_MESSAGE = "Reminders are off. Turn them on in Settings > Sessions > Reminders.";
13
+ /**
14
+ * Creates a {@link FixtureScheduleBackend}.
15
+ *
16
+ * @param options - The starting schedules, fires, setting and clock.
17
+ */
18
+ function createFixtureScheduleBackend(options = {}) {
19
+ const now = options.now ?? Date.now;
20
+ let list = [...options.schedules ?? []];
21
+ const fireLists = new Map(Object.entries(options.fires ?? {}).map(([id, fires]) => [id, [...fires]]));
22
+ let enabled = options.enabled !== false;
23
+ let seq = 0;
24
+ const listeners = /* @__PURE__ */ new Set();
25
+ const changed = (scheduleId) => {
26
+ for (const l of [...listeners]) l(scheduleId);
27
+ };
28
+ const on = () => {
29
+ if (!enabled) throw new Error(SCHEDULES_OFF_MESSAGE);
30
+ };
31
+ const find = (id) => {
32
+ on();
33
+ const found = list.find((s) => s.id === id);
34
+ if (!found) throw new Error(`No schedule "${id}".`);
35
+ return found;
36
+ };
37
+ const write = (next, scheduleId) => {
38
+ list = next;
39
+ changed(scheduleId);
40
+ };
41
+ return {
42
+ status: () => ({
43
+ enabled,
44
+ supported: true
45
+ }),
46
+ list(filter) {
47
+ on();
48
+ const sessionId = filter?.sessionId;
49
+ return sessionId === void 0 ? [...list] : list.filter((s) => scheduleTargetSession(s.action) === sessionId);
50
+ },
51
+ create(raw) {
52
+ on();
53
+ const at = now();
54
+ const input = validateScheduleInput(raw, at);
55
+ const schedule = {
56
+ id: `fixture-sch${++seq}`,
57
+ ...input.name ? { name: input.name } : {},
58
+ trigger: input.trigger,
59
+ action: input.action,
60
+ catchUp: input.catchUp ?? defaultCatchUp(input.trigger),
61
+ enabled: input.enabled !== false,
62
+ createdBy: { kind: "user" },
63
+ createdAt: at,
64
+ updatedAt: at,
65
+ nextFireAt: input.enabled === false ? null : nextFireAfter(input.trigger, at)
66
+ };
67
+ write([...list, schedule], schedule.id);
68
+ return schedule;
69
+ },
70
+ update(id, raw) {
71
+ const found = find(id);
72
+ const at = now();
73
+ const { name, expiresAt, ...rest } = validateSchedulePatch(raw, at);
74
+ const merged = {
75
+ ...found,
76
+ ...rest,
77
+ updatedAt: at
78
+ };
79
+ if (name === null) delete merged.name;
80
+ else if (name !== void 0) merged.name = name;
81
+ if (expiresAt === null) delete merged.expiresAt;
82
+ if (rest.enabled === true) delete merged.disabledReason;
83
+ merged.nextFireAt = merged.enabled ? nextFireAfter(merged.trigger, at) : null;
84
+ write(list.map((s) => s.id === id ? merged : s), id);
85
+ return merged;
86
+ },
87
+ remove(id) {
88
+ on();
89
+ if (!list.some((s) => s.id === id)) return false;
90
+ fireLists.delete(id);
91
+ write(list.filter((s) => s.id !== id), id);
92
+ return true;
93
+ },
94
+ runNow(id) {
95
+ const found = find(id);
96
+ const at = now();
97
+ const target = scheduleTargetSession(found.action);
98
+ const fire = {
99
+ id: `fixture-fire${++seq}`,
100
+ scheduleId: id,
101
+ dueAt: at,
102
+ firedAt: at,
103
+ outcome: found.action.kind === "extension" ? "handled" : "sent",
104
+ ...target ? { sessionId: target } : {}
105
+ };
106
+ fireLists.set(id, [fire, ...fireLists.get(id) ?? []]);
107
+ write(list.map((s) => s.id === id ? {
108
+ ...s,
109
+ lastFire: fire
110
+ } : s), id);
111
+ return fire;
112
+ },
113
+ fires(id, limit = 20) {
114
+ find(id);
115
+ return (fireLists.get(id) ?? []).slice(0, limit);
116
+ },
117
+ replace(next) {
118
+ write([...next]);
119
+ },
120
+ setEnabled(next) {
121
+ enabled = next;
122
+ changed();
123
+ },
124
+ subscribe(onChange) {
125
+ listeners.add(onChange);
126
+ return () => {
127
+ listeners.delete(onChange);
128
+ };
129
+ }
130
+ };
131
+ }
132
+ //#endregion
133
+ export { SCHEDULES_OFF_MESSAGE, createFixtureScheduleBackend };
@@ -8,6 +8,10 @@ interface HomeBlockSource {
8
8
  label: string;
9
9
  /** The extension that registered it. */
10
10
  owner: string;
11
+ /** That extension's display name. */
12
+ ownerName?: string;
13
+ /** That extension's own icon: its mark stands in for a panel without one. */
14
+ ownerIcon?: PanelIcon;
11
15
  /** Its icon. */
12
16
  icon?: PanelIcon;
13
17
  /** Where it may show; only panels that list `'home'` become blocks. */
@@ -23,6 +27,10 @@ interface HomeBlock {
23
27
  label: string;
24
28
  /** The extension that registered it. */
25
29
  owner: string;
30
+ /** That extension's display name. */
31
+ ownerName?: string;
32
+ /** That extension's own icon: its mark stands in for a block without one. */
33
+ ownerIcon?: PanelIcon;
26
34
  /** Its icon. */
27
35
  icon?: PanelIcon;
28
36
  /** Shares a row with another half block. */
@@ -124,9 +132,9 @@ declare function showHomeBlock(prefs: HomePrefs, blocks: readonly HomeBlock[], i
124
132
  * @param prefs - The arrangement.
125
133
  * @param id - The block.
126
134
  * @param control - The control id.
127
- * @param value - The picked option.
135
+ * @param value - The picked option, or the picked options of a `multi` control.
128
136
  */
129
- declare function setHomeBlockView(prefs: HomePrefs, id: string, control: string, value: string): HomePrefs;
137
+ declare function setHomeBlockView(prefs: HomePrefs, id: string, control: string, value: string | readonly string[]): HomePrefs;
130
138
  /**
131
139
  * Every project's arrangement from its stored form; `undefined` for anything unrecognised.
132
140
  *
@@ -21,6 +21,8 @@ function homeBlocksOf(panels) {
21
21
  id: p.id,
22
22
  label: p.label,
23
23
  owner: p.owner,
24
+ ...p.ownerName ? { ownerName: p.ownerName } : {},
25
+ ...p.ownerIcon ? { ownerIcon: p.ownerIcon } : {},
24
26
  ...p.icon ? { icon: p.icon } : {},
25
27
  width: p.home?.width === "half" ? "half" : "full"
26
28
  }));
@@ -173,10 +175,11 @@ function showHomeBlock(prefs, blocks, id, defaults) {
173
175
  * @param prefs - The arrangement.
174
176
  * @param id - The block.
175
177
  * @param control - The control id.
176
- * @param value - The picked option.
178
+ * @param value - The picked option, or the picked options of a `multi` control.
177
179
  */
178
180
  function setHomeBlockView(prefs, id, control, value) {
179
- if (prefs.views[id]?.[control] === value) return prefs;
181
+ const was = prefs.views[id]?.[control];
182
+ if (was === value || Array.isArray(was) && Array.isArray(value) && sameList(was, value)) return prefs;
180
183
  return {
181
184
  ...prefs,
182
185
  views: {
@@ -189,10 +192,12 @@ function setHomeBlockView(prefs, id, control, value) {
189
192
  };
190
193
  }
191
194
  const strings = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string" && x.length > 0 && x.length <= 512) : [];
195
+ const sameList = (a, b) => a.length === b.length && a.every((x, i) => x === b[i]);
192
196
  function parseView(v) {
193
197
  if (!v || typeof v !== "object" || Array.isArray(v)) return void 0;
194
198
  const out = {};
195
199
  for (const [k, value] of Object.entries(v)) if (typeof value === "string") out[k] = value;
200
+ else if (Array.isArray(value)) out[k] = value.filter((x) => typeof x === "string");
196
201
  return Object.keys(out).length > 0 ? out : void 0;
197
202
  }
198
203
  /**