@stigmer/runner 3.40.1 → 3.41.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/README.md +3 -1
- package/dist/.build-fingerprint +1 -1
- package/dist/__test-utils__/execution-record-fixture.d.ts +2 -1
- package/dist/__test-utils__/execution-record-fixture.js.map +1 -1
- package/dist/activities/call-agent.js +1 -1
- package/dist/activities/discover-mcp-server.d.ts +7 -1
- package/dist/activities/discover-mcp-server.js +13 -29
- package/dist/activities/discover-mcp-server.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/fold.d.ts +4 -2
- package/dist/activities/execute-cursor/__test-utils__/fold.js +3 -4
- package/dist/activities/execute-cursor/__test-utils__/fold.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +1 -0
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -1
- package/dist/activities/execute-cursor/approval-policy.d.ts +1 -29
- package/dist/activities/execute-cursor/approval-policy.js +11 -35
- package/dist/activities/execute-cursor/approval-policy.js.map +1 -1
- package/dist/activities/execute-cursor/boundary-rows.js +4 -4
- package/dist/activities/execute-cursor/boundary-rows.js.map +1 -1
- package/dist/activities/execute-cursor/translator.d.ts +12 -17
- package/dist/activities/execute-cursor/translator.js +13 -30
- package/dist/activities/execute-cursor/translator.js.map +1 -1
- package/dist/activities/execute-cursor/turn-stream.js +4 -2
- package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.d.ts +11 -1
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js +22 -3
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js.map +1 -1
- package/dist/activities/execute-deep-agent/inline-publisher.d.ts +33 -2
- package/dist/activities/execute-deep-agent/inline-publisher.js +33 -7
- package/dist/activities/execute-deep-agent/inline-publisher.js.map +1 -1
- package/dist/activities/execute-deep-agent/platform-route.d.ts +48 -13
- package/dist/activities/execute-deep-agent/platform-route.js +87 -19
- package/dist/activities/execute-deep-agent/platform-route.js.map +1 -1
- package/dist/activities/execute-deep-agent/streaming-side-effects.d.ts +20 -3
- package/dist/activities/execute-deep-agent/streaming-side-effects.js +26 -4
- package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
- package/dist/activities/execute-deep-agent/translator.d.ts +5 -4
- package/dist/activities/execute-deep-agent/translator.js +5 -4
- package/dist/activities/execute-deep-agent/translator.js.map +1 -1
- package/dist/activities/execute-deep-agent/turn-settle.js +6 -6
- package/dist/activities/execute-deep-agent/turn-settle.js.map +1 -1
- package/dist/activities/execute-deep-agent/turn-setup.js +7 -7
- package/dist/activities/execute-deep-agent/turn-setup.js.map +1 -1
- package/dist/activities/execute-deep-agent/turn-stream.d.ts +10 -6
- package/dist/activities/execute-deep-agent/turn-stream.js +6 -16
- package/dist/activities/execute-deep-agent/turn-stream.js.map +1 -1
- package/dist/activities/execute-deep-agent/turn.d.ts +4 -1
- package/dist/activities/execute-deep-agent/turn.js +15 -6
- package/dist/activities/execute-deep-agent/turn.js.map +1 -1
- package/dist/activities/workflow-event-activities.js +2 -0
- package/dist/activities/workflow-event-activities.js.map +1 -1
- package/dist/config.d.ts +9 -0
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/exit-backstop.d.ts +64 -0
- package/dist/exit-backstop.js +79 -0
- package/dist/exit-backstop.js.map +1 -0
- package/dist/harness/terminal-table.d.ts +12 -5
- package/dist/harness/terminal-table.js +13 -13
- package/dist/harness/terminal-table.js.map +1 -1
- package/dist/harness/transcript/builder.d.ts +21 -0
- package/dist/harness/transcript/builder.js +53 -31
- package/dist/harness/transcript/builder.js.map +1 -1
- package/dist/harness/transcript/events.d.ts +22 -26
- package/dist/harness/transcript/events.js +6 -5
- package/dist/harness/transcript/events.js.map +1 -1
- package/dist/harness/turn-context.d.ts +3 -1
- package/dist/harness/turn-context.js +5 -3
- package/dist/harness/turn-context.js.map +1 -1
- package/dist/main.js +10 -4
- package/dist/main.js.map +1 -1
- package/dist/middleware/cost-advisory.d.ts +6 -3
- package/dist/middleware/cost-advisory.js +6 -3
- package/dist/middleware/cost-advisory.js.map +1 -1
- package/dist/middleware/execution-budget.d.ts +22 -7
- package/dist/middleware/execution-budget.js +57 -35
- package/dist/middleware/execution-budget.js.map +1 -1
- package/dist/middleware/index.d.ts +10 -7
- package/dist/middleware/index.js +10 -7
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/types.d.ts +6 -1
- package/dist/runner-manager.js +3 -2
- package/dist/runner-manager.js.map +1 -1
- package/dist/runner.d.ts +25 -0
- package/dist/runner.js +41 -14
- package/dist/runner.js.map +1 -1
- package/dist/shared/approval-policy.d.ts +23 -13
- package/dist/shared/approval-policy.js +47 -14
- package/dist/shared/approval-policy.js.map +1 -1
- package/dist/shared/caller-identity.d.ts +4 -5
- package/dist/shared/caller-identity.js +4 -5
- package/dist/shared/caller-identity.js.map +1 -1
- package/dist/shared/channel-attachment.js +3 -2
- package/dist/shared/channel-attachment.js.map +1 -1
- package/dist/shared/connect-backfill.d.ts +2 -1
- package/dist/shared/connect-backfill.js +2 -2
- package/dist/shared/connect-backfill.js.map +1 -1
- package/dist/shared/mcp-resolver.d.ts +10 -1
- package/dist/shared/mcp-resolver.js +11 -2
- package/dist/shared/mcp-resolver.js.map +1 -1
- package/dist/shared/memory-attachment.js +3 -2
- package/dist/shared/memory-attachment.js.map +1 -1
- package/dist/shared/model-error.d.ts +25 -3
- package/dist/shared/model-error.js +78 -10
- package/dist/shared/model-error.js.map +1 -1
- package/dist/shared/platform-server-address.d.ts +71 -0
- package/dist/shared/platform-server-address.js +103 -0
- package/dist/shared/platform-server-address.js.map +1 -0
- package/dist/shared/synthesized-attachment.d.ts +0 -5
- package/dist/shared/synthesized-attachment.js +0 -12
- package/dist/shared/synthesized-attachment.js.map +1 -1
- package/dist/shared/tool-rounds.d.ts +51 -23
- package/dist/shared/tool-rounds.js +67 -24
- package/dist/shared/tool-rounds.js.map +1 -1
- package/dist/shared/workspace/platform-dir.d.ts +15 -0
- package/dist/shared/workspace/platform-dir.js +18 -1
- package/dist/shared/workspace/platform-dir.js.map +1 -1
- package/dist/shared/workspace/stigmer-link.d.ts +21 -14
- package/dist/shared/workspace/stigmer-link.js +91 -29
- package/dist/shared/workspace/stigmer-link.js.map +1 -1
- package/dist/tools/url-guard.d.ts +5 -1
- package/dist/tools/url-guard.js +9 -2
- package/dist/tools/url-guard.js.map +1 -1
- package/dist/worker.d.ts +11 -2
- package/dist/worker.js +1 -1
- package/dist/worker.js.map +1 -1
- package/dist/workflow-engine/tasks/human-input.d.ts +16 -0
- package/dist/workflow-engine/tasks/human-input.js +79 -37
- package/dist/workflow-engine/tasks/human-input.js.map +1 -1
- package/dist/workflow-engine/types.d.ts +14 -4
- package/dist/workflow-engine/types.js.map +1 -1
- package/dist/workflows/call-agent-orchestrator.d.ts +13 -0
- package/dist/workflows/call-agent-orchestrator.js.map +1 -1
- package/dist/workflows/engine-core.d.ts +25 -0
- package/dist/workflows/engine-core.js.map +1 -1
- package/dist/workflows/execute-from-execution.d.ts +3 -0
- package/dist/workflows/execute-from-execution.js.map +1 -1
- package/dist/workflows/human-input-orchestrator.d.ts +9 -12
- package/dist/workflows/human-input-orchestrator.js +12 -44
- package/dist/workflows/human-input-orchestrator.js.map +1 -1
- package/package.json +5 -5
- package/src/__test-utils__/config-fixture.ts +1 -0
- package/src/__test-utils__/execution-record-fixture.ts +2 -1
- package/src/__test-utils__/harness-contract/runtime-contract.ts +5 -3
- package/src/__test-utils__/test-gate.ts +8 -0
- package/src/__tests__/exit-backstop.test.ts +91 -0
- package/src/__tests__/golden-e2e.test.ts +80 -62
- package/src/__tests__/history-encryption-e2e.test.ts +51 -27
- package/src/__tests__/runner.test.ts +60 -2
- package/src/activities/__tests__/classify-tool-approvals.test.ts +1 -0
- package/src/activities/__tests__/discover-mcp-server.hang.test.ts +5 -1
- package/src/activities/__tests__/discover-mcp-server.test.ts +90 -49
- package/src/activities/__tests__/workflow-event-activities.test.ts +28 -7
- package/src/activities/call-agent.ts +1 -1
- package/src/activities/discover-mcp-server.ts +27 -37
- package/src/activities/execute-cursor/__test-utils__/fold.ts +6 -4
- package/src/activities/execute-cursor/__test-utils__/hermetic-cursor.ts +1 -0
- package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +4 -29
- package/src/activities/execute-cursor/__tests__/hermetic/bypassed-tool-call.test.ts +120 -0
- package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +5 -5
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/file-review-capture.status.json +0 -2
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/resolution-error.status.json +1 -1
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.error-delta.status.json +0 -3
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.output-first.status.json +0 -2
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.status.json +0 -2
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/stall.status.json +0 -2
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/unattributed-hook-block.status.json +0 -3
- package/src/activities/execute-cursor/__tests__/hermetic/shell-output-delta.test.ts +9 -7
- package/src/activities/execute-cursor/__tests__/hermetic/thrown-error-arms.test.ts +6 -1
- package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +8 -11
- package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +1 -3
- package/src/activities/execute-cursor/__tests__/translator.test.ts +68 -15
- package/src/activities/execute-cursor/approval-policy.ts +11 -36
- package/src/activities/execute-cursor/boundary-rows.ts +3 -3
- package/src/activities/execute-cursor/translator.ts +15 -32
- package/src/activities/execute-cursor/turn-stream.ts +4 -1
- package/src/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.ts +33 -2
- package/src/activities/execute-deep-agent/__tests__/adapter.test.ts +1 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/file-review-capture.test.ts +4 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/file-review-capture.status.json +1 -1
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn1.status.json +1 -1
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn2.status.json +1 -1
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/recursion-limit.persisted.status.json +8 -73
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/resolution-error.status.json +1 -1
- package/src/activities/execute-deep-agent/__tests__/hermetic/inline-artifact-across-gate.test.ts +17 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/recursion-limit.test.ts +86 -26
- package/src/activities/execute-deep-agent/__tests__/hermetic/resolution-error.test.ts +9 -5
- package/src/activities/execute-deep-agent/__tests__/hermetic/thrown-turn-drains-publishes.test.ts +159 -0
- package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +84 -0
- package/src/activities/execute-deep-agent/__tests__/platform-route.test.ts +47 -10
- package/src/activities/execute-deep-agent/__tests__/streaming-side-effects.test.ts +34 -0
- package/src/activities/execute-deep-agent/__tests__/turn-stream.test.ts +13 -5
- package/src/activities/execute-deep-agent/inline-publisher.ts +47 -7
- package/src/activities/execute-deep-agent/platform-route.ts +109 -20
- package/src/activities/execute-deep-agent/streaming-side-effects.ts +27 -4
- package/src/activities/execute-deep-agent/translator.ts +5 -4
- package/src/activities/execute-deep-agent/turn-settle.ts +6 -6
- package/src/activities/execute-deep-agent/turn-setup.ts +7 -7
- package/src/activities/execute-deep-agent/turn-stream.ts +15 -22
- package/src/activities/execute-deep-agent/turn.ts +16 -7
- package/src/activities/workflow-event-activities.ts +2 -0
- package/src/config.ts +11 -0
- package/src/exit-backstop.ts +97 -0
- package/src/harness/README.md +5 -5
- package/src/harness/terminal-table.ts +13 -13
- package/src/harness/transcript/__tests__/builder.test.ts +93 -21
- package/src/harness/transcript/builder.ts +53 -30
- package/src/harness/transcript/events.ts +22 -27
- package/src/harness/turn-context.ts +5 -3
- package/src/main.ts +9 -5
- package/src/middleware/__tests__/execution-budget.test.ts +181 -103
- package/src/middleware/cost-advisory.ts +6 -3
- package/src/middleware/execution-budget.ts +60 -34
- package/src/middleware/index.ts +10 -7
- package/src/middleware/types.ts +6 -1
- package/src/runner-manager.ts +3 -2
- package/src/runner.ts +57 -15
- package/src/shared/__tests__/approval-policy.test.ts +41 -0
- package/src/shared/__tests__/artifact-storage.test.ts +1 -0
- package/src/shared/__tests__/connect-backfill.test.ts +15 -11
- package/src/shared/__tests__/mcp-resolver.test.ts +72 -10
- package/src/shared/__tests__/model-error.test.ts +71 -3
- package/src/shared/__tests__/platform-server-address.test.ts +122 -0
- package/src/shared/__tests__/tool-rounds.test.ts +64 -14
- package/src/shared/approval-policy.ts +57 -14
- package/src/shared/caller-identity.ts +4 -5
- package/src/shared/channel-attachment.ts +2 -2
- package/src/shared/connect-backfill.ts +5 -1
- package/src/shared/mcp-resolver.ts +11 -1
- package/src/shared/memory-attachment.ts +2 -2
- package/src/shared/model-error.ts +87 -9
- package/src/shared/platform-server-address.ts +123 -0
- package/src/shared/synthesized-attachment.ts +0 -12
- package/src/shared/tool-rounds.ts +70 -24
- package/src/shared/workspace/__tests__/stigmer-link.test.ts +107 -10
- package/src/shared/workspace/platform-dir.ts +19 -1
- package/src/shared/workspace/stigmer-link.ts +99 -29
- package/src/tools/__tests__/url-guard.test.ts +19 -1
- package/src/tools/url-guard.ts +9 -1
- package/src/worker.ts +12 -2
- package/src/workflow-engine/__tests__/golden-execution.test.ts +2 -1
- package/src/workflow-engine/__tests__/loader.test.ts +1 -1
- package/src/workflow-engine/__tests__/tasks/human-input.test.ts +133 -57
- package/src/workflow-engine/tasks/human-input.ts +92 -42
- package/src/workflow-engine/types.ts +16 -4
- package/src/workflows/call-agent-orchestrator.ts +7 -0
- package/src/workflows/engine-core.ts +18 -0
- package/src/workflows/execute-from-execution.ts +2 -1
- package/src/workflows/human-input-orchestrator.ts +15 -56
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
HUMAN_INPUT_FAIL_TIMEOUT_RESOLVED_PATCH,
|
|
4
|
+
executeHumanInputTask,
|
|
5
|
+
} from "../../tasks/human-input.js";
|
|
3
6
|
import { createState } from "../../state.js";
|
|
4
7
|
import { evaluateExpressionBatch } from "../../expression.js";
|
|
5
8
|
import { TaskStatusAccumulator } from "../../task-status-accumulator.js";
|
|
@@ -8,6 +11,7 @@ import type {
|
|
|
8
11
|
TaskExecutionContext,
|
|
9
12
|
HumanInputExecutionConfig,
|
|
10
13
|
HumanInputResult,
|
|
14
|
+
HumanInputTimeout,
|
|
11
15
|
AwaitHumanInputFn,
|
|
12
16
|
EmitEventsFn,
|
|
13
17
|
ExpressionEvaluator,
|
|
@@ -17,12 +21,16 @@ import type {
|
|
|
17
21
|
|
|
18
22
|
const notAvailable = () => { throw new Error("not available in test"); };
|
|
19
23
|
|
|
24
|
+
/** What the workflow layer reports when the gate's timer wins. */
|
|
25
|
+
const TIMED_OUT: HumanInputTimeout = { timedOut: true };
|
|
26
|
+
|
|
20
27
|
interface CtxOptions {
|
|
21
28
|
readonly awaitFn?: AwaitHumanInputFn;
|
|
22
29
|
readonly emitFn?: EmitEventsFn;
|
|
23
30
|
readonly evaluateExpressions?: ExpressionEvaluator;
|
|
24
31
|
readonly promoteTaskOutput?: PromoteTaskOutputFn;
|
|
25
32
|
readonly taskStatusAccumulator?: TaskStatusAccumulator;
|
|
33
|
+
readonly isPatched?: (changeId: string) => boolean;
|
|
26
34
|
}
|
|
27
35
|
|
|
28
36
|
function makeCtx(
|
|
@@ -45,6 +53,7 @@ function makeCtx(
|
|
|
45
53
|
emitEvents: emitFn,
|
|
46
54
|
promoteTaskOutput: options?.promoteTaskOutput,
|
|
47
55
|
taskStatusAccumulator: options?.taskStatusAccumulator,
|
|
56
|
+
isPatched: options?.isPatched,
|
|
48
57
|
};
|
|
49
58
|
}
|
|
50
59
|
|
|
@@ -156,11 +165,11 @@ describe("executeHumanInputTask", () => {
|
|
|
156
165
|
expect(capturedConfig!.timeoutSeconds).toBe(0);
|
|
157
166
|
});
|
|
158
167
|
|
|
159
|
-
it("
|
|
168
|
+
it("hands the wait only the signal and the timeout; the policy stays with the kernel", async () => {
|
|
160
169
|
let capturedConfig: HumanInputExecutionConfig | undefined;
|
|
161
170
|
const awaitFn: AwaitHumanInputFn = async (config) => {
|
|
162
171
|
capturedConfig = config;
|
|
163
|
-
return { outcome: "approve"
|
|
172
|
+
return { outcome: "approve" };
|
|
164
173
|
};
|
|
165
174
|
|
|
166
175
|
const taskDef: HumanInputTaskDef = {
|
|
@@ -170,31 +179,27 @@ describe("executeHumanInputTask", () => {
|
|
|
170
179
|
|
|
171
180
|
await executeHumanInputTask(taskDef, "t", createState(), makeCtx(awaitFn));
|
|
172
181
|
|
|
173
|
-
expect(capturedConfig
|
|
182
|
+
expect(capturedConfig).toEqual({ signalName: "human_input_t", timeoutSeconds: 60 });
|
|
174
183
|
});
|
|
175
184
|
|
|
176
|
-
it("
|
|
177
|
-
let capturedConfig: HumanInputExecutionConfig | undefined;
|
|
178
|
-
const awaitFn: AwaitHumanInputFn = async (config) => {
|
|
179
|
-
capturedConfig = config;
|
|
180
|
-
return { outcome: "approve" };
|
|
181
|
-
};
|
|
182
|
-
|
|
185
|
+
it("fails the task on a timeout when onTimeout is not set", async () => {
|
|
183
186
|
const taskDef: HumanInputTaskDef = {
|
|
184
187
|
kind: "human_input",
|
|
185
188
|
humanInput: { prompt: "Review", timeout: 60 },
|
|
186
189
|
};
|
|
187
190
|
|
|
188
|
-
await
|
|
189
|
-
|
|
190
|
-
|
|
191
|
+
await expect(
|
|
192
|
+
executeHumanInputTask(taskDef, "t", createState(), makeCtx(async () => TIMED_OUT)),
|
|
193
|
+
).rejects.toThrow("human_input task timed out waiting for signal 'human_input_t'");
|
|
191
194
|
});
|
|
192
195
|
});
|
|
193
196
|
|
|
194
197
|
describe("error propagation", () => {
|
|
195
|
-
it("propagates
|
|
198
|
+
it("propagates an error from the wait untouched and logs no resolution", async () => {
|
|
199
|
+
const emitted: WorkflowEventDescriptor[][] = [];
|
|
200
|
+
const emitFn: EmitEventsFn = async (events) => { emitted.push(events); };
|
|
196
201
|
const awaitFn: AwaitHumanInputFn = async () => {
|
|
197
|
-
throw new Error("
|
|
202
|
+
throw new Error("workflow cancelled");
|
|
198
203
|
};
|
|
199
204
|
|
|
200
205
|
const taskDef: HumanInputTaskDef = {
|
|
@@ -203,18 +208,86 @@ describe("executeHumanInputTask", () => {
|
|
|
203
208
|
};
|
|
204
209
|
|
|
205
210
|
await expect(
|
|
206
|
-
executeHumanInputTask(taskDef, "t", createState(), makeCtx(awaitFn)),
|
|
211
|
+
executeHumanInputTask(taskDef, "t", createState(), makeCtx(awaitFn, emitFn)),
|
|
212
|
+
).rejects.toThrow("workflow cancelled");
|
|
213
|
+
expect(emitted.flat().map((e) => e.type)).toEqual(["approval_requested"]);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// A fail-policy timeout ends the gate without a decision. It still closes
|
|
218
|
+
// the approval in the event log, so a reader never sees a request left
|
|
219
|
+
// open, and then fails the task with the error it always raised
|
|
220
|
+
// (stigmer/stigmer#1442).
|
|
221
|
+
describe("fail-policy timeout", () => {
|
|
222
|
+
const taskDef: HumanInputTaskDef = {
|
|
223
|
+
kind: "human_input",
|
|
224
|
+
humanInput: { prompt: "Review", timeout: 10, onTimeout: "fail" },
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
it("logs one approval_resolved with auto_resolved and no outcome, then fails the task", async () => {
|
|
228
|
+
const emitted: WorkflowEventDescriptor[][] = [];
|
|
229
|
+
const emitFn: EmitEventsFn = async (events) => { emitted.push(events); };
|
|
230
|
+
const state = createState();
|
|
231
|
+
|
|
232
|
+
await expect(
|
|
233
|
+
executeHumanInputTask(taskDef, "gate", state, makeCtx(async () => TIMED_OUT, emitFn)),
|
|
234
|
+
).rejects.toThrow("human_input task timed out waiting for signal 'human_input_gate'");
|
|
235
|
+
|
|
236
|
+
const resolutions = emitted.flat().filter((e) => e.type === "approval_resolved");
|
|
237
|
+
expect(resolutions).toHaveLength(1);
|
|
238
|
+
expect(resolutions[0]).toMatchObject({
|
|
239
|
+
taskName: "gate",
|
|
240
|
+
outcome: "",
|
|
241
|
+
resolvedBy: "",
|
|
242
|
+
resolvedByActor: undefined,
|
|
243
|
+
comment: "",
|
|
244
|
+
autoResolved: true,
|
|
245
|
+
});
|
|
246
|
+
expect(state.data.gate, "a failed gate records no decision").toBeUndefined();
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it("asks the patch gate by its pinned id before logging", async () => {
|
|
250
|
+
const isPatched = vi.fn(() => true);
|
|
251
|
+
|
|
252
|
+
await expect(
|
|
253
|
+
executeHumanInputTask(taskDef, "gate", createState(), makeCtx(async () => TIMED_OUT, undefined, { isPatched })),
|
|
207
254
|
).rejects.toThrow("timed out");
|
|
255
|
+
|
|
256
|
+
expect(HUMAN_INPUT_FAIL_TIMEOUT_RESOLVED_PATCH).toBe("human-input-fail-timeout-resolved");
|
|
257
|
+
expect(isPatched).toHaveBeenCalledWith(HUMAN_INPUT_FAIL_TIMEOUT_RESOLVED_PATCH);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it("replays a history from before the patch in the old order: no resolution, the same failure", async () => {
|
|
261
|
+
const emitted: WorkflowEventDescriptor[][] = [];
|
|
262
|
+
const emitFn: EmitEventsFn = async (events) => { emitted.push(events); };
|
|
263
|
+
|
|
264
|
+
await expect(
|
|
265
|
+
executeHumanInputTask(
|
|
266
|
+
taskDef, "gate", createState(),
|
|
267
|
+
makeCtx(async () => TIMED_OUT, emitFn, { isPatched: () => false }),
|
|
268
|
+
),
|
|
269
|
+
).rejects.toThrow("human_input task timed out waiting for signal 'human_input_gate'");
|
|
270
|
+
|
|
271
|
+
expect(emitted.flat().map((e) => e.type)).toEqual(["approval_requested"]);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it("never asks the patch gate for a gate a policy or a reviewer decided", async () => {
|
|
275
|
+
const isPatched = vi.fn(() => true);
|
|
276
|
+
const approveOnTimeout: HumanInputTaskDef = {
|
|
277
|
+
kind: "human_input",
|
|
278
|
+
humanInput: { prompt: "Review", timeout: 10, onTimeout: "approve" },
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
await executeHumanInputTask(approveOnTimeout, "a", createState(), makeCtx(async () => TIMED_OUT, undefined, { isPatched }));
|
|
282
|
+
await executeHumanInputTask(taskDef, "b", createState(), makeCtx(async () => ({ outcome: "approve" }), undefined, { isPatched }));
|
|
283
|
+
|
|
284
|
+
expect(isPatched).not.toHaveBeenCalled();
|
|
208
285
|
});
|
|
209
286
|
});
|
|
210
287
|
|
|
211
288
|
describe("auto-resolved results", () => {
|
|
212
289
|
it("handles auto-approve timeout result", async () => {
|
|
213
|
-
const awaitFn: AwaitHumanInputFn = async () =>
|
|
214
|
-
outcome: "approve",
|
|
215
|
-
auto_resolved: true,
|
|
216
|
-
reason: "timeout",
|
|
217
|
-
});
|
|
290
|
+
const awaitFn: AwaitHumanInputFn = async () => TIMED_OUT;
|
|
218
291
|
|
|
219
292
|
const taskDef: HumanInputTaskDef = {
|
|
220
293
|
kind: "human_input",
|
|
@@ -229,11 +302,7 @@ describe("executeHumanInputTask", () => {
|
|
|
229
302
|
});
|
|
230
303
|
|
|
231
304
|
it("handles auto-deny timeout result", async () => {
|
|
232
|
-
const awaitFn: AwaitHumanInputFn = async () =>
|
|
233
|
-
outcome: "deny",
|
|
234
|
-
auto_resolved: true,
|
|
235
|
-
reason: "timeout",
|
|
236
|
-
});
|
|
305
|
+
const awaitFn: AwaitHumanInputFn = async () => TIMED_OUT;
|
|
237
306
|
|
|
238
307
|
const taskDef: HumanInputTaskDef = {
|
|
239
308
|
kind: "human_input",
|
|
@@ -260,11 +329,7 @@ describe("executeHumanInputTask", () => {
|
|
|
260
329
|
];
|
|
261
330
|
|
|
262
331
|
it("maps timeout auto-approve to the FIRST outcome and routes its then", async () => {
|
|
263
|
-
const awaitFn: AwaitHumanInputFn = async () =>
|
|
264
|
-
outcome: "approve",
|
|
265
|
-
auto_resolved: true,
|
|
266
|
-
reason: "timeout",
|
|
267
|
-
});
|
|
332
|
+
const awaitFn: AwaitHumanInputFn = async () => TIMED_OUT;
|
|
268
333
|
|
|
269
334
|
const taskDef: HumanInputTaskDef = {
|
|
270
335
|
kind: "human_input",
|
|
@@ -288,11 +353,7 @@ describe("executeHumanInputTask", () => {
|
|
|
288
353
|
});
|
|
289
354
|
|
|
290
355
|
it("maps timeout auto-deny to the LAST outcome", async () => {
|
|
291
|
-
const awaitFn: AwaitHumanInputFn = async () =>
|
|
292
|
-
outcome: "deny",
|
|
293
|
-
auto_resolved: true,
|
|
294
|
-
reason: "timeout",
|
|
295
|
-
});
|
|
356
|
+
const awaitFn: AwaitHumanInputFn = async () => TIMED_OUT;
|
|
296
357
|
|
|
297
358
|
const taskDef: HumanInputTaskDef = {
|
|
298
359
|
kind: "human_input",
|
|
@@ -310,11 +371,7 @@ describe("executeHumanInputTask", () => {
|
|
|
310
371
|
// — the ordinary name lookup finds the "escalate" outcome and routes its
|
|
311
372
|
// `then`, regardless of the outcome's position in the list.
|
|
312
373
|
it("resolves timeout escalate to the escalate-named outcome and routes its then", async () => {
|
|
313
|
-
const awaitFn: AwaitHumanInputFn = async () =>
|
|
314
|
-
outcome: "escalate",
|
|
315
|
-
auto_resolved: true,
|
|
316
|
-
reason: "timeout",
|
|
317
|
-
});
|
|
374
|
+
const awaitFn: AwaitHumanInputFn = async () => TIMED_OUT;
|
|
318
375
|
|
|
319
376
|
const escalateOutcomes = [
|
|
320
377
|
{ name: "proceed", label: "Proceed", then: "deployStep" },
|
|
@@ -347,11 +404,7 @@ describe("executeHumanInputTask", () => {
|
|
|
347
404
|
it("reports the mapped outcome on the approval_resolved event", async () => {
|
|
348
405
|
const emitted: WorkflowEventDescriptor[][] = [];
|
|
349
406
|
const emitFn: EmitEventsFn = async (events) => { emitted.push(events); };
|
|
350
|
-
const awaitFn: AwaitHumanInputFn = async () =>
|
|
351
|
-
outcome: "approve",
|
|
352
|
-
auto_resolved: true,
|
|
353
|
-
reason: "timeout",
|
|
354
|
-
});
|
|
407
|
+
const awaitFn: AwaitHumanInputFn = async () => TIMED_OUT;
|
|
355
408
|
|
|
356
409
|
const taskDef: HumanInputTaskDef = {
|
|
357
410
|
kind: "human_input",
|
|
@@ -385,11 +438,7 @@ describe("executeHumanInputTask", () => {
|
|
|
385
438
|
});
|
|
386
439
|
|
|
387
440
|
it("keeps plain approve/deny for binary gates without custom outcomes", async () => {
|
|
388
|
-
const awaitFn: AwaitHumanInputFn = async () =>
|
|
389
|
-
outcome: "approve",
|
|
390
|
-
auto_resolved: true,
|
|
391
|
-
reason: "timeout",
|
|
392
|
-
});
|
|
441
|
+
const awaitFn: AwaitHumanInputFn = async () => TIMED_OUT;
|
|
393
442
|
|
|
394
443
|
const taskDef: HumanInputTaskDef = {
|
|
395
444
|
kind: "human_input",
|
|
@@ -474,10 +523,40 @@ describe("executeHumanInputTask", () => {
|
|
|
474
523
|
expect(resolved.outcome).toBe("reject");
|
|
475
524
|
expect(resolved.resolvedBy).toBe("carol");
|
|
476
525
|
expect(resolved.resolvedByActor).toBeUndefined();
|
|
526
|
+
expect(resolved.comment).toBe("");
|
|
477
527
|
expect(resolved.autoResolved).toBe(false);
|
|
478
528
|
expect(resolved.waitDurationMs).toBeGreaterThanOrEqual(0);
|
|
479
529
|
});
|
|
480
530
|
|
|
531
|
+
it("carries the reviewer's comment into approval_resolved and the task output", async () => {
|
|
532
|
+
const emitted: WorkflowEventDescriptor[][] = [];
|
|
533
|
+
const emitFn: EmitEventsFn = async (events) => { emitted.push(events); };
|
|
534
|
+
const awaitFn: AwaitHumanInputFn = async () => ({
|
|
535
|
+
outcome: "deny",
|
|
536
|
+
reviewer: "ida_01abc",
|
|
537
|
+
comment: "Rollback plan is missing",
|
|
538
|
+
responded_at: "2026-05-20T10:00:00Z",
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
const taskDef: HumanInputTaskDef = {
|
|
542
|
+
kind: "human_input",
|
|
543
|
+
humanInput: { prompt: "Confirm?" },
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
const state = createState();
|
|
547
|
+
await executeHumanInputTask(taskDef, "gate", state, makeCtx(awaitFn, emitFn));
|
|
548
|
+
|
|
549
|
+
const resolved = emitted[1][0];
|
|
550
|
+
if (resolved.type !== "approval_resolved") throw new Error("unexpected");
|
|
551
|
+
expect(resolved.comment).toBe("Rollback plan is missing");
|
|
552
|
+
expect(state.data.gate).toEqual({
|
|
553
|
+
outcome: "deny",
|
|
554
|
+
reviewer: "ida_01abc",
|
|
555
|
+
comment: "Rollback plan is missing",
|
|
556
|
+
responded_at: "2026-05-20T10:00:00Z",
|
|
557
|
+
});
|
|
558
|
+
});
|
|
559
|
+
|
|
481
560
|
it("carries the reviewer_actor display snapshot into approval_resolved and the task output", async () => {
|
|
482
561
|
const emitted: WorkflowEventDescriptor[][] = [];
|
|
483
562
|
const emitFn: EmitEventsFn = async (events) => { emitted.push(events); };
|
|
@@ -607,11 +686,7 @@ describe("executeHumanInputTask", () => {
|
|
|
607
686
|
it("emits approval_resolved with autoResolved true on timeout auto-approve", async () => {
|
|
608
687
|
const emitted: WorkflowEventDescriptor[][] = [];
|
|
609
688
|
const emitFn: EmitEventsFn = async (events) => { emitted.push(events); };
|
|
610
|
-
const awaitFn: AwaitHumanInputFn = async () =>
|
|
611
|
-
outcome: "approve",
|
|
612
|
-
auto_resolved: true,
|
|
613
|
-
reason: "timeout",
|
|
614
|
-
});
|
|
689
|
+
const awaitFn: AwaitHumanInputFn = async () => TIMED_OUT;
|
|
615
690
|
|
|
616
691
|
const taskDef: HumanInputTaskDef = {
|
|
617
692
|
kind: "human_input",
|
|
@@ -625,6 +700,7 @@ describe("executeHumanInputTask", () => {
|
|
|
625
700
|
|
|
626
701
|
expect(resolved.autoResolved).toBe(true);
|
|
627
702
|
expect(resolved.outcome).toBe("approve");
|
|
703
|
+
expect(resolved.comment).toBe("");
|
|
628
704
|
});
|
|
629
705
|
});
|
|
630
706
|
|
|
@@ -32,6 +32,16 @@
|
|
|
32
32
|
* activation and attached to the approval_requested event — inline when
|
|
33
33
|
* small, as an artifact reference when at/above the promotion threshold —
|
|
34
34
|
* so the approval record captures exactly what the reviewer saw.
|
|
35
|
+
*
|
|
36
|
+
* Timeout policy lives here, whole. `ctx.awaitHumanInput()` only reports
|
|
37
|
+
* that the timer won; this module decides what that means. approve, deny and
|
|
38
|
+
* escalate resolve the gate to a declared outcome. fail logs an
|
|
39
|
+
* approval_resolved with `auto_resolved` set and no outcome, then fails the
|
|
40
|
+
* task with the error it has always raised, so every approval_requested in
|
|
41
|
+
* the event log is closed by an approval_resolved (stigmer/stigmer#1442).
|
|
42
|
+
* That emit is a new workflow command on an existing path, so it runs behind
|
|
43
|
+
* {@link HUMAN_INPUT_FAIL_TIMEOUT_RESOLVED_PATCH}: a history recorded before
|
|
44
|
+
* it replays the old order (no emit, then the throw).
|
|
35
45
|
*/
|
|
36
46
|
|
|
37
47
|
import type {
|
|
@@ -41,6 +51,9 @@ import type {
|
|
|
41
51
|
WorkflowState,
|
|
42
52
|
TaskExecutionContext,
|
|
43
53
|
HumanInputResult,
|
|
54
|
+
HumanInputResponse,
|
|
55
|
+
HumanInputTimeout,
|
|
56
|
+
HumanInputTimeoutPolicy,
|
|
44
57
|
} from "../types.js";
|
|
45
58
|
import { resolveConfigExpressions, resolveEmbeddedExpressions } from "../resolve.js";
|
|
46
59
|
import { isStrictExpr } from "../expression-utils.js";
|
|
@@ -49,6 +62,13 @@ const SIGNAL_PREFIX = "human_input_";
|
|
|
49
62
|
const DEFAULT_TIMEOUT_SECONDS = 0;
|
|
50
63
|
const DEFAULT_ON_TIMEOUT = "fail" as const;
|
|
51
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Temporal patch id for logging approval_resolved before a fail-policy
|
|
67
|
+
* timeout fails the task. Wire bytes: a rename makes every history that
|
|
68
|
+
* carries the marker replay down the old path.
|
|
69
|
+
*/
|
|
70
|
+
export const HUMAN_INPUT_FAIL_TIMEOUT_RESOLVED_PATCH = "human-input-fail-timeout-resolved";
|
|
71
|
+
|
|
52
72
|
/**
|
|
53
73
|
* Executes a human_input task. Called from `runSingleTask` in do-executor
|
|
54
74
|
* when the task kind is "human_input".
|
|
@@ -97,29 +117,22 @@ export async function executeHumanInputTask(
|
|
|
97
117
|
]);
|
|
98
118
|
}
|
|
99
119
|
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
onTimeout,
|
|
105
|
-
}),
|
|
106
|
-
config.outcomes,
|
|
107
|
-
);
|
|
120
|
+
const response = await ctx.awaitHumanInput({ signalName, timeoutSeconds });
|
|
121
|
+
const result = isTimeout(response)
|
|
122
|
+
? resolveTimeout(onTimeout, config.outcomes)
|
|
123
|
+
: response;
|
|
108
124
|
|
|
109
|
-
if (
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
resolvedByActor: result.reviewer_actor,
|
|
117
|
-
comment: "",
|
|
118
|
-
waitDurationMs: Date.now() - approvalRequestedAt,
|
|
119
|
-
autoResolved: result.auto_resolved ?? false,
|
|
120
|
-
}]);
|
|
125
|
+
if (result === undefined) {
|
|
126
|
+
if (ctx.isPatched?.(HUMAN_INPUT_FAIL_TIMEOUT_RESOLVED_PATCH) ?? true) {
|
|
127
|
+
await emitApprovalResolved(ctx, taskName, approvalRequestedAt, {
|
|
128
|
+
outcome: "", auto_resolved: true, reason: "timeout",
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
throw new Error(`human_input task timed out waiting for signal '${signalName}'`);
|
|
121
132
|
}
|
|
122
133
|
|
|
134
|
+
await emitApprovalResolved(ctx, taskName, approvalRequestedAt, result);
|
|
135
|
+
|
|
123
136
|
state.addData({ [taskName]: result });
|
|
124
137
|
|
|
125
138
|
const selectedOutcome = config.outcomes?.find(o => o.name === result.outcome);
|
|
@@ -136,35 +149,72 @@ function validateConfig(config: HumanInputConfig, taskName: string): void {
|
|
|
136
149
|
}
|
|
137
150
|
}
|
|
138
151
|
|
|
152
|
+
function isTimeout(response: HumanInputResponse): response is HumanInputTimeout {
|
|
153
|
+
return "timedOut" in response;
|
|
154
|
+
}
|
|
155
|
+
|
|
139
156
|
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
157
|
+
* The decision a timeout policy makes, or `undefined` when the policy is
|
|
158
|
+
* fail and the gate ends without one.
|
|
159
|
+
*
|
|
160
|
+
* With custom outcomes the proto contract (HumanInputTaskConfig.outcomes doc)
|
|
161
|
+
* applies: auto-approve resolves to the FIRST declared outcome and auto-deny
|
|
162
|
+
* to the LAST, so `then` routing and downstream outcome switches see declared
|
|
163
|
+
* outcome names, never approve/deny words a reviewer of a custom-outcome gate
|
|
164
|
+
* was never offered. Binary gates (no custom outcomes) keep plain approve/deny.
|
|
147
165
|
*
|
|
148
166
|
* Escalate needs no remapping by construction (stigmer/stigmer#781): its
|
|
149
|
-
* policy word IS the declared outcome's name
|
|
167
|
+
* policy word IS the declared outcome's name. The loader (and the server
|
|
150
168
|
* validator) only accept the escalate policy when an outcome named
|
|
151
|
-
* "escalate" with `then` exists, so the caller's ordinary name lookup
|
|
152
|
-
*
|
|
169
|
+
* "escalate" with `then` exists, so the caller's ordinary name lookup routes
|
|
170
|
+
* the escalation branch.
|
|
153
171
|
*/
|
|
154
|
-
function
|
|
155
|
-
|
|
172
|
+
function resolveTimeout(
|
|
173
|
+
policy: HumanInputTimeoutPolicy,
|
|
156
174
|
outcomes: HumanInputConfig["outcomes"],
|
|
157
|
-
): HumanInputResult {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
175
|
+
): HumanInputResult | undefined {
|
|
176
|
+
const decided = (outcome: string): HumanInputResult => ({
|
|
177
|
+
outcome, auto_resolved: true, reason: "timeout",
|
|
178
|
+
});
|
|
179
|
+
switch (policy) {
|
|
180
|
+
case "approve":
|
|
181
|
+
return decided(outcomes?.length ? outcomes[0].name : "approve");
|
|
182
|
+
case "deny":
|
|
183
|
+
return decided(outcomes?.length ? outcomes[outcomes.length - 1].name : "deny");
|
|
184
|
+
case "escalate":
|
|
185
|
+
return decided("escalate");
|
|
186
|
+
case "fail":
|
|
187
|
+
return undefined;
|
|
188
|
+
default: {
|
|
189
|
+
const unreachable: never = policy;
|
|
190
|
+
throw new Error(`human_input: unknown timeout policy '${String(unreachable)}'`);
|
|
191
|
+
}
|
|
166
192
|
}
|
|
167
|
-
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Logs how the gate's approval ended. A timeout's decision carries no
|
|
197
|
+
* reviewer, no comment and `auto_resolved`; a fail timeout's carries no
|
|
198
|
+
* outcome either.
|
|
199
|
+
*/
|
|
200
|
+
async function emitApprovalResolved(
|
|
201
|
+
ctx: TaskExecutionContext,
|
|
202
|
+
taskName: string,
|
|
203
|
+
approvalRequestedAt: number,
|
|
204
|
+
result: HumanInputResult,
|
|
205
|
+
): Promise<void> {
|
|
206
|
+
if (!ctx.emitEvents) return;
|
|
207
|
+
await ctx.emitEvents([{
|
|
208
|
+
type: "approval_resolved",
|
|
209
|
+
taskName,
|
|
210
|
+
occurredAt: new Date().toISOString(),
|
|
211
|
+
outcome: result.outcome,
|
|
212
|
+
resolvedBy: result.reviewer ?? "",
|
|
213
|
+
resolvedByActor: result.reviewer_actor,
|
|
214
|
+
comment: result.comment ?? "",
|
|
215
|
+
waitDurationMs: Date.now() - approvalRequestedAt,
|
|
216
|
+
autoResolved: result.auto_resolved ?? false,
|
|
217
|
+
}]);
|
|
168
218
|
}
|
|
169
219
|
|
|
170
220
|
interface ResolvedReviewPayload {
|
|
@@ -561,10 +561,20 @@ export interface RunWorkflowExecutionConfig {
|
|
|
561
561
|
}
|
|
562
562
|
|
|
563
563
|
/**
|
|
564
|
-
* Blocks workflow execution until a human provides input via signal
|
|
565
|
-
*
|
|
564
|
+
* Blocks workflow execution until a human provides input via signal, or
|
|
565
|
+
* until the timeout elapses. Reports which happened and nothing more: the
|
|
566
|
+
* timeout policy (fail, approve, deny, escalate) is the kernel's to apply
|
|
567
|
+
* (`tasks/human-input.ts`), so one module owns every way a gate can end.
|
|
566
568
|
*/
|
|
567
|
-
export type AwaitHumanInputFn = (config: HumanInputExecutionConfig) => Promise<
|
|
569
|
+
export type AwaitHumanInputFn = (config: HumanInputExecutionConfig) => Promise<HumanInputResponse>;
|
|
570
|
+
|
|
571
|
+
/** What a wait at a human_input gate ended with: the reviewer's decision, or the timeout. */
|
|
572
|
+
export type HumanInputResponse = HumanInputResult | HumanInputTimeout;
|
|
573
|
+
|
|
574
|
+
/** The gate's timeout elapsed before any decision arrived. */
|
|
575
|
+
export interface HumanInputTimeout {
|
|
576
|
+
readonly timedOut: true;
|
|
577
|
+
}
|
|
568
578
|
|
|
569
579
|
/**
|
|
570
580
|
* Internal timeout-policy vocabulary — the loader-normalized form of the
|
|
@@ -577,8 +587,8 @@ export type HumanInputTimeoutPolicy = "fail" | "approve" | "deny" | "escalate";
|
|
|
577
587
|
|
|
578
588
|
export interface HumanInputExecutionConfig {
|
|
579
589
|
readonly signalName: string;
|
|
590
|
+
/** Seconds to wait before reporting a {@link HumanInputTimeout}; 0 waits indefinitely. */
|
|
580
591
|
readonly timeoutSeconds: number;
|
|
581
|
-
readonly onTimeout: HumanInputTimeoutPolicy;
|
|
582
592
|
}
|
|
583
593
|
|
|
584
594
|
export interface HumanInputResult {
|
|
@@ -594,6 +604,8 @@ export interface HumanInputResult {
|
|
|
594
604
|
readonly reviewer_actor?: HumanInputReviewerActor;
|
|
595
605
|
readonly responded_at?: string;
|
|
596
606
|
readonly form_data?: Record<string, unknown>;
|
|
607
|
+
/** Free-text comment the reviewer attached. Absent when none was written, and always on a timeout. */
|
|
608
|
+
readonly comment?: string;
|
|
597
609
|
readonly auto_resolved?: boolean;
|
|
598
610
|
readonly reason?: string;
|
|
599
611
|
}
|
|
@@ -65,6 +65,13 @@ type AgentActivities = ReturnType<typeof createCallAgentActivities>;
|
|
|
65
65
|
type StatusActivities = ReturnType<typeof createCallAgentStatusActivities>;
|
|
66
66
|
type EventActivities = ReturnType<typeof createWorkflowEventActivities>;
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Every activity an agent call schedules. The engine's own activity type
|
|
70
|
+
* includes it, so a worker (or a test mock) that registers the engine's
|
|
71
|
+
* activities is checked against this list by the compiler.
|
|
72
|
+
*/
|
|
73
|
+
export type AgentCallActivities = AgentActivities & StatusActivities & EventActivities;
|
|
74
|
+
|
|
68
75
|
// The 1h ceiling is a deliberate platform constant, not a user knob: the
|
|
69
76
|
// old AgentCallTaskConfig declared a per-task `timeout` that nothing ever
|
|
70
77
|
// read, and #358 deleted it rather than declaring what isn't honored. The
|
|
@@ -28,6 +28,7 @@ import type { createRunCommandActivities } from "../activities/run-command.js";
|
|
|
28
28
|
import type { createWorkflowEventActivities } from "../activities/workflow-event-activities.js";
|
|
29
29
|
import type { createPromoteTaskOutputActivities } from "../activities/promote-task-output.js";
|
|
30
30
|
import { orchestrateAgentCall } from "./call-agent-orchestrator.js";
|
|
31
|
+
import type { AgentCallActivities } from "./call-agent-orchestrator.js";
|
|
31
32
|
import { orchestrateListenTask } from "./listen-orchestrator.js";
|
|
32
33
|
import { orchestrateRunWorkflow } from "./run-orchestrator.js";
|
|
33
34
|
import { orchestrateHumanInput } from "./human-input-orchestrator.js";
|
|
@@ -65,6 +66,23 @@ type RunActivities = ReturnType<typeof createRunCommandActivities>;
|
|
|
65
66
|
type EventActivities = ReturnType<typeof createWorkflowEventActivities>;
|
|
66
67
|
type PromoteActivities = ReturnType<typeof createPromoteTaskOutputActivities>;
|
|
67
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Every activity the engine can schedule, by name. A worker that runs the
|
|
71
|
+
* engine must register each one; the test mocks declare
|
|
72
|
+
* `satisfies Record<keyof EngineActivities, unknown>` so a new activity
|
|
73
|
+
* without a mock fails the typecheck instead of failing the smoke run at
|
|
74
|
+
* run time (stigmer#1300: four missing local activities made the golden
|
|
75
|
+
* suite report passes for months without running a golden).
|
|
76
|
+
*/
|
|
77
|
+
export type EngineActivities = EvalActivities &
|
|
78
|
+
HttpActivities &
|
|
79
|
+
GrpcActivities &
|
|
80
|
+
FunctionActivities &
|
|
81
|
+
RunActivities &
|
|
82
|
+
EventActivities &
|
|
83
|
+
PromoteActivities &
|
|
84
|
+
AgentCallActivities;
|
|
85
|
+
|
|
68
86
|
const evalProxy = proxyLocalActivities<EvalActivities>({
|
|
69
87
|
startToCloseTimeout: "10s",
|
|
70
88
|
});
|
|
@@ -31,7 +31,8 @@ import type { createHydrateWorkflowActivities } from "../activities/hydrate-work
|
|
|
31
31
|
// Hydration Activity Proxy
|
|
32
32
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
/** The activity this entry point schedules before it hands the model to the engine. */
|
|
35
|
+
export type HydrateActivities = ReturnType<typeof createHydrateWorkflowActivities>;
|
|
35
36
|
|
|
36
37
|
const hydrateProxy = proxyActivities<HydrateActivities>({
|
|
37
38
|
startToCloseTimeout: "2m",
|