@stigmer/runner 3.22.0 → 3.22.1
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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
2
2
|
import { toProtoEvent, initSequenceFromEventLog, emitWorkflowEvents, loadRecoveryContext } from "../workflow-event-activities.js";
|
|
3
|
+
import { StigmerClient } from "../../client/stigmer-client.js";
|
|
3
4
|
import { WorkflowEventType } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/event_pb";
|
|
4
5
|
import { WorkflowTaskKind } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/enum_pb";
|
|
5
6
|
import { WorkflowTaskStatus } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/enum_pb";
|
|
@@ -19,12 +20,9 @@ vi.mock("../../client/stigmer-client.js", () => ({
|
|
|
19
20
|
})),
|
|
20
21
|
}));
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
stigmerToken: "test-token",
|
|
26
|
-
}),
|
|
27
|
-
}));
|
|
23
|
+
// The client the factory would build from the runner's Config; the mocked
|
|
24
|
+
// constructor above is what it resolves to.
|
|
25
|
+
const client = new StigmerClient({ endpoint: "http://localhost:7234" });
|
|
28
26
|
|
|
29
27
|
const NOW = "2026-05-22T00:00:00.000Z";
|
|
30
28
|
|
|
@@ -34,7 +32,7 @@ describe("initSequenceFromEventLog", () => {
|
|
|
34
32
|
});
|
|
35
33
|
|
|
36
34
|
it("returns 0 and resets the legacy counter when executionId is empty", async () => {
|
|
37
|
-
const highWaterMark = await initSequenceFromEventLog("");
|
|
35
|
+
const highWaterMark = await initSequenceFromEventLog(client, "");
|
|
38
36
|
expect(highWaterMark).toBe(0);
|
|
39
37
|
|
|
40
38
|
const evt = toProtoEvent({
|
|
@@ -51,7 +49,7 @@ describe("initSequenceFromEventLog", () => {
|
|
|
51
49
|
it("returns 0 when server has no events", async () => {
|
|
52
50
|
mockGetEventLogHighWaterMark.mockResolvedValue(BigInt(0));
|
|
53
51
|
|
|
54
|
-
const highWaterMark = await initSequenceFromEventLog("wfx_test-1");
|
|
52
|
+
const highWaterMark = await initSequenceFromEventLog(client, "wfx_test-1");
|
|
55
53
|
expect(highWaterMark).toBe(0);
|
|
56
54
|
|
|
57
55
|
const evt = toProtoEvent({
|
|
@@ -67,7 +65,7 @@ describe("initSequenceFromEventLog", () => {
|
|
|
67
65
|
it("returns the high-water mark as a plain number (Temporal payload converter cannot carry BigInt)", async () => {
|
|
68
66
|
mockGetEventLogHighWaterMark.mockResolvedValue(BigInt(42));
|
|
69
67
|
|
|
70
|
-
const highWaterMark = await initSequenceFromEventLog("wfx_recovery-1");
|
|
68
|
+
const highWaterMark = await initSequenceFromEventLog(client, "wfx_recovery-1");
|
|
71
69
|
expect(highWaterMark).toBe(42);
|
|
72
70
|
expect(typeof highWaterMark).toBe("number");
|
|
73
71
|
});
|
|
@@ -75,7 +73,7 @@ describe("initSequenceFromEventLog", () => {
|
|
|
75
73
|
it("seeds the legacy counter so pre-patch replays continue from N+1", async () => {
|
|
76
74
|
mockGetEventLogHighWaterMark.mockResolvedValue(BigInt(42));
|
|
77
75
|
|
|
78
|
-
await initSequenceFromEventLog("wfx_recovery-1");
|
|
76
|
+
await initSequenceFromEventLog(client, "wfx_recovery-1");
|
|
79
77
|
|
|
80
78
|
const e1 = toProtoEvent({
|
|
81
79
|
type: "task_started",
|
|
@@ -101,7 +99,7 @@ describe("initSequenceFromEventLog", () => {
|
|
|
101
99
|
it("handles large sequence numbers", async () => {
|
|
102
100
|
mockGetEventLogHighWaterMark.mockResolvedValue(BigInt(999999));
|
|
103
101
|
|
|
104
|
-
const highWaterMark = await initSequenceFromEventLog("wfx_large-seq");
|
|
102
|
+
const highWaterMark = await initSequenceFromEventLog(client, "wfx_large-seq");
|
|
105
103
|
expect(highWaterMark).toBe(999999);
|
|
106
104
|
|
|
107
105
|
const evt = toProtoEvent({
|
|
@@ -117,13 +115,13 @@ describe("initSequenceFromEventLog", () => {
|
|
|
117
115
|
it("propagates server errors", async () => {
|
|
118
116
|
mockGetEventLogHighWaterMark.mockRejectedValue(new Error("server unavailable"));
|
|
119
117
|
|
|
120
|
-
await expect(initSequenceFromEventLog("wfx_error-1")).rejects.toThrow("server unavailable");
|
|
118
|
+
await expect(initSequenceFromEventLog(client, "wfx_error-1")).rejects.toThrow("server unavailable");
|
|
121
119
|
});
|
|
122
120
|
});
|
|
123
121
|
|
|
124
122
|
describe("toProtoEvent", () => {
|
|
125
123
|
beforeEach(async () => {
|
|
126
|
-
await initSequenceFromEventLog("");
|
|
124
|
+
await initSequenceFromEventLog(client, "");
|
|
127
125
|
});
|
|
128
126
|
|
|
129
127
|
describe("sequence numbering", () => {
|
|
@@ -199,7 +197,7 @@ describe("toProtoEvent", () => {
|
|
|
199
197
|
attemptNumber: 1,
|
|
200
198
|
});
|
|
201
199
|
|
|
202
|
-
await initSequenceFromEventLog("");
|
|
200
|
+
await initSequenceFromEventLog(client, "");
|
|
203
201
|
|
|
204
202
|
const evt = toProtoEvent({
|
|
205
203
|
type: "task_started",
|
|
@@ -719,7 +717,7 @@ describe("emitWorkflowEvents", () => {
|
|
|
719
717
|
});
|
|
720
718
|
|
|
721
719
|
it("returns silently for empty events array", async () => {
|
|
722
|
-
await expect(emitWorkflowEvents("exec-1", [])).resolves.toBeUndefined();
|
|
720
|
+
await expect(emitWorkflowEvents(client, "exec-1", [])).resolves.toBeUndefined();
|
|
723
721
|
expect(mockUpdateStatus).not.toHaveBeenCalled();
|
|
724
722
|
});
|
|
725
723
|
|
|
@@ -731,14 +729,14 @@ describe("emitWorkflowEvents", () => {
|
|
|
731
729
|
taskKind: "set",
|
|
732
730
|
attemptNumber: 1,
|
|
733
731
|
}];
|
|
734
|
-
await expect(emitWorkflowEvents("", events)).resolves.toBeUndefined();
|
|
732
|
+
await expect(emitWorkflowEvents(client, "", events)).resolves.toBeUndefined();
|
|
735
733
|
expect(mockUpdateStatus).not.toHaveBeenCalled();
|
|
736
734
|
});
|
|
737
735
|
|
|
738
736
|
it("sends workflow-assigned sequence numbers through to the RPC", async () => {
|
|
739
737
|
mockUpdateStatus.mockResolvedValue({});
|
|
740
738
|
|
|
741
|
-
await emitWorkflowEvents("exec-1", [{
|
|
739
|
+
await emitWorkflowEvents(client, "exec-1", [{
|
|
742
740
|
type: "task_started",
|
|
743
741
|
taskName: "t",
|
|
744
742
|
occurredAt: NOW,
|
|
@@ -765,7 +763,7 @@ describe("emitWorkflowEvents", () => {
|
|
|
765
763
|
sequenceNumber: 1,
|
|
766
764
|
}];
|
|
767
765
|
|
|
768
|
-
await expect(emitWorkflowEvents("exec-1", events)).rejects.toThrow("server unavailable");
|
|
766
|
+
await expect(emitWorkflowEvents(client, "exec-1", events)).rejects.toThrow("server unavailable");
|
|
769
767
|
});
|
|
770
768
|
});
|
|
771
769
|
|
|
@@ -792,7 +790,7 @@ describe("loadRecoveryContext", () => {
|
|
|
792
790
|
},
|
|
793
791
|
});
|
|
794
792
|
|
|
795
|
-
const result = await loadRecoveryContext("wfx_test-1");
|
|
793
|
+
const result = await loadRecoveryContext(client, "wfx_test-1");
|
|
796
794
|
|
|
797
795
|
expect(result).toHaveLength(2);
|
|
798
796
|
expect(result[0]).toEqual({
|
|
@@ -810,7 +808,7 @@ describe("loadRecoveryContext", () => {
|
|
|
810
808
|
it("returns empty array when execution has no status", async () => {
|
|
811
809
|
mockGetWorkflowExecution.mockResolvedValue({});
|
|
812
810
|
|
|
813
|
-
const result = await loadRecoveryContext("wfx_empty-1");
|
|
811
|
+
const result = await loadRecoveryContext(client, "wfx_empty-1");
|
|
814
812
|
expect(result).toEqual([]);
|
|
815
813
|
});
|
|
816
814
|
|
|
@@ -819,7 +817,7 @@ describe("loadRecoveryContext", () => {
|
|
|
819
817
|
status: { tasks: [] },
|
|
820
818
|
});
|
|
821
819
|
|
|
822
|
-
const result = await loadRecoveryContext("wfx_no-tasks-1");
|
|
820
|
+
const result = await loadRecoveryContext(client, "wfx_no-tasks-1");
|
|
823
821
|
expect(result).toEqual([]);
|
|
824
822
|
});
|
|
825
823
|
|
|
@@ -836,7 +834,7 @@ describe("loadRecoveryContext", () => {
|
|
|
836
834
|
},
|
|
837
835
|
});
|
|
838
836
|
|
|
839
|
-
const result = await loadRecoveryContext("wfx_statuses-1");
|
|
837
|
+
const result = await loadRecoveryContext(client, "wfx_statuses-1");
|
|
840
838
|
|
|
841
839
|
expect(result[0].status).toBe("started");
|
|
842
840
|
expect(result[1].status).toBe("completed");
|
|
@@ -848,6 +846,6 @@ describe("loadRecoveryContext", () => {
|
|
|
848
846
|
it("propagates server errors", async () => {
|
|
849
847
|
mockGetWorkflowExecution.mockRejectedValue(new Error("server unavailable"));
|
|
850
848
|
|
|
851
|
-
await expect(loadRecoveryContext("wfx_error-1")).rejects.toThrow("server unavailable");
|
|
849
|
+
await expect(loadRecoveryContext(client, "wfx_error-1")).rejects.toThrow("server unavailable");
|
|
852
850
|
});
|
|
853
851
|
});
|
|
@@ -37,7 +37,6 @@ import { TimingRecorder, emitTimingLog } from "../shared/cold-start-timing.js";
|
|
|
37
37
|
export function createAttachSessionActivities(config: Config) {
|
|
38
38
|
const client = new StigmerClient({
|
|
39
39
|
endpoint: config.stigmerBackendEndpoint,
|
|
40
|
-
token: config.stigmerToken,
|
|
41
40
|
tokenRef: config.stigmerTokenRef,
|
|
42
41
|
runnerTokenRef: config.stigmerRunnerTokenRef,
|
|
43
42
|
});
|
|
@@ -10,22 +10,19 @@
|
|
|
10
10
|
* WorkflowExecution when a child agent enters a tool-approval gate.
|
|
11
11
|
* - ClearWorkflowApprovalStatus: clears pending_approvals when the
|
|
12
12
|
* child agent activity completes (approvals resolved).
|
|
13
|
+
*
|
|
14
|
+
* The client is the factory's, built once from the runner's injected
|
|
15
|
+
* `Config` so it reads the live credential ref per request (config.ts
|
|
16
|
+
* header); the functions take it as an argument, which is also what a test
|
|
17
|
+
* hands them.
|
|
13
18
|
*/
|
|
14
19
|
|
|
15
20
|
import { StigmerClient } from "../client/stigmer-client.js";
|
|
16
|
-
import {
|
|
21
|
+
import type { Config } from "../config.js";
|
|
17
22
|
import { create } from "@bufbuild/protobuf";
|
|
18
23
|
import { WorkflowExecutionStatusSchema, WorkflowPendingApprovalSchema, WorkflowPendingFileReviewSchema } from "@stigmer/protos/ai/stigmer/agentic/workflowexecution/v1/api_pb";
|
|
19
24
|
import { ToolCallStatus, FileChangeSetStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
20
25
|
|
|
21
|
-
function buildClient(): StigmerClient {
|
|
22
|
-
const config = loadConfig();
|
|
23
|
-
return new StigmerClient({
|
|
24
|
-
endpoint: config.stigmerBackendEndpoint,
|
|
25
|
-
token: config.stigmerToken,
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
|
|
29
26
|
/**
|
|
30
27
|
* Derives the child's approval gate onto the parent (DD-012: identity-only
|
|
31
28
|
* signal, derive-from-child). The signal carries only the child execution id;
|
|
@@ -40,13 +37,13 @@ function buildClient(): StigmerClient {
|
|
|
40
37
|
* retry: surfacing a resolved gate would show users a stale approval card.
|
|
41
38
|
*/
|
|
42
39
|
export async function updateWorkflowTaskApprovalStatus(
|
|
40
|
+
client: StigmerClient,
|
|
43
41
|
executionId: string,
|
|
44
42
|
_taskName: string,
|
|
45
43
|
childExecutionId: string,
|
|
46
44
|
): Promise<boolean> {
|
|
47
45
|
if (!executionId || !childExecutionId) return false;
|
|
48
46
|
|
|
49
|
-
const client = buildClient();
|
|
50
47
|
const child = await client.getExecution(childExecutionId);
|
|
51
48
|
const pendingApprovals = (child?.status?.pendingApprovals ?? []).map(
|
|
52
49
|
(approval) =>
|
|
@@ -74,12 +71,12 @@ export async function updateWorkflowTaskApprovalStatus(
|
|
|
74
71
|
}
|
|
75
72
|
|
|
76
73
|
export async function clearWorkflowApprovalStatus(
|
|
74
|
+
client: StigmerClient,
|
|
77
75
|
executionId: string,
|
|
78
76
|
childExecutionId: string,
|
|
79
77
|
): Promise<void> {
|
|
80
78
|
if (!executionId || !childExecutionId) return;
|
|
81
79
|
|
|
82
|
-
const client = buildClient();
|
|
83
80
|
const status = create(WorkflowExecutionStatusSchema, {
|
|
84
81
|
pendingApprovals: [],
|
|
85
82
|
});
|
|
@@ -101,13 +98,13 @@ export async function clearWorkflowApprovalStatus(
|
|
|
101
98
|
* child's entry (per-child merge, so parallel siblings are never disturbed).
|
|
102
99
|
*/
|
|
103
100
|
export async function updateWorkflowFileReviewStatus(
|
|
101
|
+
client: StigmerClient,
|
|
104
102
|
executionId: string,
|
|
105
103
|
childExecutionId: string,
|
|
106
104
|
changeSetIds: string[],
|
|
107
105
|
): Promise<void> {
|
|
108
106
|
if (!executionId || !childExecutionId) return;
|
|
109
107
|
|
|
110
|
-
const client = buildClient();
|
|
111
108
|
const pendingFileReviews =
|
|
112
109
|
changeSetIds.length > 0
|
|
113
110
|
? [
|
|
@@ -135,12 +132,12 @@ export async function updateWorkflowFileReviewStatus(
|
|
|
135
132
|
* (the same reason getAgentExecutionProgress reads status.messages).
|
|
136
133
|
*/
|
|
137
134
|
export async function getAwaitingFileReviewChangeSetIds(
|
|
135
|
+
client: StigmerClient,
|
|
138
136
|
childExecutionId: string,
|
|
139
137
|
): Promise<string[]> {
|
|
140
138
|
if (!childExecutionId) return [];
|
|
141
139
|
|
|
142
140
|
try {
|
|
143
|
-
const client = buildClient();
|
|
144
141
|
const execution = await client.getExecution(childExecutionId);
|
|
145
142
|
const changeSets = execution?.status?.fileChangeSets ?? [];
|
|
146
143
|
return changeSets
|
|
@@ -169,12 +166,12 @@ export interface AgentProgressSummary {
|
|
|
169
166
|
}
|
|
170
167
|
|
|
171
168
|
export async function getAgentExecutionProgress(
|
|
169
|
+
client: StigmerClient,
|
|
172
170
|
childExecutionId: string,
|
|
173
171
|
): Promise<AgentProgressSummary | null> {
|
|
174
172
|
if (!childExecutionId) return null;
|
|
175
173
|
|
|
176
174
|
try {
|
|
177
|
-
const client = buildClient();
|
|
178
175
|
const execution = await client.getExecution(childExecutionId);
|
|
179
176
|
const status = execution?.status;
|
|
180
177
|
if (!status) return null;
|
|
@@ -206,12 +203,23 @@ export async function getAgentExecutionProgress(
|
|
|
206
203
|
}
|
|
207
204
|
}
|
|
208
205
|
|
|
209
|
-
export function createCallAgentStatusActivities() {
|
|
206
|
+
export function createCallAgentStatusActivities(config: Config) {
|
|
207
|
+
const client = new StigmerClient({
|
|
208
|
+
endpoint: config.stigmerBackendEndpoint,
|
|
209
|
+
tokenRef: config.stigmerTokenRef,
|
|
210
|
+
runnerTokenRef: config.stigmerRunnerTokenRef,
|
|
211
|
+
});
|
|
212
|
+
|
|
210
213
|
return {
|
|
211
|
-
UpdateWorkflowTaskApprovalStatus:
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
214
|
+
UpdateWorkflowTaskApprovalStatus: (executionId: string, taskName: string, childExecutionId: string) =>
|
|
215
|
+
updateWorkflowTaskApprovalStatus(client, executionId, taskName, childExecutionId),
|
|
216
|
+
ClearWorkflowApprovalStatus: (executionId: string, childExecutionId: string) =>
|
|
217
|
+
clearWorkflowApprovalStatus(client, executionId, childExecutionId),
|
|
218
|
+
UpdateWorkflowFileReviewStatus: (executionId: string, childExecutionId: string, changeSetIds: string[]) =>
|
|
219
|
+
updateWorkflowFileReviewStatus(client, executionId, childExecutionId, changeSetIds),
|
|
220
|
+
GetAwaitingFileReviewChangeSetIds: (childExecutionId: string) =>
|
|
221
|
+
getAwaitingFileReviewChangeSetIds(client, childExecutionId),
|
|
222
|
+
GetAgentExecutionProgress: (childExecutionId: string) =>
|
|
223
|
+
getAgentExecutionProgress(client, childExecutionId),
|
|
216
224
|
};
|
|
217
225
|
}
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
import { randomUUID } from "node:crypto";
|
|
23
23
|
import { Context, CompleteAsyncError } from "@temporalio/activity";
|
|
24
24
|
import { StigmerClient } from "../client/stigmer-client.js";
|
|
25
|
-
import {
|
|
25
|
+
import type { Config } from "../config.js";
|
|
26
26
|
import { resolveObjectPlaceholders } from "../workflow-engine/resolve.js";
|
|
27
27
|
import type { AgentCallConfig } from "../workflow-engine/types.js";
|
|
28
28
|
import { startHeartbeat } from "../shared/heartbeat.js";
|
|
@@ -49,7 +49,7 @@ export async function callAgentAction(
|
|
|
49
49
|
config: AgentCallConfig,
|
|
50
50
|
runtimeEnv: Record<string, unknown>,
|
|
51
51
|
parentWorkflowId: string,
|
|
52
|
-
appConfig: Config
|
|
52
|
+
appConfig: Config,
|
|
53
53
|
): Promise<void> {
|
|
54
54
|
const taskToken = Context.current().info.taskToken;
|
|
55
55
|
|
|
@@ -84,16 +84,15 @@ export async function callAgentAction(
|
|
|
84
84
|
|
|
85
85
|
const client = new StigmerClient({
|
|
86
86
|
endpoint: appConfig.stigmerBackendEndpoint,
|
|
87
|
-
token: appConfig.stigmerToken,
|
|
88
87
|
tokenRef: appConfig.stigmerTokenRef,
|
|
89
88
|
// The child-execution create must authenticate as the RUNNER, not the
|
|
90
89
|
// user: it stamps the workflow lineage labels, which cloud's
|
|
91
90
|
// reserved-label guard and environment composer accept only from
|
|
92
91
|
// runner-class callers. The client's credential-selection table routes
|
|
93
92
|
// exactly that create to this credential; null (OSS/local, no mint)
|
|
94
|
-
// falls through to the control-plane token unchanged. The refs
|
|
95
|
-
//
|
|
96
|
-
//
|
|
93
|
+
// falls through to the control-plane token unchanged. The refs are the
|
|
94
|
+
// runner's injected Config's (config.ts header), which is why the factory
|
|
95
|
+
// passes it in.
|
|
97
96
|
runnerTokenRef: appConfig.stigmerRunnerTokenRef,
|
|
98
97
|
});
|
|
99
98
|
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { ApplicationFailure } from "@temporalio/activity";
|
|
20
|
+
import { StigmerClient } from "../client/stigmer-client.js";
|
|
21
|
+
import type { Config } from "../config.js";
|
|
20
22
|
import { callLlmAction, type LlmCallConfig } from "./call-llm.js";
|
|
21
23
|
import { callEvalAction, type EvalConfig } from "./call-eval.js";
|
|
22
24
|
import { emitEventAction, type EmitEventConfig } from "./emit-event.js";
|
|
@@ -30,6 +32,7 @@ export async function callFunctionAction(
|
|
|
30
32
|
config: Record<string, unknown>,
|
|
31
33
|
runtimeEnv: Record<string, unknown>,
|
|
32
34
|
executionId: string,
|
|
35
|
+
client: StigmerClient,
|
|
33
36
|
): Promise<unknown> {
|
|
34
37
|
const resolved = resolveObjectPlaceholders(config, runtimeEnv) as Record<string, unknown>;
|
|
35
38
|
|
|
@@ -39,7 +42,7 @@ export async function callFunctionAction(
|
|
|
39
42
|
case "eval":
|
|
40
43
|
return callEvalAction(resolved as unknown as EvalConfig, runtimeEnv, executionId);
|
|
41
44
|
case "emit_event":
|
|
42
|
-
return emitEventAction(resolved as unknown as EmitEventConfig, executionId, runtimeEnv);
|
|
45
|
+
return emitEventAction(resolved as unknown as EmitEventConfig, executionId, runtimeEnv, client);
|
|
43
46
|
case "notification":
|
|
44
47
|
return notificationAction(resolved as unknown as NotificationConfig, runtimeEnv);
|
|
45
48
|
case "transform":
|
|
@@ -59,7 +62,16 @@ export async function callFunctionAction(
|
|
|
59
62
|
}
|
|
60
63
|
}
|
|
61
64
|
|
|
62
|
-
export function createCallFunctionActivities() {
|
|
65
|
+
export function createCallFunctionActivities(runnerConfig: Config) {
|
|
66
|
+
// The signal-delivery client for emit_event, one per factory from the
|
|
67
|
+
// runner's injected Config: it reads the live credential ref per request
|
|
68
|
+
// (config.ts header), so a rotation reaches every signal it sends.
|
|
69
|
+
const client = new StigmerClient({
|
|
70
|
+
endpoint: runnerConfig.stigmerBackendEndpoint,
|
|
71
|
+
tokenRef: runnerConfig.stigmerTokenRef,
|
|
72
|
+
runnerTokenRef: runnerConfig.stigmerRunnerTokenRef,
|
|
73
|
+
});
|
|
74
|
+
|
|
63
75
|
return {
|
|
64
76
|
CallFunction: async (
|
|
65
77
|
call: string,
|
|
@@ -67,7 +79,7 @@ export function createCallFunctionActivities() {
|
|
|
67
79
|
runtimeEnv: Record<string, unknown>,
|
|
68
80
|
executionId: string,
|
|
69
81
|
): Promise<unknown> => {
|
|
70
|
-
return callFunctionAction(call, config, runtimeEnv, executionId);
|
|
82
|
+
return callFunctionAction(call, config, runtimeEnv, executionId, client);
|
|
71
83
|
},
|
|
72
84
|
};
|
|
73
85
|
}
|
|
@@ -420,7 +420,7 @@ export function createClassifyToolApprovalsActivities(config: Config) {
|
|
|
420
420
|
// serve /v1/proxy/model-registry; that fallback does not port here.)
|
|
421
421
|
return await classifyTools(input, {
|
|
422
422
|
proxyEndpoint: config.proxyEndpoint,
|
|
423
|
-
stigmerToken: config.
|
|
423
|
+
stigmerToken: config.stigmerTokenRef.current,
|
|
424
424
|
primaryModel: config.primaryModel,
|
|
425
425
|
});
|
|
426
426
|
} finally {
|
|
@@ -613,7 +613,6 @@ async function classifyHttpOAuthFailure(
|
|
|
613
613
|
export function createDiscoverMcpServerActivities(config: Config) {
|
|
614
614
|
const stigmerClient = new StigmerClient({
|
|
615
615
|
endpoint: config.stigmerBackendEndpoint,
|
|
616
|
-
token: config.stigmerToken,
|
|
617
616
|
tokenRef: config.stigmerTokenRef,
|
|
618
617
|
runnerTokenRef: config.stigmerRunnerTokenRef,
|
|
619
618
|
});
|
|
@@ -21,13 +21,16 @@
|
|
|
21
21
|
* holding a different key. The server re-produces the payload, so each
|
|
22
22
|
* side's codec passes it through.
|
|
23
23
|
*
|
|
24
|
+
* The signal client is the caller's (`call-function.ts` builds one from the
|
|
25
|
+
* runner's injected `Config`, so it reads the live credential ref per
|
|
26
|
+
* request; config.ts header). This module constructs no client of its own.
|
|
27
|
+
*
|
|
24
28
|
* CloudEvents spec: https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/spec.md
|
|
25
29
|
*/
|
|
26
30
|
|
|
27
31
|
import { randomUUID } from "node:crypto";
|
|
28
32
|
import type { JsonObject } from "@bufbuild/protobuf";
|
|
29
|
-
import { StigmerClient } from "../client/stigmer-client.js";
|
|
30
|
-
import { loadConfig } from "../config.js";
|
|
33
|
+
import type { StigmerClient } from "../client/stigmer-client.js";
|
|
31
34
|
import { resolveRuntimePlaceholders } from "../workflow-engine/resolve.js";
|
|
32
35
|
|
|
33
36
|
export interface WebhookDeliveryTarget {
|
|
@@ -139,14 +142,6 @@ async function deliverWebhook(
|
|
|
139
142
|
}
|
|
140
143
|
}
|
|
141
144
|
|
|
142
|
-
function buildClient(): StigmerClient {
|
|
143
|
-
const config = loadConfig();
|
|
144
|
-
return new StigmerClient({
|
|
145
|
-
endpoint: config.stigmerBackendEndpoint,
|
|
146
|
-
token: config.stigmerToken,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
|
|
150
145
|
async function deliverSignal(
|
|
151
146
|
envelope: Record<string, unknown>,
|
|
152
147
|
target: SignalDeliveryTarget,
|
|
@@ -194,7 +189,8 @@ async function deliverSignal(
|
|
|
194
189
|
export async function emitEventAction(
|
|
195
190
|
config: EmitEventConfig,
|
|
196
191
|
executionId: string,
|
|
197
|
-
runtimeEnv
|
|
192
|
+
runtimeEnv: Record<string, unknown> | undefined,
|
|
193
|
+
client: StigmerClient,
|
|
198
194
|
): Promise<Record<string, unknown>> {
|
|
199
195
|
if (!config.event) {
|
|
200
196
|
throw new Error("emit_event: 'event' field is required");
|
|
@@ -212,17 +208,12 @@ export async function emitEventAction(
|
|
|
212
208
|
const errors: DeliveryError[] = [];
|
|
213
209
|
const env = runtimeEnv ?? {};
|
|
214
210
|
|
|
215
|
-
// One client serves all signal targets of this emit; constructed lazily so
|
|
216
|
-
// webhook-only emits never pay for a gRPC transport.
|
|
217
|
-
let client: StigmerClient | undefined;
|
|
218
|
-
|
|
219
211
|
for (const target of config.delivery) {
|
|
220
212
|
let err: DeliveryError | null = null;
|
|
221
213
|
|
|
222
214
|
if ("webhook" in target) {
|
|
223
215
|
err = await deliverWebhook(envelope, target.webhook, env);
|
|
224
216
|
} else if ("signal" in target) {
|
|
225
|
-
client ??= buildClient();
|
|
226
217
|
err = await deliverSignal(envelope, target.signal, client);
|
|
227
218
|
}
|
|
228
219
|
|
|
@@ -147,7 +147,7 @@ export function hermeticCursorConfig(env: HermeticEnvironment): Config {
|
|
|
147
147
|
temporalNamespace: "default",
|
|
148
148
|
stigmerBackendEndpoint: "http://localhost:7234",
|
|
149
149
|
mcpBridgeEndpoint: null,
|
|
150
|
-
|
|
150
|
+
stigmerTokenRef: { current: null },
|
|
151
151
|
cursorApiKey: FIXTURE.cursorApiKey,
|
|
152
152
|
workspaceRootDir: env.workspaceRootDir,
|
|
153
153
|
mode: "local",
|
|
@@ -66,7 +66,6 @@ export function resolveCursorConfig(config: Config): CursorAdapterConfig {
|
|
|
66
66
|
return {
|
|
67
67
|
proxyEndpoint: config.proxyEndpoint,
|
|
68
68
|
cursorApiKey: config.cursorApiKey,
|
|
69
|
-
stigmerToken: config.stigmerToken,
|
|
70
69
|
stigmerTokenRef: config.stigmerTokenRef,
|
|
71
70
|
workspaceRootDir: config.workspaceRootDir,
|
|
72
71
|
cloudModeEnabled: config.cloudModeEnabled,
|
|
@@ -81,7 +81,7 @@ import { installHitlGate, removeHitlGate, type HitlGateHandle } from "./workspac
|
|
|
81
81
|
*/
|
|
82
82
|
export type CursorAdapterConfig = Pick<
|
|
83
83
|
Config,
|
|
84
|
-
"proxyEndpoint" | "cursorApiKey" | "
|
|
84
|
+
"proxyEndpoint" | "cursorApiKey" | "stigmerTokenRef" | "workspaceRootDir" | "cloudModeEnabled" | "agentResolveTimeoutMs"
|
|
85
85
|
>;
|
|
86
86
|
|
|
87
87
|
export type CursorAgentMode = "cloud" | "local";
|
|
@@ -309,19 +309,18 @@ export async function resolveEngine(input: TurnInput, sink: TurnSink, config: Cu
|
|
|
309
309
|
|
|
310
310
|
await sink.reportProgress("Initializing Cursor agent");
|
|
311
311
|
|
|
312
|
-
// In proxy mode,
|
|
313
|
-
// validates it and injects the real
|
|
314
|
-
// In direct mode,
|
|
312
|
+
// In proxy mode, the SDK's API key is the control-plane credential, read
|
|
313
|
+
// from the ref now (per turn) — the proxy validates it and injects the real
|
|
314
|
+
// Cursor API key server-side. In direct mode, the user's own CURSOR_API_KEY.
|
|
315
315
|
const effectiveApiKey = config.proxyEndpoint
|
|
316
|
-
?
|
|
316
|
+
? config.stigmerTokenRef.current
|
|
317
317
|
: config.cursorApiKey;
|
|
318
318
|
if (!effectiveApiKey || effectiveApiKey === "proxy-managed") {
|
|
319
319
|
const source = config.proxyEndpoint ? "proxy (STIGMER_TOKEN)" : "direct (CURSOR_API_KEY)";
|
|
320
320
|
throw new Error(
|
|
321
321
|
`No Cursor API credential available. Mode=${source}, ` +
|
|
322
322
|
`proxyEndpoint=${config.proxyEndpoint ?? "unset"}, ` +
|
|
323
|
-
`hasStigmerToken=${!!config.
|
|
324
|
-
`hasTokenRef=${!!config.stigmerTokenRef?.current}`,
|
|
323
|
+
`hasStigmerToken=${!!config.stigmerTokenRef.current}`,
|
|
325
324
|
);
|
|
326
325
|
}
|
|
327
326
|
|
|
@@ -145,7 +145,7 @@ export function hermeticDeepAgentConfig(env: HermeticEnvironment, checkpointerTy
|
|
|
145
145
|
temporalNamespace: "default",
|
|
146
146
|
stigmerBackendEndpoint: "http://localhost:7234",
|
|
147
147
|
mcpBridgeEndpoint: null,
|
|
148
|
-
|
|
148
|
+
stigmerTokenRef: { current: null },
|
|
149
149
|
cursorApiKey: "",
|
|
150
150
|
workspaceRootDir: env.workspaceRootDir,
|
|
151
151
|
mode: "local",
|
|
@@ -25,7 +25,7 @@ const CONFIG: Config = {
|
|
|
25
25
|
temporalNamespace: "default",
|
|
26
26
|
stigmerBackendEndpoint: "http://localhost:7234",
|
|
27
27
|
mcpBridgeEndpoint: null,
|
|
28
|
-
|
|
28
|
+
stigmerTokenRef: { current: "tok" },
|
|
29
29
|
cursorApiKey: "",
|
|
30
30
|
workspaceRootDir: "/tmp/adapter-test",
|
|
31
31
|
mode: "local",
|
|
@@ -72,7 +72,7 @@ describe("createDeepAgentAdapter", () => {
|
|
|
72
72
|
expect(resolveDeepAgentConfig(CONFIG)).toEqual({
|
|
73
73
|
checkpointerType: "memory",
|
|
74
74
|
checkpointerProxyEndpoint: null,
|
|
75
|
-
|
|
75
|
+
stigmerTokenRef: { current: "tok" },
|
|
76
76
|
proxyEndpoint: null,
|
|
77
77
|
mode: "local",
|
|
78
78
|
});
|
|
@@ -112,7 +112,7 @@ describe("computeSummarizationDefaults", () => {
|
|
|
112
112
|
// ── 2. Checkpoint Serialization Roundtrip ───────────────────────────
|
|
113
113
|
|
|
114
114
|
describe("summarization state checkpoint serialization", () => {
|
|
115
|
-
const saver = new HttpCheckpointSaver("https://proxy.test", "test-token");
|
|
115
|
+
const saver = new HttpCheckpointSaver("https://proxy.test", { current: "test-token" });
|
|
116
116
|
|
|
117
117
|
it("serializes and deserializes _summarizationEvent through JsonPlusSerializer", async () => {
|
|
118
118
|
const summarizationEvent = {
|
|
@@ -44,7 +44,7 @@ export function resolveDeepAgentConfig(config: Config): DeepAgentAdapterConfig {
|
|
|
44
44
|
return {
|
|
45
45
|
checkpointerType: config.checkpointerType,
|
|
46
46
|
checkpointerProxyEndpoint: config.checkpointerProxyEndpoint,
|
|
47
|
-
|
|
47
|
+
stigmerTokenRef: config.stigmerTokenRef,
|
|
48
48
|
proxyEndpoint: config.proxyEndpoint,
|
|
49
49
|
mode: config.mode,
|
|
50
50
|
};
|
|
@@ -85,7 +85,7 @@ import { modelHasNativeThinking, transformAndCompileSubagents } from "./subagent
|
|
|
85
85
|
*/
|
|
86
86
|
export type DeepAgentAdapterConfig = Pick<
|
|
87
87
|
Config,
|
|
88
|
-
"checkpointerType" | "checkpointerProxyEndpoint" | "
|
|
88
|
+
"checkpointerType" | "checkpointerProxyEndpoint" | "stigmerTokenRef" | "proxyEndpoint" | "mode"
|
|
89
89
|
>;
|
|
90
90
|
|
|
91
91
|
/** The deepagents graph as this module holds it; deepagents exports no stable type for the compiled agent. */
|
|
@@ -164,7 +164,7 @@ export async function openCheckpointer(input: TurnInput, sink: TurnSink, config:
|
|
|
164
164
|
const checkpointer = await createCheckpointer({
|
|
165
165
|
type: config.checkpointerType,
|
|
166
166
|
proxyEndpoint: config.checkpointerProxyEndpoint ?? undefined,
|
|
167
|
-
authToken: config.
|
|
167
|
+
authToken: config.stigmerTokenRef,
|
|
168
168
|
sqlitePath: config.checkpointerType === "sqlite" ? await ensureCheckpointDbPath(input.sessionId) : undefined,
|
|
169
169
|
});
|
|
170
170
|
sink.setupTiming.mark("create_checkpointer");
|
|
@@ -340,12 +340,14 @@ export async function buildEngine(
|
|
|
340
340
|
|
|
341
341
|
// The model. Resolution to the provider API id happens inside
|
|
342
342
|
// buildChatModel; modelName stays the registry id for pricing, the
|
|
343
|
-
// native-thinking heuristic, and sub-agent inheritance.
|
|
343
|
+
// native-thinking heuristic, and sub-agent inheritance. The credential is
|
|
344
|
+
// read from the ref at build, once per turn: a LangChain client carries
|
|
345
|
+
// its headers for its life, so this is the freshest a turn's model can be.
|
|
344
346
|
const buildModelFor = async (name: string) =>
|
|
345
347
|
(await buildChatModel({
|
|
346
348
|
modelName: name,
|
|
347
349
|
proxyEndpoint: config.proxyEndpoint ?? undefined,
|
|
348
|
-
stigmerToken: config.
|
|
350
|
+
stigmerToken: config.stigmerTokenRef.current ?? undefined,
|
|
349
351
|
headerScope: { executionId },
|
|
350
352
|
serviceTier: input.model.serviceTier,
|
|
351
353
|
})).model;
|
|
@@ -376,7 +376,6 @@ export interface GenerateSessionSubjectInput {
|
|
|
376
376
|
export function createGenerateSessionSubjectActivities(config: Config) {
|
|
377
377
|
const client = new StigmerClient({
|
|
378
378
|
endpoint: config.stigmerBackendEndpoint,
|
|
379
|
-
token: config.stigmerToken,
|
|
380
379
|
tokenRef: config.stigmerTokenRef,
|
|
381
380
|
runnerTokenRef: config.stigmerRunnerTokenRef,
|
|
382
381
|
});
|
|
@@ -391,7 +390,7 @@ export function createGenerateSessionSubjectActivities(config: Config) {
|
|
|
391
390
|
log(`started: execution=${executionId}`);
|
|
392
391
|
await generateSessionSubject(executionId, client, {
|
|
393
392
|
proxyEndpoint: config.proxyEndpoint,
|
|
394
|
-
stigmerToken: config.
|
|
393
|
+
stigmerToken: config.stigmerTokenRef.current,
|
|
395
394
|
primaryModel: config.primaryModel,
|
|
396
395
|
});
|
|
397
396
|
} finally {
|
|
@@ -31,7 +31,6 @@ export interface HydrateInput {
|
|
|
31
31
|
export function createHydrateWorkflowActivities(config: Config) {
|
|
32
32
|
const client = new StigmerClient({
|
|
33
33
|
endpoint: config.stigmerBackendEndpoint,
|
|
34
|
-
token: config.stigmerToken,
|
|
35
34
|
tokenRef: config.stigmerTokenRef,
|
|
36
35
|
runnerTokenRef: config.stigmerRunnerTokenRef,
|
|
37
36
|
});
|