@stigmer/server 3.37.0 → 3.37.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/dist/domain/agentexecution/artifacts.d.ts.map +1 -1
- package/dist/domain/agentexecution/artifacts.js +15 -6
- package/dist/domain/agentexecution/artifacts.js.map +1 -1
- package/dist/domain/agentexecution/lifecycle.d.ts.map +1 -1
- package/dist/domain/agentexecution/lifecycle.js +5 -3
- package/dist/domain/agentexecution/lifecycle.js.map +1 -1
- package/dist/domain/agentexecution/platform-rows.d.ts +63 -0
- package/dist/domain/agentexecution/platform-rows.d.ts.map +1 -0
- package/dist/domain/agentexecution/platform-rows.js +106 -0
- package/dist/domain/agentexecution/platform-rows.js.map +1 -0
- package/dist/domain/agentexecution/subscribe.d.ts.map +1 -1
- package/dist/domain/agentexecution/subscribe.js +7 -4
- package/dist/domain/agentexecution/subscribe.js.map +1 -1
- package/dist/domain/agentexecution/update-status.d.ts.map +1 -1
- package/dist/domain/agentexecution/update-status.js +48 -17
- package/dist/domain/agentexecution/update-status.js.map +1 -1
- package/dist/domain/workflowexecution/create-steps.d.ts.map +1 -1
- package/dist/domain/workflowexecution/create-steps.js +6 -2
- package/dist/domain/workflowexecution/create-steps.js.map +1 -1
- package/dist/domain/workflowexecution/lifecycle.d.ts.map +1 -1
- package/dist/domain/workflowexecution/lifecycle.js +5 -2
- package/dist/domain/workflowexecution/lifecycle.js.map +1 -1
- package/dist/domain/workflowexecution/send-signal.d.ts.map +1 -1
- package/dist/domain/workflowexecution/send-signal.js +6 -3
- package/dist/domain/workflowexecution/send-signal.js.map +1 -1
- package/dist/domain/workflowexecution/submit-approval.d.ts.map +1 -1
- package/dist/domain/workflowexecution/submit-approval.js +6 -2
- package/dist/domain/workflowexecution/submit-approval.js.map +1 -1
- package/dist/domain/workflowexecution/submit-file-decision.d.ts.map +1 -1
- package/dist/domain/workflowexecution/submit-file-decision.js +6 -2
- package/dist/domain/workflowexecution/submit-file-decision.js.map +1 -1
- package/dist/domain/workflowexecution/submit-workflow-task-approval.d.ts.map +1 -1
- package/dist/domain/workflowexecution/submit-workflow-task-approval.js +6 -2
- package/dist/domain/workflowexecution/submit-workflow-task-approval.js.map +1 -1
- package/dist/domain/workflowexecution/subscribe-events.d.ts.map +1 -1
- package/dist/domain/workflowexecution/subscribe-events.js +6 -2
- package/dist/domain/workflowexecution/subscribe-events.js.map +1 -1
- package/dist/domain/workflowexecution/subscribe.d.ts.map +1 -1
- package/dist/domain/workflowexecution/subscribe.js +7 -4
- package/dist/domain/workflowexecution/subscribe.js.map +1 -1
- package/dist/temporal/agentexecution/runner-result.d.ts +12 -0
- package/dist/temporal/agentexecution/runner-result.d.ts.map +1 -1
- package/dist/temporal/agentexecution/runner-result.js +15 -0
- package/dist/temporal/agentexecution/runner-result.js.map +1 -1
- package/dist/temporal/agentexecution/workflows/invoke-agent-execution.d.ts.map +1 -1
- package/dist/temporal/agentexecution/workflows/invoke-agent-execution.js +44 -22
- package/dist/temporal/agentexecution/workflows/invoke-agent-execution.js.map +1 -1
- package/package.json +6 -6
- package/src/domain/agentexecution/__tests__/store-faults.test.ts +315 -0
- package/src/domain/agentexecution/__tests__/update-status.test.ts +217 -0
- package/src/domain/agentexecution/artifacts.ts +21 -12
- package/src/domain/agentexecution/lifecycle.ts +5 -3
- package/src/domain/agentexecution/platform-rows.ts +133 -0
- package/src/domain/agentexecution/subscribe.ts +7 -3
- package/src/domain/agentexecution/update-status.ts +70 -19
- package/src/domain/workflowexecution/__tests__/store-faults.test.ts +418 -0
- package/src/domain/workflowexecution/create-steps.ts +6 -2
- package/src/domain/workflowexecution/lifecycle.ts +5 -2
- package/src/domain/workflowexecution/send-signal.ts +11 -4
- package/src/domain/workflowexecution/submit-approval.ts +6 -2
- package/src/domain/workflowexecution/submit-file-decision.ts +6 -2
- package/src/domain/workflowexecution/submit-workflow-task-approval.ts +6 -2
- package/src/domain/workflowexecution/subscribe-events.ts +6 -2
- package/src/domain/workflowexecution/subscribe.ts +11 -4
- package/src/pipeline/__tests__/blind-not-found.test.ts +297 -0
- package/src/temporal/agentexecution/__tests__/invoke-workflow.test.ts +51 -1
- package/src/temporal/agentexecution/__tests__/own-behalf-status-writes.test.ts +149 -9
- package/src/temporal/agentexecution/__tests__/replay-histories/runner-failed.json +757 -0
- package/src/temporal/agentexecution/__tests__/replay-histories/user-cancel.json +755 -0
- package/src/temporal/agentexecution/__tests__/replay.test.ts +4 -2
- package/src/temporal/agentexecution/runner-result.ts +16 -0
- package/src/temporal/agentexecution/workflows/invoke-agent-execution.ts +49 -22
- package/src/domain/agentexecution/__tests__/execution-usage-report.test.ts +0 -78
|
@@ -32,8 +32,10 @@ const historyFiles = readdirSync(HISTORY_DIR).filter((name) =>
|
|
|
32
32
|
describe("invoke-agent-execution replay determinism", () => {
|
|
33
33
|
it("has committed histories to replay (the gate cannot be empty)", () => {
|
|
34
34
|
// 3 from #18 (happy, HITL, pause/resume) + the parented HITL history
|
|
35
|
-
// that pins the #23 child_approval_required sender
|
|
36
|
-
|
|
35
|
+
// that pins the #23 child_approval_required sender + the runner-failed
|
|
36
|
+
// and user-cancel histories captured before stigmer#980 changed the
|
|
37
|
+
// payloads (never the command sequence) of those two paths.
|
|
38
|
+
expect(historyFiles.length).toBeGreaterThanOrEqual(6);
|
|
37
39
|
});
|
|
38
40
|
|
|
39
41
|
it("replays every committed history deterministically", async () => {
|
|
@@ -56,3 +56,19 @@ export function getErrorFromResult(
|
|
|
56
56
|
const error = result["error"];
|
|
57
57
|
return typeof error === "string" ? error : "";
|
|
58
58
|
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The runner reported the turn FAILED (its result's phase), as opposed to
|
|
62
|
+
* the flow breaking around it. The runner has then already persisted the
|
|
63
|
+
* failed transcript with its own explanation rows (the runner's terminal
|
|
64
|
+
* table), so the workflow's failure-path status write carries phase and
|
|
65
|
+
* error only and adds no second explanation (stigmer#980). The message is
|
|
66
|
+
* the one the workflow always threw here, so the parent callback and the
|
|
67
|
+
* workflow failure read as before.
|
|
68
|
+
*/
|
|
69
|
+
export class RunnerReportedFailure extends Error {
|
|
70
|
+
constructor(runnerError: string) {
|
|
71
|
+
super(`agent execution failed: ${runnerError}`);
|
|
72
|
+
this.name = "RunnerReportedFailure";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -31,6 +31,16 @@
|
|
|
31
31
|
* context): CANCELLED persist (quiet terminal, NO status.error —
|
|
32
32
|
* stigmer#282), callback completion with "execution cancelled", EC
|
|
33
33
|
* delete.
|
|
34
|
+
* - Stop and failure copy (stigmer#980): the runner cannot tell a Pause
|
|
35
|
+
* from a Cancel (both reach its activity as the same cancellation), so
|
|
36
|
+
* this workflow, which knows which stop it runs, writes the stop row
|
|
37
|
+
* (platform-rows.ts) and the merge keeps it last. When the runner
|
|
38
|
+
* reported the turn FAILED it already explained the failure in the
|
|
39
|
+
* transcript, so the failure-path write adds no lines; only a flow
|
|
40
|
+
* that broke without a runner verdict gets the platform's two lines.
|
|
41
|
+
* Both are payload changes to activities scheduled as before, pinned
|
|
42
|
+
* by the runner-failed, user-cancel and pause-resume replay
|
|
43
|
+
* histories.
|
|
34
44
|
* - Recovery is terminate-and-start-fresh ONLY (stigmer#200); no
|
|
35
45
|
* version gates (OD-6 — no Go-era history can replay here).
|
|
36
46
|
*
|
|
@@ -42,8 +52,9 @@
|
|
|
42
52
|
* WORKFLOW-BUNDLE IMPORT DISCIPLINE: this file runs in Temporal's
|
|
43
53
|
* deterministic sandbox. Only @temporalio/workflow, @temporalio/common,
|
|
44
54
|
* @bufbuild/protobuf, generated protos, and verified-pure domain modules
|
|
45
|
-
* (filereview/gate.js by DIRECT path —
|
|
46
|
-
* node:crypto) may be imported. The SDK bundler
|
|
55
|
+
* (filereview/gate.js and platform-rows.js by DIRECT path — gate.js's
|
|
56
|
+
* sibling digest.ts pulls node:crypto) may be imported. The SDK bundler
|
|
57
|
+
* hard-fails otherwise.
|
|
47
58
|
*/
|
|
48
59
|
import { create, fromJson, toJson } from "@bufbuild/protobuf";
|
|
49
60
|
import type { JsonValue } from "@bufbuild/protobuf";
|
|
@@ -83,6 +94,10 @@ import {
|
|
|
83
94
|
hasDecidedAwaitingReconcile,
|
|
84
95
|
unresolvedGateCount,
|
|
85
96
|
} from "../../../domain/agentexecution/filereview/gate.js";
|
|
97
|
+
import {
|
|
98
|
+
CANCEL_STOP_ROW,
|
|
99
|
+
PAUSE_STOP_ROW,
|
|
100
|
+
} from "../../../domain/agentexecution/platform-rows.js";
|
|
86
101
|
import {
|
|
87
102
|
isWorkerShutdown,
|
|
88
103
|
WORKER_SHUTDOWN_STATUS_ERROR,
|
|
@@ -107,6 +122,7 @@ import {
|
|
|
107
122
|
import {
|
|
108
123
|
getErrorFromResult,
|
|
109
124
|
getPhaseFromResult,
|
|
125
|
+
RunnerReportedFailure,
|
|
110
126
|
type RunnerActivityResult,
|
|
111
127
|
} from "../runner-result.js";
|
|
112
128
|
import type { InvokeAgentExecutionWorkflowInput } from "../workflow-input.js";
|
|
@@ -764,13 +780,16 @@ async function runWithPauseAndRecovery(
|
|
|
764
780
|
pauseCycle,
|
|
765
781
|
});
|
|
766
782
|
|
|
767
|
-
// Defense-in-depth
|
|
768
|
-
//
|
|
769
|
-
//
|
|
783
|
+
// Defense-in-depth for the phase (the Pause RPC already set PAUSED)
|
|
784
|
+
// and the one writer of the pause row: the runner settles its
|
|
785
|
+
// transcript without a row because it cannot tell this stop from a
|
|
786
|
+
// cancel, and the merge appends the row and keeps it last across
|
|
787
|
+
// the runner's settle write, whichever lands first (stigmer#980).
|
|
770
788
|
await persistFinalStatus(
|
|
771
789
|
executionId,
|
|
772
790
|
create(AgentExecutionStatusSchema, {
|
|
773
791
|
phase: ExecutionPhase.EXECUTION_PAUSED,
|
|
792
|
+
messages: [{ type: MessageType.MESSAGE_SYSTEM, content: PAUSE_STOP_ROW }],
|
|
774
793
|
}),
|
|
775
794
|
"Failed to persist PAUSED status (non-fatal)",
|
|
776
795
|
);
|
|
@@ -850,7 +869,7 @@ async function runWithPauseAndRecovery(
|
|
|
850
869
|
}),
|
|
851
870
|
"Failed to persist fallback FAILED status",
|
|
852
871
|
);
|
|
853
|
-
throw new
|
|
872
|
+
throw new RunnerReportedFailure(finalError);
|
|
854
873
|
}
|
|
855
874
|
|
|
856
875
|
return finalResult;
|
|
@@ -1130,7 +1149,11 @@ async function executeWithHitlLoop(
|
|
|
1130
1149
|
* persist the honest platform-failure copy instead of raw Temporal
|
|
1131
1150
|
* internals ("Worker is shutting down and this activity did not complete
|
|
1132
1151
|
* in time" reached users in the 2026-08-08 incident, #776) — the raw text
|
|
1133
|
-
* stays in the workflow log; status.error is a user surface.
|
|
1152
|
+
* stays in the workflow log; status.error is a user surface. The two
|
|
1153
|
+
* transcript lines are the platform's explanation, sent only when the
|
|
1154
|
+
* flow broke without the runner's verdict: a RunnerReportedFailure's
|
|
1155
|
+
* transcript already carries the runner's own (stigmer#980), and the
|
|
1156
|
+
* merge appends these rather than replacing the transcript.
|
|
1134
1157
|
*/
|
|
1135
1158
|
async function updateStatusOnFailure(
|
|
1136
1159
|
executionId: string,
|
|
@@ -1145,17 +1168,20 @@ async function updateStatusOnFailure(
|
|
|
1145
1168
|
const failedStatus = create(AgentExecutionStatusSchema, {
|
|
1146
1169
|
phase: ExecutionPhase.EXECUTION_FAILED,
|
|
1147
1170
|
error: statusError,
|
|
1148
|
-
messages:
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1171
|
+
messages:
|
|
1172
|
+
originalError instanceof RunnerReportedFailure
|
|
1173
|
+
? []
|
|
1174
|
+
: [
|
|
1175
|
+
{
|
|
1176
|
+
type: MessageType.MESSAGE_SYSTEM,
|
|
1177
|
+
content:
|
|
1178
|
+
"Internal system error occurred during execution. Please contact support if this issue persists.",
|
|
1179
|
+
},
|
|
1180
|
+
{
|
|
1181
|
+
type: MessageType.MESSAGE_SYSTEM,
|
|
1182
|
+
content: `Error details: ${statusError}`,
|
|
1183
|
+
},
|
|
1184
|
+
],
|
|
1159
1185
|
});
|
|
1160
1186
|
|
|
1161
1187
|
await failurePathActivities[UPDATE_EXECUTION_STATUS_ACTIVITY_NAME](
|
|
@@ -1200,9 +1226,10 @@ async function handleCancellation(
|
|
|
1200
1226
|
* CANCELLED persist. Deliberately sets NO status.error: a user-initiated
|
|
1201
1227
|
* cancel is a quiet terminal state, not a failure (stigmer#282) — display
|
|
1202
1228
|
* layers key error styling on phase, so a sentinel here would render the
|
|
1203
|
-
* stop as a red failure. The muted
|
|
1204
|
-
*
|
|
1205
|
-
*
|
|
1229
|
+
* stop as a red failure. The muted stop row is the durable in-transcript
|
|
1230
|
+
* marker, written only here (stigmer#980): the merge appends it and keeps
|
|
1231
|
+
* it last across the runner's settle write. Regular activity for the same
|
|
1232
|
+
* replay-safety reasons as updateStatusOnFailure.
|
|
1206
1233
|
*/
|
|
1207
1234
|
async function updateStatusOnCancellation(executionId: string): Promise<void> {
|
|
1208
1235
|
const cancelledStatus = create(AgentExecutionStatusSchema, {
|
|
@@ -1210,7 +1237,7 @@ async function updateStatusOnCancellation(executionId: string): Promise<void> {
|
|
|
1210
1237
|
messages: [
|
|
1211
1238
|
{
|
|
1212
1239
|
type: MessageType.MESSAGE_SYSTEM,
|
|
1213
|
-
content:
|
|
1240
|
+
content: CANCEL_STOP_ROW,
|
|
1214
1241
|
},
|
|
1215
1242
|
],
|
|
1216
1243
|
});
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pins getExecutionUsageReport's store-fault mapping in LoadExecution — the
|
|
3
|
-
* composed suite (agentexecution.test.ts) reaches only the real store, which
|
|
4
|
-
* cannot fail selectively:
|
|
5
|
-
*
|
|
6
|
-
* - a typed ResourceNotFoundError answers the domain's NotFound copy,
|
|
7
|
-
* `agent_execution not found: <id>` (stigmer/stigmer#859);
|
|
8
|
-
* - any other store failure is an infrastructure fault: a sanitized
|
|
9
|
-
* Internal whose wire text never carries the cause, never a NotFound
|
|
10
|
-
* that would tell a client the execution does not exist.
|
|
11
|
-
*/
|
|
12
|
-
import { create } from "@bufbuild/protobuf";
|
|
13
|
-
import { Code, ConnectError } from "@connectrpc/connect";
|
|
14
|
-
import { describe, expect, it } from "vitest";
|
|
15
|
-
|
|
16
|
-
import { AgentExecutionQueryController } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/query_pb";
|
|
17
|
-
|
|
18
|
-
import { createLogger } from "../../../boot/logger.js";
|
|
19
|
-
import type { Authorizer } from "../../../extensions/authorizer.js";
|
|
20
|
-
import { testCallerIdentity } from "../../../pipeline/__tests__/support.js";
|
|
21
|
-
import { ResourceNotFoundError } from "../../../store/interface.js";
|
|
22
|
-
import type { Store } from "../../../store/interface.js";
|
|
23
|
-
|
|
24
|
-
import { getExecutionUsageReport } from "../usage.js";
|
|
25
|
-
|
|
26
|
-
const silentLogger = createLogger({
|
|
27
|
-
level: "error",
|
|
28
|
-
pretty: false,
|
|
29
|
-
write: () => {},
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
const allowingAuthorizer: Authorizer = {
|
|
33
|
-
authorize: () => Promise.resolve({ kind: "allow" }),
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
/** A store whose one read the chain makes fails with the given error. */
|
|
37
|
-
function failingStore(error: Error): Store {
|
|
38
|
-
return {
|
|
39
|
-
getResource: () => Promise.reject(error),
|
|
40
|
-
} as unknown as Store;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async function reportError(store: Store): Promise<ConnectError> {
|
|
44
|
-
const error = await getExecutionUsageReport(
|
|
45
|
-
{
|
|
46
|
-
store,
|
|
47
|
-
logger: silentLogger,
|
|
48
|
-
authorizer: allowingAuthorizer,
|
|
49
|
-
listReadScope: undefined,
|
|
50
|
-
},
|
|
51
|
-
create(AgentExecutionQueryController.method.getExecutionUsageReport.input, {
|
|
52
|
-
executionId: "aex_missing",
|
|
53
|
-
}),
|
|
54
|
-
testCallerIdentity(),
|
|
55
|
-
).catch((e: unknown) => e);
|
|
56
|
-
expect(error).toBeInstanceOf(ConnectError);
|
|
57
|
-
return error as ConnectError;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
describe("getExecutionUsageReport — LoadExecution store faults", () => {
|
|
61
|
-
it("a missing execution answers NotFound with the domain's copy", async () => {
|
|
62
|
-
const error = await reportError(
|
|
63
|
-
failingStore(new ResourceNotFoundError("agent_execution/aex_missing")),
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
expect(error.code).toBe(Code.NotFound);
|
|
67
|
-
expect(error.rawMessage).toBe("agent_execution not found: aex_missing");
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("any other store failure answers a sanitized Internal, never NotFound", async () => {
|
|
71
|
-
const error = await reportError(
|
|
72
|
-
failingStore(new Error("SQLITE_BUSY: database is locked")),
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
expect(error.code).toBe(Code.Internal);
|
|
76
|
-
expect(error.rawMessage).toBe("failed to load agent execution");
|
|
77
|
-
});
|
|
78
|
-
});
|