@stigmer/react 3.4.0 → 3.5.0

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 (208) hide show
  1. package/agent/AgentDetailView.d.ts +14 -4
  2. package/agent/AgentDetailView.d.ts.map +1 -1
  3. package/agent/AgentDetailView.js +73 -11
  4. package/agent/AgentDetailView.js.map +1 -1
  5. package/agent/internal/agentToInput.d.ts +3 -0
  6. package/agent/internal/agentToInput.d.ts.map +1 -1
  7. package/agent/internal/agentToInput.js +21 -1
  8. package/agent/internal/agentToInput.js.map +1 -1
  9. package/agent/useAgentCount.d.ts +2 -0
  10. package/agent/useAgentCount.d.ts.map +1 -1
  11. package/agent/useAgentCount.js.map +1 -1
  12. package/api-key/ApiKeyListPanel.d.ts +8 -1
  13. package/api-key/ApiKeyListPanel.d.ts.map +1 -1
  14. package/api-key/ApiKeyListPanel.js +9 -7
  15. package/api-key/ApiKeyListPanel.js.map +1 -1
  16. package/api-key/CreateApiKeyForm.d.ts +7 -1
  17. package/api-key/CreateApiKeyForm.d.ts.map +1 -1
  18. package/api-key/CreateApiKeyForm.js +2 -2
  19. package/api-key/CreateApiKeyForm.js.map +1 -1
  20. package/datastore/useDatastoreCount.d.ts +2 -0
  21. package/datastore/useDatastoreCount.d.ts.map +1 -1
  22. package/datastore/useDatastoreCount.js.map +1 -1
  23. package/dependency-graph/DependencyTreeNode.d.ts.map +1 -1
  24. package/dependency-graph/DependencyTreeNode.js +8 -0
  25. package/dependency-graph/DependencyTreeNode.js.map +1 -1
  26. package/dependency-graph/types.d.ts +20 -8
  27. package/dependency-graph/types.d.ts.map +1 -1
  28. package/dependency-graph/useDependencyGraph.d.ts.map +1 -1
  29. package/dependency-graph/useDependencyGraph.js +21 -1
  30. package/dependency-graph/useDependencyGraph.js.map +1 -1
  31. package/index.d.ts +6 -4
  32. package/index.d.ts.map +1 -1
  33. package/index.js +6 -2
  34. package/index.js.map +1 -1
  35. package/internal/scroll-area.d.ts +4 -0
  36. package/internal/scroll-area.d.ts.map +1 -0
  37. package/internal/scroll-area.js +15 -0
  38. package/internal/scroll-area.js.map +1 -0
  39. package/internal/tooltip.d.ts +7 -0
  40. package/internal/tooltip.d.ts.map +1 -0
  41. package/internal/tooltip.js +31 -0
  42. package/internal/tooltip.js.map +1 -0
  43. package/manifest/ApplyManifestDialog.d.ts +10 -5
  44. package/manifest/ApplyManifestDialog.d.ts.map +1 -1
  45. package/manifest/ApplyManifestDialog.js +30 -13
  46. package/manifest/ApplyManifestDialog.js.map +1 -1
  47. package/manifest/useApplyManifest.d.ts +6 -2
  48. package/manifest/useApplyManifest.d.ts.map +1 -1
  49. package/manifest/useApplyManifest.js +5 -5
  50. package/manifest/useApplyManifest.js.map +1 -1
  51. package/mcp-server/McpServerDetailView.d.ts +38 -2
  52. package/mcp-server/McpServerDetailView.d.ts.map +1 -1
  53. package/mcp-server/McpServerDetailView.js +21 -5
  54. package/mcp-server/McpServerDetailView.js.map +1 -1
  55. package/mcp-server/StdioSandboxNotice.d.ts +12 -11
  56. package/mcp-server/StdioSandboxNotice.d.ts.map +1 -1
  57. package/mcp-server/StdioSandboxNotice.js +13 -12
  58. package/mcp-server/StdioSandboxNotice.js.map +1 -1
  59. package/mcp-server/index.d.ts +7 -0
  60. package/mcp-server/index.d.ts.map +1 -1
  61. package/mcp-server/index.js +9 -0
  62. package/mcp-server/index.js.map +1 -1
  63. package/mcp-server/steps/EnvironmentAuthStep.d.ts +4 -0
  64. package/mcp-server/steps/EnvironmentAuthStep.d.ts.map +1 -1
  65. package/mcp-server/steps/EnvironmentAuthStep.js +4 -0
  66. package/mcp-server/steps/EnvironmentAuthStep.js.map +1 -1
  67. package/mcp-server/steps/IdentityTransportStep.d.ts +6 -0
  68. package/mcp-server/steps/IdentityTransportStep.d.ts.map +1 -1
  69. package/mcp-server/steps/IdentityTransportStep.js +7 -1
  70. package/mcp-server/steps/IdentityTransportStep.js.map +1 -1
  71. package/mcp-server/steps/ReviewStep.d.ts +5 -0
  72. package/mcp-server/steps/ReviewStep.d.ts.map +1 -1
  73. package/mcp-server/steps/ReviewStep.js +16 -2
  74. package/mcp-server/steps/ReviewStep.js.map +1 -1
  75. package/mcp-server/useMcpServerCount.d.ts +2 -0
  76. package/mcp-server/useMcpServerCount.d.ts.map +1 -1
  77. package/mcp-server/useMcpServerCount.js.map +1 -1
  78. package/package.json +4 -4
  79. package/resource-creation/CreationPicker.d.ts.map +1 -1
  80. package/resource-creation/CreationPicker.js +3 -3
  81. package/resource-creation/CreationPicker.js.map +1 -1
  82. package/resource-creation/WizardNav.d.ts.map +1 -1
  83. package/resource-creation/WizardNav.js +1 -1
  84. package/resource-creation/WizardNav.js.map +1 -1
  85. package/resource-creation/templates/mcp-server-templates.d.ts.map +1 -1
  86. package/resource-creation/templates/mcp-server-templates.js +17 -14
  87. package/resource-creation/templates/mcp-server-templates.js.map +1 -1
  88. package/resource-detail/Section.d.ts +8 -1
  89. package/resource-detail/Section.d.ts.map +1 -1
  90. package/resource-detail/Section.js +2 -2
  91. package/resource-detail/Section.js.map +1 -1
  92. package/resource-workbench/components/ResourceWorkbench.d.ts +9 -1
  93. package/resource-workbench/components/ResourceWorkbench.d.ts.map +1 -1
  94. package/resource-workbench/components/ResourceWorkbench.js +2 -1
  95. package/resource-workbench/components/ResourceWorkbench.js.map +1 -1
  96. package/resource-workbench/hooks/useResourceCollection.d.ts +9 -0
  97. package/resource-workbench/hooks/useResourceCollection.d.ts.map +1 -1
  98. package/resource-workbench/hooks/useResourceCollection.js +2 -2
  99. package/resource-workbench/hooks/useResourceCollection.js.map +1 -1
  100. package/search/useResourceCount.d.ts +7 -0
  101. package/search/useResourceCount.d.ts.map +1 -1
  102. package/search/useResourceCount.js +2 -1
  103. package/search/useResourceCount.js.map +1 -1
  104. package/session/NewSessionViewer.d.ts.map +1 -1
  105. package/session/NewSessionViewer.js +17 -3
  106. package/session/NewSessionViewer.js.map +1 -1
  107. package/session/SessionViewer.d.ts +2 -1
  108. package/session/SessionViewer.d.ts.map +1 -1
  109. package/session/SessionViewer.js +19 -11
  110. package/session/SessionViewer.js.map +1 -1
  111. package/session/SessionViewerLayout.d.ts +90 -0
  112. package/session/SessionViewerLayout.d.ts.map +1 -0
  113. package/session/SessionViewerLayout.js +32 -0
  114. package/session/SessionViewerLayout.js.map +1 -0
  115. package/session/index.d.ts +4 -0
  116. package/session/index.d.ts.map +1 -1
  117. package/session/index.js +10 -0
  118. package/session/index.js.map +1 -1
  119. package/session/useSessionRailViews.d.ts +2 -2
  120. package/sidebar/SettingsSidebar.d.ts +62 -0
  121. package/sidebar/SettingsSidebar.d.ts.map +1 -0
  122. package/sidebar/SettingsSidebar.js +60 -0
  123. package/sidebar/SettingsSidebar.js.map +1 -0
  124. package/sidebar/WorkspaceSidebar.d.ts +87 -0
  125. package/sidebar/WorkspaceSidebar.d.ts.map +1 -0
  126. package/sidebar/WorkspaceSidebar.js +95 -0
  127. package/sidebar/WorkspaceSidebar.js.map +1 -0
  128. package/sidebar/chrome.d.ts +48 -0
  129. package/sidebar/chrome.d.ts.map +1 -0
  130. package/sidebar/chrome.js +32 -0
  131. package/sidebar/chrome.js.map +1 -0
  132. package/sidebar/index.d.ts +6 -0
  133. package/sidebar/index.d.ts.map +1 -0
  134. package/sidebar/index.js +9 -0
  135. package/sidebar/index.js.map +1 -0
  136. package/sidebar/types.d.ts +54 -0
  137. package/sidebar/types.d.ts.map +1 -0
  138. package/sidebar/types.js +2 -0
  139. package/sidebar/types.js.map +1 -0
  140. package/skill/useSkillCount.d.ts +2 -0
  141. package/skill/useSkillCount.d.ts.map +1 -1
  142. package/skill/useSkillCount.js.map +1 -1
  143. package/src/agent/AgentDetailView.tsx +198 -11
  144. package/src/agent/__tests__/AgentDetailView.datastores.test.tsx +166 -0
  145. package/src/agent/internal/__tests__/agentToInput.test.ts +154 -0
  146. package/src/agent/internal/agentToInput.ts +25 -7
  147. package/src/agent/useAgentCount.ts +2 -0
  148. package/src/api-key/ApiKeyListPanel.tsx +17 -4
  149. package/src/api-key/CreateApiKeyForm.tsx +8 -1
  150. package/src/api-key/__tests__/CreateApiKeyForm.test.tsx +58 -0
  151. package/src/datastore/useDatastoreCount.ts +2 -0
  152. package/src/dependency-graph/DependencyTreeNode.tsx +8 -0
  153. package/src/dependency-graph/__tests__/useDependencyGraph.test.ts +108 -0
  154. package/src/dependency-graph/types.ts +20 -8
  155. package/src/dependency-graph/useDependencyGraph.ts +23 -1
  156. package/src/index.ts +24 -0
  157. package/src/internal/scroll-area.tsx +37 -0
  158. package/src/internal/tooltip.tsx +66 -0
  159. package/src/manifest/ApplyManifestDialog.tsx +40 -16
  160. package/src/manifest/__tests__/ApplyManifestDialog.test.tsx +147 -0
  161. package/src/manifest/__tests__/useApplyManifest.test.tsx +6 -6
  162. package/src/manifest/useApplyManifest.ts +12 -8
  163. package/src/mcp-server/McpServerDetailView.tsx +52 -4
  164. package/src/mcp-server/StdioSandboxNotice.tsx +16 -14
  165. package/src/mcp-server/__tests__/McpServerDetailView.test.tsx +345 -0
  166. package/src/mcp-server/__tests__/StdioSandboxNotice.test.tsx +3 -2
  167. package/src/mcp-server/index.ts +13 -0
  168. package/src/mcp-server/steps/EnvironmentAuthStep.tsx +4 -0
  169. package/src/mcp-server/steps/IdentityTransportStep.tsx +16 -1
  170. package/src/mcp-server/steps/ReviewStep.tsx +18 -1
  171. package/src/mcp-server/useMcpServerCount.ts +2 -0
  172. package/src/resource-creation/CreationPicker.tsx +7 -0
  173. package/src/resource-creation/WizardNav.tsx +1 -0
  174. package/src/resource-creation/templates/mcp-server-templates.ts +17 -14
  175. package/src/resource-detail/Section.tsx +9 -1
  176. package/src/resource-workbench/components/ResourceWorkbench.tsx +13 -0
  177. package/src/resource-workbench/hooks/useResourceCollection.ts +11 -1
  178. package/src/search/useResourceCount.ts +9 -1
  179. package/src/session/NewSessionViewer.tsx +48 -61
  180. package/src/session/SessionViewer.tsx +71 -86
  181. package/src/session/SessionViewerLayout.tsx +131 -0
  182. package/src/session/__tests__/SessionViewerLayout.test.tsx +140 -0
  183. package/src/session/index.ts +14 -0
  184. package/src/session/useSessionRailViews.tsx +2 -2
  185. package/src/sidebar/SettingsSidebar.tsx +173 -0
  186. package/src/sidebar/WorkspaceSidebar.tsx +386 -0
  187. package/src/sidebar/__tests__/SettingsSidebar.test.tsx +107 -0
  188. package/src/sidebar/__tests__/WorkspaceSidebar.test.tsx +236 -0
  189. package/src/sidebar/chrome.tsx +129 -0
  190. package/src/sidebar/index.ts +16 -0
  191. package/src/sidebar/types.ts +55 -0
  192. package/src/skill/useSkillCount.ts +2 -0
  193. package/src/test/__tests__/samples.test.ts +161 -2
  194. package/src/test/index.ts +1 -1
  195. package/src/test/samples.ts +157 -14
  196. package/src/workflow/useWorkflowCount.ts +2 -0
  197. package/styles.css +1 -1
  198. package/test/index.d.ts +1 -1
  199. package/test/index.d.ts.map +1 -1
  200. package/test/index.js +1 -1
  201. package/test/index.js.map +1 -1
  202. package/test/samples.d.ts +100 -7
  203. package/test/samples.d.ts.map +1 -1
  204. package/test/samples.js +133 -14
  205. package/test/samples.js.map +1 -1
  206. package/workflow/useWorkflowCount.d.ts +2 -0
  207. package/workflow/useWorkflowCount.d.ts.map +1 -1
  208. package/workflow/useWorkflowCount.js.map +1 -1
