@stigmer/react 3.1.5 → 3.1.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 (54) hide show
  1. package/execution/WriteBackCard.d.ts +7 -1
  2. package/execution/WriteBackCard.d.ts.map +1 -1
  3. package/execution/WriteBackCard.js +10 -2
  4. package/execution/WriteBackCard.js.map +1 -1
  5. package/mcp-server/McpServerConnectDialog.d.ts.map +1 -1
  6. package/mcp-server/McpServerConnectDialog.js +7 -3
  7. package/mcp-server/McpServerConnectDialog.js.map +1 -1
  8. package/mcp-server/McpServerDetailView.d.ts.map +1 -1
  9. package/mcp-server/McpServerDetailView.js +6 -3
  10. package/mcp-server/McpServerDetailView.js.map +1 -1
  11. package/mcp-server/McpServerPicker.d.ts.map +1 -1
  12. package/mcp-server/McpServerPicker.js +3 -1
  13. package/mcp-server/McpServerPicker.js.map +1 -1
  14. package/mcp-server/OAuthRequiredNotice.d.ts +29 -0
  15. package/mcp-server/OAuthRequiredNotice.d.ts.map +1 -0
  16. package/mcp-server/OAuthRequiredNotice.js +29 -0
  17. package/mcp-server/OAuthRequiredNotice.js.map +1 -0
  18. package/mcp-server/StdioSandboxNotice.d.ts +43 -0
  19. package/mcp-server/StdioSandboxNotice.d.ts.map +1 -0
  20. package/mcp-server/StdioSandboxNotice.js +45 -0
  21. package/mcp-server/StdioSandboxNotice.js.map +1 -0
  22. package/mcp-server/useMcpServerCredentials.d.ts +12 -0
  23. package/mcp-server/useMcpServerCredentials.d.ts.map +1 -1
  24. package/mcp-server/useMcpServerCredentials.js +12 -4
  25. package/mcp-server/useMcpServerCredentials.js.map +1 -1
  26. package/package.json +4 -4
  27. package/session/SessionViewer.d.ts.map +1 -1
  28. package/session/SessionViewer.js +12 -0
  29. package/session/SessionViewer.js.map +1 -1
  30. package/session/facets/ChangesTab.d.ts +24 -6
  31. package/session/facets/ChangesTab.d.ts.map +1 -1
  32. package/session/facets/ChangesTab.js +15 -6
  33. package/session/facets/ChangesTab.js.map +1 -1
  34. package/session/useSessionRailViews.d.ts +16 -1
  35. package/session/useSessionRailViews.d.ts.map +1 -1
  36. package/session/useSessionRailViews.js +9 -4
  37. package/session/useSessionRailViews.js.map +1 -1
  38. package/src/execution/WriteBackCard.tsx +18 -4
  39. package/src/execution/__tests__/WriteBackCard.test.tsx +83 -0
  40. package/src/mcp-server/McpServerConnectDialog.tsx +23 -11
  41. package/src/mcp-server/McpServerDetailView.tsx +22 -9
  42. package/src/mcp-server/McpServerPicker.tsx +12 -9
  43. package/src/mcp-server/OAuthRequiredNotice.tsx +77 -0
  44. package/src/mcp-server/StdioSandboxNotice.tsx +98 -0
  45. package/src/mcp-server/__tests__/OAuthRequiredNotice.test.tsx +25 -0
  46. package/src/mcp-server/__tests__/StdioSandboxNotice.test.tsx +71 -0
  47. package/src/mcp-server/useMcpServerCredentials.ts +25 -4
  48. package/src/session/SessionViewer.tsx +15 -0
  49. package/src/session/__tests__/useSessionRailViews.test.tsx +12 -0
  50. package/src/session/facets/ChangesTab.tsx +43 -6
  51. package/src/session/facets/__tests__/ChangesTab.test.tsx +23 -0
  52. package/src/session/useSessionRailViews.tsx +31 -3
  53. package/src/workflow/picker/__tests__/recents.test.ts +4 -4
  54. package/styles.css +1 -1
@@ -3,21 +3,30 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useSessionWriteBacks } from "../useSessionWriteBacks.js";
4
4
  import { WriteBackCard } from "../../execution/WriteBackCard.js";
5
5
  /**
6
- * Changes facet for the session panel — the session's git write-backs
7
- * (branch/commit/PR), one `WriteBackCard` per entry.
6
+ * Changes facet for the session panel — the definitive "where did my work go"
7
+ * surface: the session's git write-backs (branch/commit/PR), one
8
+ * `WriteBackCard` per entry, with honest pre-push states for cloud git
9
+ * sessions whose write-back hasn't happened yet.
8
10
  *
9
11
  * Local file changes deliberately do NOT render here: they live in the
10
12
  * transcript, where each stamped edit row shows its diff in place and the
11
13
  * per-turn decision bar carries the review controls and file list
12
- * (`FileReviewCard`). Duplicating them in a side panel gave the same change a
13
- * third rendering with no added authority. The facet therefore only surfaces
14
- * once a write-back exists (see `useSessionRailViews`).
14
+ * (`FileReviewCard`). This facet is exclusively the git OUTCOME surface
15
+ * what was reviewed there lands here as a branch and pull request.
15
16
  */
