effect-agent 0.1.0-beta.103 → 0.1.0-beta.106
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/{Agent-CM6JubNX.d.mts → Agent-Bk-mLtov.d.mts} +2 -2
- package/dist/{AgentError-CggcAQ7V.d.mts → AgentError-Bn1ytXRD.d.mts} +20 -4
- package/dist/{AgentRuntime-B1SXMVN7.d.mts → AgentRuntime-BEXToYl1.d.mts} +8 -8
- package/dist/{AgentRuntime-BWOnNc0d.mjs → AgentRuntime-e2M5UoXn.mjs} +4 -3
- package/dist/AgentRuntime-e2M5UoXn.mjs.map +1 -0
- package/dist/{DurableAgentRuntime-C9eRZS9N.mjs → DurableAgentRuntime-wd1x0TcB.mjs} +67 -36
- package/dist/DurableAgentRuntime-wd1x0TcB.mjs.map +1 -0
- package/dist/{EventSource-K7XUzurY.d.mts → EventSource-pH-v0k42.d.mts} +3 -3
- package/dist/FailureDiagnostic-BElLOoED.mjs +274 -0
- package/dist/FailureDiagnostic-BElLOoED.mjs.map +1 -0
- package/dist/FailureDiagnostic-CaAkzGHL.d.mts +223 -0
- package/dist/{Messaging-QMYhp297.d.mts → Messaging-Bl4SBKpR.d.mts} +3 -3
- package/dist/{MessagingHost-gAxs5gme.d.mts → MessagingHost-nwBMdRoh.d.mts} +3 -3
- package/dist/{Output-CjocJzsl.d.mts → Output-B6RQ5jRU.d.mts} +2 -2
- package/dist/{PreparedInputAdmission-KLk5rGFI.d.mts → PreparedInputAdmission-D3-BgmA9.d.mts} +8 -4
- package/dist/{Records-Bidb7ByL.d.mts → Records-B4S02MX1.d.mts} +201 -14
- package/dist/{RunJournal-srpbEBEy.mjs → RunJournal-BdIlgAEj.mjs} +2 -2
- package/dist/{RunJournal-srpbEBEy.mjs.map → RunJournal-BdIlgAEj.mjs.map} +1 -1
- package/dist/{RunOptions-Bruk54SE.d.mts → RunOptions-qOcPv08p.d.mts} +10 -5
- package/dist/{Schedule-Cy9up8Uf.d.mts → Schedule-D2TVEu3W.d.mts} +7 -2
- package/dist/{Scheduling-CHCsSCGo.mjs → Scheduling-DPb2QyAK.mjs} +2 -2
- package/dist/{Scheduling-CHCsSCGo.mjs.map → Scheduling-DPb2QyAK.mjs.map} +1 -1
- package/dist/{Subagent-DP0CzO16.d.mts → Subagent-BxuDuCHt.d.mts} +172 -8
- package/dist/{Subagent-C2s8PFrd.mjs → Subagent-uXWHXiKb.mjs} +64 -29
- package/dist/Subagent-uXWHXiKb.mjs.map +1 -0
- package/dist/{SubagentHost-CYcxkpRc.d.mts → SubagentHost-DEPw4YME.d.mts} +6 -3
- package/dist/{Subscription-CHyoQelH.d.mts → Subscription-nX0rz4GQ.d.mts} +3 -3
- package/dist/{SubscriptionInput-DqrHW-Ja.d.mts → SubscriptionInput-CAG3_pyJ.d.mts} +3 -3
- package/dist/{Worker--j87JI__.d.mts → Worker-Bbj9uq5H.d.mts} +5 -2
- package/dist/{agent-registration-ByeHejH3.mjs → agent-registration-B5CI-Ygk.mjs} +7 -5
- package/dist/agent-registration-B5CI-Ygk.mjs.map +1 -0
- package/dist/capabilities/Commands.d.mts +1 -1
- package/dist/capabilities/Messaging.d.mts +2 -2
- package/dist/capabilities/RunHooks.d.mts +1 -1
- package/dist/capabilities/Subagent.d.mts +2 -2
- package/dist/capabilities/Subagent.mjs +1 -1
- package/dist/core/Agent.d.mts +1 -1
- package/dist/core/AgentError.d.mts +2 -2
- package/dist/core/AgentError.mjs +19 -2
- package/dist/core/AgentError.mjs.map +1 -1
- package/dist/core/FailureDiagnostic.d.mts +2 -0
- package/dist/core/FailureDiagnostic.mjs +2 -0
- package/dist/core/Worker.d.mts +1 -1
- package/dist/core/Worker.mjs +4 -1
- package/dist/core/Worker.mjs.map +1 -1
- package/dist/durable/ActivityStore.d.mts +1 -1
- package/dist/durable/ActivityStore.mjs +1 -1
- package/dist/durable/Admin.d.mts +1 -1
- package/dist/durable/Admin.mjs +1 -1
- package/dist/durable/AgentRegistration.d.mts +1 -1
- package/dist/durable/AgentRegistration.mjs +1 -1
- package/dist/durable/Certification.d.mts +1 -1
- package/dist/durable/CommittedActivity.d.mts +1 -1
- package/dist/durable/CommittedActivity.mjs +1 -1
- package/dist/durable/Digest.d.mts +1 -1
- package/dist/durable/Digest.mjs +7 -7
- package/dist/durable/Digest.mjs.map +1 -1
- package/dist/durable/DurableAgentRuntime.d.mts +1 -1
- package/dist/durable/DurableAgentRuntime.mjs +1 -1
- package/dist/durable/EventSource.d.mts +1 -1
- package/dist/durable/EventSource.mjs +1 -1
- package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
- package/dist/durable/GitHubWorkflowSource.mjs +1 -1
- package/dist/durable/MessageDelivery.d.mts +15 -3
- package/dist/durable/MessageDelivery.mjs +44 -24
- package/dist/durable/MessageDelivery.mjs.map +1 -1
- package/dist/durable/MessageDeliveryStoreConformance.d.mts +4 -1
- package/dist/durable/MessageDeliveryStoreConformance.mjs +1 -1
- package/dist/durable/PersistentHistory.d.mts +1 -1
- package/dist/durable/PersistentHistory.mjs +2 -2
- package/dist/durable/PreparedInputAdmission.d.mts +1 -1
- package/dist/durable/PreparedInputAdmission.mjs +8 -4
- package/dist/durable/PreparedInputAdmission.mjs.map +1 -1
- package/dist/durable/Records.d.mts +1 -1
- package/dist/durable/Records.mjs +831 -2
- package/dist/durable/Records.mjs.map +1 -0
- package/dist/durable/Recovery.d.mts +1 -1
- package/dist/durable/Recovery.mjs +1 -1
- package/dist/durable/RunJournal.d.mts +1 -1
- package/dist/durable/RunJournal.mjs +1 -1
- package/dist/durable/Schedule.d.mts +1 -1
- package/dist/durable/Schedule.mjs +12 -3
- package/dist/durable/Schedule.mjs.map +1 -1
- package/dist/durable/ScheduleStoreConformance.d.mts +1 -1
- package/dist/durable/ScheduleStoreConformance.mjs +1 -1
- package/dist/durable/ScheduleTransition.d.mts +1 -1
- package/dist/durable/ScheduleTransition.mjs +1 -1
- package/dist/durable/Scheduling.d.mts +2 -2
- package/dist/durable/Scheduling.mjs +1 -1
- package/dist/durable/SqlMessageDeliveryStore.mjs +13 -11
- package/dist/durable/SqlMessageDeliveryStore.mjs.map +1 -1
- package/dist/durable/SqlStorageV2Upgrade.d.mts +2 -2
- package/dist/durable/SqlStorageV2Upgrade.mjs +1 -1
- package/dist/durable/SqlSubscriptionStore.d.mts +2 -2
- package/dist/durable/SqlSubscriptionStore.mjs +1 -1
- package/dist/durable/SqlThreadNativeReads.d.mts +1 -1
- package/dist/durable/SqlThreadNativeReads.mjs +2 -2
- package/dist/durable/SubmissionLedger.d.mts +1 -1
- package/dist/durable/SubmissionLedger.mjs +6 -3
- package/dist/durable/SubmissionLedger.mjs.map +1 -1
- package/dist/durable/SubmissionLedgerConformance.d.mts +1 -1
- package/dist/durable/SubmissionLedgerConformance.mjs +1 -1
- package/dist/durable/SubmissionStatus.d.mts +1 -1
- package/dist/durable/Subscription.d.mts +1 -1
- package/dist/durable/Subscription.mjs +1 -1
- package/dist/durable/SubscriptionInput.d.mts +1 -1
- package/dist/durable/SubscriptionInput.mjs +2 -2
- package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
- package/dist/durable/SubscriptionStoreConformance.mjs +1 -1
- package/dist/durable/SubscriptionTools.d.mts +3 -3
- package/dist/durable/SubscriptionTransition.d.mts +1 -1
- package/dist/durable/SubscriptionTransition.mjs +1 -1
- package/dist/durable/Subscriptions.d.mts +5 -5
- package/dist/durable/Subscriptions.mjs +1 -1
- package/dist/durable/ThreadContextHistory.d.mts +1 -1
- package/dist/durable/ThreadContextHistory.mjs +1 -1
- package/dist/durable/ThreadContextHistoryProjection.d.mts +1 -1
- package/dist/durable/ThreadContextHistoryProjection.mjs +1 -1
- package/dist/durable/ThreadInvariants.d.mts +1 -1
- package/dist/durable/ThreadInvariants.mjs +2 -2
- package/dist/durable/ThreadProjection.d.mts +1 -1
- package/dist/durable/ThreadProjection.mjs +1 -1
- package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
- package/dist/durable/ThreadStore.d.mts +1 -1
- package/dist/durable/ThreadStore.mjs +2 -2
- package/dist/durable/ThreadStoreConformance.d.mts +1 -1
- package/dist/durable/ThreadStoreConformance.mjs +1 -1
- package/dist/durable/ToolReconciler.mjs +1 -1
- package/dist/durable/WorkerHost.d.mts +3 -3
- package/dist/engine/AgentRuntime.d.mts +1 -1
- package/dist/engine/AgentRuntime.mjs +1 -1
- package/dist/engine/MessagingHost.d.mts +1 -1
- package/dist/engine/Output.d.mts +1 -1
- package/dist/engine/RunOptions.d.mts +1 -1
- package/dist/engine/RunOptions.mjs +2 -0
- package/dist/engine/RunOptions.mjs.map +1 -1
- package/dist/engine/SubagentHost.d.mts +1 -1
- package/dist/engine/SubagentHost.mjs.map +1 -1
- package/dist/index.d.mts +18 -17
- package/dist/index.mjs +8 -7
- package/package.json +1 -1
- package/src/capabilities/internal/subagent-background.ts +66 -23
- package/src/capabilities/internal/subagent-contract.ts +4 -0
- package/src/core/AgentError.ts +20 -0
- package/src/core/FailureDiagnostic.ts +343 -0
- package/src/core/Worker.ts +4 -1
- package/src/durable/Digest.ts +22 -28
- package/src/durable/DurableAgentRuntime.ts +37 -13
- package/src/durable/MessageDelivery.ts +82 -24
- package/src/durable/PreparedInputAdmission.ts +17 -4
- package/src/durable/Records.ts +5 -15
- package/src/durable/Schedule.ts +11 -2
- package/src/durable/SqlMessageDeliveryStore.ts +21 -9
- package/src/durable/SubmissionLedger.ts +4 -1
- package/src/durable/internal/agent-registration.ts +46 -14
- package/src/durable/internal/worker-host.ts +32 -19
- package/src/engine/RunOptions.ts +11 -3
- package/src/engine/SubagentHost.ts +3 -0
- package/src/engine/internal/agent-runtime.ts +23 -16
- package/src/index.ts +1 -0
- package/dist/AgentRuntime-BWOnNc0d.mjs.map +0 -1
- package/dist/DurableAgentRuntime-C9eRZS9N.mjs.map +0 -1
- package/dist/Records-D8167fHn.mjs +0 -844
- package/dist/Records-D8167fHn.mjs.map +0 -1
- package/dist/Subagent-C2s8PFrd.mjs.map +0 -1
- package/dist/agent-registration-ByeHejH3.mjs.map +0 -1
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Cause as EffectCause,
|
|
3
|
+
Exit,
|
|
4
|
+
Predicate,
|
|
5
|
+
Redacted,
|
|
6
|
+
Schema,
|
|
7
|
+
SchemaTransformation,
|
|
8
|
+
} from "effect";
|
|
9
|
+
|
|
10
|
+
import { strictSchema } from "./internal/strict-schema.ts";
|
|
11
|
+
|
|
12
|
+
const Text = Schema.String.check(Schema.isMaxLength(16 * 1024));
|
|
13
|
+
const Name = Schema.String.check(Schema.isMaxLength(1_024));
|
|
14
|
+
const Scalar = Schema.Union([Text, Schema.Finite, Schema.Boolean, Schema.Null]);
|
|
15
|
+
|
|
16
|
+
/** Correlation only: never include inputs, tool parameters, request bodies, or credentials. */
|
|
17
|
+
export const Context = Schema.Struct({
|
|
18
|
+
agentId: Schema.optionalKey(Name),
|
|
19
|
+
threadId: Schema.optionalKey(Name),
|
|
20
|
+
runId: Schema.optionalKey(Name),
|
|
21
|
+
submissionId: Schema.optionalKey(Name),
|
|
22
|
+
attemptId: Schema.optionalKey(Name),
|
|
23
|
+
turnId: Schema.optionalKey(Name),
|
|
24
|
+
toolCallId: Schema.optionalKey(Name),
|
|
25
|
+
toolName: Schema.optionalKey(Name),
|
|
26
|
+
callId: Schema.optionalKey(Name),
|
|
27
|
+
check: Schema.optionalKey(Name),
|
|
28
|
+
operation: Schema.optionalKey(Name),
|
|
29
|
+
}).pipe(strictSchema);
|
|
30
|
+
|
|
31
|
+
export type Context = typeof Context.Type;
|
|
32
|
+
|
|
33
|
+
type CauseReason =
|
|
34
|
+
| { readonly _tag: "Fail"; readonly error: Diagnostic }
|
|
35
|
+
| { readonly _tag: "Die"; readonly defect: Diagnostic }
|
|
36
|
+
| { readonly _tag: "Interrupt"; readonly fiberId?: number };
|
|
37
|
+
|
|
38
|
+
/** A private diagnostic projection, not a JavaScript error reconstruction or model response. */
|
|
39
|
+
export type Diagnostic =
|
|
40
|
+
| {
|
|
41
|
+
readonly _tag: "Error";
|
|
42
|
+
readonly errorTag?: string;
|
|
43
|
+
readonly name?: string;
|
|
44
|
+
readonly message?: string;
|
|
45
|
+
readonly stack?: string;
|
|
46
|
+
readonly reason?: Diagnostic;
|
|
47
|
+
readonly code?: string | number;
|
|
48
|
+
readonly cause?: Diagnostic;
|
|
49
|
+
readonly errors?: ReadonlyArray<Diagnostic>;
|
|
50
|
+
readonly context?: Context;
|
|
51
|
+
readonly omittedFields?: ReadonlyArray<string>;
|
|
52
|
+
readonly truncated?: true;
|
|
53
|
+
}
|
|
54
|
+
| {
|
|
55
|
+
readonly _tag: "Cause";
|
|
56
|
+
readonly reasons: ReadonlyArray<CauseReason>;
|
|
57
|
+
readonly truncated?: true;
|
|
58
|
+
}
|
|
59
|
+
| { readonly _tag: "Value"; readonly value: string | number | boolean | null }
|
|
60
|
+
| { readonly _tag: "Omitted"; readonly reason: "redacted" | "cycle" | "limit" | "unsupported" };
|
|
61
|
+
|
|
62
|
+
const diagnosticSchema = <Nested extends Schema.Top>(nested: Nested) =>
|
|
63
|
+
Schema.Union([
|
|
64
|
+
Schema.Struct({
|
|
65
|
+
_tag: Schema.Literal("Error"),
|
|
66
|
+
errorTag: Schema.optionalKey(Name),
|
|
67
|
+
name: Schema.optionalKey(Name),
|
|
68
|
+
message: Schema.optionalKey(Text),
|
|
69
|
+
stack: Schema.optionalKey(Text),
|
|
70
|
+
reason: Schema.optionalKey(nested),
|
|
71
|
+
code: Schema.optionalKey(Schema.Union([Name, Schema.Finite])),
|
|
72
|
+
cause: Schema.optionalKey(nested),
|
|
73
|
+
errors: Schema.optionalKey(Schema.Array(nested).check(Schema.isMaxLength(128))),
|
|
74
|
+
context: Schema.optionalKey(Context),
|
|
75
|
+
omittedFields: Schema.optionalKey(Schema.Array(Name).check(Schema.isMaxLength(32))),
|
|
76
|
+
truncated: Schema.optionalKey(Schema.Literal(true)),
|
|
77
|
+
}),
|
|
78
|
+
Schema.Struct({
|
|
79
|
+
_tag: Schema.Literal("Cause"),
|
|
80
|
+
reasons: Schema.Array(
|
|
81
|
+
Schema.Union([
|
|
82
|
+
Schema.Struct({ _tag: Schema.Literal("Fail"), error: nested }),
|
|
83
|
+
Schema.Struct({ _tag: Schema.Literal("Die"), defect: nested }),
|
|
84
|
+
Schema.Struct({
|
|
85
|
+
_tag: Schema.Literal("Interrupt"),
|
|
86
|
+
fiberId: Schema.optionalKey(Schema.Finite),
|
|
87
|
+
}),
|
|
88
|
+
]),
|
|
89
|
+
).check(Schema.isMaxLength(128)),
|
|
90
|
+
truncated: Schema.optionalKey(Schema.Literal(true)),
|
|
91
|
+
}),
|
|
92
|
+
Schema.Struct({ _tag: Schema.Literal("Value"), value: Scalar }),
|
|
93
|
+
Schema.Struct({
|
|
94
|
+
_tag: Schema.Literal("Omitted"),
|
|
95
|
+
reason: Schema.Literals(["redacted", "cycle", "limit", "unsupported"]),
|
|
96
|
+
}),
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
/** Structured, allowlisted Error fields and ordered Effect Fail/Die/Interrupt reasons. */
|
|
100
|
+
export const Diagnostic: Schema.Codec<Diagnostic> = Schema.suspend(() =>
|
|
101
|
+
diagnosticSchema(Diagnostic),
|
|
102
|
+
).pipe(strictSchema);
|
|
103
|
+
|
|
104
|
+
// Inspect one encoded node only; recursion must share capture's depth/node/text budgets.
|
|
105
|
+
const decodeDiagnosticNode = Schema.decodeUnknownExit(
|
|
106
|
+
diagnosticSchema(Schema.Unknown).pipe(strictSchema),
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
/** Remove common credential forms; hosts must keep arbitrary private payloads out of error text. */
|
|
110
|
+
export const redactText = (text: string): string =>
|
|
111
|
+
text
|
|
112
|
+
.replace(/\b(Bearer|Basic)\s+[A-Za-z0-9._~+/=-]+/gi, "$1 [REDACTED]")
|
|
113
|
+
.replace(/(https?:\/\/)[^\s/@]+:[^\s/@]+@/gi, "$1[REDACTED]@")
|
|
114
|
+
.replace(
|
|
115
|
+
/([?&](?:access_token|api_key|apikey|token|key|secret|password|signature)=)[^\s&#]*/gi,
|
|
116
|
+
"$1[REDACTED]",
|
|
117
|
+
)
|
|
118
|
+
.replace(
|
|
119
|
+
/\b((?:api[_-]?key|access[_-]?token|authorization|password|secret)\s*[=:]\s*)[^\s,;}]+/gi,
|
|
120
|
+
"$1[REDACTED]",
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Capture only diagnostic fields. Local values are never mutated. Cycles and exhausted limits
|
|
125
|
+
* are explicit, not silently dropped; capture visits at most 1,024 nodes, with 56 JSON levels
|
|
126
|
+
* and 128 KiB text. Inner causes have priority over outer stacks when the text budget is exhausted.
|
|
127
|
+
* Text remains operator-private. No generic serializer can identify secrets embedded in arbitrary
|
|
128
|
+
* prose: application error messages must exclude payloads, and hosts may supply stricter redaction.
|
|
129
|
+
*/
|
|
130
|
+
export const capture = (
|
|
131
|
+
value: unknown,
|
|
132
|
+
options?: { readonly redactText?: (text: string) => string },
|
|
133
|
+
): Diagnostic => {
|
|
134
|
+
const seen = new WeakSet<object>();
|
|
135
|
+
let remainingNodes = 1_024;
|
|
136
|
+
let remainingText = 128 * 1024;
|
|
137
|
+
|
|
138
|
+
const text = (value: string, limit = 16 * 1024): string => {
|
|
139
|
+
const safe = (options?.redactText ?? redactText)(value);
|
|
140
|
+
const length = Math.max(0, Math.min(limit, remainingText));
|
|
141
|
+
|
|
142
|
+
const marker = "[truncated]".slice(0, length);
|
|
143
|
+
|
|
144
|
+
const result =
|
|
145
|
+
safe.length <= length ? safe : `${safe.slice(0, length - marker.length)}${marker}`;
|
|
146
|
+
|
|
147
|
+
remainingText -= result.length;
|
|
148
|
+
|
|
149
|
+
return result;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const visit = (value: unknown, depth: number): Diagnostic => {
|
|
153
|
+
if (remainingNodes <= 0 || depth >= 56) return { _tag: "Omitted", reason: "limit" };
|
|
154
|
+
remainingNodes--;
|
|
155
|
+
if (Redacted.isRedacted(value)) return { _tag: "Omitted", reason: "redacted" };
|
|
156
|
+
if (Predicate.isString(value)) return { _tag: "Value", value: text(value) };
|
|
157
|
+
if (
|
|
158
|
+
value === null ||
|
|
159
|
+
Predicate.isBoolean(value) ||
|
|
160
|
+
(Predicate.isNumber(value) && Number.isFinite(value))
|
|
161
|
+
)
|
|
162
|
+
return { _tag: "Value", value };
|
|
163
|
+
if (!Predicate.isObject(value)) return { _tag: "Omitted", reason: "unsupported" };
|
|
164
|
+
if (seen.has(value)) return { _tag: "Omitted", reason: "cycle" };
|
|
165
|
+
seen.add(value);
|
|
166
|
+
const nested = (value: unknown) => visit(value, depth + 1);
|
|
167
|
+
// Local errors retain their own tag, even when it happens to be "Error".
|
|
168
|
+
const decoded = value instanceof Error ? undefined : decodeDiagnosticNode(value);
|
|
169
|
+
const node = decoded !== undefined && Exit.isSuccess(decoded) ? decoded.value : undefined;
|
|
170
|
+
|
|
171
|
+
if (node?._tag === "Omitted" || node?._tag === "Value") {
|
|
172
|
+
seen.delete(value);
|
|
173
|
+
|
|
174
|
+
return node._tag === "Value" && typeof node.value === "string"
|
|
175
|
+
? { _tag: "Value", value: text(node.value) }
|
|
176
|
+
: node;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const originalReasons = EffectCause.isCause(value)
|
|
180
|
+
? value.reasons
|
|
181
|
+
: node?._tag === "Cause"
|
|
182
|
+
? node.reasons
|
|
183
|
+
: undefined;
|
|
184
|
+
|
|
185
|
+
if (originalReasons !== undefined) {
|
|
186
|
+
const reasons: Array<CauseReason> = [];
|
|
187
|
+
|
|
188
|
+
for (const reason of originalReasons) {
|
|
189
|
+
if (reasons.length >= 128 || remainingNodes <= 0) break;
|
|
190
|
+
remainingNodes--;
|
|
191
|
+
|
|
192
|
+
// reasons -> array item -> error/defect contributes three JSON levels.
|
|
193
|
+
switch (reason._tag) {
|
|
194
|
+
case "Fail":
|
|
195
|
+
reasons.push({ _tag: "Fail", error: visit(reason.error, depth + 3) });
|
|
196
|
+
break;
|
|
197
|
+
case "Die":
|
|
198
|
+
reasons.push({ _tag: "Die", defect: visit(reason.defect, depth + 3) });
|
|
199
|
+
break;
|
|
200
|
+
case "Interrupt":
|
|
201
|
+
reasons.push({
|
|
202
|
+
_tag: "Interrupt",
|
|
203
|
+
...(reason.fiberId === undefined ? {} : { fiberId: reason.fiberId }),
|
|
204
|
+
});
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const result: Diagnostic = {
|
|
210
|
+
_tag: "Cause",
|
|
211
|
+
...(originalReasons.length > reasons.length || (node?._tag === "Cause" && node.truncated)
|
|
212
|
+
? { truncated: true }
|
|
213
|
+
: {}),
|
|
214
|
+
reasons,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
seen.delete(value);
|
|
218
|
+
|
|
219
|
+
return result;
|
|
220
|
+
}
|
|
221
|
+
const retained = node?._tag === "Error" ? node : undefined;
|
|
222
|
+
const omittedFields: Array<string> = [];
|
|
223
|
+
|
|
224
|
+
const read = (key: string): unknown => {
|
|
225
|
+
// A throwing accessor must not replace the original failure during diagnostic capture.
|
|
226
|
+
try {
|
|
227
|
+
return Reflect.get(value, key);
|
|
228
|
+
} catch {
|
|
229
|
+
omittedFields.push(key);
|
|
230
|
+
|
|
231
|
+
return undefined;
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const tag = retained === undefined ? read("_tag") : retained.errorTag;
|
|
236
|
+
const name = read("name");
|
|
237
|
+
const message = read("message");
|
|
238
|
+
const stack = read("stack");
|
|
239
|
+
const reason = read("reason");
|
|
240
|
+
const code = read("code");
|
|
241
|
+
const cause = read("cause");
|
|
242
|
+
const errors = read("errors");
|
|
243
|
+
const capturedCause = cause === undefined ? undefined : nested(cause);
|
|
244
|
+
const capturedReason = reason === undefined ? undefined : nested(reason);
|
|
245
|
+
const capturedErrors: Array<Diagnostic> = [];
|
|
246
|
+
|
|
247
|
+
if (Array.isArray(errors))
|
|
248
|
+
for (const error of errors) {
|
|
249
|
+
if (capturedErrors.length >= 128 || remainingNodes <= 0) break;
|
|
250
|
+
capturedErrors.push(visit(error, depth + 2));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
for (const field of retained?.omittedFields ?? []) omittedFields.push(text(field, 1_024));
|
|
254
|
+
|
|
255
|
+
const context: Record<string, string> = {};
|
|
256
|
+
|
|
257
|
+
for (const key of [
|
|
258
|
+
"agentId",
|
|
259
|
+
"threadId",
|
|
260
|
+
"runId",
|
|
261
|
+
"submissionId",
|
|
262
|
+
"attemptId",
|
|
263
|
+
"turnId",
|
|
264
|
+
"toolCallId",
|
|
265
|
+
"toolName",
|
|
266
|
+
"callId",
|
|
267
|
+
"check",
|
|
268
|
+
"operation",
|
|
269
|
+
] as const) {
|
|
270
|
+
const field = retained === undefined ? read(key) : retained.context?.[key];
|
|
271
|
+
|
|
272
|
+
if (Predicate.isString(field)) context[key] = text(field, 1_024);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const result: Diagnostic = {
|
|
276
|
+
_tag: "Error",
|
|
277
|
+
...(Predicate.isString(tag) ? { errorTag: text(tag, 1_024) } : {}),
|
|
278
|
+
...(Predicate.isString(name) ? { name: text(name, 1_024) } : {}),
|
|
279
|
+
...(Predicate.isString(message) ? { message: text(message) } : {}),
|
|
280
|
+
...(Predicate.isString(stack) ? { stack: text(stack) } : {}),
|
|
281
|
+
...(capturedReason === undefined ? {} : { reason: capturedReason }),
|
|
282
|
+
...(Predicate.isString(code)
|
|
283
|
+
? { code: text(code, 1_024) }
|
|
284
|
+
: Predicate.isNumber(code) && Number.isFinite(code)
|
|
285
|
+
? { code }
|
|
286
|
+
: {}),
|
|
287
|
+
...(capturedCause === undefined ? {} : { cause: capturedCause }),
|
|
288
|
+
...(Array.isArray(errors) ? { errors: capturedErrors } : {}),
|
|
289
|
+
...(Object.keys(context).length === 0 ? {} : { context }),
|
|
290
|
+
...(omittedFields.length === 0 ? {} : { omittedFields }),
|
|
291
|
+
...(retained?.truncated ||
|
|
292
|
+
(Array.isArray(errors) && errors.length > capturedErrors.length) ||
|
|
293
|
+
remainingText === 0
|
|
294
|
+
? { truncated: true }
|
|
295
|
+
: {}),
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
seen.delete(value);
|
|
299
|
+
|
|
300
|
+
return result;
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
return visit(value, 0);
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
/** Bound and redact a diagnostic copy; canonical identities stay in their owning records. */
|
|
307
|
+
export const captureContext = (context: Context): Context => {
|
|
308
|
+
const diagnostic = capture(context);
|
|
309
|
+
|
|
310
|
+
return diagnostic._tag === "Error" ? (diagnostic.context ?? {}) : {};
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Preserve the original value in-process; across JSON retain its structured diagnostic projection.
|
|
315
|
+
* Decoding returns Diagnostic data, never claims to reconstruct an application error class.
|
|
316
|
+
*/
|
|
317
|
+
export const Value = Diagnostic.pipe(
|
|
318
|
+
Schema.decodeTo(
|
|
319
|
+
Schema.Unknown,
|
|
320
|
+
SchemaTransformation.transform({
|
|
321
|
+
decode: (diagnostic): unknown => diagnostic,
|
|
322
|
+
encode: (value: unknown) => capture(value),
|
|
323
|
+
}),
|
|
324
|
+
),
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
/** Original local Effect Cause; its JSON codec preserves ordered typed, defect and interrupt reasons. */
|
|
328
|
+
export const Cause = Schema.Cause(Value, Value).pipe(
|
|
329
|
+
Schema.decodeTo(
|
|
330
|
+
Schema.declare<EffectCause.Cause<unknown>>(EffectCause.isCause),
|
|
331
|
+
SchemaTransformation.transform({ decode: (cause) => cause, encode: (cause) => cause }),
|
|
332
|
+
),
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
/** Safe summary plus optional operator-private causal evidence retained at a terminal boundary. */
|
|
336
|
+
export const Failure = Schema.Struct({
|
|
337
|
+
errorTag: Schema.NonEmptyString.check(Schema.isMaxLength(256)),
|
|
338
|
+
message: Text,
|
|
339
|
+
diagnostic: Schema.optionalKey(Diagnostic),
|
|
340
|
+
context: Schema.optionalKey(Context),
|
|
341
|
+
}).pipe(strictSchema);
|
|
342
|
+
|
|
343
|
+
export type Failure = typeof Failure.Type;
|
package/src/core/Worker.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Schema } from "effect";
|
|
|
2
2
|
|
|
3
3
|
import { AgentPolicy } from "./AgentPolicy.ts";
|
|
4
4
|
import { Update } from "./AgentUpdates.ts";
|
|
5
|
+
import * as FailureDiagnostic from "./FailureDiagnostic.ts";
|
|
5
6
|
import { AgentId, DelegationId, RunId, SettlementId, ThreadId, ToolCallId } from "./Identifiers.ts";
|
|
6
7
|
import { Receipt } from "./Receipt.ts";
|
|
7
8
|
import { SubagentExecutionFailure, SubagentGrant } from "./SubagentContract.ts";
|
|
@@ -82,11 +83,13 @@ export const WorkerHistoryEntry = Schema.Struct({
|
|
|
82
83
|
export type WorkerHistoryEntry = typeof WorkerHistoryEntry.Type;
|
|
83
84
|
|
|
84
85
|
/**
|
|
85
|
-
* Closed host-boundary failures.
|
|
86
|
+
* Closed host-boundary failures. Original causes stay private and survive diagnostic transport.
|
|
86
87
|
* `delivery-pending` confirms retained input, not destination acceptance or execution.
|
|
87
88
|
* Keep the same idempotency key and parameters when reconciling; never launch a replacement.
|
|
88
89
|
*/
|
|
89
90
|
export class WorkerError extends Schema.TaggedError<WorkerError>()("WorkerError", {
|
|
91
|
+
cause: Schema.optionalKey(FailureDiagnostic.Value),
|
|
92
|
+
stack: Schema.optionalKey(Schema.String),
|
|
90
93
|
/** Pending input or concurrency pressure may clear without changing the request. */
|
|
91
94
|
retryable: Schema.optionalKey(Schema.Literal(true)),
|
|
92
95
|
operation: Schema.Literals([
|
package/src/durable/Digest.ts
CHANGED
|
@@ -34,7 +34,7 @@ const canonicalJson = (value: Schema.Json): string => {
|
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
/** Digest a JSON value using a stable, locale-independent object-key ordering (UTF-16 code units, RFC 8785 style). */
|
|
37
|
-
export const digestJson = Effect.
|
|
37
|
+
export const digestJson = Effect.fnUntraced(function* (
|
|
38
38
|
value: Schema.Json,
|
|
39
39
|
): Effect.fn.Return<Digest, DigestError, Crypto.Crypto> {
|
|
40
40
|
const crypto = yield* Crypto.Crypto;
|
|
@@ -55,38 +55,32 @@ export const digestJson = Effect.fn("Thread.digestJson")(function* (
|
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
/** Digest a canonical batch together with the prior tail to form an append-only hash chain. */
|
|
58
|
-
export const digestCanonicalBatch =
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}),
|
|
70
|
-
),
|
|
58
|
+
export const digestCanonicalBatch = (
|
|
59
|
+
previousTailDigest: Digest,
|
|
60
|
+
batch: CanonicalBatch,
|
|
61
|
+
): Effect.Effect<Digest, DigestError, Crypto.Crypto> =>
|
|
62
|
+
Schema.encodeEffect(CanonicalBatch)(batch).pipe(
|
|
63
|
+
Effect.mapError(() => DigestError.make({ message: "Canonical batch encoding failed" })),
|
|
64
|
+
Effect.flatMap((encoded) =>
|
|
65
|
+
digestJson({
|
|
66
|
+
previousTailDigest,
|
|
67
|
+
batch: encoded,
|
|
68
|
+
}),
|
|
71
69
|
),
|
|
72
|
-
);
|
|
70
|
+
);
|
|
73
71
|
|
|
74
72
|
/** Digest one schema-encoded Agent, Model, or Toolkit definition. */
|
|
75
|
-
export const digestDefinition =
|
|
76
|
-
digestJson(definition),
|
|
77
|
-
);
|
|
73
|
+
export const digestDefinition = (definition: Schema.Json) => digestJson(definition);
|
|
78
74
|
|
|
79
75
|
/** Digest all replay-relevant definitions without hiding which authority changed. */
|
|
80
|
-
export const digestDefinitions =
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}).pipe(Effect.map((digests) => DefinitionDigests.make(digests))),
|
|
89
|
-
);
|
|
76
|
+
export const digestDefinitions = (
|
|
77
|
+
definitions: DefinitionDigestInput,
|
|
78
|
+
): Effect.Effect<DefinitionDigests, DigestError, Crypto.Crypto> =>
|
|
79
|
+
Effect.all({
|
|
80
|
+
agent: digestDefinition(definitions.agent),
|
|
81
|
+
model: digestDefinition(definitions.model),
|
|
82
|
+
tools: digestDefinition(definitions.tools),
|
|
83
|
+
}).pipe(Effect.map((digests) => DefinitionDigests.make(digests)));
|
|
90
84
|
|
|
91
85
|
export const EMPTY_TAIL_DIGEST = Schema.decodeSync(Digest)(
|
|
92
86
|
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
@@ -31,10 +31,12 @@ import {
|
|
|
31
31
|
AgentApprovalPending,
|
|
32
32
|
AgentInputError,
|
|
33
33
|
AgentToolAuthorizationDenied,
|
|
34
|
+
type AgentToolAuthorizationCheckError,
|
|
34
35
|
PolicyLimit,
|
|
35
36
|
} from "../core/AgentError.ts";
|
|
36
37
|
import { AgentPolicy } from "../core/AgentPolicy.ts";
|
|
37
38
|
import { UpdateError } from "../core/AgentUpdates.ts";
|
|
39
|
+
import * as FailureDiagnostic from "../core/FailureDiagnostic.ts";
|
|
38
40
|
import {
|
|
39
41
|
type ReceiptId,
|
|
40
42
|
ThreadId,
|
|
@@ -2922,7 +2924,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
2922
2924
|
.completeInput(submission)
|
|
2923
2925
|
.pipe(
|
|
2924
2926
|
Effect.mapError((cause) =>
|
|
2925
|
-
LedgerError.make({ operation: "worker-completion", message: cause.reason }),
|
|
2927
|
+
LedgerError.make({ operation: "worker-completion", message: cause.reason, cause }),
|
|
2926
2928
|
),
|
|
2927
2929
|
);
|
|
2928
2930
|
const linkage = submission.parentLinkage;
|
|
@@ -4231,12 +4233,18 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
4231
4233
|
}
|
|
4232
4234
|
});
|
|
4233
4235
|
|
|
4234
|
-
const failureOutcome = (
|
|
4236
|
+
const failureOutcome = (
|
|
4237
|
+
error: unknown,
|
|
4238
|
+
cause: Cause.Cause<unknown>,
|
|
4239
|
+
context: FailureDiagnostic.Context,
|
|
4240
|
+
): Effect.Effect<AttemptOutcome> =>
|
|
4235
4241
|
Schema.decodeEffect(SettlementFailureDiagnostic)({
|
|
4236
4242
|
errorTag: errorTagOf(error).slice(0, 256) || "UnknownError",
|
|
4237
4243
|
message: errorMessageOf(error).slice(0, MAX_FAILURE_MESSAGE_LENGTH),
|
|
4244
|
+
diagnostic: FailureDiagnostic.capture(cause),
|
|
4245
|
+
context: FailureDiagnostic.captureContext(context),
|
|
4238
4246
|
}).pipe(
|
|
4239
|
-
// The exact diagnostic Schema admits
|
|
4247
|
+
// The exact diagnostic Schema admits only causal fields, never arbitrary provider payloads.
|
|
4240
4248
|
Effect.orDie,
|
|
4241
4249
|
Effect.map((result) => ({
|
|
4242
4250
|
_tag: "failed" as const,
|
|
@@ -6729,7 +6737,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
6729
6737
|
join: joinSubagent,
|
|
6730
6738
|
};
|
|
6731
6739
|
|
|
6732
|
-
const toolAuthorization: RunToolAuthorizationHook = {
|
|
6740
|
+
const toolAuthorization: RunToolAuthorizationHook<AgentToolAuthorizationCheckError> = {
|
|
6733
6741
|
authorize: (request: RunToolAuthorizationRequest) =>
|
|
6734
6742
|
inheritedGrant !== undefined &&
|
|
6735
6743
|
!inheritedGrant.allowedToolNames.includes(request.call.toolName)
|
|
@@ -6750,6 +6758,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
6750
6758
|
|
|
6751
6759
|
const options: RunOptions<
|
|
6752
6760
|
| CoordinatorHalt
|
|
6761
|
+
| AgentToolAuthorizationCheckError
|
|
6753
6762
|
| CompactionError
|
|
6754
6763
|
| RunContextPreparationError
|
|
6755
6764
|
| Agent.Failure<typeof agent>,
|
|
@@ -7364,9 +7373,11 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
7364
7373
|
).pipe(Effect.provideService(IdGenerator, idGenerator));
|
|
7365
7374
|
|
|
7366
7375
|
const result = yield* raced.pipe(
|
|
7367
|
-
Effect.
|
|
7376
|
+
Effect.catchCauseFilter(
|
|
7377
|
+
Cause.findError,
|
|
7368
7378
|
(
|
|
7369
7379
|
error,
|
|
7380
|
+
cause,
|
|
7370
7381
|
): Effect.Effect<
|
|
7371
7382
|
| { readonly _tag: "failedRun"; readonly outcome: AttemptOutcome }
|
|
7372
7383
|
| { readonly _tag: "aborted" }
|
|
@@ -7430,14 +7441,19 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
7430
7441
|
}
|
|
7431
7442
|
}
|
|
7432
7443
|
|
|
7433
|
-
//
|
|
7444
|
+
// Report the original live Cause here, before its private diagnostic projection,
|
|
7434
7445
|
// after excluding suspensions, so hosts retain its cause and stack exactly
|
|
7435
7446
|
// once per failed Run; reading or retrying its receipt never reports again.
|
|
7436
7447
|
if (
|
|
7437
|
-
|
|
7438
|
-
|
|
7448
|
+
cause.reasons.some(
|
|
7449
|
+
(reason) =>
|
|
7450
|
+
reason._tag === "Die" ||
|
|
7451
|
+
(reason._tag === "Fail" &&
|
|
7452
|
+
!(reason.error instanceof AgentApprovalDenied) &&
|
|
7453
|
+
!(reason.error instanceof AgentToolAuthorizationDenied)),
|
|
7454
|
+
)
|
|
7439
7455
|
)
|
|
7440
|
-
yield* Effect.logError("Agent run failed",
|
|
7456
|
+
yield* Effect.logError("Agent run failed", cause).pipe(
|
|
7441
7457
|
Effect.annotateLogs({
|
|
7442
7458
|
agentId: agent.definition.id,
|
|
7443
7459
|
runId,
|
|
@@ -7449,7 +7465,13 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
7449
7465
|
|
|
7450
7466
|
return {
|
|
7451
7467
|
_tag: "failedRun" as const,
|
|
7452
|
-
outcome: yield* failureOutcome(error
|
|
7468
|
+
outcome: yield* failureOutcome(error, cause, {
|
|
7469
|
+
agentId: agent.definition.id,
|
|
7470
|
+
runId,
|
|
7471
|
+
threadId: ctx.threadId,
|
|
7472
|
+
submissionId,
|
|
7473
|
+
attemptId: lineage.attemptId,
|
|
7474
|
+
}),
|
|
7453
7475
|
};
|
|
7454
7476
|
});
|
|
7455
7477
|
},
|
|
@@ -7574,7 +7596,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
7574
7596
|
.ensureOrigin(submission.workerAdmission.origin)
|
|
7575
7597
|
.pipe(
|
|
7576
7598
|
Effect.mapError((cause) =>
|
|
7577
|
-
LedgerError.make({ operation: "worker-origin", message: cause.reason }),
|
|
7599
|
+
LedgerError.make({ operation: "worker-origin", message: cause.reason, cause }),
|
|
7578
7600
|
),
|
|
7579
7601
|
);
|
|
7580
7602
|
}
|
|
@@ -9007,7 +9029,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
9007
9029
|
.ensureOrigin(submission.workerAdmission.origin)
|
|
9008
9030
|
.pipe(
|
|
9009
9031
|
Effect.mapError((cause) =>
|
|
9010
|
-
LedgerError.make({ operation: "worker-origin", message: cause.reason }),
|
|
9032
|
+
LedgerError.make({ operation: "worker-origin", message: cause.reason, cause }),
|
|
9011
9033
|
),
|
|
9012
9034
|
);
|
|
9013
9035
|
}
|
|
@@ -9578,6 +9600,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
9578
9600
|
? "occupied"
|
|
9579
9601
|
: "refused",
|
|
9580
9602
|
code: `worker-${cause.reason}`,
|
|
9603
|
+
cause,
|
|
9581
9604
|
}),
|
|
9582
9605
|
),
|
|
9583
9606
|
);
|
|
@@ -9609,6 +9632,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
9609
9632
|
? "unavailable"
|
|
9610
9633
|
: "refused",
|
|
9611
9634
|
code: `message-${cause.reason}`,
|
|
9635
|
+
cause,
|
|
9612
9636
|
}),
|
|
9613
9637
|
),
|
|
9614
9638
|
);
|
|
@@ -9701,7 +9725,7 @@ const make = Effect.fn("DurableAgentRuntime.make")(function* (
|
|
|
9701
9725
|
.ensureOrigin(workerAdmission.origin)
|
|
9702
9726
|
.pipe(
|
|
9703
9727
|
Effect.mapError((cause) =>
|
|
9704
|
-
LedgerError.make({ operation: "worker-origin", message: cause.reason }),
|
|
9728
|
+
LedgerError.make({ operation: "worker-origin", message: cause.reason, cause }),
|
|
9705
9729
|
),
|
|
9706
9730
|
);
|
|
9707
9731
|
}
|