@@ -1,7 +1,9 @@
1
- import { describe, it, expect } from "vitest";
1
+ import { describe, it, expect, afterEach, vi } from "vitest";
2
+ import { timestampDate } from "@bufbuild/protobuf/wkt";
2
3
  import { ExecutionPhase, MessageType, ToolCallStatus, ExecutionArtifactKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
3
4
  import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
4
- import { samples } from "../samples";
5
+ import { formatDuration } from "../../execution/ToolCallDetail";
6
+ import { samples, SAMPLE_INSTANT, sampleInstant, sampleDate } from "../samples";
5
7
 
6
8
  describe("samples", () => {
7
9
  describe("session", () => {
@@ -169,3 +171,160 @@ describe("samples", () => {
169
171
  });
170
172
  });
171
173
  });
174
+
175
+ // ---------------------------------------------------------------------------
176
+ // Determinism — the reason these factories exist. A `samples.*` value is
177
+ // rendered into Scenar tours that replay in the browser and export to video
178
+ // frame by frame, so any live-clock read paints a pixel that changes between
179
+ // runs (scenar-cloud DD-006). These tests are the regression lock: each one
180
+ // FAILS if a factory is reverted to reading the clock.
181
+ // ---------------------------------------------------------------------------
182
+
183
+ describe("samples determinism", () => {
184
+ afterEach(() => {
185
+ vi.useRealTimers();
186
+ });
187
+
188
+ // A zero-arg call for every factory, with representative arguments. The
189
+ // completeness test below asserts this map covers `samples` exactly, so a
190
+ // newly-added factory that reads the clock cannot slip past the stability
191
+ // sweep unnoticed.
192
+ const invoke: Record<keyof typeof samples, () => unknown> = {
193
+ session: () => samples.session(),
194
+ agent: () => samples.agent(),
195
+ agentExecution: () => samples.agentExecution(),
196
+ skill: () => samples.skill(),
197
+ mcpServer: () => samples.mcpServer(),
198
+ environment: () => samples.environment(),
199
+ agentInstance: () => samples.agentInstance(),
200
+ apiKey: () => samples.apiKey(),
201
+ organization: () => samples.organization(),
202
+ organizationList: () => samples.organizationList(),
203
+ humanMessage: () => samples.humanMessage("hello"),
204
+ aiMessage: () => samples.aiMessage("hi"),
205
+ toolCall: () => samples.toolCall("lookup_order", '{"orderId":"123"}'),
206
+ artifact: () => samples.artifact("report.md"),
207
+ sessionList: () => samples.sessionList(),
208
+ agentExecutionList: () => samples.agentExecutionList(),
209
+ searchResponse: () => samples.searchResponse(),
210
+ apiKeyList: () => samples.apiKeyList(),
211
+ searchResult: () => samples.searchResult(),
212
+ };
213
+
214
+ it("the stability sweep covers every factory", () => {
215
+ expect(Object.keys(invoke).sort()).toEqual(Object.keys(samples).sort());
216
+ });
217
+
218
+ // Two calls under system clocks a full calendar day apart must produce
219
+ // identical objects. The day boundary is deliberate: it is what reproduces
220
+ // the `apiKey` bug (a `createdAt` date that changed daily) and the message
221
+ // and tool-call timestamp drift, none of which a same-millisecond back-to-
222
+ // back call would expose.
223
+ it.each(Object.keys(invoke) as (keyof typeof samples)[])(
224
+ "%s is identical across a day boundary",
225
+ (name) => {
226
+ vi.useFakeTimers();
227
+
228
+ vi.setSystemTime(new Date("2026-03-01T08:15:00Z"));
229
+ const first = invoke[name]();
230
+
231
+ vi.setSystemTime(new Date("2026-03-02T21:45:30Z"));
232
+ const second = invoke[name]();
233
+
234
+ expect(second).toEqual(first);
235
+ },
236
+ );
237
+
238
+ it("stamps the frozen instant on every timestamped primitive", () => {
239
+ expect(samples.humanMessage("x").timestamp).toBe(SAMPLE_INSTANT);
240
+ expect(samples.aiMessage("x").timestamp).toBe(SAMPLE_INSTANT);
241
+ expect(samples.artifact("x").createdAt).toBe(SAMPLE_INSTANT);
242
+ expect(samples.toolCall("x", "y").startedAt).toBe(SAMPLE_INSTANT);
243
+
244
+ const createdAt = samples.apiKey().status?.audit?.specAudit?.createdAt;
245
+ expect(createdAt).toBeDefined();
246
+ expect(timestampDate(createdAt!).toISOString()).toBe(SAMPLE_INSTANT);
247
+ });
248
+
249
+ it("renders a stable duration chip rather than the old 0ms flake", () => {
250
+ const tc = samples.toolCall("lookup_order", "{}");
251
+ expect(formatDuration(tc.startedAt, tc.completedAt)).toBe("1.2s");
252
+ });
253
+
254
+ it("derives a clock-free tool-call id from the name", () => {
255
+ expect(samples.toolCall("process_return", "{}").id).toBe("tc-process_return");
256
+ });
257
+
258
+ it("honors an ApiKeyOverrides.createdAt override", () => {
259
+ const frozen = "2026-01-02T03:04:05.000Z";
260
+ const key = samples.apiKey({ createdAt: frozen });
261
+ const createdAt = key.status?.audit?.specAudit?.createdAt;
262
+ expect(timestampDate(createdAt!).toISOString()).toBe(frozen);
263
+ });
264
+ });
265
+
266
+ // ---------------------------------------------------------------------------
267
+ // The reader offset window — why SAMPLE_INSTANT is 11:00 UTC. Components
268
+ // format dates in the reader's local time, and real UTC offsets span −11:00
269
+ // to +14:00 (25 hours against a 24-hour day), so no instant renders one
270
+ // calendar date everywhere. The supported window is UTC−11:00 (Midway)
271
+ // through UTC+12:45 (Chatham, southern winter): every inhabited zone except
272
+ // Tongatapu (+13) and Kiritimati (+14). Local date is monotonic in offset,
273
+ // so agreement at the two boundaries proves agreement throughout.
274
+ //
275
+ // `scripts/verify-scenar-tours.mjs` exports the same boundaries as
276
+ // READER_OFFSET_WINDOW (its own test locks those values); this suite locks
277
+ // the anchor's side of the contract.
278
+ // ---------------------------------------------------------------------------
279
+
280
+ describe("SAMPLE_INSTANT reader offset window", () => {
281
+ const WEST_OFFSET_MINUTES = -11 * 60; // UTC−11:00, Midway
282
+ const EAST_OFFSET_MINUTES = 12 * 60 + 45; // UTC+12:45, Chatham (Jul)
283
+
284
+ /** Calendar date (YYYY-MM-DD) the instant renders at a fixed UTC offset. */
285
+ const localDateAtOffset = (iso: string, offsetMinutes: number): string =>
286
+ new Date(Date.parse(iso) + offsetMinutes * 60_000).toISOString().slice(0, 10);
287
+
288
+ it("renders one calendar date at both window boundaries", () => {
289
+ const west = localDateAtOffset(SAMPLE_INSTANT, WEST_OFFSET_MINUTES);
290
+ const east = localDateAtOffset(SAMPLE_INSTANT, EAST_OFFSET_MINUTES);
291
+ expect(west).toBe("2026-07-20");
292
+ expect(east).toBe("2026-07-20");
293
+ });
294
+
295
+ // The two anchors this one replaced, kept as negative cases so nobody
296
+ // "corrects" the constant back: 09:30Z was the original tour-world instant
297
+ // (read Jul 19 in Honolulu); noon was SAMPLE_INSTANT's first value and the
298
+ // once-planned fix (read Jul 21 in Auckland, Fiji, and Chatham).
299
+ it.each([
300
+ ["2026-07-20T09:30:00.000Z", "drifts at the west boundary"],
301
+ ["2026-07-20T12:00:00.000Z", "drifts at the east boundary"],
302
+ ])("rejected anchor %s %s", (iso) => {
303
+ expect(localDateAtOffset(iso, WEST_OFFSET_MINUTES)).not.toBe(
304
+ localDateAtOffset(iso, EAST_OFFSET_MINUTES),
305
+ );
306
+ });
307
+ });
308
+
309
+ describe("sampleInstant / sampleDate derivation", () => {
310
+ it("sampleInstant with no delta is the anchor itself", () => {
311
+ expect(sampleInstant()).toBe(SAMPLE_INSTANT);
312
+ });
313
+
314
+ it("applies positive and negative deltas exactly", () => {
315
+ expect(sampleInstant(2_400)).toBe("2026-07-20T11:00:02.400Z");
316
+ expect(sampleInstant(-86_400_000)).toBe("2026-07-19T11:00:00.000Z");
317
+ });
318
+
319
+ it("sampleDate agrees with sampleInstant at every delta", () => {
320
+ for (const deltaMs of [0, 1_200, 2_400, -3_600_000, 86_400_000]) {
321
+ expect(sampleDate(deltaMs).toISOString()).toBe(sampleInstant(deltaMs));
322
+ }
323
+ });
324
+
325
+ it("the default toolCall span derives from the anchor", () => {
326
+ const tc = samples.toolCall("lookup_order", "{}");
327
+ expect(tc.startedAt).toBe(sampleInstant());
328
+ expect(tc.completedAt).toBe(sampleInstant(1_200));
329
+ });
330
+ });
package/src/test/index.ts CHANGED
@@ -3,4 +3,4 @@
3
3
  // Provides domain-specific data builders for unit tests, Storybook,
4
4
  // Playwright, and Scenar preview scenarios.
5
5
 
6
- export { samples } from "./samples.js";
6
+ export { samples, SAMPLE_INSTANT, sampleInstant, sampleDate } from "./samples.js";
@@ -1,4 +1,5 @@
1
1
  import { create } from "@bufbuild/protobuf";
2
+ import { timestampFromDate } from "@bufbuild/protobuf/wkt";
2
3
  import { AgentSchema, type Agent } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
3
4
  import { AgentSpecSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/spec_pb";
4
5
  import {
@@ -66,6 +67,15 @@ import {
66
67
  import { SessionSpecSchema } from "@stigmer/protos/ai/stigmer/agentic/session/v1/spec_pb";
67
68
  import { SkillSchema, type Skill } from "@stigmer/protos/ai/stigmer/agentic/skill/v1/api_pb";
68
69
  import { SkillSpecSchema } from "@stigmer/protos/ai/stigmer/agentic/skill/v1/spec_pb";
70
+ import {
71
+ OrganizationSchema,
72
+ type Organization,
73
+ } from "@stigmer/protos/ai/stigmer/tenancy/organization/v1/api_pb";
74
+ import {
75
+ OrganizationsSchema,
76
+ type Organizations,
77
+ } from "@stigmer/protos/ai/stigmer/tenancy/organization/v1/io_pb";
78
+ import { OrganizationSpecSchema } from "@stigmer/protos/ai/stigmer/tenancy/organization/v1/spec_pb";
69
79
  import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
70
80
  import { ApiResourceMetadataSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/metadata_pb";
71
81
  import {
@@ -75,6 +85,77 @@ import {
75
85
  type SearchResult,
76
86
  } from "@stigmer/protos/ai/stigmer/search/v1/io_pb";
77
87
 
88
+ // ---------------------------------------------------------------------------
89
+ // The frozen instant
90
+ // ---------------------------------------------------------------------------
91
+
92
+ /**
93
+ * The frozen instant every timestamped `samples.*` factory defaults to — the
94
+ * tour world's single clock. Anything in a demo that needs an instant derives
95
+ * from this one via {@link sampleInstant} / {@link sampleDate}; nothing
96
+ * authors a second instant of its own (`scripts/verify-scenar-tours.mjs`
97
+ * enforces that under `demos/tours/`).
98
+ *
99
+ * These factories must never read the live clock. A `samples.*` value is
100
+ * rendered into Scenar tours that replay in the browser and export to video
101
+ * frame by frame; a clock read paints a pixel that changes between runs, which
102
+ * breaks the "same source, same result" guarantee (scenar-cloud DD-006).
103
+ *
104
+ * The date is `2026-07-20`, the tour world's demo day. The time is 11:00 UTC,
105
+ * chosen by the *reader offset window*: components format dates in the
106
+ * reader's local time (e.g. `ApiKeyListPanel` via `toLocaleDateString`), and
107
+ * real UTC offsets span −11:00 (Midway) to +14:00 (Kiritimati) — a 25-hour
108
+ * range against a 24-hour day, so **no instant renders one calendar date
109
+ * everywhere**. The supported window is UTC−11:00 through UTC+12:45 — every
110
+ * inhabited zone except Tongatapu (+13, ~100k) and Kiritimati (+14, ~6k).
111
+ * 11:00Z holds `Jul 20` across that whole window; the previous noon anchor
112
+ * flipped to `Jul 21` at UTC+12:45 and beyond (Auckland, Fiji, Chatham).
113
+ * Instants whose calendar *date* is rendered must stay inside the window's
114
+ * stable band, 11:00:00Z–11:14:59Z — the anchor sits at its start so all
115
+ * derivation headroom points forward. Duration deltas (end minus start) are
116
+ * zone-invariant and may leave the band freely.
117
+ *
118
+ * The window stabilises the rendered *date*, not the rendered *string*:
119
+ * locale-unpinned formatters still vary the format per reader ("Jul 20, 2026"
120
+ * vs "20 Jul 2026"), and a component that renders a *time of day* is
121
+ * zone-dependent no matter which instant is chosen. The property test in
122
+ * `__tests__/samples.test.ts` locks the date-stability claim at both window
123
+ * boundaries.
124
+ */
125
+ export const SAMPLE_INSTANT = "2026-07-20T11:00:00.000Z";
126
+
127
+ /** {@link SAMPLE_INSTANT} in epoch milliseconds, the base all derivation uses. */
128
+ const SAMPLE_INSTANT_MS = Date.parse(SAMPLE_INSTANT);
129
+
130
+ /**
131
+ * An ISO-8601 instant `deltaMs` after {@link SAMPLE_INSTANT} — the way a
132
+ * fixture expresses "2.4 seconds later" or "a day earlier" without authoring
133
+ * a second instant. `sampleInstant()` with no argument is the anchor itself.
134
+ *
135
+ * Prefer this over a hand-written literal even for values that never render:
136
+ * derivation keeps the whole fixture world moving together if the demo day is
137
+ * ever refreshed, and the tour gate rejects authored instant literals anyway.
138
+ */
139
+ export function sampleInstant(deltaMs = 0): string {
140
+ return new Date(SAMPLE_INSTANT_MS + deltaMs).toISOString();
141
+ }
142
+
143
+ /**
144
+ * {@link sampleInstant} as a `Date`, for APIs that take one (e.g. protobuf's
145
+ * `timestampFromDate`). Same instant, same derivation rule.
146
+ */
147
+ export function sampleDate(deltaMs = 0): Date {
148
+ return new Date(SAMPLE_INSTANT_MS + deltaMs);
149
+ }
150
+
151
+ /**
152
+ * When a default {@link samples.toolCall} completes: {@link SAMPLE_INSTANT}
153
+ * plus 1.2s, so the rendered duration chip reads a stable "1.2s" — long
154
+ * enough to never collapse to the "0ms" that two adjacent live clock reads
155
+ * used to produce.
156
+ */
157
+ const SAMPLE_TOOL_CALL_COMPLETED_AT = sampleInstant(1_200);
158
+
78
159
  // ---------------------------------------------------------------------------
79
160
  // Override interfaces — flat projections of commonly-customized fields
80
161
  // ---------------------------------------------------------------------------
@@ -145,6 +226,16 @@ export interface ApiKeyOverrides {
145
226
  readonly fingerprint?: string;
146
227
  readonly neverExpires?: boolean;
147
228
  readonly keyHash?: string;
229
+ /** Frozen ISO-8601 instant for the key's `createdAt`. Defaults to {@link SAMPLE_INSTANT}. */
230
+ readonly createdAt?: string;
231
+ }
232
+
233
+ export interface OrganizationOverrides {
234
+ readonly id?: string;
235
+ readonly name?: string;
236
+ readonly slug?: string;
237
+ /** Personal orgs render a person icon in the switcher; teams a building. */
238
+ readonly isPersonal?: boolean;
148
239
  }
149
240
 
150
241
  export interface SearchResultOverrides {
@@ -166,17 +257,30 @@ export interface SearchResultOverrides {
166
257
  *
167
258
  * Each factory returns a realistic protobuf object with sensible defaults.
168
259
  * Pass an overrides object to customize the most commonly-needed fields.
169
- * For deeper customization, modify the returned object directly — protobuf
170
- * messages from `create()` are mutable plain objects.
260
+ *
261
+ * Every timestamp is frozen at {@link SAMPLE_INSTANT} — these factories never
262
+ * read the live clock, so a fixture renders identical pixels on every browser
263
+ * replay and every video-export frame (scenar-cloud DD-006). Do not
264
+ * reintroduce `Date.now()` or `new Date()` here.
265
+ *
266
+ * For a value the overrides do not cover — a different timestamp, a distinct
267
+ * tool-call id — modify the returned object directly rather than growing the
268
+ * override surface: protobuf messages from `create()` are mutable plain
269
+ * objects. Timestamps derive from the anchor via {@link sampleInstant} rather
270
+ * than a hand-written literal, so the fixture world keeps a single clock.
171
271
  *
172
272
  * @example
173
273
  * ```ts
174
- * import { samples } from "@stigmer/react/test";
274
+ * import { samples, sampleInstant } from "@stigmer/react/test";
175
275
  *
176
276
  * const scenario = buildScenario(
177
277
  * fixtures.session.get(() => samples.session({ subject: "My topic" })),
178
278
  * fixtures.agent.getByReference(() => samples.agent({ name: "My Agent" })),
179
279
  * );
280
+ *
281
+ * // Deeper customization — derive "5 seconds later" from the anchor:
282
+ * const later = samples.humanMessage("later");
283
+ * later.timestamp = sampleInstant(5_000);
180
284
  * ```
181
285
  */
182
286
  export const samples = {
@@ -363,53 +467,92 @@ export const samples = {
363
467
  status: create(ApiKeyStatusSchema, {
364
468
  audit: create(ApiResourceAuditSchema, {
365
469
  specAudit: create(ApiResourceAuditInfoSchema, {
366
- createdAt: { seconds: BigInt(Math.floor(Date.now() / 1000)), nanos: 0 },
470
+ createdAt: timestampFromDate(new Date(o?.createdAt ?? SAMPLE_INSTANT)),
367
471
  }),
368
472
  }),
369
473
  }),
370
474
  });
371
475
  },
372
476
 
477
+ /**
478
+ * An organization resource with metadata and spec.
479
+ * Default: team org `Acme Corp` (slug `acme`) — the tour world's
480
+ * depicted organization, matching `DEMO_ORG` in the demos workspace.
481
+ */
482
+ organization(o?: OrganizationOverrides): Organization {
483
+ return create(OrganizationSchema, {
484
+ apiVersion: "tenancy.stigmer.ai/v1",
485
+ kind: "Organization",
486
+ metadata: create(ApiResourceMetadataSchema, {
487
+ id: o?.id ?? "org-00000000-0000-0000-0000-000000000001",
488
+ name: o?.name ?? "Acme Corp",
489
+ slug: o?.slug ?? "acme",
490
+ }),
491
+ spec: create(OrganizationSpecSchema, {
492
+ isPersonal: o?.isPersonal ?? false,
493
+ }),
494
+ });
495
+ },
496
+
497
+ /**
498
+ * An `Organizations` response for `findMyOrganizations` — what
499
+ * `OrgProvider` fetches to drive the org switcher.
500
+ * Default: a single {@link samples.organization}.
501
+ */
502
+ organizationList(entries?: Organization[]): Organizations {
503
+ return create(OrganizationsSchema, {
504
+ entries: entries ?? [samples.organization()],
505
+ });
506
+ },
507
+
373
508
  // ---- Message & artifact primitives ----
374
509
 
375
- /** A human (user) message. */
510
+ /** A human (user) message, stamped at {@link SAMPLE_INSTANT}. */
376
511
  humanMessage(content: string): AgentMessage {
377
512
  return create(AgentMessageSchema, {
378
513
  type: MessageType.MESSAGE_HUMAN,
379
514
  content,
380
- timestamp: new Date().toISOString(),
515
+ timestamp: SAMPLE_INSTANT,
381
516
  });
382
517
  },
383
518
 
384
- /** An AI (assistant) message, optionally with tool calls. */
519
+ /** An AI (assistant) message, optionally with tool calls, stamped at {@link SAMPLE_INSTANT}. */
385
520
  aiMessage(content: string, toolCalls?: ToolCall[]): AgentMessage {
386
521
  return create(AgentMessageSchema, {
387
522
  type: MessageType.MESSAGE_AI,
388
523
  content,
389
- timestamp: new Date().toISOString(),
524
+ timestamp: SAMPLE_INSTANT,
390
525
  toolCalls: toolCalls ?? [],
391
526
  });
392
527
  },
393
528
 
394
- /** A completed tool call with a result. */
529
+ /**
530
+ * A completed tool call with a result, spanning {@link SAMPLE_INSTANT} to 1.2s
531
+ * later so its duration chip reads a stable "1.2s".
532
+ *
533
+ * The `id` is `tc-${name}`, derived from the name so it is stable across
534
+ * runs. Two tool calls sharing a name therefore share an id — if a single
535
+ * demo renders both and one drives an approval (`useApproval(toolCall.id)`),
536
+ * build the second by hand with a distinct id via `create(ToolCallSchema)`.
537
+ */
395
538
  toolCall(name: string, result: string): ToolCall {
396
539
  return create(ToolCallSchema, {
397
- id: `tc-${name}-${Date.now()}`,
540
+ id: `tc-${name}`,
398
541
  name,
399
542
  result,
400
543
  status: ToolCallStatus.TOOL_CALL_COMPLETED,
401
- startedAt: new Date().toISOString(),
402
- completedAt: new Date().toISOString(),
544
+ startedAt: SAMPLE_INSTANT,
545
+ completedAt: SAMPLE_TOOL_CALL_COMPLETED_AT,
403
546
  });
404
547
  },
405
548
 
406
- /** A file artifact produced by an execution. */
549
+ /** A file artifact produced by an execution, stamped at {@link SAMPLE_INSTANT}. */
407
550
  artifact(name: string, kind?: ExecutionArtifactKind): ExecutionArtifact {
408
551
  return create(ExecutionArtifactSchema, {
409
552
  name,
410
553
  kind: kind ?? ExecutionArtifactKind.FILE,
411
554
  storageKey: `demo-artifact-${name}`,
412
- createdAt: new Date().toISOString(),
555
+ createdAt: SAMPLE_INSTANT,
413
556
  });
414
557
  },
415
558
 
@@ -17,6 +17,8 @@ export interface UseWorkflowCountOptions {
17
17
  * @default "org"
18
18
  */
19
19
  readonly scope?: ResourceListScope;
20
+ /** Opaque token that forces a recount when its value changes. */
21
+ readonly refetchToken?: unknown;
20
22
  }
21
23
 
22
24
  /** Return value of {@link useWorkflowCount}. */