16
- export function ChangesTab({ executions }) {
17
+ export function ChangesTab({ executions, expectsWriteBack = false, isSettled = false, }) {
17
18
  const { writeBacks, hasWriteBacks } = useSessionWriteBacks(executions);
18
19
  if (hasWriteBacks) {
19
20
  return (_jsx("div", { role: "list", className: "space-y-2", children: writeBacks.map((entry) => (_jsx("div", { role: "listitem", children: _jsx(WriteBackCard, { writeBack: entry.writeBack }) }, entry.writeBack.workspaceEntryName))) }));
20
21
  }
22
+ if (expectsWriteBack) {
23
+ return (_jsx("div", { className: "flex flex-col items-center justify-center px-4 py-8 text-center", children: _jsx("p", { className: "text-xs text-muted-foreground", children: isSettled
24
+ ? "No changes have been pushed yet. When the agent changes files " +
25
+ "and you approve them, they are pushed to a branch and pull " +
26
+ "request here."
27
+ : "Changes stay in the session workspace while you review them. " +
28
+ "Approved changes are pushed to a branch and pull request here." }) }));
29
+ }
21
30
  return (_jsx("div", { className: "flex flex-col items-center justify-center px-4 py-8 text-center", children: _jsx("p", { className: "text-xs text-muted-foreground", children: "No changes yet. Pull requests will appear here once the agent pushes its work back to your repository." }) }));
22
31
  }
23
32
  //# sourceMappingURL=ChangesTab.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChangesTab.js","sourceRoot":"","sources":["../../../src/session/facets/ChangesTab.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAGb,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAMjE;;;;;;;;;;GAUG;AACH,MAAM,UAAU,UAAU,CAAC,EAAE,UAAU,EAAmB;IACxD,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,GAAG,oBAAoB,CAAC,UAAU,CAAC,CAAC;IAEvE,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,CACL,cAAK,IAAI,EAAC,MAAM,EAAC,SAAS,EAAC,WAAW,YACnC,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CACzB,cAA8C,IAAI,EAAC,UAAU,YAC3D,KAAC,aAAa,IAAC,SAAS,EAAE,KAAK,CAAC,SAAS,GAAI,IADrC,KAAK,CAAC,SAAS,CAAC,kBAAkB,CAEtC,CACP,CAAC,GACE,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,cAAK,SAAS,EAAC,iEAAiE,YAC9E,YAAG,SAAS,EAAC,+BAA+B,uHAGxC,GACA,CACP,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"ChangesTab.js","sourceRoot":"","sources":["../../../src/session/facets/ChangesTab.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAGb,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAuBjE;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU,CAAC,EACzB,UAAU,EACV,gBAAgB,GAAG,KAAK,EACxB,SAAS,GAAG,KAAK,GACD;IAChB,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,GAAG,oBAAoB,CAAC,UAAU,CAAC,CAAC;IAEvE,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO,CACL,cAAK,IAAI,EAAC,MAAM,EAAC,SAAS,EAAC,WAAW,YACnC,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CACzB,cAA8C,IAAI,EAAC,UAAU,YAC3D,KAAC,aAAa,IAAC,SAAS,EAAE,KAAK,CAAC,SAAS,GAAI,IADrC,KAAK,CAAC,SAAS,CAAC,kBAAkB,CAEtC,CACP,CAAC,GACE,CACP,CAAC;IACJ,CAAC;IAED,IAAI,gBAAgB,EAAE,CAAC;QACrB,OAAO,CACL,cAAK,SAAS,EAAC,iEAAiE,YAC9E,YAAG,SAAS,EAAC,+BAA+B,YACzC,SAAS;oBACR,CAAC,CAAC,gEAAgE;wBAChE,6DAA6D;wBAC7D,eAAe;oBACjB,CAAC,CAAC,+DAA+D;wBAC/D,gEAAgE,GAClE,GACA,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,cAAK,SAAS,EAAC,iEAAiE,YAC9E,YAAG,SAAS,EAAC,+BAA+B,uHAGxC,GACA,CACP,CAAC;AACJ,CAAC"}
@@ -42,6 +42,21 @@ export interface UseSessionRailViewsOptions {
42
42
  * @default true
43
43
  */
44
44
  readonly includeExecutionFacets?: boolean;
45
+ /**
46
+ * Whether the session is expected to push approved changes to a git remote
47
+ * (a cloud session with git workspace entries). When `true` the Changes
48
+ * facet is offered from the start — with an honest pre-push state — instead
49
+ * of only materializing once a write-back exists, so "where does my work
50
+ * go?" always has an answer. See {@link ChangesTabProps.expectsWriteBack}.
51
+ * @default false
52
+ */
53
+ readonly expectsWriteBack?: boolean;
54
+ /**
55
+ * Whether the session's latest execution is settled (terminal) — forwarded
56
+ * to the Changes facet's pre-push states. See {@link ChangesTabProps.isSettled}.
57
+ * @default false
58
+ */
59
+ readonly changesSettled?: boolean;
45
60
  }
46
61
  /**
47
62
  * Composes the session facets (Config / Changes / Artifacts / Usage / Inspect)
@@ -53,5 +68,5 @@ export interface UseSessionRailViewsOptions {
53
68
  * tab rules — Changes/Artifacts surface only when data exists (with count
54
69
  * badges), Inspect only when a thread item is selected.
55
70
  */
56
- export declare function useSessionRailViews({ allExecutions, org, sessionConfig, selectedItem, onApplied, onImplementPlan, onOpenPlan, onOpenArtifact, onActivateArtifact, includeExecutionFacets, }: UseSessionRailViewsOptions): readonly SurfaceRailView[];
71
+ export declare function useSessionRailViews({ allExecutions, org, sessionConfig, selectedItem, onApplied, onImplementPlan, onOpenPlan, onOpenArtifact, onActivateArtifact, includeExecutionFacets, expectsWriteBack, changesSettled, }: UseSessionRailViewsOptions): readonly SurfaceRailView[];
57
72
  //# sourceMappingURL=useSessionRailViews.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSessionRailViews.d.ts","sourceRoot":"","sources":["../../src/session/useSessionRailViews.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6DAA6D,CAAC;AAClG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAE1E,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAMpE,+CAA+C;AAC/C,MAAM,WAAW,0BAA0B;IACzC,sEAAsE;IACtE,QAAQ,CAAC,aAAa,EAAE,SAAS,cAAc,EAAE,CAAC;IAClD,gDAAgD;IAChD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,QAAQ,CAAC,aAAa,EAAE,aAAa,GAAG,SAAS,CAAC;IAClD,mEAAmE;IACnE,QAAQ,CAAC,YAAY,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACjD,mEAAmE;IACnE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC3D,6EAA6E;IAC7E,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAChE;;;OAGG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACpE;;;;;OAKG;IACH,QAAQ,CAAC,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAC3C;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,aAAa,EACb,GAAG,EACH,aAAa,EACb,YAAY,EACZ,SAAS,EACT,eAAe,EACf,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,sBAA6B,GAC9B,EAAE,0BAA0B,GAAG,SAAS,eAAe,EAAE,CAiFzD"}
1
+ {"version":3,"file":"useSessionRailViews.d.ts","sourceRoot":"","sources":["../../src/session/useSessionRailViews.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6DAA6D,CAAC;AAClG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAE1E,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAMpE,+CAA+C;AAC/C,MAAM,WAAW,0BAA0B;IACzC,sEAAsE;IACtE,QAAQ,CAAC,aAAa,EAAE,SAAS,cAAc,EAAE,CAAC;IAClD,gDAAgD;IAChD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,QAAQ,CAAC,aAAa,EAAE,aAAa,GAAG,SAAS,CAAC;IAClD,mEAAmE;IACnE,QAAQ,CAAC,YAAY,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACjD,mEAAmE;IACnE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC3D,6EAA6E;IAC7E,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAChE;;;OAGG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACpE;;;;;OAKG;IACH,QAAQ,CAAC,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAC1C;;;;;;;OAOG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IACpC;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACnC;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,aAAa,EACb,GAAG,EACH,aAAa,EACb,YAAY,EACZ,SAAS,EACT,eAAe,EACf,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,sBAA6B,EAC7B,gBAAwB,EACxB,cAAsB,GACvB,EAAE,0BAA0B,GAAG,SAAS,eAAe,EAAE,CA4FzD"}
@@ -18,7 +18,7 @@ import { InspectTab } from "./facets/InspectTab.js";
18
18
  * tab rules — Changes/Artifacts surface only when data exists (with count
19
19
  * badges), Inspect only when a thread item is selected.
20
20
  */
21
- export function useSessionRailViews({ allExecutions, org, sessionConfig, selectedItem, onApplied, onImplementPlan, onOpenPlan, onOpenArtifact, onActivateArtifact, includeExecutionFacets = true, }) {
21
+ export function useSessionRailViews({ allExecutions, org, sessionConfig, selectedItem, onApplied, onImplementPlan, onOpenPlan, onOpenArtifact, onActivateArtifact, includeExecutionFacets = true, expectsWriteBack = false, changesSettled = false, }) {
22
22
  const { hasWriteBacks, writeBackCount } = useSessionWriteBacks(allExecutions);
23
23
  const { hasArtifacts, artifactCount } = useSessionArtifacts(allExecutions);
24
24
  return useMemo(() => {
@@ -32,13 +32,16 @@ export function useSessionRailViews({ allExecutions, org, sessionConfig, selecte
32
32
  });
33
33
  }
34
34
  if (includeExecutionFacets) {
35
- if (hasWriteBacks) {
35
+ // Offered when a write-back exists OR the session is expected to
36
+ // produce one — the facet then carries an honest pre-push state
37
+ // instead of being invisible until the first push.
38
+ if (hasWriteBacks || expectsWriteBack) {
36
39
  views.push({
37
40
  id: "changes",
38
41
  label: "Changes",
39
42
  icon: _jsx(ChangesIcon, {}),
40
- badge: writeBackCount,
41
- content: _jsx(ChangesTab, { executions: allExecutions }),
43
+ badge: writeBackCount > 0 ? writeBackCount : undefined,
44
+ content: (_jsx(ChangesTab, { executions: allExecutions, expectsWriteBack: expectsWriteBack, isSettled: changesSettled })),
42
45
  });
43
46
  }
44
47
  if (hasArtifacts) {
@@ -69,6 +72,8 @@ export function useSessionRailViews({ allExecutions, org, sessionConfig, selecte
69
72
  }, [
70
73
  sessionConfig,
71
74
  includeExecutionFacets,
75
+ expectsWriteBack,
76
+ changesSettled,
72
77
  hasWriteBacks,
73
78
  writeBackCount,
74
79
  hasArtifacts,
@@ -1 +1 @@
1
- {"version":3,"file":"useSessionRailViews.js","sourceRoot":"","sources":["../../src/session/useSessionRailViews.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useSessionRailViews.js","sourceRoot":"","sources":["../../src/session/useSessionRailViews.tsx"],"names":[],"mappings":"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"}
@@ -17,7 +17,13 @@ export interface WriteBackCardProps {
17
17
  *
18
18
  * Shows the workspace entry name, branch, diff summary, phase
19
19
  * indicator, and a "View PR" link when the pull request was
20
- * successfully created. On failure, displays the error message.
20
+ * successfully created.
21
+ *
22
+ * Error rendering follows the record's phase honestly (DD-006):
23
+ * a FAILED record's error is destructive (the work did not reach the
24
+ * remote), while a PUSHED record carrying an error renders it as a
25
+ * degraded notice — the branch is live and its info stays usable; only
26
+ * the PR step failed.
21
27
  *
22
28
  * Themed via standard semantic tokens — no hardcoded colors, no
23
29
  * Console dependencies. Embedders can theme this card through
@@ -85,9 +91,17 @@ export function WriteBackCard({ writeBack, className }: WriteBackCardProps) {
85
91
  </div>
86
92
  )}
87
93
 
88
- {/* Error message */}
89
- {isFailed && writeBack.error && (
90
- <div className="mt-1.5 rounded bg-destructive-subtle px-2 py-1 text-xs text-destructive">
94
+ {/* Error message: destructive when the write-back itself failed,
95
+ degraded when the branch pushed but a later step (the PR) did not. */}
96
+ {writeBack.error && (
97
+ <div
98
+ className={cn(
99
+ "mt-1.5 rounded px-2 py-1 text-xs",
100
+ isFailed
101
+ ? "bg-destructive-subtle text-destructive"
102
+ : "bg-status-degraded-subtle text-status-degraded",
103
+ )}
104
+ >
91
105
  {writeBack.error}
92
106
  </div>
93
107
  )}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * WriteBackCard — phase-honest rendering of a workspace write-back record.
3
+ * The high-stakes matrix is the error treatment: FAILED errors are
4
+ * destructive, while a PUSHED record carrying a PR error renders it as a
5
+ * degraded notice with the branch info intact (the branch IS live).
6
+ */
7
+ import { describe, it, expect, afterEach } from "vitest";
8
+ import { render, screen, cleanup } from "@testing-library/react";
9
+ import { create } from "@bufbuild/protobuf";
10
+ import {
11
+ WorkspaceWriteBackSchema,
12
+ WorkspaceWriteBackPhase,
13
+ } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/writeback_pb";
14
+ import { WriteBackCard } from "../WriteBackCard";
15
+
16
+ function makeWriteBack(overrides: Record<string, unknown> = {}) {
17
+ return create(WorkspaceWriteBackSchema, {
18
+ workspaceEntryName: "acme/api",
19
+ branchName: "stigmer/ses-01test",
20
+ baseBranch: "main",
21
+ commitSha: "abc123",
22
+ diffSummary: " 2 files changed",
23
+ phase: WorkspaceWriteBackPhase.WORKSPACE_WRITE_BACK_PR_CREATED,
24
+ pullRequestUrl: "https://github.com/acme/api/pull/12",
25
+ pullRequestNumber: 12,
26
+ ...overrides,
27
+ });
28
+ }
29
+
30
+ afterEach(() => cleanup());
31
+
32
+ describe("WriteBackCard", () => {
33
+ it("renders branch, diff summary, and the PR link for a PR_CREATED record", () => {
34
+ render(<WriteBackCard writeBack={makeWriteBack()} />);
35
+ expect(screen.getByText("stigmer/ses-01test")).toBeTruthy();
36
+ expect(screen.getByText(/2 files changed/)).toBeTruthy();
37
+ const link = screen.getByRole("link", { name: /view pr #12/i });
38
+ expect(link.getAttribute("href")).toBe("https://github.com/acme/api/pull/12");
39
+ expect(screen.getByText("PR Created")).toBeTruthy();
40
+ });
41
+
42
+ it("renders a FAILED record's error destructively", () => {
43
+ render(
44
+ <WriteBackCard
45
+ writeBack={makeWriteBack({
46
+ phase: WorkspaceWriteBackPhase.WORKSPACE_WRITE_BACK_FAILED,
47
+ error: "git push failed: connection reset",
48
+ pullRequestUrl: "",
49
+ pullRequestNumber: 0,
50
+ })}
51
+ />,
52
+ );
53
+ const error = screen.getByText(/git push failed/);
54
+ expect(error.className).toContain("text-destructive");
55
+ expect(screen.getByText("Failed")).toBeTruthy();
56
+ });
57
+
58
+ it("renders a PUSHED record's PR error as a degraded notice, branch info intact", () => {
59
+ render(
60
+ <WriteBackCard
61
+ writeBack={makeWriteBack({
62
+ phase: WorkspaceWriteBackPhase.WORKSPACE_WRITE_BACK_PUSHED,
63
+ error: "No GitHub token available to open a pull request.",
64
+ pullRequestUrl: "",
65
+ pullRequestNumber: 0,
66
+ })}
67
+ />,
68
+ );
69
+ // The branch is live — never styled as a failure.
70
+ expect(screen.getByText("stigmer/ses-01test")).toBeTruthy();
71
+ expect(screen.getByText("Pushed")).toBeTruthy();
72
+ const notice = screen.getByText(/no github token/i);
73
+ expect(notice.className).toContain("text-status-degraded");
74
+ expect(notice.className).not.toContain("text-destructive");
75
+ expect(screen.queryByRole("link")).toBeNull();
76
+ });
77
+
78
+ it("renders no error block for a clean record", () => {
79
+ const { container } = render(<WriteBackCard writeBack={makeWriteBack()} />);
80
+ expect(container.querySelector(".bg-destructive-subtle")).toBeNull();
81
+ expect(container.querySelector(".bg-status-degraded-subtle")).toBeNull();
82
+ });
83
+ });
@@ -11,6 +11,8 @@ import { useMcpServerConnect } from "./useMcpServerConnect.js";
11
11
  import { useDisconnectOAuth } from "./useDisconnectOAuth.js";
12
12
  import { EnvVarForm } from "../environment/EnvVarForm.js";
13
13
  import { ErrorMessage } from "../error/ErrorMessage.js";
14
+ import { StdioSandboxNotice } from "./StdioSandboxNotice.js";
15
+ import { OAuthRequiredNotice } from "./OAuthRequiredNotice.js";
14
16
 
15
17
  /** Props for {@link McpServerConnectDialog}. */
16
18
  export interface McpServerConnectDialogProps {
@@ -276,6 +278,9 @@ function ConnectDialogContent({
276
278
  </p>
277
279
  )}
278
280
 
281
+ <StdioSandboxNotice serverType={mcpServer.spec?.serverType} className="mb-4" />
282
+ <OAuthRequiredNotice oauthOnly={mcpServer.spec?.auth?.oauthOnly} className="mb-4" />
283
+
279
284
  {activeError && (
280
285
  <div className="mb-4">
281
286
  <ErrorMessage
@@ -295,7 +300,11 @@ function ConnectDialogContent({
295
300
  phase={oauth.phase}
296
301
  onSignIn={handleOAuthSignIn}
297
302
  isVendorApprovalBlocked={creds.isVendorApprovalBlocked}
298
- onSwitchToManual={() => creds.setManualOverride(true)}
303
+ onSwitchToManual={
304
+ creds.manualEntrySupported
305
+ ? () => creds.setManualOverride(true)
306
+ : undefined
307
+ }
299
308
  disabled={isConnectingPhase}
300
309
  />
301
310
  )}
@@ -408,7 +417,8 @@ function OAuthSection({
408
417
  readonly phase: OAuthConnectPhase;
409
418
  readonly onSignIn: () => void;
410
419
  readonly isVendorApprovalBlocked: boolean;
411
- readonly onSwitchToManual: () => void;
420
+ /** When omitted (e.g. `oauth_only` servers), no manual-entry link is shown. */
421
+ readonly onSwitchToManual?: () => void;
412
422
  readonly disabled?: boolean;
413
423
  }) {
414
424
  if (isConnected) {
@@ -445,19 +455,21 @@ function OAuthSection({
445
455
  {isInProgress && <LoadingSpinner size="sm" />}
446
456
  {isInProgress ? (phaseLabel[phase] ?? "Connecting...") : "Sign in with OAuth"}
447
457
  </button>
448
- {isVendorApprovalBlocked && (
458
+ {isVendorApprovalBlocked && onSwitchToManual && (
449
459
  <p className="text-xs text-muted-foreground">
450
460
  OAuth sign-in is pending vendor approval. You can enter your token manually instead.
451
461
  </p>
452
462
  )}
453
- <button
454
- type="button"
455
- onClick={onSwitchToManual}
456
- disabled={disabled || isInProgress}
457
- className="text-xs text-muted-foreground underline hover:text-foreground"
458
- >
459
- Enter token manually
460
- </button>
463
+ {onSwitchToManual && (
464
+ <button
465
+ type="button"
466
+ onClick={onSwitchToManual}
467
+ disabled={disabled || isInProgress}
468
+ className="text-xs text-muted-foreground underline hover:text-foreground"
469
+ >
470
+ Enter token manually
471
+ </button>
472
+ )}
461
473
  </div>
462
474
  );
463
475
  }
@@ -20,6 +20,8 @@ import { useMcpServerConnect } from "./useMcpServerConnect.js";
20
20
  import { useMcpServerCredentials } from "./useMcpServerCredentials.js";
21
21
  import { useMcpServerOAuthConnect } from "./useMcpServerOAuthConnect.js";
22
22
  import type { OAuthConnectPhase } from "./useMcpServerOAuthConnect.js";
23
+ import { StdioSandboxNotice } from "./StdioSandboxNotice.js";
24
+ import { OAuthRequiredNotice } from "./OAuthRequiredNotice.js";
23
25
  import { useDisconnectOAuth } from "./useDisconnectOAuth.js";
24
26
  import { useOrgOAuthApp } from "./useOrgOAuthApp.js";
25
27
  import { OAuthAppForm } from "./OAuthAppForm.js";
@@ -488,7 +490,10 @@ export function McpServerDetailView({
488
490
  )}
489
491
 
490
492
  <Section title="Connection">
493
+ <StdioSandboxNotice serverType={spec?.serverType} className="mb-3" />
494
+ <OAuthRequiredNotice oauthOnly={spec?.auth?.oauthOnly} className="mb-3" />
491
495
  <ConnectBar
496
+ manualEntrySupported={credentials.manualEntrySupported}
492
497
  isConnecting={connection.isConnecting || oauth.isInProgress}
493
498
  connectionError={combinedError}
494
499
  onConnect={handleConnectClick}
@@ -709,6 +714,7 @@ function ConnectBar({
709
714
  isRemovingOrgApp,
710
715
  removeOrgAppError,
711
716
  onClearRemoveOrgAppError,
717
+ manualEntrySupported,
712
718
  manualOverride,
713
719
  onManualOverride,
714
720
  onBackToOAuth,
@@ -742,6 +748,8 @@ function ConnectBar({
742
748
  readonly isRemovingOrgApp: boolean;
743
749
  readonly removeOrgAppError: Error | null;
744
750
  readonly onClearRemoveOrgAppError: () => void;
751
+ /** `false` for `oauth_only` servers — the manual-entry link is suppressed. */
752
+ readonly manualEntrySupported: boolean;
745
753
  readonly manualOverride: boolean;
746
754
  readonly onManualOverride: () => void;
747
755
  readonly onBackToOAuth: () => void;
@@ -1047,8 +1055,11 @@ function ConnectBar({
1047
1055
  </div>
1048
1056
  )}
1049
1057
 
1050
- {/* Secondary actions: manual entry, BYOA, back to OAuth */}
1051
- {authMode === "oauth" && !isOAuthConnected && !isOAuthBusy && !isConnecting && (
1058
+ {/* Secondary actions: manual entry, BYOA, back to OAuth. Suppressed
1059
+ entirely for oauth_only servers with no BYOA option, so no empty
1060
+ action bar renders. */}
1061
+ {authMode === "oauth" && !isOAuthConnected && !isOAuthBusy && !isConnecting &&
1062
+ (manualOverride || manualEntrySupported || canBringOwnApp) && (
1052
1063
  <div className="flex items-center gap-3 border-t border-border px-3 py-1.5">
1053
1064
  {manualOverride ? (
1054
1065
  <button
@@ -1060,13 +1071,15 @@ function ConnectBar({
1060
1071
  </button>
1061
1072
  ) : (
1062
1073
  <>
1063
- <button
1064
- type="button"
1065
- onClick={onManualOverride}
1066
- className="text-[11px] text-muted-foreground underline decoration-muted-foreground/40 underline-offset-2 hover:text-foreground hover:decoration-foreground"
1067
- >
1068
- Enter token manually
1069
- </button>
1074
+ {manualEntrySupported && (
1075
+ <button
1076
+ type="button"
1077
+ onClick={onManualOverride}
1078
+ className="text-[11px] text-muted-foreground underline decoration-muted-foreground/40 underline-offset-2 hover:text-foreground hover:decoration-foreground"
1079
+ >
1080
+ Enter token manually
1081
+ </button>
1082
+ )}
1070
1083
  {canBringOwnApp && !isVendorApprovalBlocked && (
1071
1084
  <button
1072
1085
  type="button"
@@ -404,6 +404,8 @@ export function McpServerPicker({
404
404
  const auth = entry.mcpServer.spec?.auth;
405
405
  const oauthTargetEnvVar = auth?.targetEnvVar || null;
406
406
  const hasOAuth = !!auth;
407
+ // oauth_only endpoints reject static tokens — never offer manual entry.
408
+ const manualEntrySupported = !auth?.oauthOnly;
407
409
  const isManualOverride = manualOverrideKeys.has(view.serverKey);
408
410
 
409
411
  const entryMissingVars =
@@ -454,15 +456,16 @@ export function McpServerPicker({
454
456
  }
455
457
  : undefined;
456
458
 
457
- const handleSwitchToManual = hasOAuth
458
- ? () => {
459
- setManualOverrideKeys((prev) => {
460
- const next = new Set(prev);
461
- next.add(view.serverKey);
462
- return next;
463
- });
464
- }
465
- : undefined;
459
+ const handleSwitchToManual =
460
+ hasOAuth && manualEntrySupported
461
+ ? () => {
462
+ setManualOverrideKeys((prev) => {
463
+ const next = new Set(prev);
464
+ next.add(view.serverKey);
465
+ return next;
466
+ });
467
+ }
468
+ : undefined;
466
469
 
467
470
  const handleSwitchToOAuth =
468
471
  hasOAuth && isManualOverride
@@ -0,0 +1,77 @@
1
+ "use client";
2
+
3
+ import { cn } from "@stigmer/theme";
4
+
5
+ /** Props for {@link OAuthRequiredNotice}. */
6
+ export interface OAuthRequiredNoticeProps {
7
+ /**
8
+ * The server's `spec.auth.oauth_only` flag. When `true`, the endpoint
9
+ * rejects manually-entered static tokens and OAuth is the only path.
10
+ */
11
+ readonly oauthOnly: boolean | undefined;
12
+ /** Additional CSS class names for the root container. */
13
+ readonly className?: string;
14
+ }
15
+
16
+ /**
17
+ * Sets accurate expectations for connecting an **OAuth-only** MCP server —
18
+ * one whose hosted endpoint rejects manually-entered static tokens
19
+ * (`spec.auth.oauth_only`, e.g. Notion, Monday).
20
+ *
21
+ * Without this notice a user might expect the (now-hidden) manual token
22
+ * option and wonder why only "Sign in" is offered. It explains, in one line,
23
+ * that OAuth is required and a pasted API token would be rejected — turning a
24
+ * silent constraint into a clear one (Nielsen: visibility of system status).
25
+ *
26
+ * Self-gating: renders `null` unless the server is `oauth_only`. Callers
27
+ * render it unconditionally next to their Connect action, mirroring
28
+ * {@link StdioSandboxNotice}.
29
+ *
30
+ * All visual properties flow through `--stgm-*` design tokens. No
31
+ * Console-specific dependencies — safe for platform-builder embedding.
32
+ */
33
+ export function OAuthRequiredNotice({
34
+ oauthOnly,
35
+ className,
36
+ }: OAuthRequiredNoticeProps) {
37
+ if (!oauthOnly) return null;
38
+
39
+ return (
40
+ <div
41
+ role="status"
42
+ className={cn(
43
+ "bg-muted-subtle text-muted-foreground flex items-start gap-2.5 rounded-lg border border-transparent px-4 py-3",
44
+ className,
45
+ )}
46
+ >
47
+ <KeyIcon className="mt-0.5 size-4 shrink-0" />
48
+ <p className="text-xs leading-relaxed">
49
+ This server requires OAuth. Sign in to connect — its hosted endpoint
50
+ rejects manually-entered API tokens, so a pasted token won&apos;t work
51
+ here.
52
+ </p>
53
+ </div>
54
+ );
55
+ }
56
+
57
+ function KeyIcon({ className }: { className?: string }) {
58
+ return (
59
+ <svg
60
+ width="16"
61
+ height="16"
62
+ viewBox="0 0 16 16"
63
+ fill="none"
64
+ stroke="currentColor"
65
+ strokeWidth="1.5"
66
+ strokeLinecap="round"
67
+ strokeLinejoin="round"
68
+ className={className}
69
+ aria-hidden="true"
70
+ >
71
+ <circle cx="5" cy="11" r="2.5" />
72
+ <path d="M6.8 9.2 13 3" />
73
+ <path d="M11 5l1.5 1.5" />
74
+ <path d="M9 7l1.5 1.5" />
75
+ </svg>
76
+ );
77
+ }
@@ -0,0 +1,98 @@
1
+ "use client";
2
+
3
+ import { cn } from "@stigmer/theme";
4
+ import type { DeploymentMode } from "@stigmer/sdk";
5
+ import type { McpServerSpec } from "@stigmer/protos/ai/stigmer/agentic/mcpserver/v1/spec_pb";
6
+ import { useDeploymentMode } from "../deployment-mode.js";
7
+
8
+ /** The `server_type` oneof discriminator from an MCP server's spec. */
9
+ type McpServerType = McpServerSpec["serverType"];
10
+
11
+ /**
12
+ * Whether an MCP server's transport is stdio while connected to Stigmer Cloud.
13
+ *
14
+ * stdio servers run as a subprocess inside an isolated Daytona sandbox in the
15
+ * cloud — not on the user's own computer. This predicate identifies exactly
16
+ * that combination so the connect UI can set accurate expectations. HTTP
17
+ * servers and any transport in local mode return `false`.
18
+ *
19
+ * Pure and framework-free so it can be unit-tested without rendering.
20
+ */
21
+ export function isStdioInCloud(
22
+ mode: DeploymentMode,
23
+ serverType: McpServerType | undefined,
24
+ ): boolean {
25
+ return mode === "cloud" && serverType?.case === "stdio";
26
+ }
27
+
28
+ /** Props for {@link StdioSandboxNotice}. */
29
+ export interface StdioSandboxNoticeProps {
30
+ /** The MCP server's `spec.serverType` oneof. */
31
+ readonly serverType: McpServerType | undefined;
32
+ /** Additional CSS class names for the root container. */
33
+ readonly className?: string;
34
+ }
35
+
36
+ /**
37
+ * Sets accurate expectations for connecting an **stdio** MCP server from
38
+ * **Stigmer Cloud**.
39
+ *
40
+ * stdio servers are a supported cloud capability — Stigmer runs them in an
41
+ * isolated Daytona sandbox. But that sandbox is not the user's own machine,
42
+ * so a server that expects access to local files, applications, or a private
43
+ * network (e.g. a filesystem server) won't behave as the user intends. This
44
+ * notice explains that up front; Connect itself stays enabled because
45
+ * self-contained servers work fine.
46
+ *
47
+ * Self-gating: reads the deployment mode from context and renders `null`
48
+ * unless the server is stdio and the backend is cloud. Callers render it
49
+ * unconditionally next to their Connect action.
50
+ *
51
+ * All visual properties flow through `--stgm-*` design tokens. No
52
+ * Console-specific dependencies — safe for platform-builder embedding.
53
+ */
54
+ export function StdioSandboxNotice({
55
+ serverType,
56
+ className,
57
+ }: StdioSandboxNoticeProps) {
58
+ const mode = useDeploymentMode();
59
+ if (!isStdioInCloud(mode, serverType)) return null;
60
+
61
+ return (
62
+ <div
63
+ role="status"
64
+ className={cn(
65
+ "bg-muted-subtle text-muted-foreground flex items-start gap-2.5 rounded-lg border border-transparent px-4 py-3",
66
+ className,
67
+ )}
68
+ >
69
+ <SandboxIcon className="mt-0.5 size-4 shrink-0" />
70
+ <p className="text-xs leading-relaxed">
71
+ This stdio server runs in an isolated cloud sandbox, not on your own
72
+ computer. Tools that need access to your local files, applications, or
73
+ private network won&apos;t be available here.
74
+ </p>
75
+ </div>
76
+ );
77
+ }
78
+
79
+ function SandboxIcon({ className }: { className?: string }) {
80
+ return (
81
+ <svg
82
+ width="16"
83
+ height="16"
84
+ viewBox="0 0 16 16"
85
+ fill="none"
86
+ stroke="currentColor"
87
+ strokeWidth="1.5"
88
+ strokeLinecap="round"
89
+ strokeLinejoin="round"
90
+ className={className}
91
+ aria-hidden="true"
92
+ >
93
+ <path d="M2 5.5 8 2.5l6 3-6 3-6-3Z" />
94
+ <path d="M2 5.5v5l6 3 6-3v-5" />
95
+ <path d="M8 8.5v5" />
96
+ </svg>
97
+ );
98
+ }
@@ -0,0 +1,25 @@
1
+ import { describe, it, expect, afterEach } from "vitest";
2
+ import { render, screen, cleanup } from "@testing-library/react";
3
+ import { OAuthRequiredNotice } from "../OAuthRequiredNotice";
4
+
5
+ afterEach(cleanup);
6
+
7
+ describe("OAuthRequiredNotice", () => {
8
+ it("renders the OAuth-required caveat when oauth_only is true", () => {
9
+ render(<OAuthRequiredNotice oauthOnly={true} />);
10
+ expect(screen.getByRole("status")).toBeTruthy();
11
+ expect(screen.getByText(/requires OAuth/i)).toBeTruthy();
12
+ // It explains why manual entry is gone, not just that OAuth is used.
13
+ expect(screen.getByText(/rejects manually-entered API tokens/i)).toBeTruthy();
14
+ });
15
+
16
+ it("renders nothing when oauth_only is false", () => {
17
+ const { container } = render(<OAuthRequiredNotice oauthOnly={false} />);
18
+ expect(container.firstChild).toBeNull();
19
+ });
20
+
21
+ it("renders nothing when oauth_only is undefined", () => {
22
+ const { container } = render(<OAuthRequiredNotice oauthOnly={undefined} />);
23
+ expect(container.firstChild).toBeNull();
24
+ });
25
+ });