@stigmer/react 3.12.6 → 3.12.7

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 (149) hide show
  1. package/composer/ComposerToolbar.d.ts +19 -1
  2. package/composer/ComposerToolbar.d.ts.map +1 -1
  3. package/composer/ComposerToolbar.js +7 -2
  4. package/composer/ComposerToolbar.js.map +1 -1
  5. package/composer/SessionComposer.d.ts +10 -0
  6. package/composer/SessionComposer.d.ts.map +1 -1
  7. package/composer/SessionComposer.js +2 -2
  8. package/composer/SessionComposer.js.map +1 -1
  9. package/composer/index.d.ts +1 -0
  10. package/composer/index.d.ts.map +1 -1
  11. package/composer/index.js.map +1 -1
  12. package/execution/MessageThread.d.ts.map +1 -1
  13. package/execution/MessageThread.js +8 -11
  14. package/execution/MessageThread.js.map +1 -1
  15. package/execution/useFileChangeContent.d.ts +2 -6
  16. package/execution/useFileChangeContent.d.ts.map +1 -1
  17. package/execution/useFileChangeContent.js +4 -12
  18. package/execution/useFileChangeContent.js.map +1 -1
  19. package/index.d.ts +6 -3
  20. package/index.d.ts.map +1 -1
  21. package/index.js +5 -1
  22. package/index.js.map +1 -1
  23. package/internal/download.d.ts +8 -0
  24. package/internal/download.d.ts.map +1 -0
  25. package/internal/download.js +25 -0
  26. package/internal/download.js.map +1 -0
  27. package/library/useExportResource.d.ts.map +1 -1
  28. package/library/useExportResource.js +3 -17
  29. package/library/useExportResource.js.map +1 -1
  30. package/memory/MemoryListPanel.d.ts +35 -0
  31. package/memory/MemoryListPanel.d.ts.map +1 -0
  32. package/memory/MemoryListPanel.js +141 -0
  33. package/memory/MemoryListPanel.js.map +1 -0
  34. package/memory/index.d.ts +8 -0
  35. package/memory/index.d.ts.map +1 -0
  36. package/memory/index.js +12 -0
  37. package/memory/index.js.map +1 -0
  38. package/memory/memoryGroups.d.ts +31 -0
  39. package/memory/memoryGroups.d.ts.map +1 -0
  40. package/memory/memoryGroups.js +52 -0
  41. package/memory/memoryGroups.js.map +1 -0
  42. package/memory/useConfirmMemory.d.ts +31 -0
  43. package/memory/useConfirmMemory.d.ts.map +1 -0
  44. package/memory/useConfirmMemory.js +43 -0
  45. package/memory/useConfirmMemory.js.map +1 -0
  46. package/memory/useDeleteMemory.d.ts +30 -0
  47. package/memory/useDeleteMemory.d.ts.map +1 -0
  48. package/memory/useDeleteMemory.js +42 -0
  49. package/memory/useDeleteMemory.js.map +1 -0
  50. package/memory/useMemories.d.ts +33 -0
  51. package/memory/useMemories.d.ts.map +1 -0
  52. package/memory/useMemories.js +42 -0
  53. package/memory/useMemories.js.map +1 -0
  54. package/memory/useRejectMemory.d.ts +34 -0
  55. package/memory/useRejectMemory.d.ts.map +1 -0
  56. package/memory/useRejectMemory.js +46 -0
  57. package/memory/useRejectMemory.js.map +1 -0
  58. package/memory/useUpdateMemoryContent.d.ts +32 -0
  59. package/memory/useUpdateMemoryContent.d.ts.map +1 -0
  60. package/memory/useUpdateMemoryContent.js +48 -0
  61. package/memory/useUpdateMemoryContent.js.map +1 -0
  62. package/package.json +4 -4
  63. package/session/NewSessionViewer.d.ts.map +1 -1
  64. package/session/NewSessionViewer.js +9 -1
  65. package/session/NewSessionViewer.js.map +1 -1
  66. package/session/SessionViewer.d.ts +14 -1
  67. package/session/SessionViewer.d.ts.map +1 -1
  68. package/session/SessionViewer.js +21 -25
  69. package/session/SessionViewer.js.map +1 -1
  70. package/session/TranscriptExportMenu.d.ts +24 -0
  71. package/session/TranscriptExportMenu.d.ts.map +1 -0
  72. package/session/TranscriptExportMenu.js +26 -0
  73. package/session/TranscriptExportMenu.js.map +1 -0
  74. package/session/index.d.ts +4 -0
  75. package/session/index.d.ts.map +1 -1
  76. package/session/index.js +2 -0
  77. package/session/index.js.map +1 -1
  78. package/session/useExportTranscript.d.ts +44 -0
  79. package/session/useExportTranscript.d.ts.map +1 -0
  80. package/session/useExportTranscript.js +109 -0
  81. package/session/useExportTranscript.js.map +1 -0
  82. package/session/useNewSessionFlow.d.ts +11 -0
  83. package/session/useNewSessionFlow.d.ts.map +1 -1
  84. package/session/useNewSessionFlow.js +14 -6
  85. package/session/useNewSessionFlow.js.map +1 -1
  86. package/session/useSessionConversation.d.ts.map +1 -1
  87. package/session/useSessionConversation.js +7 -19
  88. package/session/useSessionConversation.js.map +1 -1
  89. package/session/useSessionExecutions.d.ts +2 -14
  90. package/session/useSessionExecutions.d.ts.map +1 -1
  91. package/session/useSessionExecutions.js +4 -26
  92. package/session/useSessionExecutions.js.map +1 -1
  93. package/session/useSessionPageFlow.d.ts +23 -10
  94. package/session/useSessionPageFlow.d.ts.map +1 -1
  95. package/session/useSessionPageFlow.js +61 -9
  96. package/session/useSessionPageFlow.js.map +1 -1
  97. package/settings/MemorySection.d.ts +12 -0
  98. package/settings/MemorySection.d.ts.map +1 -0
  99. package/settings/MemorySection.js +21 -0
  100. package/settings/MemorySection.js.map +1 -0
  101. package/settings/index.d.ts +1 -0
  102. package/settings/index.d.ts.map +1 -1
  103. package/settings/index.js +1 -0
  104. package/settings/index.js.map +1 -1
  105. package/settings/settings-nav.d.ts.map +1 -1
  106. package/settings/settings-nav.js +2 -1
  107. package/settings/settings-nav.js.map +1 -1
  108. package/src/__tests__/public-api.test.ts +21 -0
  109. package/src/composer/ComposerToolbar.tsx +58 -0
  110. package/src/composer/SessionComposer.tsx +13 -1
  111. package/src/composer/__tests__/ComposerToolbar-autoApprove.test.tsx +80 -0
  112. package/src/composer/index.ts +1 -0
  113. package/src/execution/MessageThread.tsx +8 -11
  114. package/src/execution/useFileChangeContent.ts +5 -12
  115. package/src/index.ts +34 -0
  116. package/src/internal/download.ts +29 -0
  117. package/src/library/useExportResource.ts +3 -19
  118. package/src/memory/MemoryListPanel.tsx +532 -0
  119. package/src/memory/__tests__/MemoryListPanel.test.tsx +259 -0
  120. package/src/memory/__tests__/memoryHooks.test.tsx +220 -0
  121. package/src/memory/index.ts +19 -0
  122. package/src/memory/memoryGroups.ts +64 -0
  123. package/src/memory/useConfirmMemory.ts +62 -0
  124. package/src/memory/useDeleteMemory.ts +61 -0
  125. package/src/memory/useMemories.ts +66 -0
  126. package/src/memory/useRejectMemory.ts +65 -0
  127. package/src/memory/useUpdateMemoryContent.ts +67 -0
  128. package/src/session/NewSessionViewer.tsx +15 -1
  129. package/src/session/SessionViewer.tsx +48 -49
  130. package/src/session/TranscriptExportMenu.tsx +78 -0
  131. package/src/session/__tests__/SessionViewer-autoApproveToggle.test.tsx +178 -0
  132. package/src/session/__tests__/SessionViewer-transcriptExport.test.tsx +151 -0
  133. package/src/session/__tests__/TranscriptExportMenu.test.tsx +97 -0
  134. package/src/session/__tests__/useExportTranscript.test.tsx +187 -0
  135. package/src/session/__tests__/useNewSessionFlow.test.tsx +33 -0
  136. package/src/session/__tests__/useSessionPageFlow.autoApprove.test.tsx +176 -2
  137. package/src/session/index.ts +9 -0
  138. package/src/session/useExportTranscript.ts +171 -0
  139. package/src/session/useNewSessionFlow.ts +28 -7
  140. package/src/session/useSessionConversation.ts +10 -16
  141. package/src/session/useSessionExecutions.ts +5 -26
  142. package/src/session/useSessionPageFlow.ts +84 -21
  143. package/src/settings/MemorySection.tsx +45 -0
  144. package/src/settings/index.ts +1 -0
  145. package/src/settings/settings-nav.ts +2 -0
  146. package/src/usage/useExportCSV.ts +2 -14
  147. package/usage/useExportCSV.d.ts.map +1 -1
  148. package/usage/useExportCSV.js +2 -13
  149. package/usage/useExportCSV.js.map +1 -1
