@stigmer/runner 3.22.0 → 3.22.1-dev.20260922095755
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/README.md +1 -1
- package/dist/.build-fingerprint +1 -1
- package/dist/activities/attach-session.js +0 -1
- package/dist/activities/attach-session.js.map +1 -1
- package/dist/activities/call-agent-status.d.ts +18 -11
- package/dist/activities/call-agent-status.js +21 -24
- package/dist/activities/call-agent-status.js.map +1 -1
- package/dist/activities/call-agent.d.ts +2 -2
- package/dist/activities/call-agent.js +4 -6
- package/dist/activities/call-agent.js.map +1 -1
- package/dist/activities/call-function.d.ts +4 -2
- package/dist/activities/call-function.js +13 -4
- package/dist/activities/call-function.js.map +1 -1
- package/dist/activities/classify-tool-approvals.js +1 -1
- package/dist/activities/classify-tool-approvals.js.map +1 -1
- package/dist/activities/discover-mcp-server.js +0 -1
- package/dist/activities/discover-mcp-server.js.map +1 -1
- package/dist/activities/emit-event.d.ts +6 -1
- package/dist/activities/emit-event.js +5 -14
- package/dist/activities/emit-event.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +1 -1
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -1
- package/dist/activities/execute-cursor/adapter.js +0 -1
- package/dist/activities/execute-cursor/adapter.js.map +1 -1
- package/dist/activities/execute-cursor/turn-setup.d.ts +1 -1
- package/dist/activities/execute-cursor/turn-setup.js +5 -6
- package/dist/activities/execute-cursor/turn-setup.js.map +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js.map +1 -1
- package/dist/activities/execute-deep-agent/adapter.js +1 -1
- package/dist/activities/execute-deep-agent/adapter.js.map +1 -1
- package/dist/activities/execute-deep-agent/turn-setup.d.ts +1 -1
- package/dist/activities/execute-deep-agent/turn-setup.js +5 -3
- package/dist/activities/execute-deep-agent/turn-setup.js.map +1 -1
- package/dist/activities/generate-session-subject.js +1 -2
- package/dist/activities/generate-session-subject.js.map +1 -1
- package/dist/activities/hydrate-workflow-execution.js +0 -1
- package/dist/activities/hydrate-workflow-execution.js.map +1 -1
- package/dist/activities/promote-task-output.d.ts +2 -1
- package/dist/activities/promote-task-output.js +10 -14
- package/dist/activities/promote-task-output.js.map +1 -1
- package/dist/activities/workflow-event-activities.d.ts +9 -7
- package/dist/activities/workflow-event-activities.js +14 -18
- package/dist/activities/workflow-event-activities.js.map +1 -1
- package/dist/client/stigmer-client.d.ts +13 -13
- package/dist/client/stigmer-client.js +4 -7
- package/dist/client/stigmer-client.js.map +1 -1
- package/dist/config.d.ts +40 -20
- package/dist/config.js +25 -9
- package/dist/config.js.map +1 -1
- package/dist/harness/registry.js +0 -1
- package/dist/harness/registry.js.map +1 -1
- package/dist/harness/turn-context.js +2 -4
- package/dist/harness/turn-context.js.map +1 -1
- package/dist/main.js +13 -6
- package/dist/main.js.map +1 -1
- package/dist/runner-manager.d.ts +8 -6
- package/dist/runner-manager.js +20 -10
- package/dist/runner-manager.js.map +1 -1
- package/dist/runner-token-coordinator.d.ts +8 -3
- package/dist/runner-token-coordinator.js +8 -3
- package/dist/runner-token-coordinator.js.map +1 -1
- package/dist/runner.d.ts +10 -2
- package/dist/runner.js +31 -25
- package/dist/runner.js.map +1 -1
- package/dist/shared/artifact-storage.d.ts +6 -6
- package/dist/shared/artifact-storage.js +5 -12
- package/dist/shared/artifact-storage.js.map +1 -1
- package/dist/shared/checkpointer/factory.js +5 -1
- package/dist/shared/checkpointer/factory.js.map +1 -1
- package/dist/shared/checkpointer/http-saver.d.ts +13 -2
- package/dist/shared/checkpointer/http-saver.js +17 -5
- package/dist/shared/checkpointer/http-saver.js.map +1 -1
- package/dist/shared/checkpointer/types.d.ts +10 -1
- package/dist/shared/checkpointer/types.js +16 -0
- package/dist/shared/checkpointer/types.js.map +1 -1
- package/dist/shared/extract-structured-output.js +1 -1
- package/dist/shared/extract-structured-output.js.map +1 -1
- package/package.json +5 -5
- package/src/__test-utils__/config-fixture.ts +2 -2
- package/src/__test-utils__/module-specifiers.ts +45 -3
- package/src/__tests__/config-boundary.test.ts +135 -0
- package/src/__tests__/config.test.ts +7 -16
- package/src/__tests__/runner-manager.test.ts +28 -6
- package/src/__tests__/runner.test.ts +28 -4
- package/src/activities/__tests__/attach-session.test.ts +1 -1
- package/src/activities/__tests__/call-agent-contracts.test.ts +4 -7
- package/src/activities/__tests__/call-agent-status.test.ts +17 -16
- package/src/activities/__tests__/call-agent.test.ts +28 -7
- package/src/activities/__tests__/call-function.test.ts +5 -4
- package/src/activities/__tests__/classify-tool-approvals.test.ts +1 -1
- package/src/activities/__tests__/discover-mcp-server.test.ts +1 -1
- package/src/activities/__tests__/generate-session-subject-wire.test.ts +1 -1
- package/src/activities/__tests__/workflow-event-activities.test.ts +21 -23
- package/src/activities/attach-session.ts +0 -1
- package/src/activities/call-agent-status.ts +28 -20
- package/src/activities/call-agent.ts +5 -6
- package/src/activities/call-function.ts +15 -3
- package/src/activities/classify-tool-approvals.ts +1 -1
- package/src/activities/discover-mcp-server.ts +0 -1
- package/src/activities/emit-event.ts +7 -16
- package/src/activities/execute-cursor/__test-utils__/hermetic-cursor.ts +1 -1
- package/src/activities/execute-cursor/adapter.ts +0 -1
- package/src/activities/execute-cursor/turn-setup.ts +6 -7
- package/src/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.ts +1 -1
- package/src/activities/execute-deep-agent/__tests__/adapter.test.ts +2 -2
- package/src/activities/execute-deep-agent/__tests__/summarization-verification.test.ts +1 -1
- package/src/activities/execute-deep-agent/adapter.ts +1 -1
- package/src/activities/execute-deep-agent/turn-setup.ts +6 -4
- package/src/activities/generate-session-subject.ts +1 -2
- package/src/activities/hydrate-workflow-execution.ts +0 -1
- package/src/activities/promote-task-output.ts +11 -15
- package/src/activities/workflow-event-activities.ts +17 -18
- package/src/client/__tests__/stigmer-client.test.ts +8 -19
- package/src/client/stigmer-client.ts +18 -20
- package/src/config.ts +51 -23
- package/src/harness/registry.ts +0 -1
- package/src/harness/turn-context.ts +2 -4
- package/src/main.ts +13 -6
- package/src/runner-manager.ts +25 -15
- package/src/runner-token-coordinator.ts +8 -3
- package/src/runner.ts +39 -30
- package/src/shared/__tests__/artifact-storage-extended.test.ts +9 -9
- package/src/shared/__tests__/artifact-storage.test.ts +33 -30
- package/src/shared/__tests__/extract-structured-output.test.ts +2 -2
- package/src/shared/artifact-storage.ts +12 -17
- package/src/shared/checkpointer/__tests__/factory.test.ts +23 -3
- package/src/shared/checkpointer/__tests__/http-saver.test.ts +25 -2
- package/src/shared/checkpointer/factory.ts +5 -1
- package/src/shared/checkpointer/http-saver.ts +20 -6
- package/src/shared/checkpointer/types.ts +12 -1
- package/src/shared/extract-structured-output.ts +1 -1
- package/src/shared/filereview/__tests__/cas-substrate.test.ts +1 -1
- package/src/shared/filereview/__tests__/proxy-reconcile.test.ts +1 -1
- package/src/workflow-engine/__tests__/tasks/emit-event.test.ts +32 -54
|
@@ -28,7 +28,7 @@ describe("loadConfig", () => {
|
|
|
28
28
|
expect(config.temporalAddress).toBe("");
|
|
29
29
|
expect(config.temporalNamespace).toBe("default");
|
|
30
30
|
expect(config.stigmerBackendEndpoint).toBe("http://localhost:7234");
|
|
31
|
-
expect(config.
|
|
31
|
+
expect(config.stigmerTokenRef.current).toBeNull();
|
|
32
32
|
expect(config.cursorApiKey).toBe("test-key");
|
|
33
33
|
expect(config.proxyEndpoint).toBeNull();
|
|
34
34
|
expect(config.maxConcurrentActivities).toBe(5);
|
|
@@ -85,7 +85,7 @@ describe("loadConfig", () => {
|
|
|
85
85
|
|
|
86
86
|
const config = loadConfig();
|
|
87
87
|
expect(config.temporalAddress).toBe("");
|
|
88
|
-
expect(config.
|
|
88
|
+
expect(config.stigmerTokenRef.current).toBe("token");
|
|
89
89
|
});
|
|
90
90
|
|
|
91
91
|
it("requires STIGMER_TOKEN in cloud mode", () => {
|
|
@@ -102,18 +102,19 @@ describe("loadConfig", () => {
|
|
|
102
102
|
const config = loadConfig();
|
|
103
103
|
|
|
104
104
|
expect(config.proxyEndpoint).toBe("https://proxy.example.com");
|
|
105
|
-
expect(config.
|
|
105
|
+
expect(config.stigmerTokenRef.current).toBe("token");
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
-
it("
|
|
108
|
+
it("never copies STIGMER_TOKEN into cursorApiKey in proxy mode: the placeholder the option mappers use, the credential only in the ref", () => {
|
|
109
109
|
process.env.STIGMER_PROXY_ENDPOINT = "https://api.stigmer.ai";
|
|
110
110
|
process.env.STIGMER_TOKEN = "eyJhbGciOiJSUzI1NiJ9.test-jwt-token";
|
|
111
111
|
const config = loadConfig();
|
|
112
112
|
|
|
113
|
-
expect(config.cursorApiKey).toBe("
|
|
113
|
+
expect(config.cursorApiKey).toBe("proxy-managed");
|
|
114
|
+
expect(config.stigmerTokenRef.current).toBe("eyJhbGciOiJSUzI1NiJ9.test-jwt-token");
|
|
114
115
|
});
|
|
115
116
|
|
|
116
|
-
it("
|
|
117
|
+
it("keeps an explicit CURSOR_API_KEY in proxy mode", () => {
|
|
117
118
|
process.env.STIGMER_PROXY_ENDPOINT = "https://api.stigmer.ai";
|
|
118
119
|
process.env.STIGMER_TOKEN = "stigmer-jwt";
|
|
119
120
|
process.env.CURSOR_API_KEY = "explicit-cursor-key";
|
|
@@ -122,16 +123,6 @@ describe("loadConfig", () => {
|
|
|
122
123
|
expect(config.cursorApiKey).toBe("explicit-cursor-key");
|
|
123
124
|
});
|
|
124
125
|
|
|
125
|
-
it("falls back to proxy-managed when no token in proxy mode", () => {
|
|
126
|
-
process.env.MODE = "local";
|
|
127
|
-
process.env.STIGMER_PROXY_ENDPOINT = "http://localhost:9090";
|
|
128
|
-
process.env.STIGMER_TOKEN = "token";
|
|
129
|
-
const config = loadConfig();
|
|
130
|
-
|
|
131
|
-
// With STIGMER_TOKEN present, cursorApiKey should be the token
|
|
132
|
-
expect(config.cursorApiKey).toBe("token");
|
|
133
|
-
});
|
|
134
|
-
|
|
135
126
|
it("normalizes bare host:port to http://", () => {
|
|
136
127
|
process.env.STIGMER_BACKEND_ENDPOINT = "backend:7234";
|
|
137
128
|
process.env.CURSOR_API_KEY = "test-key";
|
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
2
|
import { mapManagerOptionsToConfig } from "../runner-manager.js";
|
|
3
3
|
import type { RunnerManagerOptions } from "../runner-manager.js";
|
|
4
|
+
import type { TokenRef } from "../config.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* The manager maps its options into a runner {@link Config}. The critical
|
|
7
8
|
* invariant these tests lock is that execution location (`mode`) is decoupled
|
|
8
9
|
* from credential transport (`proxyEndpoint`): the desktop runner executes
|
|
9
10
|
* LOCALLY while still routing Cursor traffic through the proxy. Re-coupling the
|
|
10
|
-
* two previously broke local-path workspace sessions.
|
|
11
|
+
* two previously broke local-path workspace sessions. The second invariant:
|
|
12
|
+
* the config carries the root's credential REFS and no credential by value,
|
|
13
|
+
* so a rotation the root writes is what every reader sees (config.ts header).
|
|
11
14
|
*/
|
|
12
15
|
describe("mapManagerOptionsToConfig", () => {
|
|
13
16
|
const base: RunnerManagerOptions = {
|
|
14
17
|
temporalAddress: "localhost:7233",
|
|
15
18
|
stigmerEndpoint: "http://localhost:7234",
|
|
16
19
|
};
|
|
20
|
+
const refs = (): [TokenRef, TokenRef] => [{ current: null }, { current: null }];
|
|
17
21
|
|
|
18
22
|
it("defaults mode to local when executionMode is unset", () => {
|
|
19
|
-
const config = mapManagerOptionsToConfig(base);
|
|
23
|
+
const config = mapManagerOptionsToConfig(base, ...refs());
|
|
20
24
|
expect(config.mode).toBe("local");
|
|
21
25
|
});
|
|
22
26
|
|
|
@@ -25,7 +29,7 @@ describe("mapManagerOptionsToConfig", () => {
|
|
|
25
29
|
...base,
|
|
26
30
|
proxyEndpoint: "https://localhost:9090",
|
|
27
31
|
stigmerToken: "tok",
|
|
28
|
-
});
|
|
32
|
+
}, ...refs());
|
|
29
33
|
expect(config.mode).toBe("local");
|
|
30
34
|
expect(config.proxyEndpoint).toBe("https://localhost:9090");
|
|
31
35
|
// Proxy transport still engages independently of execution location.
|
|
@@ -48,7 +52,25 @@ describe("mapManagerOptionsToConfig", () => {
|
|
|
48
52
|
// After the mint the two refs diverge; the proxy credential follows the mint.
|
|
49
53
|
runnerTokenRef.current = "minted-runner-token";
|
|
50
54
|
expect(config.proxyTokenRef?.current).toBe("minted-runner-token");
|
|
51
|
-
expect(config.stigmerTokenRef
|
|
55
|
+
expect(config.stigmerTokenRef.current).toBe("control-plane-token");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("carries the control-plane credential as the root's ref and nowhere by value: a rotation the root writes is what a reader sees", () => {
|
|
59
|
+
const tokenRef: TokenRef = { current: "boot-credential" };
|
|
60
|
+
const runnerTokenRef: TokenRef = { current: "boot-credential" };
|
|
61
|
+
const config = mapManagerOptionsToConfig(
|
|
62
|
+
{ ...base, proxyEndpoint: "https://localhost:9090", stigmerToken: "boot-credential" },
|
|
63
|
+
tokenRef,
|
|
64
|
+
runnerTokenRef,
|
|
65
|
+
);
|
|
66
|
+
expect(config.stigmerTokenRef).toBe(tokenRef);
|
|
67
|
+
expect(config.cursorApiKey).toBe("proxy-managed");
|
|
68
|
+
|
|
69
|
+
// The pre-mint lockstep the coordinator keeps: one rotation writes both refs.
|
|
70
|
+
tokenRef.current = "session-credential";
|
|
71
|
+
runnerTokenRef.current = "session-credential";
|
|
72
|
+
expect(config.stigmerTokenRef.current).toBe("session-credential");
|
|
73
|
+
expect(JSON.stringify(config)).not.toContain("boot-credential");
|
|
52
74
|
});
|
|
53
75
|
|
|
54
76
|
it("uses cloud only when executionMode is explicitly cloud", () => {
|
|
@@ -57,7 +79,7 @@ describe("mapManagerOptionsToConfig", () => {
|
|
|
57
79
|
executionMode: "cloud",
|
|
58
80
|
proxyEndpoint: "https://proxy.example.com",
|
|
59
81
|
stigmerToken: "tok",
|
|
60
|
-
});
|
|
82
|
+
}, ...refs());
|
|
61
83
|
expect(config.mode).toBe("cloud");
|
|
62
84
|
});
|
|
63
85
|
|
|
@@ -67,7 +89,7 @@ describe("mapManagerOptionsToConfig", () => {
|
|
|
67
89
|
executionMode: "local",
|
|
68
90
|
proxyEndpoint: "https://proxy.example.com",
|
|
69
91
|
stigmerToken: "tok",
|
|
70
|
-
});
|
|
92
|
+
}, ...refs());
|
|
71
93
|
expect(config.mode).toBe("local");
|
|
72
94
|
});
|
|
73
95
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
2
|
import { mapOptionsToConfig } from "../runner.js";
|
|
3
3
|
import type { StigmerRunnerOptions, StigmerRunner } from "../runner.js";
|
|
4
|
+
import type { TokenRef } from "../config.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Unit tests for the createStigmerRunner factory.
|
|
@@ -107,6 +108,9 @@ describe("StigmerRunnerOptions type contract", () => {
|
|
|
107
108
|
* credential transport (`proxyEndpoint`): an explicit `executionMode` always
|
|
108
109
|
* wins, and only when it is unset does `mode` fall back to the proxy-derived
|
|
109
110
|
* default for backward compatibility. This mirrors `mapManagerOptionsToConfig`.
|
|
111
|
+
* The static root binds ONE ref as both the control-plane and the proxy
|
|
112
|
+
* credential (it mints no separate proxy token), and the config carries no
|
|
113
|
+
* credential by value (config.ts header).
|
|
110
114
|
*/
|
|
111
115
|
describe("mapOptionsToConfig — execution mode", () => {
|
|
112
116
|
const base: StigmerRunnerOptions = {
|
|
@@ -114,9 +118,10 @@ describe("mapOptionsToConfig — execution mode", () => {
|
|
|
114
118
|
temporalAddress: "localhost:7233",
|
|
115
119
|
stigmerEndpoint: "http://localhost:7234",
|
|
116
120
|
};
|
|
121
|
+
const refs = (): [TokenRef, TokenRef] => [{ current: null }, { current: null }];
|
|
117
122
|
|
|
118
123
|
it("defaults mode to local with no proxy and no executionMode", () => {
|
|
119
|
-
const config = mapOptionsToConfig(base);
|
|
124
|
+
const config = mapOptionsToConfig(base, ...refs());
|
|
120
125
|
expect(config.mode).toBe("local");
|
|
121
126
|
});
|
|
122
127
|
|
|
@@ -125,7 +130,7 @@ describe("mapOptionsToConfig — execution mode", () => {
|
|
|
125
130
|
...base,
|
|
126
131
|
proxyEndpoint: "https://proxy.example.com",
|
|
127
132
|
stigmerToken: "tok",
|
|
128
|
-
});
|
|
133
|
+
}, ...refs());
|
|
129
134
|
expect(config.mode).toBe("cloud");
|
|
130
135
|
// Proxy transport still engages independently of execution location.
|
|
131
136
|
expect(config.proxyEndpoint).toBe("https://proxy.example.com");
|
|
@@ -139,7 +144,7 @@ describe("mapOptionsToConfig — execution mode", () => {
|
|
|
139
144
|
executionMode: "local",
|
|
140
145
|
proxyEndpoint: "https://proxy.example.com",
|
|
141
146
|
stigmerToken: "tok",
|
|
142
|
-
});
|
|
147
|
+
}, ...refs());
|
|
143
148
|
expect(config.mode).toBe("local");
|
|
144
149
|
// Transport still routes through the proxy despite local execution.
|
|
145
150
|
expect(config.proxyEndpoint).toBe("https://proxy.example.com");
|
|
@@ -147,9 +152,28 @@ describe("mapOptionsToConfig — execution mode", () => {
|
|
|
147
152
|
});
|
|
148
153
|
|
|
149
154
|
it("honors explicit cloud executionMode without a proxy", () => {
|
|
150
|
-
const config = mapOptionsToConfig({ ...base, executionMode: "cloud" });
|
|
155
|
+
const config = mapOptionsToConfig({ ...base, executionMode: "cloud" }, ...refs());
|
|
151
156
|
expect(config.mode).toBe("cloud");
|
|
152
157
|
});
|
|
158
|
+
|
|
159
|
+
it("binds the one ref as both the control-plane and the proxy credential, and carries no credential by value", () => {
|
|
160
|
+
const tokenRef: TokenRef = { current: "boot-credential" };
|
|
161
|
+
const runnerTokenRef: TokenRef = { current: null };
|
|
162
|
+
const config = mapOptionsToConfig(
|
|
163
|
+
{ ...base, proxyEndpoint: "https://proxy.example.com", stigmerToken: "boot-credential" },
|
|
164
|
+
tokenRef,
|
|
165
|
+
runnerTokenRef,
|
|
166
|
+
);
|
|
167
|
+
expect(config.stigmerTokenRef).toBe(tokenRef);
|
|
168
|
+
expect(config.proxyTokenRef).toBe(tokenRef);
|
|
169
|
+
expect(config.stigmerRunnerTokenRef).toBe(runnerTokenRef);
|
|
170
|
+
expect(config.cursorApiKey).toBe("proxy-managed");
|
|
171
|
+
|
|
172
|
+
tokenRef.current = "renewed-credential";
|
|
173
|
+
expect(config.stigmerTokenRef.current).toBe("renewed-credential");
|
|
174
|
+
expect(config.proxyTokenRef?.current).toBe("renewed-credential");
|
|
175
|
+
expect(JSON.stringify(config)).not.toContain("boot-credential");
|
|
176
|
+
});
|
|
153
177
|
});
|
|
154
178
|
|
|
155
179
|
describe("StigmerRunner type contract", () => {
|
|
@@ -61,18 +61,14 @@ vi.mock("../../client/stigmer-client.js", () => ({
|
|
|
61
61
|
})),
|
|
62
62
|
}));
|
|
63
63
|
|
|
64
|
-
vi.mock("../../config.js", () => ({
|
|
65
|
-
loadConfig: () => ({
|
|
66
|
-
stigmerBackendEndpoint: "http://localhost:7234",
|
|
67
|
-
stigmerToken: "test-token",
|
|
68
|
-
}),
|
|
69
|
-
}));
|
|
70
|
-
|
|
71
64
|
vi.mock("../../shared/heartbeat.js", () => ({
|
|
72
65
|
startHeartbeat: () => ({ stop: vi.fn() }),
|
|
73
66
|
}));
|
|
74
67
|
|
|
75
68
|
import { callAgentAction } from "../call-agent.js";
|
|
69
|
+
import { testConfig } from "../../__test-utils__/config-fixture.js";
|
|
70
|
+
|
|
71
|
+
const appConfig = testConfig({ stigmerTokenRef: { current: "test-token" } });
|
|
76
72
|
|
|
77
73
|
describe("CallAgent server contract compliance", () => {
|
|
78
74
|
beforeEach(() => {
|
|
@@ -93,6 +89,7 @@ describe("CallAgent server contract compliance", () => {
|
|
|
93
89
|
{ agent: "notification-analyst", message: "Analyze cohort data", ...config },
|
|
94
90
|
{ __stigmer_org_id: "tt-demo", ...env },
|
|
95
91
|
"wfl_parent_123",
|
|
92
|
+
appConfig,
|
|
96
93
|
);
|
|
97
94
|
} catch (e: any) {
|
|
98
95
|
if (e.name !== "CompleteAsyncError") throw e;
|
|
@@ -27,10 +27,7 @@ vi.mock("../../client/stigmer-client.js", () => ({
|
|
|
27
27
|
})),
|
|
28
28
|
}));
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
loadConfig: () => ({ stigmerBackendEndpoint: "http://localhost:7234", stigmerToken: "t" }),
|
|
32
|
-
}));
|
|
33
|
-
|
|
30
|
+
import { StigmerClient } from "../../client/stigmer-client.js";
|
|
34
31
|
import {
|
|
35
32
|
updateWorkflowFileReviewStatus,
|
|
36
33
|
getAwaitingFileReviewChangeSetIds,
|
|
@@ -38,6 +35,10 @@ import {
|
|
|
38
35
|
clearWorkflowApprovalStatus,
|
|
39
36
|
} from "../call-agent-status.js";
|
|
40
37
|
|
|
38
|
+
// The client the factory would build from the runner's Config; the mocked
|
|
39
|
+
// constructor above is what it resolves to.
|
|
40
|
+
const client = new StigmerClient({ endpoint: "http://localhost:7234" });
|
|
41
|
+
|
|
41
42
|
describe("call-agent-status file-review activities", () => {
|
|
42
43
|
beforeEach(() => {
|
|
43
44
|
capturedUpdates = [];
|
|
@@ -46,7 +47,7 @@ describe("call-agent-status file-review activities", () => {
|
|
|
46
47
|
|
|
47
48
|
describe("updateWorkflowFileReviewStatus", () => {
|
|
48
49
|
it("writes a single per-child reference scoped to the child", async () => {
|
|
49
|
-
await updateWorkflowFileReviewStatus("wfx_1", "aex_child", ["fcs_1", "fcs_2"]);
|
|
50
|
+
await updateWorkflowFileReviewStatus(client, "wfx_1", "aex_child", ["fcs_1", "fcs_2"]);
|
|
50
51
|
|
|
51
52
|
expect(capturedUpdates).toHaveLength(1);
|
|
52
53
|
const { executionId, status, options } = capturedUpdates[0];
|
|
@@ -59,7 +60,7 @@ describe("call-agent-status file-review activities", () => {
|
|
|
59
60
|
});
|
|
60
61
|
|
|
61
62
|
it("empty changeSetIds writes an empty list (scoped clear for the child)", async () => {
|
|
62
|
-
await updateWorkflowFileReviewStatus("wfx_1", "aex_child", []);
|
|
63
|
+
await updateWorkflowFileReviewStatus(client, "wfx_1", "aex_child", []);
|
|
63
64
|
|
|
64
65
|
expect(capturedUpdates).toHaveLength(1);
|
|
65
66
|
const { status, options } = capturedUpdates[0];
|
|
@@ -69,8 +70,8 @@ describe("call-agent-status file-review activities", () => {
|
|
|
69
70
|
});
|
|
70
71
|
|
|
71
72
|
it("is a no-op when executionId or childExecutionId is missing", async () => {
|
|
72
|
-
await updateWorkflowFileReviewStatus("", "aex_child", ["fcs_1"]);
|
|
73
|
-
await updateWorkflowFileReviewStatus("wfx_1", "", ["fcs_1"]);
|
|
73
|
+
await updateWorkflowFileReviewStatus(client, "", "aex_child", ["fcs_1"]);
|
|
74
|
+
await updateWorkflowFileReviewStatus(client, "wfx_1", "", ["fcs_1"]);
|
|
74
75
|
expect(capturedUpdates).toHaveLength(0);
|
|
75
76
|
});
|
|
76
77
|
});
|
|
@@ -88,17 +89,17 @@ describe("call-agent-status file-review activities", () => {
|
|
|
88
89
|
},
|
|
89
90
|
};
|
|
90
91
|
|
|
91
|
-
const ids = await getAwaitingFileReviewChangeSetIds("aex_child");
|
|
92
|
+
const ids = await getAwaitingFileReviewChangeSetIds(client, "aex_child");
|
|
92
93
|
expect(ids).toEqual(["fcs_await1", "fcs_await2"]);
|
|
93
94
|
});
|
|
94
95
|
|
|
95
96
|
it("returns empty when the child has no file change sets", async () => {
|
|
96
97
|
mockGetExecutionResult = { status: {} };
|
|
97
|
-
expect(await getAwaitingFileReviewChangeSetIds("aex_child")).toEqual([]);
|
|
98
|
+
expect(await getAwaitingFileReviewChangeSetIds(client, "aex_child")).toEqual([]);
|
|
98
99
|
});
|
|
99
100
|
|
|
100
101
|
it("returns empty (non-fatal) on a missing child id", async () => {
|
|
101
|
-
expect(await getAwaitingFileReviewChangeSetIds("")).toEqual([]);
|
|
102
|
+
expect(await getAwaitingFileReviewChangeSetIds(client, "")).toEqual([]);
|
|
102
103
|
expect(capturedUpdates).toHaveLength(0);
|
|
103
104
|
});
|
|
104
105
|
});
|
|
@@ -114,7 +115,7 @@ describe("call-agent-status file-review activities", () => {
|
|
|
114
115
|
},
|
|
115
116
|
};
|
|
116
117
|
|
|
117
|
-
const surfaced = await updateWorkflowTaskApprovalStatus("wfx_1", "task_a", "aex_child");
|
|
118
|
+
const surfaced = await updateWorkflowTaskApprovalStatus(client, "wfx_1", "task_a", "aex_child");
|
|
118
119
|
|
|
119
120
|
expect(surfaced).toBe(true);
|
|
120
121
|
expect(capturedUpdates).toHaveLength(1);
|
|
@@ -131,19 +132,19 @@ describe("call-agent-status file-review activities", () => {
|
|
|
131
132
|
// approval card, and the orchestrator deliberately does not retry.
|
|
132
133
|
mockGetExecutionResult = { status: { pendingApprovals: [] } };
|
|
133
134
|
|
|
134
|
-
const surfaced = await updateWorkflowTaskApprovalStatus("wfx_1", "task_a", "aex_child");
|
|
135
|
+
const surfaced = await updateWorkflowTaskApprovalStatus(client, "wfx_1", "task_a", "aex_child");
|
|
135
136
|
|
|
136
137
|
expect(surfaced).toBe(false);
|
|
137
138
|
expect(capturedUpdates).toHaveLength(0);
|
|
138
139
|
});
|
|
139
140
|
|
|
140
141
|
it("updateWorkflowTaskApprovalStatus is a no-op without a child id", async () => {
|
|
141
|
-
expect(await updateWorkflowTaskApprovalStatus("wfx_1", "task_a", "")).toBe(false);
|
|
142
|
+
expect(await updateWorkflowTaskApprovalStatus(client, "wfx_1", "task_a", "")).toBe(false);
|
|
142
143
|
expect(capturedUpdates).toHaveLength(0);
|
|
143
144
|
});
|
|
144
145
|
|
|
145
146
|
it("clearWorkflowApprovalStatus scopes the clear to the given child", async () => {
|
|
146
|
-
await clearWorkflowApprovalStatus("wfx_1", "aex_child");
|
|
147
|
+
await clearWorkflowApprovalStatus(client, "wfx_1", "aex_child");
|
|
147
148
|
|
|
148
149
|
expect(capturedUpdates).toHaveLength(1);
|
|
149
150
|
const { status, options } = capturedUpdates[0];
|
|
@@ -153,7 +154,7 @@ describe("call-agent-status file-review activities", () => {
|
|
|
153
154
|
});
|
|
154
155
|
|
|
155
156
|
it("clearWorkflowApprovalStatus is a no-op without a child id", async () => {
|
|
156
|
-
await clearWorkflowApprovalStatus("wfx_1", "");
|
|
157
|
+
await clearWorkflowApprovalStatus(client, "wfx_1", "");
|
|
157
158
|
expect(capturedUpdates).toHaveLength(0);
|
|
158
159
|
});
|
|
159
160
|
});
|
|
@@ -30,18 +30,16 @@ vi.mock("../../client/stigmer-client.js", () => ({
|
|
|
30
30
|
})),
|
|
31
31
|
}));
|
|
32
32
|
|
|
33
|
-
vi.mock("../../config.js", () => ({
|
|
34
|
-
loadConfig: () => ({
|
|
35
|
-
stigmerBackendEndpoint: "http://localhost:7234",
|
|
36
|
-
stigmerToken: "test-token",
|
|
37
|
-
}),
|
|
38
|
-
}));
|
|
39
|
-
|
|
40
33
|
vi.mock("../../shared/heartbeat.js", () => ({
|
|
41
34
|
startHeartbeat: () => ({ stop: vi.fn() }),
|
|
42
35
|
}));
|
|
43
36
|
|
|
44
37
|
import { callAgentAction } from "../call-agent.js";
|
|
38
|
+
import { testConfig } from "../../__test-utils__/config-fixture.js";
|
|
39
|
+
|
|
40
|
+
// The runner's injected Config, as the factory passes it (the mocked client
|
|
41
|
+
// constructor above is what its refs resolve to).
|
|
42
|
+
const appConfig = testConfig({ stigmerTokenRef: { current: "test-token" } });
|
|
45
43
|
|
|
46
44
|
describe("callAgentAction", () => {
|
|
47
45
|
beforeEach(() => {
|
|
@@ -68,6 +66,7 @@ describe("callAgentAction", () => {
|
|
|
68
66
|
{ agent: "notification-analyst", message: "Analyze data" },
|
|
69
67
|
{ __stigmer_org_id: "tt-demo" },
|
|
70
68
|
"wfl_parent123",
|
|
69
|
+
appConfig,
|
|
71
70
|
),
|
|
72
71
|
).rejects.toThrow("CompleteAsyncError");
|
|
73
72
|
|
|
@@ -84,6 +83,7 @@ describe("callAgentAction", () => {
|
|
|
84
83
|
{ agent: "acme/my-agent", message: "Do something" },
|
|
85
84
|
{ __stigmer_org_id: "default-org" },
|
|
86
85
|
"wfl_parent456",
|
|
86
|
+
appConfig,
|
|
87
87
|
),
|
|
88
88
|
).rejects.toThrow("CompleteAsyncError");
|
|
89
89
|
|
|
@@ -100,6 +100,7 @@ describe("callAgentAction", () => {
|
|
|
100
100
|
{ agent: "explicit-org/my-agent", message: "Hello" },
|
|
101
101
|
{ __stigmer_org_id: "workflow-org" },
|
|
102
102
|
"wfl_parent789",
|
|
103
|
+
appConfig,
|
|
103
104
|
),
|
|
104
105
|
).rejects.toThrow("CompleteAsyncError");
|
|
105
106
|
|
|
@@ -122,6 +123,7 @@ describe("callAgentAction", () => {
|
|
|
122
123
|
{ agent: "my-agent", message: "Hello" },
|
|
123
124
|
{},
|
|
124
125
|
"wfl_parent",
|
|
126
|
+
appConfig,
|
|
125
127
|
),
|
|
126
128
|
).rejects.toThrow("call:agent requires an organization context");
|
|
127
129
|
});
|
|
@@ -132,6 +134,7 @@ describe("callAgentAction", () => {
|
|
|
132
134
|
{ agent: "my-agent", message: "Hello" },
|
|
133
135
|
{ __stigmer_org_id: "env-org" },
|
|
134
136
|
"wfl_parent",
|
|
137
|
+
appConfig,
|
|
135
138
|
),
|
|
136
139
|
).rejects.toThrow("CompleteAsyncError");
|
|
137
140
|
|
|
@@ -152,6 +155,7 @@ describe("callAgentAction", () => {
|
|
|
152
155
|
{ agent: "ghost-agent", message: "Hello" },
|
|
153
156
|
{ __stigmer_org_id: "test-org" },
|
|
154
157
|
"wfl_parent",
|
|
158
|
+
appConfig,
|
|
155
159
|
),
|
|
156
160
|
).rejects.toThrow("resolved but has no metadata.id");
|
|
157
161
|
});
|
|
@@ -164,6 +168,7 @@ describe("callAgentAction", () => {
|
|
|
164
168
|
{ agent: "", message: "Hello" },
|
|
165
169
|
{ __stigmer_org_id: "test-org" },
|
|
166
170
|
"wfl_parent",
|
|
171
|
+
appConfig,
|
|
167
172
|
),
|
|
168
173
|
).rejects.toThrow("'agent' resolved to empty");
|
|
169
174
|
});
|
|
@@ -174,6 +179,7 @@ describe("callAgentAction", () => {
|
|
|
174
179
|
{ agent: "my-agent", message: "" },
|
|
175
180
|
{ __stigmer_org_id: "test-org" },
|
|
176
181
|
"wfl_parent",
|
|
182
|
+
appConfig,
|
|
177
183
|
),
|
|
178
184
|
).rejects.toThrow("'message' resolved to empty");
|
|
179
185
|
});
|
|
@@ -186,6 +192,7 @@ describe("callAgentAction", () => {
|
|
|
186
192
|
{ agent: "my-agent", message: "Hello", harness: "CURSOR" },
|
|
187
193
|
{ __stigmer_org_id: "test-org" },
|
|
188
194
|
"wfl_parent",
|
|
195
|
+
appConfig,
|
|
189
196
|
),
|
|
190
197
|
).rejects.toThrow("CompleteAsyncError");
|
|
191
198
|
|
|
@@ -204,6 +211,7 @@ describe("callAgentAction", () => {
|
|
|
204
211
|
{ agent: "my-agent", message: "Hello" },
|
|
205
212
|
{ __stigmer_org_id: "test-org" },
|
|
206
213
|
"wfl_parent",
|
|
214
|
+
appConfig,
|
|
207
215
|
),
|
|
208
216
|
).rejects.toThrow("CompleteAsyncError");
|
|
209
217
|
|
|
@@ -218,6 +226,7 @@ describe("callAgentAction", () => {
|
|
|
218
226
|
{ agent: "my-agent", message: "Review this" },
|
|
219
227
|
{ __stigmer_org_id: "test-org" },
|
|
220
228
|
"wfl_parent_id",
|
|
229
|
+
appConfig,
|
|
221
230
|
),
|
|
222
231
|
).rejects.toThrow("CompleteAsyncError");
|
|
223
232
|
|
|
@@ -243,6 +252,7 @@ describe("callAgentAction", () => {
|
|
|
243
252
|
__stigmer_activity_task_queue: "wfexec:wex_abc123",
|
|
244
253
|
},
|
|
245
254
|
"wfl_parent",
|
|
255
|
+
appConfig,
|
|
246
256
|
),
|
|
247
257
|
).rejects.toThrow("CompleteAsyncError");
|
|
248
258
|
|
|
@@ -259,6 +269,7 @@ describe("callAgentAction", () => {
|
|
|
259
269
|
__stigmer_activity_task_queue: "stigmer_runner",
|
|
260
270
|
},
|
|
261
271
|
"wfl_parent",
|
|
272
|
+
appConfig,
|
|
262
273
|
),
|
|
263
274
|
).rejects.toThrow("CompleteAsyncError");
|
|
264
275
|
|
|
@@ -282,6 +293,7 @@ describe("callAgentAction", () => {
|
|
|
282
293
|
},
|
|
283
294
|
{ __stigmer_org_id: "test-org" },
|
|
284
295
|
"wfl_parent",
|
|
296
|
+
appConfig,
|
|
285
297
|
),
|
|
286
298
|
).rejects.toThrow("CompleteAsyncError");
|
|
287
299
|
|
|
@@ -302,6 +314,7 @@ describe("callAgentAction", () => {
|
|
|
302
314
|
},
|
|
303
315
|
{ __stigmer_org_id: "test-org" },
|
|
304
316
|
"wfl_parent",
|
|
317
|
+
appConfig,
|
|
305
318
|
),
|
|
306
319
|
).rejects.toThrow("CompleteAsyncError");
|
|
307
320
|
|
|
@@ -323,6 +336,7 @@ describe("callAgentAction", () => {
|
|
|
323
336
|
},
|
|
324
337
|
{ __stigmer_org_id: "test-org" },
|
|
325
338
|
"wfl_parent",
|
|
339
|
+
appConfig,
|
|
326
340
|
),
|
|
327
341
|
).rejects.toThrow(
|
|
328
342
|
"call:agent run_config.service_tier 'SERVICE_TIER_TURBO' has no proto mapping",
|
|
@@ -335,6 +349,7 @@ describe("callAgentAction", () => {
|
|
|
335
349
|
{ agent: "my-agent", message: "Hello" },
|
|
336
350
|
{ __stigmer_org_id: "test-org" },
|
|
337
351
|
"wfl_parent",
|
|
352
|
+
appConfig,
|
|
338
353
|
),
|
|
339
354
|
).rejects.toThrow("CompleteAsyncError");
|
|
340
355
|
|
|
@@ -352,6 +367,7 @@ describe("callAgentAction", () => {
|
|
|
352
367
|
},
|
|
353
368
|
{ __stigmer_org_id: "test-org" },
|
|
354
369
|
"wfl_parent",
|
|
370
|
+
appConfig,
|
|
355
371
|
),
|
|
356
372
|
).rejects.toThrow("CompleteAsyncError");
|
|
357
373
|
|
|
@@ -379,6 +395,7 @@ describe("callAgentAction", () => {
|
|
|
379
395
|
},
|
|
380
396
|
{ __stigmer_org_id: "test-org" },
|
|
381
397
|
"wfl_parent",
|
|
398
|
+
appConfig,
|
|
382
399
|
),
|
|
383
400
|
).rejects.toThrow("CompleteAsyncError");
|
|
384
401
|
|
|
@@ -398,6 +415,7 @@ describe("callAgentAction", () => {
|
|
|
398
415
|
{ agent: "my-agent", message: "Hello" },
|
|
399
416
|
{ __stigmer_org_id: "test-org" },
|
|
400
417
|
"wfl_parent",
|
|
418
|
+
appConfig,
|
|
401
419
|
),
|
|
402
420
|
).rejects.toThrow("CompleteAsyncError");
|
|
403
421
|
|
|
@@ -416,6 +434,7 @@ describe("callAgentAction", () => {
|
|
|
416
434
|
} as any,
|
|
417
435
|
{ __stigmer_org_id: "test-org" },
|
|
418
436
|
"wfl_parent",
|
|
437
|
+
appConfig,
|
|
419
438
|
),
|
|
420
439
|
).rejects.toThrow("CompleteAsyncError");
|
|
421
440
|
|
|
@@ -430,6 +449,7 @@ describe("callAgentAction", () => {
|
|
|
430
449
|
{ agent: "my-agent", message: "Hello" },
|
|
431
450
|
{ __stigmer_org_id: "test-org" },
|
|
432
451
|
"wfl_parent",
|
|
452
|
+
appConfig,
|
|
433
453
|
),
|
|
434
454
|
).rejects.toThrow("CompleteAsyncError");
|
|
435
455
|
|
|
@@ -463,6 +483,7 @@ describe("callAgentAction", () => {
|
|
|
463
483
|
},
|
|
464
484
|
{ __stigmer_org_id: "test-org" },
|
|
465
485
|
"wfl_parent",
|
|
486
|
+
appConfig,
|
|
466
487
|
),
|
|
467
488
|
).rejects.toThrow("CompleteAsyncError");
|
|
468
489
|
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
2
|
import { createCallFunctionActivities } from "../call-function.js";
|
|
3
|
+
import { testConfig } from "../../__test-utils__/config-fixture.js";
|
|
3
4
|
|
|
4
5
|
describe("createCallFunctionActivities", () => {
|
|
5
6
|
it("creates activities object with CallFunction method", () => {
|
|
6
|
-
const activities = createCallFunctionActivities();
|
|
7
|
+
const activities = createCallFunctionActivities(testConfig());
|
|
7
8
|
expect(typeof activities.CallFunction).toBe("function");
|
|
8
9
|
});
|
|
9
10
|
|
|
10
11
|
it("throws non-retryable error for unknown call type", async () => {
|
|
11
|
-
const activities = createCallFunctionActivities();
|
|
12
|
+
const activities = createCallFunctionActivities(testConfig());
|
|
12
13
|
|
|
13
14
|
try {
|
|
14
15
|
await activities.CallFunction("unknown_function", {}, {}, "exec-1");
|
|
@@ -20,7 +21,7 @@ describe("createCallFunctionActivities", () => {
|
|
|
20
21
|
});
|
|
21
22
|
|
|
22
23
|
it("throws non-retryable error for call:agent (not yet implemented)", async () => {
|
|
23
|
-
const activities = createCallFunctionActivities();
|
|
24
|
+
const activities = createCallFunctionActivities(testConfig());
|
|
24
25
|
|
|
25
26
|
try {
|
|
26
27
|
await activities.CallFunction("agent", { agent: "code-reviewer" }, {}, "exec-1");
|
|
@@ -31,7 +32,7 @@ describe("createCallFunctionActivities", () => {
|
|
|
31
32
|
});
|
|
32
33
|
|
|
33
34
|
it("resolves runtime placeholders in config before dispatching", async () => {
|
|
34
|
-
const activities = createCallFunctionActivities();
|
|
35
|
+
const activities = createCallFunctionActivities(testConfig());
|
|
35
36
|
|
|
36
37
|
try {
|
|
37
38
|
await activities.CallFunction(
|
|
@@ -726,7 +726,7 @@ function makeConfig() {
|
|
|
726
726
|
temporalNamespace: "default",
|
|
727
727
|
stigmerBackendEndpoint: "http://localhost:7234",
|
|
728
728
|
mcpBridgeEndpoint: null,
|
|
729
|
-
|
|
729
|
+
stigmerTokenRef: { current: "test-token" },
|
|
730
730
|
cursorApiKey: "",
|
|
731
731
|
workspaceRootDir: "/tmp/test",
|
|
732
732
|
mode: "local" as const,
|
|
@@ -902,7 +902,7 @@ function makeConfig() {
|
|
|
902
902
|
temporalNamespace: "default",
|
|
903
903
|
stigmerBackendEndpoint: "http://localhost:7234",
|
|
904
904
|
mcpBridgeEndpoint: null,
|
|
905
|
-
|
|
905
|
+
stigmerTokenRef: { current: "test-token" },
|
|
906
906
|
cursorApiKey: "",
|
|
907
907
|
workspaceRootDir: "/tmp/test",
|
|
908
908
|
mode: "local" as const,
|
|
@@ -84,7 +84,7 @@ function activityUnderTest() {
|
|
|
84
84
|
bindHermeticClient(client);
|
|
85
85
|
const config = {
|
|
86
86
|
stigmerBackendEndpoint: "http://localhost:0",
|
|
87
|
-
|
|
87
|
+
stigmerTokenRef: { current: null },
|
|
88
88
|
proxyEndpoint: null,
|
|
89
89
|
primaryModel: "claude-sonnet-4.5",
|
|
90
90
|
} as unknown as Config;
|