@stigmer/react 0.0.68 → 0.0.69

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 (108) hide show
  1. package/README.md +1 -1
  2. package/composer/SessionComposer.d.ts.map +1 -1
  3. package/composer/SessionComposer.js +12 -3
  4. package/composer/SessionComposer.js.map +1 -1
  5. package/demo/__tests__/demo-client.test.d.ts +2 -0
  6. package/demo/__tests__/demo-client.test.d.ts.map +1 -0
  7. package/demo/__tests__/demo-client.test.js +133 -0
  8. package/demo/__tests__/demo-client.test.js.map +1 -0
  9. package/demo/__tests__/fixtures.test.d.ts +2 -0
  10. package/demo/__tests__/fixtures.test.d.ts.map +1 -0
  11. package/demo/__tests__/fixtures.test.js +135 -0
  12. package/demo/__tests__/fixtures.test.js.map +1 -0
  13. package/demo/__tests__/samples.test.d.ts +2 -0
  14. package/demo/__tests__/samples.test.d.ts.map +1 -0
  15. package/demo/__tests__/samples.test.js +152 -0
  16. package/demo/__tests__/samples.test.js.map +1 -0
  17. package/demo/client.d.ts +29 -0
  18. package/demo/client.d.ts.map +1 -0
  19. package/demo/client.js +52 -0
  20. package/demo/client.js.map +1 -0
  21. package/demo/fixtures.d.ts +190 -0
  22. package/demo/fixtures.d.ts.map +1 -0
  23. package/demo/fixtures.js +263 -0
  24. package/demo/fixtures.js.map +1 -0
  25. package/demo/index.d.ts +6 -0
  26. package/demo/index.d.ts.map +1 -0
  27. package/demo/index.js +6 -0
  28. package/demo/index.js.map +1 -0
  29. package/demo/samples.d.ts +166 -0
  30. package/demo/samples.d.ts.map +1 -0
  31. package/demo/samples.js +308 -0
  32. package/demo/samples.js.map +1 -0
  33. package/demo/transport.d.ts +59 -0
  34. package/demo/transport.d.ts.map +1 -0
  35. package/demo/transport.js +75 -0
  36. package/demo/transport.js.map +1 -0
  37. package/demo/types.d.ts +62 -0
  38. package/demo/types.d.ts.map +1 -0
  39. package/demo/types.js +16 -0
  40. package/demo/types.js.map +1 -0
  41. package/environment/EnvVarForm.d.ts.map +1 -1
  42. package/environment/EnvVarForm.js +1 -1
  43. package/environment/EnvVarForm.js.map +1 -1
  44. package/environment/__tests__/systemEnvVars.test.d.ts +2 -0
  45. package/environment/__tests__/systemEnvVars.test.d.ts.map +1 -0
  46. package/environment/__tests__/systemEnvVars.test.js +76 -0
  47. package/environment/__tests__/systemEnvVars.test.js.map +1 -0
  48. package/environment/index.d.ts +1 -0
  49. package/environment/index.d.ts.map +1 -1
  50. package/environment/index.js +1 -0
  51. package/environment/index.js.map +1 -1
  52. package/environment/systemEnvVars.d.ts +52 -0
  53. package/environment/systemEnvVars.d.ts.map +1 -0
  54. package/environment/systemEnvVars.js +91 -0
  55. package/environment/systemEnvVars.js.map +1 -0
  56. package/execution/ApprovalCard.d.ts.map +1 -1
  57. package/execution/ApprovalCard.js +3 -3
  58. package/execution/ApprovalCard.js.map +1 -1
  59. package/index.d.ts +1 -1
  60. package/index.d.ts.map +1 -1
  61. package/index.js +2 -2
  62. package/index.js.map +1 -1
  63. package/internal/Tabs.d.ts +41 -0
  64. package/internal/Tabs.d.ts.map +1 -0
  65. package/internal/Tabs.js +65 -0
  66. package/internal/Tabs.js.map +1 -0
  67. package/mcp-server/McpServerDetailView.d.ts +33 -7
  68. package/mcp-server/McpServerDetailView.d.ts.map +1 -1
  69. package/mcp-server/McpServerDetailView.js +53 -37
  70. package/mcp-server/McpServerDetailView.js.map +1 -1
  71. package/mcp-server/useMcpServerCredentials.d.ts.map +1 -1
  72. package/mcp-server/useMcpServerCredentials.js +2 -1
  73. package/mcp-server/useMcpServerCredentials.js.map +1 -1
  74. package/models/index.d.ts +1 -1
  75. package/models/index.d.ts.map +1 -1
  76. package/models/index.js +1 -1
  77. package/models/index.js.map +1 -1
  78. package/models/registry.d.ts +10 -0
  79. package/models/registry.d.ts.map +1 -1
  80. package/models/registry.js +13 -0
  81. package/models/registry.js.map +1 -1
  82. package/models/useModelRegistry.d.ts.map +1 -1
  83. package/models/useModelRegistry.js +7 -3
  84. package/models/useModelRegistry.js.map +1 -1
  85. package/package.json +9 -5
  86. package/src/composer/SessionComposer.tsx +21 -3
  87. package/src/demo/__tests__/demo-client.test.tsx +213 -0
  88. package/src/demo/__tests__/fixtures.test.ts +214 -0
  89. package/src/demo/__tests__/samples.test.ts +171 -0
  90. package/src/demo/client.ts +78 -0
  91. package/src/demo/fixtures.ts +401 -0
  92. package/src/demo/index.ts +12 -0
  93. package/src/demo/samples.ts +470 -0
  94. package/src/demo/transport.ts +116 -0
  95. package/src/demo/types.ts +69 -0
  96. package/src/environment/EnvVarForm.tsx +1 -0
  97. package/src/environment/__tests__/systemEnvVars.test.ts +120 -0
  98. package/src/environment/index.ts +6 -0
  99. package/src/environment/systemEnvVars.ts +104 -0
  100. package/src/execution/ApprovalCard.tsx +4 -0
  101. package/src/index.ts +5 -1
  102. package/src/internal/Tabs.tsx +166 -0
  103. package/src/mcp-server/McpServerDetailView.tsx +273 -204
  104. package/src/mcp-server/useMcpServerCredentials.ts +4 -1
  105. package/src/models/index.ts +1 -1
  106. package/src/models/registry.ts +14 -0
  107. package/src/models/useModelRegistry.ts +7 -2
  108. package/styles.css +1 -1
