@stigmer/mcp-server 3.35.0 → 3.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/mcp-server-stigmer.d.ts.map +1 -1
- package/cli/mcp-server-stigmer.js +1 -1
- package/cli/mcp-server-stigmer.js.map +1 -1
- package/config.d.ts.map +1 -1
- package/config.js.map +1 -1
- package/domains/agentexecutions/approve.d.ts.map +1 -1
- package/domains/agentexecutions/approve.js.map +1 -1
- package/domains/agentexecutions/fetch.d.ts.map +1 -1
- package/domains/agentexecutions/fetch.js.map +1 -1
- package/domains/agentexecutions/run.d.ts.map +1 -1
- package/domains/agentexecutions/run.js.map +1 -1
- package/domains/agentexecutions/tools.d.ts.map +1 -1
- package/domains/agentexecutions/tools.js.map +1 -1
- package/domains/agents/apply.d.ts.map +1 -1
- package/domains/agents/apply.js.map +1 -1
- package/domains/agents/delete.d.ts.map +1 -1
- package/domains/agents/delete.js.map +1 -1
- package/domains/agents/fetch.d.ts.map +1 -1
- package/domains/agents/fetch.js.map +1 -1
- package/domains/agents/resources.d.ts.map +1 -1
- package/domains/agents/resources.js.map +1 -1
- package/domains/agents/tools.d.ts.map +1 -1
- package/domains/agents/tools.js.map +1 -1
- package/domains/apply-visibility.d.ts.map +1 -1
- package/domains/apply-visibility.js.map +1 -1
- package/domains/channels/calls.d.ts.map +1 -1
- package/domains/channels/calls.js.map +1 -1
- package/domains/channels/errors.d.ts.map +1 -1
- package/domains/channels/errors.js.map +1 -1
- package/domains/channels/tools.d.ts.map +1 -1
- package/domains/channels/tools.js.map +1 -1
- package/domains/client.d.ts.map +1 -1
- package/domains/client.js.map +1 -1
- package/domains/conversation/calls.d.ts.map +1 -1
- package/domains/conversation/calls.js.map +1 -1
- package/domains/conversation/errors.d.ts.map +1 -1
- package/domains/conversation/errors.js.map +1 -1
- package/domains/conversation/tools.d.ts.map +1 -1
- package/domains/conversation/tools.js.map +1 -1
- package/domains/environments/apply.d.ts.map +1 -1
- package/domains/environments/apply.js.map +1 -1
- package/domains/environments/delete.d.ts.map +1 -1
- package/domains/environments/delete.js.map +1 -1
- package/domains/environments/fetch.d.ts.map +1 -1
- package/domains/environments/fetch.js.map +1 -1
- package/domains/environments/resources.d.ts.map +1 -1
- package/domains/environments/resources.js.map +1 -1
- package/domains/environments/tools.d.ts.map +1 -1
- package/domains/environments/tools.js.map +1 -1
- package/domains/executions/cancel.d.ts.map +1 -1
- package/domains/executions/cancel.js.map +1 -1
- package/domains/executions/tools.d.ts.map +1 -1
- package/domains/executions/tools.js.map +1 -1
- package/domains/marshal.d.ts.map +1 -1
- package/domains/marshal.js.map +1 -1
- package/domains/mcpservers/apply.d.ts.map +1 -1
- package/domains/mcpservers/apply.js.map +1 -1
- package/domains/mcpservers/delete.d.ts.map +1 -1
- package/domains/mcpservers/delete.js.map +1 -1
- package/domains/mcpservers/fetch.d.ts.map +1 -1
- package/domains/mcpservers/fetch.js.map +1 -1
- package/domains/mcpservers/resources.d.ts.map +1 -1
- package/domains/mcpservers/resources.js.map +1 -1
- package/domains/mcpservers/tools.d.ts.map +1 -1
- package/domains/mcpservers/tools.js.map +1 -1
- package/domains/memory/calls.d.ts.map +1 -1
- package/domains/memory/calls.js.map +1 -1
- package/domains/memory/context.d.ts.map +1 -1
- package/domains/memory/context.js.map +1 -1
- package/domains/memory/errors.d.ts.map +1 -1
- package/domains/memory/errors.js.map +1 -1
- package/domains/memory/tools.d.ts.map +1 -1
- package/domains/memory/tools.js.map +1 -1
- package/domains/resourcehandler.d.ts.map +1 -1
- package/domains/resourcehandler.js.map +1 -1
- package/domains/resourceuri.d.ts.map +1 -1
- package/domains/resourceuri.js.map +1 -1
- package/domains/rpcerr.d.ts.map +1 -1
- package/domains/rpcerr.js.map +1 -1
- package/domains/search/tools.d.ts.map +1 -1
- package/domains/search/tools.js.map +1 -1
- package/domains/skills/delete.d.ts.map +1 -1
- package/domains/skills/delete.js.map +1 -1
- package/domains/skills/fetch.d.ts.map +1 -1
- package/domains/skills/fetch.js.map +1 -1
- package/domains/skills/resources.d.ts.map +1 -1
- package/domains/skills/resources.js.map +1 -1
- package/domains/skills/tools.d.ts.map +1 -1
- package/domains/skills/tools.js.map +1 -1
- package/domains/skills/versions.d.ts.map +1 -1
- package/domains/skills/versions.js.map +1 -1
- package/domains/toolresult.d.ts.map +1 -1
- package/domains/toolresult.js.map +1 -1
- package/domains/workflowexecutions/approvals.d.ts.map +1 -1
- package/domains/workflowexecutions/approvals.js.map +1 -1
- package/domains/workflowexecutions/run.d.ts.map +1 -1
- package/domains/workflowexecutions/run.js.map +1 -1
- package/domains/workflowexecutions/tools.d.ts.map +1 -1
- package/domains/workflowexecutions/tools.js.map +1 -1
- package/domains/workflows/apply.d.ts.map +1 -1
- package/domains/workflows/apply.js.map +1 -1
- package/domains/workflows/delete.d.ts.map +1 -1
- package/domains/workflows/delete.js.map +1 -1
- package/domains/workflows/fetch.d.ts.map +1 -1
- package/domains/workflows/fetch.js.map +1 -1
- package/domains/workflows/resources.d.ts.map +1 -1
- package/domains/workflows/resources.js.map +1 -1
- package/domains/workflows/taskkinds.d.ts.map +1 -1
- package/domains/workflows/taskkinds.js.map +1 -1
- package/domains/workflows/tools.d.ts.map +1 -1
- package/domains/workflows/tools.js.map +1 -1
- package/domains/workflows/validate.d.ts.map +1 -1
- package/domains/workflows/validate.js.map +1 -1
- package/domains/workflows/versions.d.ts.map +1 -1
- package/domains/workflows/versions.js.map +1 -1
- package/gen/agent.d.ts.map +1 -1
- package/gen/agent.js.map +1 -1
- package/gen/apply-runtime.d.ts.map +1 -1
- package/gen/apply-runtime.js.map +1 -1
- package/gen/environment.d.ts.map +1 -1
- package/gen/environment.js.map +1 -1
- package/gen/mcpserver.d.ts.map +1 -1
- package/gen/mcpserver.js.map +1 -1
- package/gen/workflow.d.ts.map +1 -1
- package/gen/workflow.js.map +1 -1
- package/index.d.ts.map +1 -1
- package/index.js.map +1 -1
- package/logger.d.ts.map +1 -1
- package/logger.js.map +1 -1
- package/package.json +3 -3
- package/readiness.d.ts.map +1 -1
- package/readiness.js.map +1 -1
- package/server.d.ts.map +1 -1
- package/server.js.map +1 -1
- package/src/config.test.ts +0 -88
- package/src/domains/apply.integration.test.ts +0 -313
- package/src/domains/channels/channels.integration.test.ts +0 -272
- package/src/domains/channels/errors.test.ts +0 -52
- package/src/domains/conversation/conversation.integration.test.ts +0 -233
- package/src/domains/deletes.integration.test.ts +0 -147
- package/src/domains/executions.integration.test.ts +0 -376
- package/src/domains/memory/context.test.ts +0 -95
- package/src/domains/memory/errors.test.ts +0 -54
- package/src/domains/memory/memory.integration.test.ts +0 -253
- package/src/domains/reads.integration.test.ts +0 -163
- package/src/domains/resources.integration.test.ts +0 -157
- package/src/domains/resourceuri.test.ts +0 -104
- package/src/domains/rpcerr.test.ts +0 -62
- package/src/domains/search/search.integration.test.ts +0 -141
- package/src/domains/versions.integration.test.ts +0 -212
- package/src/domains/workflows/validate.integration.test.ts +0 -117
- package/src/domains/workflows/workflow-tools.integration.test.ts +0 -148
- package/src/http.integration.test.ts +0 -235
- package/src/readiness.test.ts +0 -90
- package/src/server.integration.test.ts +0 -82
|
@@ -1,376 +0,0 @@
|
|
|
1
|
-
// In-process integration test for the execution-loop tools: run_agent,
|
|
2
|
-
// run_workflow, get_agent_execution, the two approval tools,
|
|
3
|
-
// list_pending_approvals, and cancel_execution.
|
|
4
|
-
//
|
|
5
|
-
// Same harness as reads.integration.test.ts: a real Connect backend serving
|
|
6
|
-
// stubbed controllers, the MCP server driven through an in-memory client. The
|
|
7
|
-
// stubs capture requests so the tests assert the exact protos the tools send
|
|
8
|
-
// (slug→ID resolution, runtime-env conversion, approval enum mapping) and
|
|
9
|
-
// script execution state (running vs terminal, long message histories) to
|
|
10
|
-
// exercise the compact projection and the cancel short-circuit.
|
|
11
|
-
|
|
12
|
-
import { create, toJson } from "@bufbuild/protobuf";
|
|
13
|
-
import type { ConnectRouter } from "@connectrpc/connect";
|
|
14
|
-
import { connectNodeAdapter } from "@connectrpc/connect-node";
|
|
15
|
-
import {
|
|
16
|
-
createServer as createHttp2Server,
|
|
17
|
-
type Http2Server,
|
|
18
|
-
type ServerHttp2Session,
|
|
19
|
-
} from "node:http2";
|
|
20
|
-
import type { AddressInfo } from "node:net";
|
|
21
|
-
|
|
22
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
23
|
-
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
24
|
-
import { AgentSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
|
|
25
|
-
import { AgentQueryController } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/query_pb";
|
|
26
|
-
import {
|
|
27
|
-
AgentExecutionSchema,
|
|
28
|
-
type AgentExecution,
|
|
29
|
-
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
30
|
-
import { AgentExecutionCommandController } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/command_pb";
|
|
31
|
-
import {
|
|
32
|
-
ApprovalAction,
|
|
33
|
-
ExecutionPhase,
|
|
34
|
-
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
35
|
-
import { AgentExecutionQueryController } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/query_pb";
|
|
36
|
-
import type { SubmitApprovalInput } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/io_pb";
|
|
37
|
-
import { WorkflowSchema } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/api_pb";
|
|
38
|
-
import { WorkflowQueryController } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/query_pb";
|
|
39
|
-
import {
|
|
40
|
-
WorkflowExecutionSchema,
|
|
41
|
-
type WorkflowExecution,
|
|
42
|
-
} from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/api_pb";
|
|
43
|
-
import { WorkflowExecutionCommandController } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/command_pb";
|
|
44
|
-
import { ExecutionPhase as WorkflowExecutionPhase } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/enum_pb";
|
|
45
|
-
import {
|
|
46
|
-
PendingApprovalsListSchema,
|
|
47
|
-
type ListPendingApprovalsRequest,
|
|
48
|
-
type SubmitWorkflowTaskApprovalInput,
|
|
49
|
-
} from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/io_pb";
|
|
50
|
-
import { WorkflowExecutionQueryController } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/query_pb";
|
|
51
|
-
import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
52
|
-
|
|
53
|
-
import { configureLogger } from "../logger";
|
|
54
|
-
import { createServer } from "../server";
|
|
55
|
-
|
|
56
|
-
configureLogger({ level: "error", format: "text" });
|
|
57
|
-
|
|
58
|
-
const knownAgent = create(AgentSchema, {
|
|
59
|
-
apiVersion: "v1",
|
|
60
|
-
kind: "agent",
|
|
61
|
-
metadata: { name: "Code Reviewer", slug: "code-reviewer", org: "acme", id: "agt_1" },
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
const knownWorkflow = create(WorkflowSchema, {
|
|
65
|
-
apiVersion: "v1",
|
|
66
|
-
kind: "workflow",
|
|
67
|
-
metadata: { name: "Release", slug: "release", org: "acme", id: "wkf_1" },
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
/** An execution with a scriptable phase and an 8-message history. */
|
|
71
|
-
function agentExecutionFixture(phase: ExecutionPhase): AgentExecution {
|
|
72
|
-
return create(AgentExecutionSchema, {
|
|
73
|
-
apiVersion: "v1",
|
|
74
|
-
kind: "AgentExecution",
|
|
75
|
-
metadata: { name: "run", org: "acme", id: "aex_1" },
|
|
76
|
-
spec: { agentId: "agt_1", sessionId: "ses_1", message: "review this" },
|
|
77
|
-
status: {
|
|
78
|
-
phase,
|
|
79
|
-
messages: Array.from({ length: 8 }, (_, i) => ({ content: `msg-${i}` })),
|
|
80
|
-
// Bulk fields the compact view must prune.
|
|
81
|
-
subAgentExecutions: [{ name: "researcher", input: "dig into the logs" }],
|
|
82
|
-
},
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function workflowExecutionFixture(phase: WorkflowExecutionPhase): WorkflowExecution {
|
|
87
|
-
return create(WorkflowExecutionSchema, {
|
|
88
|
-
apiVersion: "v1",
|
|
89
|
-
kind: "WorkflowExecution",
|
|
90
|
-
metadata: { name: "run", org: "acme", id: "wex_1" },
|
|
91
|
-
spec: { workflowId: "wkf_1" },
|
|
92
|
-
status: { phase },
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const pendingApprovals = create(PendingApprovalsListSchema, {
|
|
97
|
-
entries: [{ executionId: "wex_1", workflowName: "Release", taskName: "sign-off" }],
|
|
98
|
-
totalCount: 1,
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
let backend: Http2Server;
|
|
102
|
-
let client: Client;
|
|
103
|
-
const openSessions = new Set<ServerHttp2Session>();
|
|
104
|
-
|
|
105
|
-
// Captured requests / scripted state, reset per test.
|
|
106
|
-
let createdAgentExecution: AgentExecution | undefined;
|
|
107
|
-
let createdWorkflowExecution: WorkflowExecution | undefined;
|
|
108
|
-
let agentExecutionState: AgentExecution;
|
|
109
|
-
let workflowExecutionState: WorkflowExecution;
|
|
110
|
-
let lastAgentApproval: SubmitApprovalInput | undefined;
|
|
111
|
-
let lastWorkflowApproval: SubmitWorkflowTaskApprovalInput | undefined;
|
|
112
|
-
let lastPendingApprovalsRequest: ListPendingApprovalsRequest | undefined;
|
|
113
|
-
let agentCancelCalls = 0;
|
|
114
|
-
let workflowCancelCalls = 0;
|
|
115
|
-
|
|
116
|
-
interface ToolResult {
|
|
117
|
-
content: Array<{ type: string; text?: string }>;
|
|
118
|
-
isError?: boolean;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async function callTool(name: string, args: Record<string, unknown>): Promise<ToolResult> {
|
|
122
|
-
return (await client.callTool({ name, arguments: args })) as ToolResult;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function parseText(result: ToolResult): Record<string, unknown> {
|
|
126
|
-
return JSON.parse(result.content[0]?.text ?? "{}") as Record<string, unknown>;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
beforeAll(async () => {
|
|
130
|
-
const routes = (router: ConnectRouter) => {
|
|
131
|
-
router.service(AgentQueryController, { getByReference: () => knownAgent });
|
|
132
|
-
router.service(AgentExecutionQueryController, {
|
|
133
|
-
get: () => agentExecutionState,
|
|
134
|
-
});
|
|
135
|
-
router.service(AgentExecutionCommandController, {
|
|
136
|
-
create: (req) => {
|
|
137
|
-
createdAgentExecution = req;
|
|
138
|
-
return agentExecutionFixture(ExecutionPhase.EXECUTION_PENDING);
|
|
139
|
-
},
|
|
140
|
-
submitApproval: (req) => {
|
|
141
|
-
lastAgentApproval = req;
|
|
142
|
-
return agentExecutionFixture(ExecutionPhase.EXECUTION_IN_PROGRESS);
|
|
143
|
-
},
|
|
144
|
-
cancel: () => {
|
|
145
|
-
agentCancelCalls++;
|
|
146
|
-
return agentExecutionFixture(ExecutionPhase.EXECUTION_CANCELLED);
|
|
147
|
-
},
|
|
148
|
-
});
|
|
149
|
-
router.service(WorkflowQueryController, { getByReference: () => knownWorkflow });
|
|
150
|
-
router.service(WorkflowExecutionQueryController, {
|
|
151
|
-
get: () => workflowExecutionState,
|
|
152
|
-
listPendingApprovals: (req) => {
|
|
153
|
-
lastPendingApprovalsRequest = req;
|
|
154
|
-
return pendingApprovals;
|
|
155
|
-
},
|
|
156
|
-
});
|
|
157
|
-
router.service(WorkflowExecutionCommandController, {
|
|
158
|
-
create: (req) => {
|
|
159
|
-
createdWorkflowExecution = req;
|
|
160
|
-
return workflowExecutionFixture(WorkflowExecutionPhase.EXECUTION_PENDING);
|
|
161
|
-
},
|
|
162
|
-
submitWorkflowTaskApproval: (req) => {
|
|
163
|
-
lastWorkflowApproval = req;
|
|
164
|
-
return workflowExecutionFixture(WorkflowExecutionPhase.EXECUTION_IN_PROGRESS);
|
|
165
|
-
},
|
|
166
|
-
cancel: () => {
|
|
167
|
-
workflowCancelCalls++;
|
|
168
|
-
return workflowExecutionFixture(WorkflowExecutionPhase.EXECUTION_CANCELLED);
|
|
169
|
-
},
|
|
170
|
-
});
|
|
171
|
-
};
|
|
172
|
-
backend = createHttp2Server(connectNodeAdapter({ routes }));
|
|
173
|
-
backend.on("session", (session) => {
|
|
174
|
-
openSessions.add(session);
|
|
175
|
-
session.on("close", () => openSessions.delete(session));
|
|
176
|
-
});
|
|
177
|
-
await new Promise<void>((resolve) => backend.listen(0, "127.0.0.1", resolve));
|
|
178
|
-
const port = (backend.address() as AddressInfo).port;
|
|
179
|
-
|
|
180
|
-
const mcp = createServer({ serverAddress: `127.0.0.1:${port}`, apiKey: "" });
|
|
181
|
-
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
|
|
182
|
-
client = new Client({ name: "executions-integration", version: "test" });
|
|
183
|
-
await Promise.all([mcp.connect(serverTransport), client.connect(clientTransport)]);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
beforeEach(() => {
|
|
187
|
-
createdAgentExecution = undefined;
|
|
188
|
-
createdWorkflowExecution = undefined;
|
|
189
|
-
agentExecutionState = agentExecutionFixture(ExecutionPhase.EXECUTION_IN_PROGRESS);
|
|
190
|
-
workflowExecutionState = workflowExecutionFixture(WorkflowExecutionPhase.EXECUTION_IN_PROGRESS);
|
|
191
|
-
lastAgentApproval = undefined;
|
|
192
|
-
lastWorkflowApproval = undefined;
|
|
193
|
-
lastPendingApprovalsRequest = undefined;
|
|
194
|
-
agentCancelCalls = 0;
|
|
195
|
-
workflowCancelCalls = 0;
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
afterAll(async () => {
|
|
199
|
-
await client?.close();
|
|
200
|
-
for (const session of openSessions) session.destroy();
|
|
201
|
-
await new Promise<void>((resolve) => backend.close(() => resolve()));
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
describe("execution tools integration", () => {
|
|
205
|
-
it("advertises the execution-loop tools", async () => {
|
|
206
|
-
const { tools } = await client.listTools();
|
|
207
|
-
expect(tools.map((t) => t.name)).toEqual(
|
|
208
|
-
expect.arrayContaining([
|
|
209
|
-
"run_agent",
|
|
210
|
-
"run_workflow",
|
|
211
|
-
"get_agent_execution",
|
|
212
|
-
"submit_agent_execution_approval",
|
|
213
|
-
"list_pending_approvals",
|
|
214
|
-
"submit_workflow_task_approval",
|
|
215
|
-
"cancel_execution",
|
|
216
|
-
]),
|
|
217
|
-
);
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
it("run_agent resolves the slug and creates the execution", async () => {
|
|
221
|
-
const result = await callTool("run_agent", {
|
|
222
|
-
org: "acme",
|
|
223
|
-
agent: "code-reviewer",
|
|
224
|
-
message: "review this PR",
|
|
225
|
-
runtime_env: { REPO: "stigmer/stigmer" },
|
|
226
|
-
});
|
|
227
|
-
expect(result.isError).toBeFalsy();
|
|
228
|
-
|
|
229
|
-
expect(createdAgentExecution?.spec?.agentId).toBe("agt_1");
|
|
230
|
-
expect(createdAgentExecution?.spec?.message).toBe("review this PR");
|
|
231
|
-
expect(createdAgentExecution?.spec?.sessionId).toBe("");
|
|
232
|
-
expect(createdAgentExecution?.metadata?.org).toBe("acme");
|
|
233
|
-
// Runtime env values through MCP are never secrets.
|
|
234
|
-
expect(createdAgentExecution?.spec?.runtimeEnv?.REPO?.value).toBe("stigmer/stigmer");
|
|
235
|
-
expect(createdAgentExecution?.spec?.runtimeEnv?.REPO?.isSecret).toBe(false);
|
|
236
|
-
|
|
237
|
-
// The created execution comes back as plain protojson (its status is small).
|
|
238
|
-
const created = parseText(result);
|
|
239
|
-
expect((created.metadata as Record<string, unknown>).id).toBe("aex_1");
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it("run_agent threads a session follow-up", async () => {
|
|
243
|
-
await callTool("run_agent", {
|
|
244
|
-
org: "acme",
|
|
245
|
-
agent: "code-reviewer",
|
|
246
|
-
message: "and the tests?",
|
|
247
|
-
session_id: "ses_42",
|
|
248
|
-
});
|
|
249
|
-
expect(createdAgentExecution?.spec?.sessionId).toBe("ses_42");
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
it("run_workflow creates the execution with the org env injected", async () => {
|
|
253
|
-
const result = await callTool("run_workflow", { org: "acme", workflow: "release" });
|
|
254
|
-
expect(result.isError).toBeFalsy();
|
|
255
|
-
|
|
256
|
-
expect(createdWorkflowExecution?.spec?.workflowId).toBe("wkf_1");
|
|
257
|
-
expect(createdWorkflowExecution?.spec?.triggerMessage).toBe("execute");
|
|
258
|
-
// The CLI-parity org injection.
|
|
259
|
-
expect(createdWorkflowExecution?.spec?.runtimeEnv?.STIGMER_ORG_ID?.value).toBe("acme");
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
it("run_workflow lets a caller-supplied STIGMER_ORG_ID win", async () => {
|
|
263
|
-
await callTool("run_workflow", {
|
|
264
|
-
org: "acme",
|
|
265
|
-
workflow: "release",
|
|
266
|
-
message: "ship it",
|
|
267
|
-
runtime_env: { STIGMER_ORG_ID: "other-org" },
|
|
268
|
-
});
|
|
269
|
-
expect(createdWorkflowExecution?.spec?.triggerMessage).toBe("ship it");
|
|
270
|
-
expect(createdWorkflowExecution?.spec?.runtimeEnv?.STIGMER_ORG_ID?.value).toBe("other-org");
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
it("get_agent_execution defaults to the compact view", async () => {
|
|
274
|
-
const result = await callTool("get_agent_execution", { execution_id: "aex_1" });
|
|
275
|
-
expect(result.isError).toBeFalsy();
|
|
276
|
-
|
|
277
|
-
const body = parseText(result);
|
|
278
|
-
expect(body.view).toBe("compact");
|
|
279
|
-
expect(body.total_messages).toBe(8);
|
|
280
|
-
|
|
281
|
-
const execution = body.execution as Record<string, unknown>;
|
|
282
|
-
const status = (execution.status ?? {}) as Record<string, unknown>;
|
|
283
|
-
const messages = status.messages as Array<Record<string, unknown>>;
|
|
284
|
-
// Default tail of 5, ending with the newest message.
|
|
285
|
-
expect(messages).toHaveLength(5);
|
|
286
|
-
expect(messages[4]?.content).toBe("msg-7");
|
|
287
|
-
// Bulk bookkeeping fields are pruned.
|
|
288
|
-
expect(status.sub_agent_executions).toBeUndefined();
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
it("get_agent_execution honors message_limit", async () => {
|
|
292
|
-
const body = parseText(
|
|
293
|
-
await callTool("get_agent_execution", { execution_id: "aex_1", message_limit: 2 }),
|
|
294
|
-
);
|
|
295
|
-
const status = ((body.execution as Record<string, unknown>).status ?? {}) as Record<
|
|
296
|
-
string,
|
|
297
|
-
unknown
|
|
298
|
-
>;
|
|
299
|
-
expect(status.messages as unknown[]).toHaveLength(2);
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
it("get_agent_execution view=full returns the backend protojson verbatim", async () => {
|
|
303
|
-
const result = await callTool("get_agent_execution", { execution_id: "aex_1", view: "full" });
|
|
304
|
-
expect(parseText(result)).toEqual(
|
|
305
|
-
toJson(AgentExecutionSchema, agentExecutionState, { useProtoFieldName: true }),
|
|
306
|
-
);
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
it("submit_agent_execution_approval maps the action and returns the compact view", async () => {
|
|
310
|
-
const result = await callTool("submit_agent_execution_approval", {
|
|
311
|
-
execution_id: "aex_1",
|
|
312
|
-
tool_call_id: "call_7",
|
|
313
|
-
action: "reject",
|
|
314
|
-
comment: "wrong repository",
|
|
315
|
-
});
|
|
316
|
-
expect(result.isError).toBeFalsy();
|
|
317
|
-
expect(lastAgentApproval?.agentExecutionId).toBe("aex_1");
|
|
318
|
-
expect(lastAgentApproval?.toolCallId).toBe("call_7");
|
|
319
|
-
expect(lastAgentApproval?.action).toBe(ApprovalAction.REJECT);
|
|
320
|
-
expect(lastAgentApproval?.comment).toBe("wrong repository");
|
|
321
|
-
expect(parseText(result).view).toBe("compact");
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
it("list_pending_approvals forwards the org and returns the inbox", async () => {
|
|
325
|
-
const result = await callTool("list_pending_approvals", { org: "acme" });
|
|
326
|
-
expect(result.isError).toBeFalsy();
|
|
327
|
-
expect(lastPendingApprovalsRequest?.org).toBe("acme");
|
|
328
|
-
expect(parseText(result)).toEqual(
|
|
329
|
-
toJson(PendingApprovalsListSchema, pendingApprovals, { useProtoFieldName: true }),
|
|
330
|
-
);
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
it("submit_workflow_task_approval forwards the decision and form data", async () => {
|
|
334
|
-
const result = await callTool("submit_workflow_task_approval", {
|
|
335
|
-
execution_id: "wex_1",
|
|
336
|
-
task_name: "sign-off",
|
|
337
|
-
outcome: "approve",
|
|
338
|
-
comment: "lgtm",
|
|
339
|
-
form_data: { severity: "low" },
|
|
340
|
-
});
|
|
341
|
-
expect(result.isError).toBeFalsy();
|
|
342
|
-
expect(lastWorkflowApproval?.executionId).toBe("wex_1");
|
|
343
|
-
expect(lastWorkflowApproval?.taskName).toBe("sign-off");
|
|
344
|
-
expect(lastWorkflowApproval?.outcome).toBe("approve");
|
|
345
|
-
expect(lastWorkflowApproval?.comment).toBe("lgtm");
|
|
346
|
-
expect(lastWorkflowApproval?.formData).toMatchObject({ severity: "low" });
|
|
347
|
-
// The reviewer field is server-attributed; interactive clients must not set it.
|
|
348
|
-
expect(lastWorkflowApproval?.reviewer).toBe("");
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
it("cancel_execution cancels a running agent execution", async () => {
|
|
352
|
-
const body = parseText(await callTool("cancel_execution", { execution_id: "aex_1" }));
|
|
353
|
-
expect(agentCancelCalls).toBe(1);
|
|
354
|
-
expect(body.already_terminal).toBe(false);
|
|
355
|
-
expect(body.view).toBe("compact");
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
it("cancel_execution short-circuits a terminal agent execution", async () => {
|
|
359
|
-
agentExecutionState = agentExecutionFixture(ExecutionPhase.EXECUTION_COMPLETED);
|
|
360
|
-
const body = parseText(await callTool("cancel_execution", { execution_id: "aex_1" }));
|
|
361
|
-
expect(agentCancelCalls).toBe(0);
|
|
362
|
-
expect(body.already_terminal).toBe(true);
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
it("cancel_execution routes workflow executions by prefix", async () => {
|
|
366
|
-
const body = parseText(await callTool("cancel_execution", { execution_id: "wex_1" }));
|
|
367
|
-
expect(workflowCancelCalls).toBe(1);
|
|
368
|
-
expect(body.already_terminal).toBe(false);
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
it("cancel_execution rejects an unrecognized ID format", async () => {
|
|
372
|
-
const result = await callTool("cancel_execution", { execution_id: "ses_123" });
|
|
373
|
-
expect(result.isError).toBe(true);
|
|
374
|
-
expect(result.content[0]?.text).toContain("unrecognized execution ID format");
|
|
375
|
-
});
|
|
376
|
-
});
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
// Unit tests for the capture-context resolution — the attribution twin of
|
|
2
|
-
// client.ts resolveToken: per-request headers (http) supersede the
|
|
3
|
-
// startup environment (stdio), whole-object, never mixed per-field.
|
|
4
|
-
|
|
5
|
-
import { describe, expect, it } from "vitest";
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
EMPTY_CAPTURE_CONTEXT,
|
|
9
|
-
loadCaptureContextFromEnv,
|
|
10
|
-
resolveCaptureContext,
|
|
11
|
-
type CaptureContext,
|
|
12
|
-
} from "./context";
|
|
13
|
-
|
|
14
|
-
const startup: CaptureContext = {
|
|
15
|
-
org: "env-org",
|
|
16
|
-
agentId: "agt_env",
|
|
17
|
-
sessionId: "ses_env",
|
|
18
|
-
agentExecutionId: "aex_env",
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
describe("loadCaptureContextFromEnv", () => {
|
|
22
|
-
it("reads the four STIGMER_MEMORY_* variables", () => {
|
|
23
|
-
const ctx = loadCaptureContextFromEnv({
|
|
24
|
-
STIGMER_MEMORY_ORG: "acme",
|
|
25
|
-
STIGMER_MEMORY_AGENT_ID: "agt_1",
|
|
26
|
-
STIGMER_MEMORY_SESSION_ID: "ses_1",
|
|
27
|
-
STIGMER_MEMORY_EXECUTION_ID: "aex_1",
|
|
28
|
-
});
|
|
29
|
-
expect(ctx).toEqual({
|
|
30
|
-
org: "acme",
|
|
31
|
-
agentId: "agt_1",
|
|
32
|
-
sessionId: "ses_1",
|
|
33
|
-
agentExecutionId: "aex_1",
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("degrades missing variables to empty strings (best-effort attribution)", () => {
|
|
38
|
-
expect(loadCaptureContextFromEnv({})).toEqual(EMPTY_CAPTURE_CONTEXT);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
describe("resolveCaptureContext", () => {
|
|
43
|
-
it("uses the startup context when the transport carries no request info (stdio)", () => {
|
|
44
|
-
expect(resolveCaptureContext(undefined, startup)).toEqual(startup);
|
|
45
|
-
expect(resolveCaptureContext({}, startup)).toEqual(startup);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("uses the request headers when present (http), superseding startup wholesale", () => {
|
|
49
|
-
const ctx = resolveCaptureContext(
|
|
50
|
-
{
|
|
51
|
-
requestInfo: {
|
|
52
|
-
headers: {
|
|
53
|
-
"x-stigmer-memory-org": "acme",
|
|
54
|
-
"x-stigmer-memory-agent-id": "agt_http",
|
|
55
|
-
"x-stigmer-memory-session-id": "ses_http",
|
|
56
|
-
"x-stigmer-memory-execution-id": "aex_http",
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
startup,
|
|
61
|
-
);
|
|
62
|
-
expect(ctx).toEqual({
|
|
63
|
-
org: "acme",
|
|
64
|
-
agentId: "agt_http",
|
|
65
|
-
sessionId: "ses_http",
|
|
66
|
-
agentExecutionId: "aex_http",
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("never mixes carriers: a header set missing a field yields empty, not the env value", () => {
|
|
71
|
-
// The two carriers are written by different processes; falling back
|
|
72
|
-
// per-field would attribute an http request to a stale stdio env.
|
|
73
|
-
const ctx = resolveCaptureContext(
|
|
74
|
-
{ requestInfo: { headers: { "x-stigmer-memory-org": "acme" } } },
|
|
75
|
-
startup,
|
|
76
|
-
);
|
|
77
|
-
expect(ctx).toEqual({ org: "acme", agentId: "", sessionId: "", agentExecutionId: "" });
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("looks headers up case-insensitively and collapses Node's array form", () => {
|
|
81
|
-
const ctx = resolveCaptureContext(
|
|
82
|
-
{
|
|
83
|
-
requestInfo: {
|
|
84
|
-
headers: {
|
|
85
|
-
"X-Stigmer-Memory-Org": "acme",
|
|
86
|
-
"x-stigmer-memory-agent-id": ["agt_1", "agt_dup"],
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
startup,
|
|
91
|
-
);
|
|
92
|
-
expect(ctx.org).toBe("acme");
|
|
93
|
-
expect(ctx.agentId).toBe("agt_1");
|
|
94
|
-
});
|
|
95
|
-
});
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
// Unit tests for the memory-domain error projection: the verbatim message
|
|
2
|
-
// and status name always; the google.rpc.ErrorInfo reason only when the
|
|
3
|
-
// server attached one with a non-empty reason (absence means the message
|
|
4
|
-
// alone carries the contract — "memory is full" needs no reason code to
|
|
5
|
-
// be relayed honestly).
|
|
6
|
-
|
|
7
|
-
import { Code, ConnectError } from "@connectrpc/connect";
|
|
8
|
-
import { create } from "@bufbuild/protobuf";
|
|
9
|
-
import { describe, expect, it } from "vitest";
|
|
10
|
-
import { ErrorInfoSchema } from "@stigmer/protos/google/rpc/error_details_pb";
|
|
11
|
-
|
|
12
|
-
import { domainError } from "./errors";
|
|
13
|
-
|
|
14
|
-
function preconditionWith(reason: string): ConnectError {
|
|
15
|
-
return new ConnectError(
|
|
16
|
-
"memory is disabled for this organization",
|
|
17
|
-
Code.FailedPrecondition,
|
|
18
|
-
undefined,
|
|
19
|
-
[
|
|
20
|
-
{
|
|
21
|
-
desc: ErrorInfoSchema,
|
|
22
|
-
value: create(ErrorInfoSchema, { reason, domain: "stigmer.ai" }),
|
|
23
|
-
},
|
|
24
|
-
],
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
describe("domainError", () => {
|
|
29
|
-
it("carries the ErrorInfo reason when the server attached one", () => {
|
|
30
|
-
expect(domainError(preconditionWith("MEMORY_DISABLED_FOR_ORG"))).toEqual({
|
|
31
|
-
error: "memory is disabled for this organization",
|
|
32
|
-
code: "FAILED_PRECONDITION",
|
|
33
|
-
reason: "MEMORY_DISABLED_FOR_ORG",
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("omits the reason key when the attached ErrorInfo has an empty reason", () => {
|
|
38
|
-
expect(domainError(preconditionWith(""))).toEqual({
|
|
39
|
-
error: "memory is disabled for this organization",
|
|
40
|
-
code: "FAILED_PRECONDITION",
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("omits the reason key when no ErrorInfo detail is attached", () => {
|
|
45
|
-
const ce = new ConnectError(
|
|
46
|
-
"memory content must be 1..500 characters",
|
|
47
|
-
Code.InvalidArgument,
|
|
48
|
-
);
|
|
49
|
-
expect(domainError(ce)).toEqual({
|
|
50
|
-
error: "memory content must be 1..500 characters",
|
|
51
|
-
code: "INVALID_ARGUMENT",
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
});
|