@@ -0,0 +1,171 @@
1
+ "use client";
2
+
3
+ import { useCallback, useMemo, useState } from "react";
4
+ import {
5
+ fetchSessionTranscript,
6
+ resolvedSubject,
7
+ transcriptToJson,
8
+ transcriptToMarkdown,
9
+ type SessionTranscript,
10
+ } from "@stigmer/sdk";
11
+ import { toast } from "../feedback/toast.js";
12
+ import { useStigmer } from "../hooks.js";
13
+ import { downloadTextFile } from "../internal/download.js";
14
+ import { generateSlug } from "../internal/slug.js";
15
+ import { toError } from "../internal/toError.js";
16
+
17
+ /** Options for {@link useExportTranscript}. */
18
+ export interface UseExportTranscriptOptions {
19
+ /**
20
+ * Keep turns replaced by edit-and-resubmit in the export, marked as
21
+ * superseded. Defaults to `false` — the conversation as the viewer
22
+ * shows it.
23
+ */
24
+ readonly includeSuperseded?: boolean;
25
+ }
26
+
27
+ /** Return value of {@link useExportTranscript}. */
28
+ export interface UseExportTranscriptReturn {
29
+ /** Copy the whole conversation as Markdown to the clipboard. */
30
+ readonly copyMarkdown: () => Promise<void>;
31
+ /** Download the whole conversation as a Markdown file. */
32
+ readonly downloadMarkdown: () => Promise<void>;
33
+ /** Download the whole conversation as a structured JSON file. */
34
+ readonly downloadJson: () => Promise<void>;
35
+ /** `true` while a transcript fetch (and offload resolution) is in flight. */
36
+ readonly isExporting: boolean;
37
+ /** The last export failure, or `null`. Cleared when a new export starts. */
38
+ readonly error: Error | null;
39
+ }
40
+
41
+ /**
42
+ * Behavior hook that exports a session's whole conversation — thinking,
43
+ * tool calls with offloaded outputs resolved, sub-agent turns, timestamps —
44
+ * as Markdown (copy or download) or structured JSON (stigmer/stigmer#814).
45
+ *
46
+ * Lazy: nothing is fetched until an action is invoked. Each action fetches
47
+ * the canonical transcript via `fetchSessionTranscript` (the SDK's one
48
+ * authoritative assembly), so every export surface produces identical
49
+ * content. Feedback follows the `useExportResource` convention: toasts for
50
+ * success/failure, plus a typed `error` for hosts that render their own.
51
+ *
52
+ * @example
53
+ * ```tsx
54
+ * const exporter = useExportTranscript(sessionId);
55
+ *
56
+ * <ActionMenu.Item onSelect={exporter.copyMarkdown}>Copy transcript</ActionMenu.Item>
57
+ * <ActionMenu.Item onSelect={exporter.downloadMarkdown}>Download .md</ActionMenu.Item>
58
+ * <ActionMenu.Item onSelect={exporter.downloadJson}>Download .json</ActionMenu.Item>
59
+ * ```
60
+ */
61
+ export function useExportTranscript(
62
+ sessionId: string | null,
63
+ options?: UseExportTranscriptOptions,
64
+ ): UseExportTranscriptReturn {
65
+ const stigmer = useStigmer();
66
+ const includeSuperseded = options?.includeSuperseded === true;
67
+ const [isExporting, setIsExporting] = useState(false);
68
+ const [error, setError] = useState<Error | null>(null);
69
+
70
+ const fetchTranscript = useCallback((): Promise<SessionTranscript> => {
71
+ if (!sessionId) {
72
+ return Promise.reject(
73
+ new Error(
74
+ "Cannot export a transcript without a session id. Pass the session's " +
75
+ "id to useExportTranscript once the session exists.",
76
+ ),
77
+ );
78
+ }
79
+ return fetchSessionTranscript(stigmer, sessionId, { includeSuperseded });
80
+ }, [stigmer, sessionId, includeSuperseded]);
81
+
82
+ const copyMarkdown = useCallback(async (): Promise<void> => {
83
+ setError(null);
84
+ setIsExporting(true);
85
+ const markdown = fetchTranscript().then(
86
+ (t) => transcriptToMarkdown(t, { generatedAt: new Date().toISOString() }),
87
+ );
88
+ try {
89
+ // The transcript is fetched before it can be copied, and WebKit (the
90
+ // desktop app's webview) only honors clipboard writes inside the user
91
+ // gesture's task — a plain writeText after the await is rejected
92
+ // there. ClipboardItem accepts a promise, letting the write start
93
+ // synchronously in the gesture while the content resolves.
94
+ if (typeof ClipboardItem !== "undefined" && navigator.clipboard?.write) {
95
+ await navigator.clipboard.write([
96
+ new ClipboardItem({
97
+ "text/plain": markdown.then(
98
+ (text) => new Blob([text], { type: "text/plain" }),
99
+ ),
100
+ }),
101
+ ]);
102
+ } else {
103
+ await navigator.clipboard.writeText(await markdown);
104
+ }
105
+ toast.success("Transcript copied to clipboard");
106
+ } catch (e) {
107
+ const err = toError(e);
108
+ setError(err);
109
+ toast.error(`Couldn't copy the transcript: ${err.message}`);
110
+ } finally {
111
+ setIsExporting(false);
112
+ }
113
+ }, [fetchTranscript]);
114
+
115
+ const download = useCallback(
116
+ async (serialize: (t: SessionTranscript) => { content: string; extension: string; mimeType: string }): Promise<void> => {
117
+ setError(null);
118
+ setIsExporting(true);
119
+ try {
120
+ const transcript = await fetchTranscript();
121
+ const { content, extension, mimeType } = serialize(transcript);
122
+ downloadTextFile(
123
+ content,
124
+ `${transcriptFilename(transcript)}.${extension}`,
125
+ mimeType,
126
+ );
127
+ } catch (e) {
128
+ const err = toError(e);
129
+ setError(err);
130
+ toast.error(`Couldn't export the transcript: ${err.message}`);
131
+ } finally {
132
+ setIsExporting(false);
133
+ }
134
+ },
135
+ [fetchTranscript],
136
+ );
137
+
138
+ const downloadMarkdown = useCallback(
139
+ () =>
140
+ download((t) => ({
141
+ content: transcriptToMarkdown(t, {
142
+ generatedAt: new Date().toISOString(),
143
+ }),
144
+ extension: "md",
145
+ mimeType: "text/markdown",
146
+ })),
147
+ [download],
148
+ );
149
+
150
+ const downloadJson = useCallback(
151
+ () =>
152
+ download((t) => ({
153
+ content: JSON.stringify(transcriptToJson(t), null, 2),
154
+ extension: "json",
155
+ mimeType: "application/json",
156
+ })),
157
+ [download],
158
+ );
159
+
160
+ return useMemo(
161
+ () => ({ copyMarkdown, downloadMarkdown, downloadJson, isExporting, error }),
162
+ [copyMarkdown, downloadMarkdown, downloadJson, isExporting, error],
163
+ );
164
+ }
165
+
166
+ /** `<subject-slug>-transcript`, falling back to the session id. */
167
+ function transcriptFilename(transcript: SessionTranscript): string {
168
+ const subject = resolvedSubject(transcript.session.spec?.subject);
169
+ const slug = subject ? generateSlug(subject).slice(0, 60) : "";
170
+ return `${slug || transcript.session.metadata?.id || "session"}-transcript`;
171
+ }
@@ -218,6 +218,18 @@ export interface UseNewSessionFlowReturn {
218
218
  /** Session variables (per-execution secrets) manager. */
219
219
  readonly sessionVariables: UseSessionVariablesReturn;
220
220
 
221
+ /**
222
+ * Pre-arm "auto-approve tool calls" for the session this surface will
223
+ * create (stigmer/stigmer#816, the walk-away scenario). Seeded from the
224
+ * host's `StigmerProvider` `approvalDefaults` (#302, guests excluded);
225
+ * the user's toggle wins from then on. Carried into the bootstrap create
226
+ * as `spec.auto_approve_all` — the whole-run server-side bypass — so the
227
+ * first run stays covered even if the user closes the tab.
228
+ */
229
+ readonly autoApproveAll: boolean;
230
+ /** Set the user's explicit auto-approve choice for the created session. */
231
+ readonly setAutoApproveAll: (value: boolean) => void;
232
+
221
233
  /** `true` while the create session + execution flow is in flight. */
222
234
  readonly isSubmitting: boolean;
223
235
  /** Human-readable error from the last failed submission, or `null`. */
@@ -311,13 +323,18 @@ export function useNewSessionFlow(
311
323
  const contextTarget = useExecutionTarget();
312
324
  const executionTarget = options.executionTarget ?? contextTarget;
313
325
  const adapter = useRunnerAdapter();
314
- // Host approval default (#302): pre-arm auto_approve_all on the bootstrap
315
- // create when the provider says so. Guests never inherit it — a share-link
316
- // visitor is not the operator the host's trust judgment covers (the
317
- // GUEST_HARNESS fixed-platform-policy reasoning).
326
+ // Auto-approve for the session about to be created (#816): the host
327
+ // approval default (#302) seeds the INITIAL state only; the user's
328
+ // composer toggle wins from then on. Guests never inherit the default
329
+ // and never see the toggle — a share-link visitor is not the operator
330
+ // the host's trust judgment covers (the GUEST_HARNESS
331
+ // fixed-platform-policy reasoning). Carried into the bootstrap create as
332
+ // spec.auto_approve_all, the whole-run server-side bypass — the created
333
+ // run stays covered even if this tab dies before its first gate.
318
334
  const approvalDefaults = useApprovalDefaults();
319
- const autoApproveAll =
320
- (!isGuest && approvalDefaults?.autoApproveAll) || undefined;
335
+ const [autoApproveAll, setAutoApproveAll] = useState(
336
+ () => !isGuest && (approvalDefaults?.autoApproveAll ?? false),
337
+ );
321
338
 
322
339
  const [harness, setHarnessRaw] = useState<HarnessOption>(() => {
323
340
  // Guests get the fixed platform policy (see GUEST_HARNESS) and never
@@ -496,7 +513,9 @@ export function useNewSessionFlow(
496
513
  ? (pinnedThinkingMode === "enabled" ? "enabled" : undefined)
497
514
  : context?.thinkingMode,
498
515
  workspaceFileRefs: context?.workspaceFileRefs,
499
- autoApproveAll,
516
+ // Only an armed state travels; false stays off the wire (the
517
+ // serviceTier/thinkingMode only-explicit discipline).
518
+ autoApproveAll: autoApproveAll || undefined,
500
519
  };
501
520
 
502
521
  // Resolve which agent the bootstrapped session runs against: an
@@ -624,6 +643,8 @@ export function useNewSessionFlow(
624
643
  setSkillRefs,
625
644
  workspace,
626
645
  sessionVariables,
646
+ autoApproveAll,
647
+ setAutoApproveAll,
627
648
  isSubmitting,
628
649
  submitError,
629
650
  submit,
@@ -10,6 +10,7 @@ import type { McpServerUsage as ProtoMcpServerUsage } from "@stigmer/protos/ai/s
10
10
  import type { WorkspaceEntry as ProtoWorkspaceEntry } from "@stigmer/protos/ai/stigmer/agentic/session/v1/workspace_pb";
11
11
  import type { ApiResourceReference as ProtoApiResourceReference } from "@stigmer/protos/ai/stigmer/commons/apiresource/io_pb";
12
12
  import {
13
+ supersededExecutionIds,
13
14
  toSessionUpdateInput,
14
15
  type AttachmentInput,
15
16
  type EnvVarInput,
@@ -544,13 +545,11 @@ export function useSessionConversation(
544
545
  }
545
546
  }, [activeExecutionId, stream.phase, refetch]);
546
547
 
547
- // Executions replaced via edit-and-resubmit. The successor carries
548
- // `spec.supersedes_execution_id`; hiding the superseded turn makes the
549
- // edited message read as a single corrected exchange (in-place replace).
550
- // The link must also be read off the live stream copy — right after a
551
- // resubmit, the successor streams before the list refetch delivers it.
552
- // Display-only: the raw `executions` list (and therefore active-id
553
- // resolution above) is never filtered.
548
+ // Executions replaced via edit-and-resubmit (the shared conversation rule —
549
+ // see supersededExecutionIds in @stigmer/sdk for the why). The extra id is
550
+ // the live stream copy's link: right after a resubmit, the successor streams
551
+ // before the list refetch delivers it. Display-only: the raw `executions`
552
+ // list (and therefore active-id resolution above) is never filtered.
554
553
  //
555
554
  // Dep is the scalar link, NOT stream.execution: the stream object changes
556
555
  // reference every frame, and rebuilding the Set per frame would hand
@@ -558,15 +557,10 @@ export function useSessionConversation(
558
557
  // memoization (DD-010).
559
558
  const streamSupersededId =
560
559
  stream.execution?.spec?.supersedesExecutionId || null;
561
- const supersededIds = useMemo(() => {
562
- const ids = new Set<string>();
563
- for (const e of executions) {
564
- const superseded = e.spec?.supersedesExecutionId;
565
- if (superseded) ids.add(superseded);
566
- }
567
- if (streamSupersededId) ids.add(streamSupersededId);
568
- return ids;
569
- }, [executions, streamSupersededId]);
560
+ const supersededIds = useMemo(
561
+ () => supersededExecutionIds(executions, streamSupersededId),
562
+ [executions, streamSupersededId],
563
+ );
570
564
 
571
565
  const completedExecutions = useMemo(() => {
572
566
  return executions.filter((e) => {
@@ -3,9 +3,14 @@
3
3
  import { create } from "@bufbuild/protobuf";
4
4
  import type { AgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
5
5
  import { ListAgentExecutionsBySessionRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/io_pb";
6
+ import { sortChronologically } from "@stigmer/sdk";
6
7
  import { useStigmer } from "../hooks.js";
7
8
  import { useFetch } from "../internal/useFetch.js";
8
9
 
10
+ // The ordering rule lives in @stigmer/sdk with the other shared
11
+ // conversation-assembly rules; re-exported here for existing consumers.
12
+ export { sortChronologically };
13
+
9
14
  /** Options for {@link useSessionExecutions}. */
10
15
  export interface UseSessionExecutionsOptions {
11
16
  /**
@@ -71,32 +76,6 @@ export interface UseSessionExecutionsReturn {
71
76
  * const { executions } = useSessionExecutions(sessionId ?? null);
72
77
  * ```
73
78
  */
74
- /**
75
- * Returns the executions in chronological (oldest-first) order — the order
76
- * the conversation thread renders top-to-bottom.
77
- *
78
- * Defense-in-depth against an unordered list response: the executions ARE the
79
- * transcript, so a scrambled order drops the newest turns out of view (they no
80
- * longer sort to the bottom). The server orders this list, but the thread must
81
- * never depend on that alone. Resource ids are time-sortable ULIDs
82
- * (`aex_01k…`), so an ascending id sort is creation order without parsing
83
- * timestamps; entries missing an id sort last but keep a stable relative order.
84
- *
85
- * @internal Exported for testing — not part of the public API.
86
- */
87
- export function sortChronologically(
88
- executions: readonly AgentExecution[],
89
- ): AgentExecution[] {
90
- return [...executions].sort((a, b) => {
91
- const aId = a.metadata?.id ?? "";
92
- const bId = b.metadata?.id ?? "";
93
- if (aId === bId) return 0;
94
- if (!aId) return 1;
95
- if (!bId) return -1;
96
- return aId < bId ? -1 : 1;
97
- });
98
- }
99
-
100
79
  export function useSessionExecutions(
101
80
  sessionId: string | null,
102
81
  options?: UseSessionExecutionsOptions,
@@ -20,6 +20,7 @@ import { useAgentRefFromSession } from "./useAgentRefFromSession.js";
20
20
  import { usePersistedModel, type UsePersistedModelReturn } from "./usePersistedModel.js";
21
21
  import { toSessionUpdateInput } from "@stigmer/sdk";
22
22
  import type { SessionAudience } from "./audience.js";
23
+ import { isChannelOriginSession } from "./channelOrigin.js";
23
24
  import { assertValidRunConfig, type SessionRunConfig } from "./run-config.js";
24
25
 
25
26
  /**
@@ -140,20 +141,33 @@ export interface UseSessionPageFlowReturn {
140
141
  readonly sessionVariables: UseSessionVariablesReturn;
141
142
 
142
143
  /**
143
- * Session-scoped "auto-approve tool calls" preference.
144
+ * Session-scoped "auto-approve tool calls" state (stigmer/stigmer#816).
144
145
  *
145
- * `false` by default; a host can pre-arm it app-wide via
146
- * `StigmerProvider`'s `approvalDefaults` (#302, guests excluded). Flipped
147
- * to `true` when the user chooses "Approve & don't ask again" at an
148
- * approval gate (see {@link submitApproval}), and carried into every
149
- * subsequent follow-up via {@link handleSubmit}. Held in memory only
150
- * reset on reload / new session back to the host default, never persisted
151
- * server-side.
146
+ * `false` by default. `true` when any of its sources arms it the user's
147
+ * explicit choice always winning:
148
+ *
149
+ * 1. the user's composer toggle ({@link setAutoApproveAll});
150
+ * 2. "Approve & don't ask again" at an approval gate (see
151
+ * {@link submitApproval});
152
+ * 3. the host's app-wide `StigmerProvider` `approvalDefaults` (#302,
153
+ * guests excluded);
154
+ * 4. the active in-flight execution having been created with
155
+ * `spec.auto_approve_all` (e.g. armed on the new-session surface), so
156
+ * the state survives the launcher → session-page handoff.
157
+ *
158
+ * While `true`, follow-ups carry `auto_approve_all` ({@link handleSubmit})
159
+ * and gates appearing in the in-flight run are auto-released (never for
160
+ * guest/observer/channel-origin surfaces). Held in memory only — reset on
161
+ * reload back to the host default, never persisted server-side.
152
162
  */
153
163
  readonly autoApproveAll: boolean;
154
164
  /**
155
- * Toggle the session-scoped auto-approve preference. The reversible "Turn off"
156
- * control in the UI calls this with `false`.
165
+ * Set the user's explicit session-scoped auto-approve choice. Wired to the
166
+ * composer's always-visible toggle; an explicit `false` wins over the host
167
+ * default and over an armed in-flight run for everything the client
168
+ * controls (follow-up carry, gate auto-release) — an already-armed run's
169
+ * server-side bypass cannot be revoked mid-run, exactly as with today's
170
+ * gate-time "Approve & don't ask again".
157
171
  */
158
172
  readonly setAutoApproveAll: (value: boolean) => void;
159
173
 
@@ -322,17 +336,34 @@ export function useSessionPageFlow(
322
336
  const [skillRefs, setSkillRefs] = useState<ResourceRef[]>([]);
323
337
  const initialSyncDone = useRef(false);
324
338
 
325
- // Session-scoped auto-approve. Armed either at the approval gate ("Approve &
326
- // don't ask again") or from the host's provider-level approvalDefaults
327
- // (#302) an app-level trust judgment, so it seeds the INITIAL state only;
328
- // the user's in-session "Turn off" always wins from then on. Guests never
329
- // inherit the host default (a share-link visitor is not the operator the
330
- // host's trust judgment covers). Lives only in memory for the life of this
331
- // page reset on reload re-applies the host default, never a user choice.
339
+ // Session-scoped auto-approve (#816). Derived like interactionMode above
340
+ // the user's explicit in-session decision wins, otherwise the truth is
341
+ // computed from its sources never an effect-synced copy:
342
+ //
343
+ // effective = userChoice ?? (hostSeed || runArmed)
344
+ //
345
+ // - `userChoice`: the composer toggle, or the gate-time "Approve & don't
346
+ // ask again" (which has always escalated the session preference).
347
+ // - `hostSeed`: the host's provider-level approvalDefaults (#302) — an
348
+ // app-level trust judgment. Guests never inherit it (a share-link
349
+ // visitor is not the operator the host's trust judgment covers).
350
+ // - `runArmed`: the ACTIVE execution was created with
351
+ // spec.auto_approve_all, so the toggle reflects an armed in-flight run —
352
+ // this is what carries the state across the new-session → session-page
353
+ // handoff. Deliberately derived from the active execution ONLY, never
354
+ // from history: deriving from past executions would silently survive a
355
+ // reload and change the reset-on-reload consent contract below.
356
+ //
357
+ // Lives only in memory for the life of this page — reset on reload
358
+ // re-applies the host default, never a user choice.
332
359
  const approvalDefaults = useApprovalDefaults();
333
- const [autoApproveAll, setAutoApproveAll] = useState(
334
- () => !isGuest && (approvalDefaults?.autoApproveAll ?? false),
335
- );
360
+ const [autoApproveChoice, setAutoApproveChoice] = useState<boolean | null>(null);
361
+ const hostSeed = !isGuest && (approvalDefaults?.autoApproveAll ?? false);
362
+ const runArmed = conv.activeStreamExecution?.spec?.autoApproveAll === true;
363
+ const autoApproveAll = autoApproveChoice ?? (hostSeed || runArmed);
364
+ const setAutoApproveAll = useCallback((value: boolean) => {
365
+ setAutoApproveChoice(value);
366
+ }, []);
336
367
 
337
368
  const submitApproval = useCallback<UseSessionConversationReturn["submitApproval"]>(
338
369
  async (toolCallId, action, comment) => {
@@ -341,13 +372,45 @@ export function useSessionPageFlow(
341
372
  // plane separately resolves the current execution's remaining gates and
342
373
  // the runner skips the gate for the rest of that run.
343
374
  if (action === ApprovalAction.APPROVE_ALL) {
344
- setAutoApproveAll(true);
375
+ setAutoApproveChoice(true);
345
376
  }
346
377
  await conv.submitApproval(toolCallId, action, comment);
347
378
  },
348
379
  [conv.submitApproval],
349
380
  );
350
381
 
382
+ // Armed responder (#816, the walk-away scenario): while auto-approve is ON,
383
+ // answer each approval gate as it appears in the in-flight run. This must be
384
+ // a standing responder, not a flip-time one-shot: a gate-time APPROVE_ALL
385
+ // grants a lease scoped to ONE tool class (see APPROVAL_ACTION_APPROVE_ALL
386
+ // in agentexecution enum.proto), so a later gate of a different class would
387
+ // still park the run — the responder covers each class as it surfaces, and
388
+ // every released call keeps an honest audit decision.
389
+ //
390
+ // Read-only surfaces must never auto-submit approvals on a viewer's behalf:
391
+ // the predicate mirrors SessionViewer's read-only derivation (guest audience,
392
+ // observer audience, channel-origin session) exactly.
393
+ const canRespondToGates =
394
+ !isGuest &&
395
+ options.audience !== "observer" &&
396
+ !isChannelOriginSession(conv.session);
397
+ const respondedToolCallIds = useRef<Set<string>>(new Set());
398
+ useEffect(() => {
399
+ if (!autoApproveAll || !canRespondToGates) return;
400
+ for (const approval of conv.pendingApprovals) {
401
+ const toolCallId = approval.toolCallId;
402
+ // Marked before the submit resolves so a re-render mid-flight cannot
403
+ // double-submit. A failed submission is deliberately NOT retried — the
404
+ // approval card stays for a manual decision instead of a retry storm.
405
+ if (!toolCallId || respondedToolCallIds.current.has(toolCallId)) continue;
406
+ respondedToolCallIds.current.add(toolCallId);
407
+ void conv.submitApproval(toolCallId, ApprovalAction.APPROVE_ALL).catch(() => {
408
+ // Swallowed by design (see above); the card's own error surface
409
+ // reports the failure where the user can act on it.
410
+ });
411
+ }
412
+ }, [autoApproveAll, canRespondToGates, conv.pendingApprovals, conv.submitApproval]);
413
+
351
414
  // -------------------------------------------------------------------------
352
415
  // Agent — derive from session, allow mid-session changes
353
416
  // -------------------------------------------------------------------------
@@ -0,0 +1,45 @@
1
+ "use client";
2
+
3
+ import { useId } from "react";
4
+ import { MemoryListPanel } from "../memory/MemoryListPanel.js";
5
+ import { useActiveOrgSlug } from "../organization/OrgProvider.js";
6
+
7
+ /**
8
+ * Settings section listing everything the platform remembers about the
9
+ * caller in the active organization.
10
+ *
11
+ * The helper copy carries the DD-006 D6 transparency statement: a
12
+ * confirmed memory is injected into the caller's future sessions and
13
+ * appears in those executions' records. Memory content everywhere else
14
+ * is subject-only — this page shows the caller their own records and
15
+ * nobody else's.
16
+ */
17
+ export function MemorySection() {
18
+ const headingId = useId();
19
+ const org = useActiveOrgSlug();
20
+
21
+ return (
22
+ <section aria-labelledby={headingId}>
23
+ <h2
24
+ id={headingId}
25
+ className="stg:text-foreground stg:mb-1 stg:text-sm stg:font-semibold"
26
+ >
27
+ Memory
28
+ </h2>
29
+ <p className="stg:text-muted-foreground stg:mb-6 stg:text-xs">
30
+ Facts agents proposed to remember about you in this organization.
31
+ Confirmed memories are shared with agents in your future sessions
32
+ and appear in those executions&apos; records. Only you can see
33
+ them here, and you can edit or delete any of them at any time.
34
+ </p>
35
+
36
+ {!org ? (
37
+ <p className="stg:text-muted-foreground stg:py-4 stg:text-center stg:text-xs">
38
+ Select an organization to view its memories.
39
+ </p>
40
+ ) : (
41
+ <MemoryListPanel org={org} />
42
+ )}
43
+ </section>
44
+ );
45
+ }
@@ -12,6 +12,7 @@ export { MembersSection } from "./MembersSection.js";
12
12
  export { OrgProfileSection } from "./OrgProfileSection.js";
13
13
  export { OrgPreferencesSection } from "./OrgPreferencesSection.js";
14
14
  export { AccountPreferencesSection } from "./AccountPreferencesSection.js";
15
+ export { MemorySection } from "./MemorySection.js";
15
16
  export { EnvironmentsSection } from "./EnvironmentsSection.js";
16
17
  export { InvitationsSection } from "./InvitationsSection.js";
17
18
  export { IdentityProvidersSection } from "./IdentityProvidersSection.js";
@@ -4,6 +4,7 @@ import {
4
4
  AppWindow,
5
5
  BarChart3,
6
6
  Box,
7
+ Brain,
7
8
  Building2,
8
9
  CreditCard,
9
10
  KeyRound,
@@ -108,6 +109,7 @@ export const SETTINGS_NAV_GROUPS: readonly SettingsNavGroup[] = [
108
109
  label: "Preferences",
109
110
  icon: UserCog,
110
111
  },
112
+ { href: "/settings/memory", label: "Memory", icon: Brain },
111
113
  ],
112
114
  },
113
115
  ];
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { useCallback, useState } from "react";
4
4
  import type { GetOrgUsageReportOutput } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/io_pb";
5
+ import { downloadTextFile } from "../internal/download.js";
5
6
 
6
7
  /** Export format for the CSV download. */
7
8
  export type ExportFormat = "daily_summary" | "model_breakdown";
@@ -41,7 +42,7 @@ export function useExportCSV(
41
42
  ? buildDailySummaryCSV(report, orgId)
42
43
  : buildModelBreakdownCSV(report, orgId);
43
44
 
44
- downloadCSV(csv, filename);
45
+ downloadTextFile(csv, filename, "text/csv");
45
46
  } finally {
46
47
  setIsExporting(false);
47
48
  }
@@ -100,16 +101,3 @@ function escapeCsvField(value: string): string {
100
101
  }
101
102
  return value;
102
103
  }
103
-
104
- function downloadCSV(csv: string, filename: string): void {
105
- const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
106
- const url = URL.createObjectURL(blob);
107
- const link = document.createElement("a");
108
- link.href = url;
109
- link.download = filename;
110
- link.style.display = "none";
111
- document.body.appendChild(link);
112
- link.click();
113
- document.body.removeChild(link);
114
- URL.revokeObjectURL(url);
115
- }
@@ -1 +1 @@
1
- {"version":3,"file":"useExportCSV.d.ts","sourceRoot":"","sources":["../../src/usage/useExportCSV.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,4DAA4D,CAAC;AAE1G,0CAA0C;AAC1C,MAAM,MAAM,YAAY,GAAG,eAAe,GAAG,iBAAiB,CAAC;AAE/D,4CAA4C;AAC5C,MAAM,WAAW,kBAAkB;IACjC,mDAAmD;IACnD,QAAQ,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IACnD,4CAA4C;IAC5C,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,uBAAuB,GAAG,IAAI,EACtC,KAAK,EAAE,MAAM,GACZ,kBAAkB,CAsBpB"}
1
+ {"version":3,"file":"useExportCSV.d.ts","sourceRoot":"","sources":["../../src/usage/useExportCSV.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,4DAA4D,CAAC;AAG1G,0CAA0C;AAC1C,MAAM,MAAM,YAAY,GAAG,eAAe,GAAG,iBAAiB,CAAC;AAE/D,4CAA4C;AAC5C,MAAM,WAAW,kBAAkB;IACjC,mDAAmD;IACnD,QAAQ,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IACnD,4CAA4C;IAC5C,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,uBAAuB,GAAG,IAAI,EACtC,KAAK,EAAE,MAAM,GACZ,kBAAkB,CAsBpB"}
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { useCallback, useState } from "react";
3
+ import { downloadTextFile } from "../internal/download.js";
3
4
  /**
4
5
  * Behavior hook that generates and downloads usage data as CSV.
5
6
  *
@@ -21,7 +22,7 @@ export function useExportCSV(report, orgId) {
21
22
  const { csv, filename } = format === "daily_summary"
22
23
  ? buildDailySummaryCSV(report, orgId)
23
24
  : buildModelBreakdownCSV(report, orgId);
24
- downloadCSV(csv, filename);
25
+ downloadTextFile(csv, filename, "text/csv");
25
26
  }
26
27
  finally {
27
28
  setIsExporting(false);
@@ -66,16 +67,4 @@ function escapeCsvField(value) {
66
67
  }
67
68
  return value;
68
69
  }
69
- function downloadCSV(csv, filename) {
70
- const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
71
- const url = URL.createObjectURL(blob);
72
- const link = document.createElement("a");
73
- link.href = url;
74
- link.download = filename;
75
- link.style.display = "none";
76
- document.body.appendChild(link);
77
- link.click();
78
- document.body.removeChild(link);
79
- URL.revokeObjectURL(url);
80
- }
81
70
  //# sourceMappingURL=useExportCSV.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useExportCSV.js","sourceRoot":"","sources":["../../src/usage/useExportCSV.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useExportCSV.js","sourceRoot":"","sources":["../../src/usage/useExportCSV.ts"],"names":[],"mappings":"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"}