@@ -0,0 +1,213 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { render, screen } from "@testing-library/react";
3
+ import { StigmerProvider } from "../../provider";
4
+ import { createDemoClient } from "../client";
5
+ import { DemoTransport } from "../transport";
6
+ import { rpcKey } from "../types";
7
+
8
+ describe("createDemoClient", () => {
9
+ it("returns a client accepted by StigmerProvider", () => {
10
+ const client = createDemoClient({ fixtures: new Map() });
11
+
12
+ render(
13
+ <StigmerProvider client={client}>
14
+ <div data-testid="child">rendered</div>
15
+ </StigmerProvider>,
16
+ );
17
+
18
+ expect(screen.getByTestId("child").textContent).toBe("rendered");
19
+ });
20
+
21
+ it("provides all expected resource clients", () => {
22
+ const client = createDemoClient({ fixtures: new Map() });
23
+
24
+ expect(client.agent).toBeDefined();
25
+ expect(client.agentExecution).toBeDefined();
26
+ expect(client.agentInstance).toBeDefined();
27
+ expect(client.apiKey).toBeDefined();
28
+ expect(client.environment).toBeDefined();
29
+ expect(client.executionContext).toBeDefined();
30
+ expect(client.iamPolicy).toBeDefined();
31
+ expect(client.identityAccount).toBeDefined();
32
+ expect(client.identityProvider).toBeDefined();
33
+ expect(client.mcpServer).toBeDefined();
34
+ expect(client.organization).toBeDefined();
35
+ expect(client.project).toBeDefined();
36
+ expect(client.session).toBeDefined();
37
+ expect(client.skill).toBeDefined();
38
+ expect(client.workflow).toBeDefined();
39
+ expect(client.workflowExecution).toBeDefined();
40
+ expect(client.workflowInstance).toBeDefined();
41
+ expect(client.search).toBeDefined();
42
+ expect(client.github).toBeDefined();
43
+ });
44
+ });
45
+
46
+ describe("DemoTransport", () => {
47
+ it("calls unary fixture handler and returns its result", async () => {
48
+ const fixtureResponse = { id: "test-123", name: "mock-session" };
49
+ const fixtures = new Map([
50
+ [
51
+ "test.Service/get",
52
+ { unary: () => fixtureResponse },
53
+ ],
54
+ ]);
55
+
56
+ const transport = new DemoTransport(fixtures);
57
+ const result = await transport.unary(
58
+ { parent: { typeName: "test.Service" }, name: "get" },
59
+ undefined,
60
+ undefined,
61
+ undefined,
62
+ { id: "test-123" },
63
+ );
64
+
65
+ expect(result.stream).toBe(false);
66
+ expect(result.message).toBe(fixtureResponse);
67
+ expect(result.header).toBeInstanceOf(Headers);
68
+ expect(result.trailer).toBeInstanceOf(Headers);
69
+ });
70
+
71
+ it("passes request to unary fixture handler", async () => {
72
+ const request = { value: "session-42" };
73
+ let receivedRequest: unknown;
74
+
75
+ const fixtures = new Map([
76
+ [
77
+ "test.Service/get",
78
+ {
79
+ unary: (req: unknown) => {
80
+ receivedRequest = req;
81
+ return {};
82
+ },
83
+ },
84
+ ],
85
+ ]);
86
+
87
+ const transport = new DemoTransport(fixtures);
88
+ await transport.unary(
89
+ { parent: { typeName: "test.Service" }, name: "get" },
90
+ undefined,
91
+ undefined,
92
+ undefined,
93
+ request,
94
+ );
95
+
96
+ expect(receivedRequest).toBe(request);
97
+ });
98
+
99
+ it("calls stream fixture handler and yields results", async () => {
100
+ const items = [
101
+ { id: "1", status: "running" },
102
+ { id: "1", status: "completed" },
103
+ ];
104
+
105
+ const fixtures = new Map([
106
+ [
107
+ "test.Service/subscribe",
108
+ { stream: () => items },
109
+ ],
110
+ ]);
111
+
112
+ const transport = new DemoTransport(fixtures);
113
+
114
+ async function* singleMessage() {
115
+ yield { value: "exec-1" };
116
+ }
117
+
118
+ const result = await transport.stream(
119
+ { parent: { typeName: "test.Service" }, name: "subscribe" },
120
+ undefined,
121
+ undefined,
122
+ undefined,
123
+ singleMessage(),
124
+ );
125
+
126
+ expect(result.stream).toBe(true);
127
+
128
+ const collected: unknown[] = [];
129
+ for await (const msg of result.message) {
130
+ collected.push(msg);
131
+ }
132
+ expect(collected).toEqual(items);
133
+ });
134
+
135
+ it("throws descriptive error for missing unary fixture", async () => {
136
+ const transport = new DemoTransport(new Map());
137
+
138
+ await expect(
139
+ transport.unary(
140
+ {
141
+ parent: {
142
+ typeName:
143
+ "ai.stigmer.agentic.session.v1.SessionQueryController",
144
+ },
145
+ name: "get",
146
+ },
147
+ undefined,
148
+ undefined,
149
+ undefined,
150
+ {},
151
+ ),
152
+ ).rejects.toThrow("No demo fixture for SessionQueryController/get");
153
+ });
154
+
155
+ it("throws descriptive error for missing stream fixture", async () => {
156
+ const transport = new DemoTransport(new Map());
157
+
158
+ async function* empty() {
159
+ yield {};
160
+ }
161
+
162
+ await expect(
163
+ transport.stream(
164
+ {
165
+ parent: {
166
+ typeName:
167
+ "ai.stigmer.agentic.agentexecution.v1.AgentExecutionQueryController",
168
+ },
169
+ name: "subscribe",
170
+ },
171
+ undefined,
172
+ undefined,
173
+ undefined,
174
+ empty(),
175
+ ),
176
+ ).rejects.toThrow(
177
+ "No demo fixture for AgentExecutionQueryController/subscribe",
178
+ );
179
+ });
180
+
181
+ it("includes the full fixture key in the error message", async () => {
182
+ const transport = new DemoTransport(new Map());
183
+
184
+ await expect(
185
+ transport.unary(
186
+ {
187
+ parent: {
188
+ typeName:
189
+ "ai.stigmer.agentic.session.v1.SessionQueryController",
190
+ },
191
+ name: "get",
192
+ },
193
+ undefined,
194
+ undefined,
195
+ undefined,
196
+ {},
197
+ ),
198
+ ).rejects.toThrow(
199
+ 'key "ai.stigmer.agentic.session.v1.SessionQueryController/get"',
200
+ );
201
+ });
202
+ });
203
+
204
+ describe("rpcKey", () => {
205
+ it("constructs a fixture key from service descriptor and method name", () => {
206
+ const service = {
207
+ typeName: "ai.stigmer.agentic.session.v1.SessionQueryController",
208
+ };
209
+ expect(rpcKey(service, "get")).toBe(
210
+ "ai.stigmer.agentic.session.v1.SessionQueryController/get",
211
+ );
212
+ });
213
+ });
@@ -0,0 +1,214 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
3
+ import { fixtures, buildScenario, type FixtureSpec } from "../fixtures";
4
+ import { rpcKey } from "../types";
5
+
6
+ describe("fixtures", () => {
7
+ describe("session", () => {
8
+ it("get returns correct key and unary entry", () => {
9
+ const handler = () => ({ id: "test" });
10
+ const spec = fixtures.session.get(handler);
11
+
12
+ expect(spec.key).toBe(
13
+ "ai.stigmer.agentic.session.v1.SessionQueryController/get",
14
+ );
15
+ expect(spec.entry.unary).toBe(handler);
16
+ expect(spec.entry.stream).toBeUndefined();
17
+ expect(spec.searchResourceKind).toBeUndefined();
18
+ });
19
+
20
+ it("list returns correct key", () => {
21
+ const spec = fixtures.session.list(() => ({}));
22
+ expect(spec.key).toBe(
23
+ "ai.stigmer.agentic.session.v1.SessionQueryController/list",
24
+ );
25
+ });
26
+
27
+ it("create returns correct key", () => {
28
+ const spec = fixtures.session.create(() => ({}));
29
+ expect(spec.key).toBe(
30
+ "ai.stigmer.agentic.session.v1.SessionCommandController/create",
31
+ );
32
+ });
33
+
34
+ it("update returns correct key", () => {
35
+ const spec = fixtures.session.update(() => ({}));
36
+ expect(spec.key).toBe(
37
+ "ai.stigmer.agentic.session.v1.SessionCommandController/update",
38
+ );
39
+ });
40
+ });
41
+
42
+ describe("agentExecution", () => {
43
+ it("subscribe returns a stream entry", () => {
44
+ const handler = () => [{ id: "1" }, { id: "2" }];
45
+ const spec = fixtures.agentExecution.subscribe(handler);
46
+
47
+ expect(spec.key).toBe(
48
+ "ai.stigmer.agentic.agentexecution.v1.AgentExecutionQueryController/subscribe",
49
+ );
50
+ expect(spec.entry.stream).toBe(handler);
51
+ expect(spec.entry.unary).toBeUndefined();
52
+ });
53
+
54
+ it("listBySession returns correct key", () => {
55
+ const spec = fixtures.agentExecution.listBySession(() => ({}));
56
+ expect(spec.key).toBe(
57
+ "ai.stigmer.agentic.agentexecution.v1.AgentExecutionQueryController/listBySession",
58
+ );
59
+ });
60
+
61
+ it("create returns correct key", () => {
62
+ const spec = fixtures.agentExecution.create(() => ({}));
63
+ expect(spec.key).toBe(
64
+ "ai.stigmer.agentic.agentexecution.v1.AgentExecutionCommandController/create",
65
+ );
66
+ });
67
+
68
+ it("getArtifactContent returns correct key", () => {
69
+ const spec = fixtures.agentExecution.getArtifactContent(() => ({}));
70
+ expect(spec.key).toBe(
71
+ "ai.stigmer.agentic.agentexecution.v1.AgentExecutionQueryController/getArtifactContent",
72
+ );
73
+ });
74
+ });
75
+
76
+ describe("search-backed list helpers", () => {
77
+ const searchKey = "ai.stigmer.search.v1.SearchService/search";
78
+
79
+ it("agent.list sets searchResourceKind to agent", () => {
80
+ const spec = fixtures.agent.list(() => ({}));
81
+ expect(spec.key).toBe(searchKey);
82
+ expect(spec.searchResourceKind).toBe(ApiResourceKind.agent);
83
+ });
84
+
85
+ it("skill.list sets searchResourceKind to skill", () => {
86
+ const spec = fixtures.skill.list(() => ({}));
87
+ expect(spec.key).toBe(searchKey);
88
+ expect(spec.searchResourceKind).toBe(ApiResourceKind.skill);
89
+ });
90
+
91
+ it("mcpServer.list sets searchResourceKind to mcp_server", () => {
92
+ const spec = fixtures.mcpServer.list(() => ({}));
93
+ expect(spec.key).toBe(searchKey);
94
+ expect(spec.searchResourceKind).toBe(ApiResourceKind.mcp_server);
95
+ });
96
+ });
97
+
98
+ describe("other domains", () => {
99
+ it("agent.getByReference returns correct key", () => {
100
+ const spec = fixtures.agent.getByReference(() => ({}));
101
+ expect(spec.key).toBe(
102
+ "ai.stigmer.agentic.agent.v1.AgentQueryController/getByReference",
103
+ );
104
+ });
105
+
106
+ it("skill.getByReference returns correct key", () => {
107
+ const spec = fixtures.skill.getByReference(() => ({}));
108
+ expect(spec.key).toBe(
109
+ "ai.stigmer.agentic.skill.v1.SkillQueryController/getByReference",
110
+ );
111
+ });
112
+
113
+ it("environment.list returns correct key", () => {
114
+ const spec = fixtures.environment.list(() => ({}));
115
+ expect(spec.key).toBe(
116
+ "ai.stigmer.agentic.environment.v1.EnvironmentQueryController/list",
117
+ );
118
+ });
119
+
120
+ it("agentInstance.create returns correct key", () => {
121
+ const spec = fixtures.agentInstance.create(() => ({}));
122
+ expect(spec.key).toBe(
123
+ "ai.stigmer.agentic.agentinstance.v1.AgentInstanceCommandController/create",
124
+ );
125
+ });
126
+
127
+ it("apiKey.findAll returns correct key", () => {
128
+ const spec = fixtures.apiKey.findAll(() => ({}));
129
+ expect(spec.key).toBe(
130
+ "ai.stigmer.iam.apikey.v1.ApiKeyQueryController/findAll",
131
+ );
132
+ });
133
+
134
+ it("github.getOAuthAuthorizeUrl returns correct key", () => {
135
+ const spec = fixtures.github.getOAuthAuthorizeUrl(() => ({}));
136
+ expect(spec.key).toBe(
137
+ "ai.stigmer.platform.github.v1.GitHubService/getOAuthAuthorizeUrl",
138
+ );
139
+ });
140
+
141
+ it("organization.create returns correct key", () => {
142
+ const spec = fixtures.organization.create(() => ({}));
143
+ expect(spec.key).toBe(
144
+ "ai.stigmer.tenancy.organization.v1.OrganizationCommandController/create",
145
+ );
146
+ });
147
+ });
148
+ });
149
+
150
+ describe("buildScenario", () => {
151
+ it("creates a DemoScenario from direct fixture specs", () => {
152
+ const sessionData = { id: "ses-1" };
153
+ const scenario = buildScenario(
154
+ fixtures.session.get(() => sessionData),
155
+ );
156
+
157
+ expect(scenario.fixtures).toBeInstanceOf(Map);
158
+ const key = rpcKey(
159
+ { typeName: "ai.stigmer.agentic.session.v1.SessionQueryController" },
160
+ "get",
161
+ );
162
+ const entry = scenario.fixtures.get(key);
163
+ expect(entry?.unary?.({})).toBe(sessionData);
164
+ });
165
+
166
+ it("merges search-backed list fixtures into a dispatch handler", () => {
167
+ const agentData = { kind: "agent" };
168
+ const skillData = { kind: "skill" };
169
+
170
+ const scenario = buildScenario(
171
+ fixtures.agent.list(() => agentData),
172
+ fixtures.skill.list(() => skillData),
173
+ );
174
+
175
+ const searchKey = "ai.stigmer.search.v1.SearchService/search";
176
+ const entry = scenario.fixtures.get(searchKey);
177
+ expect(entry).toBeDefined();
178
+
179
+ expect(entry!.unary!({ kinds: [ApiResourceKind.agent] })).toBe(agentData);
180
+ expect(entry!.unary!({ kinds: [ApiResourceKind.skill] })).toBe(skillData);
181
+ });
182
+
183
+ it("throws for unregistered search resource kind", () => {
184
+ const scenario = buildScenario(
185
+ fixtures.agent.list(() => ({})),
186
+ );
187
+
188
+ const searchKey = "ai.stigmer.search.v1.SearchService/search";
189
+ const entry = scenario.fixtures.get(searchKey)!;
190
+
191
+ expect(() => entry.unary!({ kinds: [ApiResourceKind.skill] })).toThrow(
192
+ "No search fixture for resource kind skill",
193
+ );
194
+ });
195
+
196
+ it("combines direct and search specs", () => {
197
+ const scenario = buildScenario(
198
+ fixtures.session.get(() => ({ id: "ses-1" })),
199
+ fixtures.agent.list(() => ({ entries: [] })),
200
+ );
201
+
202
+ const sessionKey =
203
+ "ai.stigmer.agentic.session.v1.SessionQueryController/get";
204
+ const searchKey = "ai.stigmer.search.v1.SearchService/search";
205
+
206
+ expect(scenario.fixtures.has(sessionKey)).toBe(true);
207
+ expect(scenario.fixtures.has(searchKey)).toBe(true);
208
+ });
209
+
210
+ it("returns empty fixtures when called with no args", () => {
211
+ const scenario = buildScenario();
212
+ expect(scenario.fixtures.size).toBe(0);
213
+ });
214
+ });
@@ -0,0 +1,171 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { ExecutionPhase, MessageType, ToolCallStatus, ExecutionArtifactKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
3
+ import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
4
+ import { samples } from "../samples";
5
+
6
+ describe("samples", () => {
7
+ describe("session", () => {
8
+ it("creates a session with defaults", () => {
9
+ const s = samples.session();
10
+ expect(s.apiVersion).toBe("agentic.stigmer.ai/v1");
11
+ expect(s.kind).toBe("Session");
12
+ expect(s.metadata?.name).toBe("demo-session");
13
+ expect(s.metadata?.org).toBe("demo");
14
+ expect(s.spec?.subject).toBe("Demo conversation");
15
+ });
16
+
17
+ it("applies overrides", () => {
18
+ const s = samples.session({ name: "my-chat", subject: "Custom topic", org: "acme" });
19
+ expect(s.metadata?.name).toBe("my-chat");
20
+ expect(s.metadata?.org).toBe("acme");
21
+ expect(s.spec?.subject).toBe("Custom topic");
22
+ });
23
+ });
24
+
25
+ describe("agent", () => {
26
+ it("creates an agent with defaults", () => {
27
+ const a = samples.agent();
28
+ expect(a.kind).toBe("Agent");
29
+ expect(a.metadata?.name).toBe("Demo Agent");
30
+ expect(a.spec?.description).toContain("sample agent");
31
+ });
32
+
33
+ it("derives slug from name override", () => {
34
+ const a = samples.agent({ name: "My Custom Agent" });
35
+ expect(a.metadata?.slug).toBe("my-custom-agent");
36
+ });
37
+ });
38
+
39
+ describe("agentExecution", () => {
40
+ it("creates a completed execution with default messages", () => {
41
+ const ex = samples.agentExecution();
42
+ expect(ex.kind).toBe("AgentExecution");
43
+ expect(ex.status?.phase).toBe(ExecutionPhase.EXECUTION_COMPLETED);
44
+ expect(ex.status?.messages.length).toBeGreaterThanOrEqual(2);
45
+ });
46
+
47
+ it("uses provided phase and messages", () => {
48
+ const msgs = [
49
+ samples.humanMessage("test input"),
50
+ samples.aiMessage("test response"),
51
+ ];
52
+ const ex = samples.agentExecution({
53
+ phase: ExecutionPhase.EXECUTION_IN_PROGRESS,
54
+ messages: msgs,
55
+ });
56
+ expect(ex.status?.phase).toBe(ExecutionPhase.EXECUTION_IN_PROGRESS);
57
+ expect(ex.status?.messages).toHaveLength(2);
58
+ expect(ex.status?.messages[0].content).toBe("test input");
59
+ });
60
+ });
61
+
62
+ describe("skill", () => {
63
+ it("creates a skill with SKILL.md content", () => {
64
+ const sk = samples.skill();
65
+ expect(sk.kind).toBe("Skill");
66
+ expect(sk.spec?.skillMd).toContain("# Demo Skill");
67
+ });
68
+
69
+ it("applies custom skillMd", () => {
70
+ const sk = samples.skill({ skillMd: "# My Skill\nCustom content" });
71
+ expect(sk.spec?.skillMd).toContain("# My Skill");
72
+ });
73
+ });
74
+
75
+ describe("mcpServer", () => {
76
+ it("creates an MCP server with defaults", () => {
77
+ const m = samples.mcpServer();
78
+ expect(m.kind).toBe("McpServer");
79
+ expect(m.metadata?.name).toBe("Demo MCP Server");
80
+ });
81
+ });
82
+
83
+ describe("environment", () => {
84
+ it("creates an environment with defaults", () => {
85
+ const e = samples.environment();
86
+ expect(e.kind).toBe("Environment");
87
+ expect(e.metadata?.name).toBe("demo-env");
88
+ });
89
+ });
90
+
91
+ describe("agentInstance", () => {
92
+ it("creates an agent instance referencing demo agent", () => {
93
+ const ai = samples.agentInstance();
94
+ expect(ai.kind).toBe("AgentInstance");
95
+ expect(ai.spec?.agentId).toContain("agt-");
96
+ });
97
+ });
98
+
99
+ describe("message primitives", () => {
100
+ it("humanMessage has correct type", () => {
101
+ const msg = samples.humanMessage("Hello");
102
+ expect(msg.type).toBe(MessageType.MESSAGE_HUMAN);
103
+ expect(msg.content).toBe("Hello");
104
+ expect(msg.timestamp).toBeTruthy();
105
+ });
106
+
107
+ it("aiMessage has correct type and optional tool calls", () => {
108
+ const tc = samples.toolCall("web-search", "results here");
109
+ const msg = samples.aiMessage("Here are the results", [tc]);
110
+ expect(msg.type).toBe(MessageType.MESSAGE_AI);
111
+ expect(msg.toolCalls).toHaveLength(1);
112
+ expect(msg.toolCalls[0].name).toBe("web-search");
113
+ });
114
+
115
+ it("toolCall is completed with a result", () => {
116
+ const tc = samples.toolCall("lookup-order", '{"orderId": "123"}');
117
+ expect(tc.status).toBe(ToolCallStatus.TOOL_CALL_COMPLETED);
118
+ expect(tc.name).toBe("lookup-order");
119
+ expect(tc.result).toBe('{"orderId": "123"}');
120
+ });
121
+
122
+ it("artifact defaults to FILE kind", () => {
123
+ const a = samples.artifact("report.md");
124
+ expect(a.name).toBe("report.md");
125
+ expect(a.kind).toBe(ExecutionArtifactKind.FILE);
126
+ expect(a.storageKey).toContain("demo-artifact-");
127
+ });
128
+ });
129
+
130
+ describe("list responses", () => {
131
+ it("sessionList wraps sessions", () => {
132
+ const list = samples.sessionList();
133
+ expect(list.entries).toHaveLength(1);
134
+ expect(list.totalPages).toBe(1);
135
+ });
136
+
137
+ it("sessionList accepts custom entries", () => {
138
+ const sessions = [
139
+ samples.session({ name: "s1" }),
140
+ samples.session({ name: "s2" }),
141
+ ];
142
+ const list = samples.sessionList(sessions);
143
+ expect(list.entries).toHaveLength(2);
144
+ });
145
+
146
+ it("agentExecutionList wraps executions", () => {
147
+ const list = samples.agentExecutionList();
148
+ expect(list.entries).toHaveLength(1);
149
+ expect(list.totalPages).toBe(1);
150
+ });
151
+
152
+ it("searchResponse wraps search results", () => {
153
+ const resp = samples.searchResponse();
154
+ expect(resp.entries).toHaveLength(1);
155
+ expect(resp.totalCount).toBe(1);
156
+ expect(resp.totalPages).toBe(1);
157
+ });
158
+
159
+ it("searchResult accepts overrides", () => {
160
+ const r = samples.searchResult({
161
+ kind: ApiResourceKind.skill,
162
+ name: "My Skill",
163
+ slug: "my-skill",
164
+ org: "acme",
165
+ });
166
+ expect(r.kind).toBe(ApiResourceKind.skill);
167
+ expect(r.name).toBe("My Skill");
168
+ expect(r.qualifiedSlug).toBe("acme/my-skill");
169
+ });
170
+ });
171
+ });
@@ -0,0 +1,78 @@
1
+ import type { Transport } from "@connectrpc/connect";
2
+ import type { Stigmer } from "@stigmer/sdk";
3
+ import {
4
+ AgentClient,
5
+ AgentExecutionClient,
6
+ AgentInstanceClient,
7
+ ApiKeyClient,
8
+ EnvironmentClient,
9
+ ExecutionContextClient,
10
+ GitHubClient,
11
+ IamPolicyClient,
12
+ IdentityAccountClient,
13
+ IdentityProviderClient,
14
+ McpServerClient,
15
+ OrganizationClient,
16
+ ProjectClient,
17
+ SearchClient,
18
+ SessionClient,
19
+ SkillClient,
20
+ WorkflowClient,
21
+ WorkflowExecutionClient,
22
+ WorkflowInstanceClient,
23
+ } from "@stigmer/sdk";
24
+ import { DemoTransport } from "./transport";
25
+ import type { DemoScenario } from "./types";
26
+
27
+ /**
28
+ * Create a `Stigmer`-compatible client backed by in-memory fixture data.
29
+ *
30
+ * The returned object is structurally identical to a real {@link Stigmer}
31
+ * instance — all 17 resource clients plus `search` and `github` — but
32
+ * every RPC resolves through a {@link DemoTransport} instead of a network
33
+ * connection. Pass it to `<StigmerProvider client={...}>` and all
34
+ * descendant hooks and components work without a live backend.
35
+ *
36
+ * RPCs that have no registered fixture throw a descriptive error
37
+ * identifying the missing key. Register fixtures in the
38
+ * {@link DemoScenario} to cover the RPCs your components need.
39
+ *
40
+ * @example
41
+ * ```tsx
42
+ * import { StigmerProvider } from "@stigmer/react";
43
+ * import { createDemoClient } from "@stigmer/react/demo";
44
+ *
45
+ * const client = createDemoClient({ fixtures: myScenarioFixtures });
46
+ *
47
+ * <StigmerProvider client={client}>
48
+ * <App />
49
+ * </StigmerProvider>
50
+ * ```
51
+ */
52
+ export function createDemoClient(scenario: DemoScenario): Stigmer {
53
+ const transport = new DemoTransport(
54
+ scenario.fixtures,
55
+ ) as unknown as Transport;
56
+
57
+ return {
58
+ agent: new AgentClient(transport),
59
+ agentExecution: new AgentExecutionClient(transport),
60
+ agentInstance: new AgentInstanceClient(transport),
61
+ apiKey: new ApiKeyClient(transport),
62
+ environment: new EnvironmentClient(transport),
63
+ executionContext: new ExecutionContextClient(transport),
64
+ iamPolicy: new IamPolicyClient(transport),
65
+ identityAccount: new IdentityAccountClient(transport),
66
+ identityProvider: new IdentityProviderClient(transport),
67
+ mcpServer: new McpServerClient(transport),
68
+ organization: new OrganizationClient(transport),
69
+ project: new ProjectClient(transport),
70
+ session: new SessionClient(transport),
71
+ skill: new SkillClient(transport),
72
+ workflow: new WorkflowClient(transport),
73
+ workflowExecution: new WorkflowExecutionClient(transport),
74
+ workflowInstance: new WorkflowInstanceClient(transport),
75
+ search: new SearchClient(transport),
76
+ github: new GitHubClient(transport),
77
+ } as Stigmer;
78
+ }