@senpi/runtime 1.1.0-dev.runtime-phase-2.20260521133839 → 1.1.0-dev.runtime-phase-2.20260522115501
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/actions/close-position/close-position.action.d.ts.map +1 -1
- package/dist/actions/close-position/close-position.action.js +4 -1
- package/dist/actions/close-position/close-position.action.js.map +1 -1
- package/dist/actions/llm-decision/anthropic-llm-decision.d.ts.map +1 -1
- package/dist/actions/llm-decision/anthropic-llm-decision.js +41 -25
- package/dist/actions/llm-decision/anthropic-llm-decision.js.map +1 -1
- package/dist/actions/llm-decision/openclaw-llm-decision.d.ts +30 -0
- package/dist/actions/llm-decision/openclaw-llm-decision.d.ts.map +1 -1
- package/dist/actions/llm-decision/openclaw-llm-decision.js +87 -16
- package/dist/actions/llm-decision/openclaw-llm-decision.js.map +1 -1
- package/dist/actions/open-position/open-position.action.d.ts.map +1 -1
- package/dist/actions/open-position/open-position.action.js +4 -1
- package/dist/actions/open-position/open-position.action.js.map +1 -1
- package/dist/runtime/decision-errors.d.ts +166 -0
- package/dist/runtime/decision-errors.d.ts.map +1 -0
- package/dist/runtime/decision-errors.js +328 -0
- package/dist/runtime/decision-errors.js.map +1 -0
- package/dist/runtime/notifications.d.ts +15 -1
- package/dist/runtime/notifications.d.ts.map +1 -1
- package/dist/runtime/notifications.js +16 -57
- package/dist/runtime/notifications.js.map +1 -1
- package/dist/runtime/runtime-schema.d.ts +96 -96
- package/dist/senpi/client.d.ts.map +1 -1
- package/dist/senpi/client.js +30 -17
- package/dist/senpi/client.js.map +1 -1
- package/dist/tracking/spans/constants.d.ts +0 -1
- package/dist/tracking/spans/constants.d.ts.map +1 -1
- package/dist/tracking/spans/constants.js +0 -6
- package/dist/tracking/spans/constants.js.map +1 -1
- package/dist/tracking/spans/span-hooks.d.ts +13 -2
- package/dist/tracking/spans/span-hooks.d.ts.map +1 -1
- package/dist/tracking/spans/span-hooks.js +12 -119
- package/dist/tracking/spans/span-hooks.js.map +1 -1
- package/dist/tracking/spans/span-tracker.d.ts +0 -7
- package/dist/tracking/spans/span-tracker.d.ts.map +1 -1
- package/dist/tracking/spans/span-tracker.js +55 -26
- package/dist/tracking/spans/span-tracker.js.map +1 -1
- package/dist/tracking/spans/span-transport.d.ts.map +1 -1
- package/dist/tracking/spans/span-transport.js +21 -2
- package/dist/tracking/spans/span-transport.js.map +1 -1
- package/dist/tracking/spans/types.d.ts +6 -9
- package/dist/tracking/spans/types.d.ts.map +1 -1
- package/dist/utils/retry.d.ts +7 -0
- package/dist/utils/retry.d.ts.map +1 -1
- package/dist/utils/retry.js +5 -1
- package/dist/utils/retry.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classification of LLM-decision failures into user-facing messages + a retry flag.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this module exists
|
|
5
|
+
*
|
|
6
|
+
* When an action runs an LLM decision (`OpenClawLlmDecision.decide`,
|
|
7
|
+
* `src/actions/llm-decision/openclaw-llm-decision.ts`) and the OpenClaw `llm-task`
|
|
8
|
+
* tool fails, the only thing the runtime has is a flat error string. Two consumers
|
|
9
|
+
* need to make decisions from that string:
|
|
10
|
+
*
|
|
11
|
+
* 1. The **notification layer** ({@link formatDecisionErrorNotification} in
|
|
12
|
+
* `notifications.ts`) — what to tell the skill author in Telegram.
|
|
13
|
+
* 2. The **decision client** (`openclaw-llm-decision.ts`) — whether a retry can
|
|
14
|
+
* plausibly succeed.
|
|
15
|
+
*
|
|
16
|
+
* ## Three pieces, separated on purpose
|
|
17
|
+
*
|
|
18
|
+
* 1. {@link DECISION_ERROR_MESSAGES} — a **flat catalog** of every user-facing
|
|
19
|
+
* string, keyed by {@link DecisionErrorCode}. This is the single place to read,
|
|
20
|
+
* review, or edit copy. Templates use `{placeholder}` tokens.
|
|
21
|
+
* 2. {@link DECISION_ERROR_TABLE} — the ordered matchers (raw string → code) and
|
|
22
|
+
* the `retryable` flag, plus the per-code logic that produces template values.
|
|
23
|
+
* 3. {@link classifyDecisionError} — walks the table, renders the catalog entry.
|
|
24
|
+
*
|
|
25
|
+
* To change wording: edit {@link DECISION_ERROR_MESSAGES} only. To change matching
|
|
26
|
+
* or retry behavior: edit {@link DECISION_ERROR_TABLE} only.
|
|
27
|
+
*
|
|
28
|
+
* ## Where the raw strings come from
|
|
29
|
+
*
|
|
30
|
+
* The granular OpenClaw messages (e.g. `"LLM returned empty output"`,
|
|
31
|
+
* `"prompt required"`) are thrown by the **external gateway**, not by this plugin.
|
|
32
|
+
* They reach us **embedded** inside one of the wrappers that
|
|
33
|
+
* `OpenClawLlmDecision.decide` throws:
|
|
34
|
+
*
|
|
35
|
+
* - `OpenClaw gateway error (<status>): <body-text>` (non-2xx HTTP)
|
|
36
|
+
* - `OpenClaw gateway rejected request: <body.error>` (HTTP 200, `body.ok === false`)
|
|
37
|
+
*
|
|
38
|
+
* Matchers therefore look for the granular substring **anywhere** in the string,
|
|
39
|
+
* not anchored. Our own parse failures (`Invalid llm-task response: ...` from
|
|
40
|
+
* `extractDecision`) are classified too.
|
|
41
|
+
*
|
|
42
|
+
* ## v2026.2.22 caveat
|
|
43
|
+
*
|
|
44
|
+
* On OpenClaw v2026.2.22 (current prod) the gateway collapses most `llm-task`
|
|
45
|
+
* failures into a generic `tool_error` whose message is `"tool execution failed"`,
|
|
46
|
+
* so the granular substrings frequently do **not** surface there — only on
|
|
47
|
+
* v2026.5.7+. The table is built in full anyway: on v2026.2.22 the generic
|
|
48
|
+
* `tool_error` matches {@link DecisionErrorCode.ProviderError}; on newer gateways
|
|
49
|
+
* the specific code matches.
|
|
50
|
+
*/
|
|
51
|
+
/** Stable identifier for each classified LLM-decision failure mode. */
|
|
52
|
+
export declare enum DecisionErrorCode {
|
|
53
|
+
/** Gateway auth token missing in env/config — thrown before any HTTP call. */
|
|
54
|
+
GatewayTokenMissing = "GATEWAY_TOKEN_MISSING",
|
|
55
|
+
/** `fetch()` rejected before a response (connection refused/reset/DNS). */
|
|
56
|
+
GatewayUnreachable = "GATEWAY_UNREACHABLE",
|
|
57
|
+
/** Gateway returned 404 — plugin not installed at this path / version mismatch. */
|
|
58
|
+
PluginEndpointMissing = "PLUGIN_ENDPOINT_MISSING",
|
|
59
|
+
/** `llm-task` invoked with empty/whitespace prompt — missing `decision_prompt`. */
|
|
60
|
+
PromptRequired = "PROMPT_REQUIRED",
|
|
61
|
+
/** `decision_model` did not resolve to a known provider/model pair. */
|
|
62
|
+
ProviderModelUnresolved = "PROVIDER_MODEL_UNRESOLVED",
|
|
63
|
+
/** Resolved model is not on the gateway's `allowedModels` allowlist. */
|
|
64
|
+
ModelNotAllowed = "MODEL_NOT_ALLOWED",
|
|
65
|
+
/** Context/payload passed to `llm-task` was not JSON-serializable — internal bug. */
|
|
66
|
+
InputNotSerializable = "INPUT_NOT_SERIALIZABLE",
|
|
67
|
+
/** Provider responded but produced no text (safety filter / refusal / empty completion). */
|
|
68
|
+
LlmEmptyOutput = "LLM_EMPTY_OUTPUT",
|
|
69
|
+
/** Model reply could not be parsed as JSON (truncation / prose around JSON). */
|
|
70
|
+
LlmInvalidJson = "LLM_INVALID_JSON",
|
|
71
|
+
/** Model reply was valid JSON but failed schema validation. */
|
|
72
|
+
LlmSchemaMismatch = "LLM_SCHEMA_MISMATCH",
|
|
73
|
+
/** Upstream provider error (rate limit / outage) or generic `tool_error`. */
|
|
74
|
+
ProviderError = "PROVIDER_ERROR",
|
|
75
|
+
/** Gateway/upstream timeout (408/504) or runtime-side abort — we already waited 90s. */
|
|
76
|
+
Timeout = "TIMEOUT",
|
|
77
|
+
/** Embedded LLM helper failed to load on the host — corrupt/partial OpenClaw install. */
|
|
78
|
+
EmbeddedAgentUnavailable = "EMBEDDED_AGENT_UNAVAILABLE",
|
|
79
|
+
/** Thinking level string does not normalize to any known level (v2026.5.7+). */
|
|
80
|
+
InvalidThinkingLevel = "INVALID_THINKING_LEVEL",
|
|
81
|
+
/** Thinking level is known but unsupported by the resolved model (v2026.5.7+). */
|
|
82
|
+
ThinkingLevelUnsupported = "THINKING_LEVEL_UNSUPPORTED",
|
|
83
|
+
/** Gateway returned 200 with a response shape this plugin can't parse — internal bug. */
|
|
84
|
+
PluginResponseShape = "PLUGIN_RESPONSE_SHAPE",
|
|
85
|
+
/** Any other 5xx with no recognizable structured body. */
|
|
86
|
+
OpenClawUnexpected = "OPENCLAW_UNEXPECTED",
|
|
87
|
+
/** No matcher matched — surfaces the raw message so operators can still diagnose. */
|
|
88
|
+
Unknown = "UNKNOWN"
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The single catalog of every user-facing message, keyed by {@link DecisionErrorCode}.
|
|
92
|
+
*
|
|
93
|
+
* Templates use `{token}` placeholders, filled by the matching entry's `vars`
|
|
94
|
+
* function in {@link DECISION_ERROR_TABLE} (e.g. `{name}` = action name, `{model}`,
|
|
95
|
+
* `{list}`, `{errors}`, `{level}`, `{hint}`, `{providerModel}`, `{raw}`). A `{token}`
|
|
96
|
+
* with no supplied value is left verbatim so a missing var is visible, not silent.
|
|
97
|
+
*
|
|
98
|
+
* **This is the only place to edit copy.** Nothing else hardcodes a message.
|
|
99
|
+
*/
|
|
100
|
+
export declare const DECISION_ERROR_MESSAGES: Record<DecisionErrorCode, string>;
|
|
101
|
+
/**
|
|
102
|
+
* Plugin-side context the gateway error string does not carry but the message
|
|
103
|
+
* needs (e.g. the action's YAML name, the configured `decision_model`).
|
|
104
|
+
*/
|
|
105
|
+
export interface DecisionErrorContext {
|
|
106
|
+
/** The failing action's `name` from the runtime YAML (the `{name}` placeholder). */
|
|
107
|
+
actionName?: string;
|
|
108
|
+
/** The action's configured `decision_model` (fallback for the `{model}` placeholder). */
|
|
109
|
+
model?: string;
|
|
110
|
+
}
|
|
111
|
+
/** Result of classifying a raw LLM-decision error string. */
|
|
112
|
+
export interface ClassifiedDecisionError {
|
|
113
|
+
code: DecisionErrorCode;
|
|
114
|
+
/** Whether a retry of the `llm-task` call can plausibly succeed. */
|
|
115
|
+
retryable: boolean;
|
|
116
|
+
/** User-facing message for Telegram. */
|
|
117
|
+
userMessage: string;
|
|
118
|
+
}
|
|
119
|
+
/** One row of {@link DECISION_ERROR_TABLE}. */
|
|
120
|
+
interface DecisionErrorEntry {
|
|
121
|
+
code: DecisionErrorCode;
|
|
122
|
+
/**
|
|
123
|
+
* Tested against the raw error string. Returning a {@link RegExpMatchArray}
|
|
124
|
+
* (truthy) or `true` counts as a match; `null`/`false` means no match. Capture
|
|
125
|
+
* groups are forwarded to {@link DecisionErrorEntry.vars} as `caps`.
|
|
126
|
+
*/
|
|
127
|
+
match: (raw: string) => RegExpMatchArray | boolean | null;
|
|
128
|
+
retryable: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* Optional: produces the `{token}` substitutions for this code's catalog entry.
|
|
131
|
+
* Omit for static messages.
|
|
132
|
+
* @param caps - Capture groups from {@link DecisionErrorEntry.match}, or `null`.
|
|
133
|
+
* @param ctx - Plugin-side context (action name, configured model).
|
|
134
|
+
* @param raw - The original raw error string.
|
|
135
|
+
*/
|
|
136
|
+
vars?: (caps: RegExpMatchArray | null, ctx: DecisionErrorContext, raw: string) => Record<string, string>;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Ordered classification table — matchers + retry flag + template-variable logic.
|
|
140
|
+
* First match wins, so keep specific matchers above generic ones (e.g.
|
|
141
|
+
* `LlmEmptyOutput` before the catch-all `ProviderError`, since both can appear
|
|
142
|
+
* inside a `"rejected request: ..."` wrapper). Copy lives in
|
|
143
|
+
* {@link DECISION_ERROR_MESSAGES}, not here.
|
|
144
|
+
*/
|
|
145
|
+
export declare const DECISION_ERROR_TABLE: readonly DecisionErrorEntry[];
|
|
146
|
+
/**
|
|
147
|
+
* Classify a raw LLM-decision error string into a {@link ClassifiedDecisionError}.
|
|
148
|
+
*
|
|
149
|
+
* Walks {@link DECISION_ERROR_TABLE} in order; the first matching entry wins. If
|
|
150
|
+
* nothing matches, returns {@link DecisionErrorCode.Unknown} (not retryable). The
|
|
151
|
+
* message is always rendered from {@link DECISION_ERROR_MESSAGES}.
|
|
152
|
+
*
|
|
153
|
+
* @param rawMessage - The error string thrown by the decision client (may embed an
|
|
154
|
+
* OpenClaw gateway message). Non-string / empty inputs are coerced safely.
|
|
155
|
+
* @param ctx - Optional plugin-side context for placeholders the gateway can't supply.
|
|
156
|
+
*/
|
|
157
|
+
export declare function classifyDecisionError(rawMessage: string, ctx?: DecisionErrorContext): ClassifiedDecisionError;
|
|
158
|
+
/**
|
|
159
|
+
* Convenience predicate for retry wiring: `true` when a retry of the failed
|
|
160
|
+
* `llm-task` call can plausibly succeed.
|
|
161
|
+
*
|
|
162
|
+
* @param error - The thrown error (or any value); only its message is inspected.
|
|
163
|
+
*/
|
|
164
|
+
export declare function isRetryableDecisionError(error: unknown): boolean;
|
|
165
|
+
export {};
|
|
166
|
+
//# sourceMappingURL=decision-errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-errors.d.ts","sourceRoot":"","sources":["../../src/runtime/decision-errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAEH,uEAAuE;AACvE,oBAAY,iBAAiB;IAC3B,8EAA8E;IAC9E,mBAAmB,0BAA0B;IAC7C,2EAA2E;IAC3E,kBAAkB,wBAAwB;IAC1C,mFAAmF;IACnF,qBAAqB,4BAA4B;IACjD,mFAAmF;IACnF,cAAc,oBAAoB;IAClC,uEAAuE;IACvE,uBAAuB,8BAA8B;IACrD,wEAAwE;IACxE,eAAe,sBAAsB;IACrC,qFAAqF;IACrF,oBAAoB,2BAA2B;IAC/C,4FAA4F;IAC5F,cAAc,qBAAqB;IACnC,gFAAgF;IAChF,cAAc,qBAAqB;IACnC,+DAA+D;IAC/D,iBAAiB,wBAAwB;IACzC,6EAA6E;IAC7E,aAAa,mBAAmB;IAChC,wFAAwF;IACxF,OAAO,YAAY;IACnB,yFAAyF;IACzF,wBAAwB,+BAA+B;IACvD,gFAAgF;IAChF,oBAAoB,2BAA2B;IAC/C,kFAAkF;IAClF,wBAAwB,+BAA+B;IACvD,yFAAyF;IACzF,mBAAmB,0BAA0B;IAC7C,0DAA0D;IAC1D,kBAAkB,wBAAwB;IAC1C,qFAAqF;IACrF,OAAO,YAAY;CACpB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,iBAAiB,EAAE,MAAM,CAgFrE,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yFAAyF;IACzF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,6DAA6D;AAC7D,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,iBAAiB,CAAC;IACxB,oEAAoE;IACpE,SAAS,EAAE,OAAO,CAAC;IACnB,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,+CAA+C;AAC/C,UAAU,kBAAkB;IAC1B,IAAI,EAAE,iBAAiB,CAAC;IACxB;;;;OAIG;IACH,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,gBAAgB,GAAG,OAAO,GAAG,IAAI,CAAC;IAC1D,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,CACL,IAAI,EAAE,gBAAgB,GAAG,IAAI,EAC7B,GAAG,EAAE,oBAAoB,EACzB,GAAG,EAAE,MAAM,KACR,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7B;AAeD;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,EAAE,SAAS,kBAAkB,EA+HpD,CAAC;AAEX;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,EAClB,GAAG,GAAE,oBAAyB,GAC7B,uBAAuB,CAoBzB;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGhE"}
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classification of LLM-decision failures into user-facing messages + a retry flag.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this module exists
|
|
5
|
+
*
|
|
6
|
+
* When an action runs an LLM decision (`OpenClawLlmDecision.decide`,
|
|
7
|
+
* `src/actions/llm-decision/openclaw-llm-decision.ts`) and the OpenClaw `llm-task`
|
|
8
|
+
* tool fails, the only thing the runtime has is a flat error string. Two consumers
|
|
9
|
+
* need to make decisions from that string:
|
|
10
|
+
*
|
|
11
|
+
* 1. The **notification layer** ({@link formatDecisionErrorNotification} in
|
|
12
|
+
* `notifications.ts`) — what to tell the skill author in Telegram.
|
|
13
|
+
* 2. The **decision client** (`openclaw-llm-decision.ts`) — whether a retry can
|
|
14
|
+
* plausibly succeed.
|
|
15
|
+
*
|
|
16
|
+
* ## Three pieces, separated on purpose
|
|
17
|
+
*
|
|
18
|
+
* 1. {@link DECISION_ERROR_MESSAGES} — a **flat catalog** of every user-facing
|
|
19
|
+
* string, keyed by {@link DecisionErrorCode}. This is the single place to read,
|
|
20
|
+
* review, or edit copy. Templates use `{placeholder}` tokens.
|
|
21
|
+
* 2. {@link DECISION_ERROR_TABLE} — the ordered matchers (raw string → code) and
|
|
22
|
+
* the `retryable` flag, plus the per-code logic that produces template values.
|
|
23
|
+
* 3. {@link classifyDecisionError} — walks the table, renders the catalog entry.
|
|
24
|
+
*
|
|
25
|
+
* To change wording: edit {@link DECISION_ERROR_MESSAGES} only. To change matching
|
|
26
|
+
* or retry behavior: edit {@link DECISION_ERROR_TABLE} only.
|
|
27
|
+
*
|
|
28
|
+
* ## Where the raw strings come from
|
|
29
|
+
*
|
|
30
|
+
* The granular OpenClaw messages (e.g. `"LLM returned empty output"`,
|
|
31
|
+
* `"prompt required"`) are thrown by the **external gateway**, not by this plugin.
|
|
32
|
+
* They reach us **embedded** inside one of the wrappers that
|
|
33
|
+
* `OpenClawLlmDecision.decide` throws:
|
|
34
|
+
*
|
|
35
|
+
* - `OpenClaw gateway error (<status>): <body-text>` (non-2xx HTTP)
|
|
36
|
+
* - `OpenClaw gateway rejected request: <body.error>` (HTTP 200, `body.ok === false`)
|
|
37
|
+
*
|
|
38
|
+
* Matchers therefore look for the granular substring **anywhere** in the string,
|
|
39
|
+
* not anchored. Our own parse failures (`Invalid llm-task response: ...` from
|
|
40
|
+
* `extractDecision`) are classified too.
|
|
41
|
+
*
|
|
42
|
+
* ## v2026.2.22 caveat
|
|
43
|
+
*
|
|
44
|
+
* On OpenClaw v2026.2.22 (current prod) the gateway collapses most `llm-task`
|
|
45
|
+
* failures into a generic `tool_error` whose message is `"tool execution failed"`,
|
|
46
|
+
* so the granular substrings frequently do **not** surface there — only on
|
|
47
|
+
* v2026.5.7+. The table is built in full anyway: on v2026.2.22 the generic
|
|
48
|
+
* `tool_error` matches {@link DecisionErrorCode.ProviderError}; on newer gateways
|
|
49
|
+
* the specific code matches.
|
|
50
|
+
*/
|
|
51
|
+
/** Stable identifier for each classified LLM-decision failure mode. */
|
|
52
|
+
export var DecisionErrorCode;
|
|
53
|
+
(function (DecisionErrorCode) {
|
|
54
|
+
/** Gateway auth token missing in env/config — thrown before any HTTP call. */
|
|
55
|
+
DecisionErrorCode["GatewayTokenMissing"] = "GATEWAY_TOKEN_MISSING";
|
|
56
|
+
/** `fetch()` rejected before a response (connection refused/reset/DNS). */
|
|
57
|
+
DecisionErrorCode["GatewayUnreachable"] = "GATEWAY_UNREACHABLE";
|
|
58
|
+
/** Gateway returned 404 — plugin not installed at this path / version mismatch. */
|
|
59
|
+
DecisionErrorCode["PluginEndpointMissing"] = "PLUGIN_ENDPOINT_MISSING";
|
|
60
|
+
/** `llm-task` invoked with empty/whitespace prompt — missing `decision_prompt`. */
|
|
61
|
+
DecisionErrorCode["PromptRequired"] = "PROMPT_REQUIRED";
|
|
62
|
+
/** `decision_model` did not resolve to a known provider/model pair. */
|
|
63
|
+
DecisionErrorCode["ProviderModelUnresolved"] = "PROVIDER_MODEL_UNRESOLVED";
|
|
64
|
+
/** Resolved model is not on the gateway's `allowedModels` allowlist. */
|
|
65
|
+
DecisionErrorCode["ModelNotAllowed"] = "MODEL_NOT_ALLOWED";
|
|
66
|
+
/** Context/payload passed to `llm-task` was not JSON-serializable — internal bug. */
|
|
67
|
+
DecisionErrorCode["InputNotSerializable"] = "INPUT_NOT_SERIALIZABLE";
|
|
68
|
+
/** Provider responded but produced no text (safety filter / refusal / empty completion). */
|
|
69
|
+
DecisionErrorCode["LlmEmptyOutput"] = "LLM_EMPTY_OUTPUT";
|
|
70
|
+
/** Model reply could not be parsed as JSON (truncation / prose around JSON). */
|
|
71
|
+
DecisionErrorCode["LlmInvalidJson"] = "LLM_INVALID_JSON";
|
|
72
|
+
/** Model reply was valid JSON but failed schema validation. */
|
|
73
|
+
DecisionErrorCode["LlmSchemaMismatch"] = "LLM_SCHEMA_MISMATCH";
|
|
74
|
+
/** Upstream provider error (rate limit / outage) or generic `tool_error`. */
|
|
75
|
+
DecisionErrorCode["ProviderError"] = "PROVIDER_ERROR";
|
|
76
|
+
/** Gateway/upstream timeout (408/504) or runtime-side abort — we already waited 90s. */
|
|
77
|
+
DecisionErrorCode["Timeout"] = "TIMEOUT";
|
|
78
|
+
/** Embedded LLM helper failed to load on the host — corrupt/partial OpenClaw install. */
|
|
79
|
+
DecisionErrorCode["EmbeddedAgentUnavailable"] = "EMBEDDED_AGENT_UNAVAILABLE";
|
|
80
|
+
/** Thinking level string does not normalize to any known level (v2026.5.7+). */
|
|
81
|
+
DecisionErrorCode["InvalidThinkingLevel"] = "INVALID_THINKING_LEVEL";
|
|
82
|
+
/** Thinking level is known but unsupported by the resolved model (v2026.5.7+). */
|
|
83
|
+
DecisionErrorCode["ThinkingLevelUnsupported"] = "THINKING_LEVEL_UNSUPPORTED";
|
|
84
|
+
/** Gateway returned 200 with a response shape this plugin can't parse — internal bug. */
|
|
85
|
+
DecisionErrorCode["PluginResponseShape"] = "PLUGIN_RESPONSE_SHAPE";
|
|
86
|
+
/** Any other 5xx with no recognizable structured body. */
|
|
87
|
+
DecisionErrorCode["OpenClawUnexpected"] = "OPENCLAW_UNEXPECTED";
|
|
88
|
+
/** No matcher matched — surfaces the raw message so operators can still diagnose. */
|
|
89
|
+
DecisionErrorCode["Unknown"] = "UNKNOWN";
|
|
90
|
+
})(DecisionErrorCode || (DecisionErrorCode = {}));
|
|
91
|
+
/**
|
|
92
|
+
* The single catalog of every user-facing message, keyed by {@link DecisionErrorCode}.
|
|
93
|
+
*
|
|
94
|
+
* Templates use `{token}` placeholders, filled by the matching entry's `vars`
|
|
95
|
+
* function in {@link DECISION_ERROR_TABLE} (e.g. `{name}` = action name, `{model}`,
|
|
96
|
+
* `{list}`, `{errors}`, `{level}`, `{hint}`, `{providerModel}`, `{raw}`). A `{token}`
|
|
97
|
+
* with no supplied value is left verbatim so a missing var is visible, not silent.
|
|
98
|
+
*
|
|
99
|
+
* **This is the only place to edit copy.** Nothing else hardcodes a message.
|
|
100
|
+
*/
|
|
101
|
+
export const DECISION_ERROR_MESSAGES = {
|
|
102
|
+
[DecisionErrorCode.GatewayTokenMissing]: "Senpi: OpenClaw gateway token not configured. Set OPENCLAW_GATEWAY_TOKEN.",
|
|
103
|
+
[DecisionErrorCode.GatewayUnreachable]: "Senpi: OpenClaw Gateway is unreachable.",
|
|
104
|
+
[DecisionErrorCode.PluginEndpointMissing]: "Senpi: OpenClaw is missing the plugin endpoint. Please reinstall the plugin.",
|
|
105
|
+
[DecisionErrorCode.PromptRequired]: "Your agent can't trade until its template is fixed. Action `{name}` in your runtime YAML is " +
|
|
106
|
+
"missing a `decision_prompt`. To recover: delete this runtime, ask your OpenClaw agent to add a " +
|
|
107
|
+
"`decision_prompt` to that action, and install it again. If you didn't write this skill, contact " +
|
|
108
|
+
"Senpi support.",
|
|
109
|
+
[DecisionErrorCode.ProviderModelUnresolved]: "Your agent can't trade because the AI model `{model}` isn't recognized by your OpenClaw host. " +
|
|
110
|
+
"To recover: delete this runtime, set a valid `decision_model` in your runtime YAML, and install it " +
|
|
111
|
+
"again. If you didn't write this skill, contact Senpi support.",
|
|
112
|
+
[DecisionErrorCode.ModelNotAllowed]: "Your agent can't trade because `{model}` isn't on your OpenClaw host's allowed-models list. " +
|
|
113
|
+
"Allowed: `{list}`. To recover: pick a model from the allowed list (delete the runtime, edit " +
|
|
114
|
+
"`decision_model` in the runtime YAML, install again) or add it to your host's allowedModels config.",
|
|
115
|
+
[DecisionErrorCode.InputNotSerializable]: "Your agent hit an internal bug while building the AI request (data wasn't serializable). Please " +
|
|
116
|
+
"report this to Senpi support — it isn't something you can fix from your side.",
|
|
117
|
+
[DecisionErrorCode.LlmEmptyOutput]: "Your agent dropped one signal because the AI model returned no answer. This is usually a safety " +
|
|
118
|
+
"filter or a transient model quirk. If it keeps happening, try a different `decision_model` in your " +
|
|
119
|
+
"runtime YAML (delete the runtime, edit, reinstall) or rephrase your `decision_prompt` to be less " +
|
|
120
|
+
"ambiguous.",
|
|
121
|
+
[DecisionErrorCode.LlmInvalidJson]: "Your agent dropped one signal because the AI model's reply wasn't valid JSON — usually a truncation " +
|
|
122
|
+
"or a prompt that didn't force JSON output. To recover: tighten your `decision_prompt` in the runtime " +
|
|
123
|
+
"YAML to require strict JSON, then delete and reinstall the runtime. If you didn't write this skill, " +
|
|
124
|
+
"contact Senpi support.",
|
|
125
|
+
[DecisionErrorCode.LlmSchemaMismatch]: "Your agent dropped one signal because the AI model's reply was valid JSON but didn't match the " +
|
|
126
|
+
"expected shape: `{errors}`. To recover: tweak your `decision_prompt` so the model returns the " +
|
|
127
|
+
"required fields, or have your OpenClaw agent loosen the schema (delete the runtime, edit the YAML, " +
|
|
128
|
+
"reinstall). If you didn't write this skill, contact Senpi support.",
|
|
129
|
+
[DecisionErrorCode.ProviderError]: "Your agent dropped one signal because the AI provider rejected the call (e.g. rate limit or outage). " +
|
|
130
|
+
"Usually transient — the next signal often goes through. If it keeps happening, try switching " +
|
|
131
|
+
"providers or using a different `decision_model` in your runtime YAML.",
|
|
132
|
+
[DecisionErrorCode.Timeout]: "Your agent dropped one signal because the AI model didn't reply in 90 seconds. This is usually " +
|
|
133
|
+
"transient — the next signal often goes through. If it keeps happening, switch to a faster " +
|
|
134
|
+
"`decision_model` in your runtime YAML (delete the runtime, edit, reinstall).",
|
|
135
|
+
[DecisionErrorCode.EmbeddedAgentUnavailable]: "Your agent dropped one signal because OpenClaw's internal LLM helper failed to load on your host. " +
|
|
136
|
+
"This usually means the OpenClaw install is corrupted or partially upgraded. To recover: reinstall " +
|
|
137
|
+
"OpenClaw on your host (or upgrade to the latest version — this error no longer exists in v2026.5.7+). " +
|
|
138
|
+
"If it keeps happening after reinstall, contact Senpi support.",
|
|
139
|
+
[DecisionErrorCode.InvalidThinkingLevel]: "Your agent's runtime YAML uses an unknown thinking level (`{level}`). Valid options: `{hint}`. " +
|
|
140
|
+
"To recover: delete this runtime, set `thinking` to a valid value in the runtime YAML, and install it " +
|
|
141
|
+
"again. If you didn't write this skill, contact Senpi support.",
|
|
142
|
+
[DecisionErrorCode.ThinkingLevelUnsupported]: "Your agent's model (`{providerModel}`) doesn't support the thinking level `{level}`. Supported for " +
|
|
143
|
+
"this model: `{hint}`. To recover: delete this runtime, change either the model or the thinking level " +
|
|
144
|
+
"in the runtime YAML, and install it again.",
|
|
145
|
+
[DecisionErrorCode.PluginResponseShape]: "Your agent hit an internal bug parsing the AI model's response. Please report this to Senpi support " +
|
|
146
|
+
"— it isn't something you can fix from your side.",
|
|
147
|
+
[DecisionErrorCode.OpenClawUnexpected]: "Senpi: Signal Dropped - OpenClaw hit an unexpected error.",
|
|
148
|
+
[DecisionErrorCode.Unknown]: "Senpi: Trading decision failed — {raw}",
|
|
149
|
+
};
|
|
150
|
+
/** Trim a captured token; falls back to `fallback` when empty/absent. */
|
|
151
|
+
function clean(value, fallback) {
|
|
152
|
+
const t = value?.trim();
|
|
153
|
+
return t && t.length > 0 ? t : fallback;
|
|
154
|
+
}
|
|
155
|
+
/** Fill `{token}` placeholders from `vars`; unknown tokens are left verbatim. */
|
|
156
|
+
function renderTemplate(template, vars) {
|
|
157
|
+
return template.replace(/\{(\w+)\}/g, (whole, key) => Object.prototype.hasOwnProperty.call(vars, key) ? vars[key] : whole);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Ordered classification table — matchers + retry flag + template-variable logic.
|
|
161
|
+
* First match wins, so keep specific matchers above generic ones (e.g.
|
|
162
|
+
* `LlmEmptyOutput` before the catch-all `ProviderError`, since both can appear
|
|
163
|
+
* inside a `"rejected request: ..."` wrapper). Copy lives in
|
|
164
|
+
* {@link DECISION_ERROR_MESSAGES}, not here.
|
|
165
|
+
*/
|
|
166
|
+
export const DECISION_ERROR_TABLE = [
|
|
167
|
+
// ── Pre-HTTP / infra (not retryable) ──────────────────────────────────────
|
|
168
|
+
{
|
|
169
|
+
code: DecisionErrorCode.GatewayTokenMissing,
|
|
170
|
+
match: (s) => /gateway token not configured|OPENCLAW_GATEWAY_TOKEN/.test(s),
|
|
171
|
+
retryable: false,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
code: DecisionErrorCode.GatewayUnreachable,
|
|
175
|
+
match: (s) => /^fetch failed$|fetch failed:|ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENOTFOUND|EAI_AGAIN/.test(s),
|
|
176
|
+
retryable: false,
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
code: DecisionErrorCode.PluginEndpointMissing,
|
|
180
|
+
match: (s) => /OpenClaw gateway error \(404\)/.test(s),
|
|
181
|
+
retryable: false,
|
|
182
|
+
},
|
|
183
|
+
// ── Author/config errors surfaced by llm-task (not retryable) ─────────────
|
|
184
|
+
{
|
|
185
|
+
code: DecisionErrorCode.PromptRequired,
|
|
186
|
+
match: (s) => /prompt required/i.test(s),
|
|
187
|
+
retryable: false,
|
|
188
|
+
vars: (_caps, ctx) => ({ name: clean(ctx.actionName, "<name>") }),
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
code: DecisionErrorCode.ProviderModelUnresolved,
|
|
192
|
+
match: (s) => s.match(/provider\/model could not be resolved \(provider=(.*?), model=(.*?)\)/),
|
|
193
|
+
retryable: false,
|
|
194
|
+
vars: (caps, ctx) => ({ model: clean(caps?.[2], clean(ctx.model, "your configured model")) }),
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
code: DecisionErrorCode.ModelNotAllowed,
|
|
198
|
+
match: (s) => s.match(/Model not allowed by llm-task plugin config:\s*([^.]+)\.\s*Allowed models:\s*([^"}]+)/),
|
|
199
|
+
retryable: false,
|
|
200
|
+
vars: (caps, ctx) => ({
|
|
201
|
+
model: clean(caps?.[1], clean(ctx.model, "that model")),
|
|
202
|
+
list: clean(caps?.[2], "(see your host's allowedModels config)"),
|
|
203
|
+
}),
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
code: DecisionErrorCode.InputNotSerializable,
|
|
207
|
+
match: (s) => /input must be JSON-serializable/i.test(s),
|
|
208
|
+
retryable: false,
|
|
209
|
+
},
|
|
210
|
+
// ── Model output problems ─────────────────────────────────────────────────
|
|
211
|
+
{
|
|
212
|
+
// Retryable: usually a transient safety-filter block or empty completion.
|
|
213
|
+
code: DecisionErrorCode.LlmEmptyOutput,
|
|
214
|
+
match: (s) => /LLM returned empty output/i.test(s),
|
|
215
|
+
retryable: true,
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
// Not retryable: a fixed prompt that fails to force JSON will fail identically.
|
|
219
|
+
code: DecisionErrorCode.LlmInvalidJson,
|
|
220
|
+
match: (s) => /LLM returned invalid JSON/i.test(s),
|
|
221
|
+
retryable: false,
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
// Not retryable: deterministic given a fixed prompt + schema.
|
|
225
|
+
code: DecisionErrorCode.LlmSchemaMismatch,
|
|
226
|
+
match: (s) => s.match(/LLM JSON did not match schema:\s*([^"}]+)/),
|
|
227
|
+
retryable: false,
|
|
228
|
+
vars: (caps) => ({ errors: clean(caps?.[1], "(schema validation failed)") }),
|
|
229
|
+
},
|
|
230
|
+
// ── Thinking-level misconfig (v2026.5.7+, not retryable) ──────────────────
|
|
231
|
+
{
|
|
232
|
+
code: DecisionErrorCode.InvalidThinkingLevel,
|
|
233
|
+
match: (s) => s.match(/Invalid thinking level "(.*?)"\.\s*Use one of:\s*([^"}]+?)\./),
|
|
234
|
+
retryable: false,
|
|
235
|
+
vars: (caps) => ({
|
|
236
|
+
level: clean(caps?.[1], "that value"),
|
|
237
|
+
hint: clean(caps?.[2], "the supported levels"),
|
|
238
|
+
}),
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
code: DecisionErrorCode.ThinkingLevelUnsupported,
|
|
242
|
+
match: (s) => s.match(/Thinking level "(.*?)" is not supported for (.+?)\.\s*Use one of:\s*([^"}]+?)\./),
|
|
243
|
+
retryable: false,
|
|
244
|
+
vars: (caps, ctx) => ({
|
|
245
|
+
level: clean(caps?.[1], "that level"),
|
|
246
|
+
providerModel: clean(caps?.[2], clean(ctx.model, "this model")),
|
|
247
|
+
hint: clean(caps?.[3], "the supported levels"),
|
|
248
|
+
}),
|
|
249
|
+
},
|
|
250
|
+
// ── Host/load failure (not retryable) ─────────────────────────────────────
|
|
251
|
+
{
|
|
252
|
+
code: DecisionErrorCode.EmbeddedAgentUnavailable,
|
|
253
|
+
match: (s) => /runEmbeddedPiAgent not available/.test(s),
|
|
254
|
+
retryable: false,
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
code: DecisionErrorCode.PluginResponseShape,
|
|
258
|
+
match: (s) => /Invalid llm-task response/.test(s),
|
|
259
|
+
retryable: false,
|
|
260
|
+
},
|
|
261
|
+
// ── Transient transport (timeout NOT retried; provider errors ARE) ────────
|
|
262
|
+
{
|
|
263
|
+
// Not retryable: we already waited 90s, and signal processing is strictly
|
|
264
|
+
// serial — retrying would stall the whole runtime's queue (see runtime.ts).
|
|
265
|
+
code: DecisionErrorCode.Timeout,
|
|
266
|
+
match: (s) => /OpenClaw gateway error \(408\)|OpenClaw gateway error \(504\)|AbortError|operation was aborted/.test(s),
|
|
267
|
+
retryable: false,
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
// Retryable: rate limit / transient outage / generic tool_error (the shape
|
|
271
|
+
// v2026.2.22 collapses empty-output and provider blips into).
|
|
272
|
+
code: DecisionErrorCode.ProviderError,
|
|
273
|
+
match: (s) => (/OpenClaw gateway error \(5\d\d\)/.test(s) && /"type":\s*"tool_error"|tool execution failed/.test(s)) ||
|
|
274
|
+
/OpenClaw gateway error \(429\)/.test(s) ||
|
|
275
|
+
/OpenClaw gateway rejected request/.test(s) ||
|
|
276
|
+
/tool execution failed/.test(s),
|
|
277
|
+
retryable: true,
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
// Any other 5xx with no recognizable body. Not retryable under the locked
|
|
281
|
+
// policy (only tool_error-carrying 5xx / 429 retry).
|
|
282
|
+
code: DecisionErrorCode.OpenClawUnexpected,
|
|
283
|
+
match: (s) => /OpenClaw gateway error \(5\d\d\)/.test(s),
|
|
284
|
+
retryable: false,
|
|
285
|
+
},
|
|
286
|
+
];
|
|
287
|
+
/**
|
|
288
|
+
* Classify a raw LLM-decision error string into a {@link ClassifiedDecisionError}.
|
|
289
|
+
*
|
|
290
|
+
* Walks {@link DECISION_ERROR_TABLE} in order; the first matching entry wins. If
|
|
291
|
+
* nothing matches, returns {@link DecisionErrorCode.Unknown} (not retryable). The
|
|
292
|
+
* message is always rendered from {@link DECISION_ERROR_MESSAGES}.
|
|
293
|
+
*
|
|
294
|
+
* @param rawMessage - The error string thrown by the decision client (may embed an
|
|
295
|
+
* OpenClaw gateway message). Non-string / empty inputs are coerced safely.
|
|
296
|
+
* @param ctx - Optional plugin-side context for placeholders the gateway can't supply.
|
|
297
|
+
*/
|
|
298
|
+
export function classifyDecisionError(rawMessage, ctx = {}) {
|
|
299
|
+
const raw = typeof rawMessage === "string" ? rawMessage : String(rawMessage ?? "");
|
|
300
|
+
for (const entry of DECISION_ERROR_TABLE) {
|
|
301
|
+
const result = entry.match(raw);
|
|
302
|
+
if (!result)
|
|
303
|
+
continue;
|
|
304
|
+
const caps = Array.isArray(result) ? result : null;
|
|
305
|
+
const vars = entry.vars ? entry.vars(caps, ctx, raw) : {};
|
|
306
|
+
return {
|
|
307
|
+
code: entry.code,
|
|
308
|
+
retryable: entry.retryable,
|
|
309
|
+
userMessage: renderTemplate(DECISION_ERROR_MESSAGES[entry.code], vars),
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
code: DecisionErrorCode.Unknown,
|
|
314
|
+
retryable: false,
|
|
315
|
+
userMessage: renderTemplate(DECISION_ERROR_MESSAGES[DecisionErrorCode.Unknown], { raw }),
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Convenience predicate for retry wiring: `true` when a retry of the failed
|
|
320
|
+
* `llm-task` call can plausibly succeed.
|
|
321
|
+
*
|
|
322
|
+
* @param error - The thrown error (or any value); only its message is inspected.
|
|
323
|
+
*/
|
|
324
|
+
export function isRetryableDecisionError(error) {
|
|
325
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
326
|
+
return classifyDecisionError(message).retryable;
|
|
327
|
+
}
|
|
328
|
+
//# sourceMappingURL=decision-errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-errors.js","sourceRoot":"","sources":["../../src/runtime/decision-errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAEH,uEAAuE;AACvE,MAAM,CAAN,IAAY,iBAqCX;AArCD,WAAY,iBAAiB;IAC3B,8EAA8E;IAC9E,kEAA6C,CAAA;IAC7C,2EAA2E;IAC3E,+DAA0C,CAAA;IAC1C,mFAAmF;IACnF,sEAAiD,CAAA;IACjD,mFAAmF;IACnF,uDAAkC,CAAA;IAClC,uEAAuE;IACvE,0EAAqD,CAAA;IACrD,wEAAwE;IACxE,0DAAqC,CAAA;IACrC,qFAAqF;IACrF,oEAA+C,CAAA;IAC/C,4FAA4F;IAC5F,wDAAmC,CAAA;IACnC,gFAAgF;IAChF,wDAAmC,CAAA;IACnC,+DAA+D;IAC/D,8DAAyC,CAAA;IACzC,6EAA6E;IAC7E,qDAAgC,CAAA;IAChC,wFAAwF;IACxF,wCAAmB,CAAA;IACnB,yFAAyF;IACzF,4EAAuD,CAAA;IACvD,gFAAgF;IAChF,oEAA+C,CAAA;IAC/C,kFAAkF;IAClF,4EAAuD,CAAA;IACvD,yFAAyF;IACzF,kEAA6C,CAAA;IAC7C,0DAA0D;IAC1D,+DAA0C,CAAA;IAC1C,qFAAqF;IACrF,wCAAmB,CAAA;AACrB,CAAC,EArCW,iBAAiB,KAAjB,iBAAiB,QAqC5B;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAsC;IACxE,CAAC,iBAAiB,CAAC,mBAAmB,CAAC,EACrC,2EAA2E;IAE7E,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,EAAE,yCAAyC;IAEjF,CAAC,iBAAiB,CAAC,qBAAqB,CAAC,EACvC,8EAA8E;IAEhF,CAAC,iBAAiB,CAAC,cAAc,CAAC,EAChC,8FAA8F;QAC9F,iGAAiG;QACjG,kGAAkG;QAClG,gBAAgB;IAElB,CAAC,iBAAiB,CAAC,uBAAuB,CAAC,EACzC,gGAAgG;QAChG,qGAAqG;QACrG,+DAA+D;IAEjE,CAAC,iBAAiB,CAAC,eAAe,CAAC,EACjC,8FAA8F;QAC9F,8FAA8F;QAC9F,qGAAqG;IAEvG,CAAC,iBAAiB,CAAC,oBAAoB,CAAC,EACtC,kGAAkG;QAClG,+EAA+E;IAEjF,CAAC,iBAAiB,CAAC,cAAc,CAAC,EAChC,kGAAkG;QAClG,qGAAqG;QACrG,mGAAmG;QACnG,YAAY;IAEd,CAAC,iBAAiB,CAAC,cAAc,CAAC,EAChC,sGAAsG;QACtG,uGAAuG;QACvG,sGAAsG;QACtG,wBAAwB;IAE1B,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,EACnC,iGAAiG;QACjG,gGAAgG;QAChG,qGAAqG;QACrG,oEAAoE;IAEtE,CAAC,iBAAiB,CAAC,aAAa,CAAC,EAC/B,uGAAuG;QACvG,+FAA+F;QAC/F,uEAAuE;IAEzE,CAAC,iBAAiB,CAAC,OAAO,CAAC,EACzB,iGAAiG;QACjG,4FAA4F;QAC5F,8EAA8E;IAEhF,CAAC,iBAAiB,CAAC,wBAAwB,CAAC,EAC1C,oGAAoG;QACpG,oGAAoG;QACpG,wGAAwG;QACxG,+DAA+D;IAEjE,CAAC,iBAAiB,CAAC,oBAAoB,CAAC,EACtC,iGAAiG;QACjG,uGAAuG;QACvG,+DAA+D;IAEjE,CAAC,iBAAiB,CAAC,wBAAwB,CAAC,EAC1C,qGAAqG;QACrG,uGAAuG;QACvG,4CAA4C;IAE9C,CAAC,iBAAiB,CAAC,mBAAmB,CAAC,EACrC,sGAAsG;QACtG,kDAAkD;IAEpD,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,EAAE,2DAA2D;IAEnG,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE,wCAAwC;CACtE,CAAC;AA8CF,yEAAyE;AACzE,SAAS,KAAK,CAAC,KAAyB,EAAE,QAAgB;IACxD,MAAM,CAAC,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC;IACxB,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC1C,CAAC;AAED,iFAAiF;AACjF,SAAS,cAAc,CAAC,QAAgB,EAAE,IAA4B;IACpE,OAAO,QAAQ,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,GAAW,EAAE,EAAE,CAC3D,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CACpE,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAkC;IACjE,6EAA6E;IAC7E;QACE,IAAI,EAAE,iBAAiB,CAAC,mBAAmB;QAC3C,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,qDAAqD,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3E,SAAS,EAAE,KAAK;KACjB;IACD;QACE,IAAI,EAAE,iBAAiB,CAAC,kBAAkB;QAC1C,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,oFAAoF,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1G,SAAS,EAAE,KAAK;KACjB;IACD;QACE,IAAI,EAAE,iBAAiB,CAAC,qBAAqB;QAC7C,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,gCAAgC,CAAC,IAAI,CAAC,CAAC,CAAC;QACtD,SAAS,EAAE,KAAK;KACjB;IAED,6EAA6E;IAC7E;QACE,IAAI,EAAE,iBAAiB,CAAC,cAAc;QACtC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;QACxC,SAAS,EAAE,KAAK;QAChB,IAAI,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,CAAC;KAClE;IACD;QACE,IAAI,EAAE,iBAAiB,CAAC,uBAAuB;QAC/C,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,uEAAuE,CAAC;QAC9F,SAAS,EAAE,KAAK;QAChB,IAAI,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,uBAAuB,CAAC,CAAC,EAAE,CAAC;KAC9F;IACD;QACE,IAAI,EAAE,iBAAiB,CAAC,eAAe;QACvC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,uFAAuF,CAAC;QAC9G,SAAS,EAAE,KAAK;QAChB,IAAI,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;YACpB,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;YACvD,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,wCAAwC,CAAC;SACjE,CAAC;KACH;IACD;QACE,IAAI,EAAE,iBAAiB,CAAC,oBAAoB;QAC5C,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,kCAAkC,CAAC,IAAI,CAAC,CAAC,CAAC;QACxD,SAAS,EAAE,KAAK;KACjB;IAED,6EAA6E;IAC7E;QACE,0EAA0E;QAC1E,IAAI,EAAE,iBAAiB,CAAC,cAAc;QACtC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,4BAA4B,CAAC,IAAI,CAAC,CAAC,CAAC;QAClD,SAAS,EAAE,IAAI;KAChB;IACD;QACE,gFAAgF;QAChF,IAAI,EAAE,iBAAiB,CAAC,cAAc;QACtC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,4BAA4B,CAAC,IAAI,CAAC,CAAC,CAAC;QAClD,SAAS,EAAE,KAAK;KACjB;IACD;QACE,8DAA8D;QAC9D,IAAI,EAAE,iBAAiB,CAAC,iBAAiB;QACzC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,2CAA2C,CAAC;QAClE,SAAS,EAAE,KAAK;QAChB,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,4BAA4B,CAAC,EAAE,CAAC;KAC7E;IAED,6EAA6E;IAC7E;QACE,IAAI,EAAE,iBAAiB,CAAC,oBAAoB;QAC5C,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,8DAA8D,CAAC;QACrF,SAAS,EAAE,KAAK;QAChB,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACf,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC;YACrC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,sBAAsB,CAAC;SAC/C,CAAC;KACH;IACD;QACE,IAAI,EAAE,iBAAiB,CAAC,wBAAwB;QAChD,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,iFAAiF,CAAC;QACxG,SAAS,EAAE,KAAK;QAChB,IAAI,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;YACpB,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC;YACrC,aAAa,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;YAC/D,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,sBAAsB,CAAC;SAC/C,CAAC;KACH;IAED,6EAA6E;IAC7E;QACE,IAAI,EAAE,iBAAiB,CAAC,wBAAwB;QAChD,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,kCAAkC,CAAC,IAAI,CAAC,CAAC,CAAC;QACxD,SAAS,EAAE,KAAK;KACjB;IACD;QACE,IAAI,EAAE,iBAAiB,CAAC,mBAAmB;QAC3C,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,2BAA2B,CAAC,IAAI,CAAC,CAAC,CAAC;QACjD,SAAS,EAAE,KAAK;KACjB;IAED,6EAA6E;IAC7E;QACE,0EAA0E;QAC1E,4EAA4E;QAC5E,IAAI,EAAE,iBAAiB,CAAC,OAAO;QAC/B,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CACX,gGAAgG,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1G,SAAS,EAAE,KAAK;KACjB;IACD;QACE,2EAA2E;QAC3E,8DAA8D;QAC9D,IAAI,EAAE,iBAAiB,CAAC,aAAa;QACrC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CACX,CAAC,kCAAkC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,8CAA8C,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACtG,gCAAgC,CAAC,IAAI,CAAC,CAAC,CAAC;YACxC,mCAAmC,CAAC,IAAI,CAAC,CAAC,CAAC;YAC3C,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC;QACjC,SAAS,EAAE,IAAI;KAChB;IACD;QACE,0EAA0E;QAC1E,qDAAqD;QACrD,IAAI,EAAE,iBAAiB,CAAC,kBAAkB;QAC1C,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,kCAAkC,CAAC,IAAI,CAAC,CAAC,CAAC;QACxD,SAAS,EAAE,KAAK;KACjB;CACO,CAAC;AAEX;;;;;;;;;;GAUG;AACH,MAAM,UAAU,qBAAqB,CACnC,UAAkB,EAClB,MAA4B,EAAE;IAE9B,MAAM,GAAG,GAAG,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;IAEnF,KAAK,MAAM,KAAK,IAAI,oBAAoB,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,CAAC,MAAM;YAAE,SAAS;QACtB,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;QACnD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1D,OAAO;YACL,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,WAAW,EAAE,cAAc,CAAC,uBAAuB,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC;SACvE,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,iBAAiB,CAAC,OAAO;QAC/B,SAAS,EAAE,KAAK;QAChB,WAAW,EAAE,cAAc,CAAC,uBAAuB,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC;KACzF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,KAAc;IACrD,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,qBAAqB,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC;AAClD,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { McpTransport } from "../senpi/types.js";
|
|
2
2
|
import type { NotificationService } from "../types/notification.js";
|
|
3
|
+
import { type DecisionErrorContext } from "./decision-errors.js";
|
|
3
4
|
export interface NotificationServiceOptions {
|
|
4
5
|
telegram_chat_id?: string;
|
|
5
6
|
mcpTransport?: McpTransport;
|
|
@@ -7,7 +8,20 @@ export interface NotificationServiceOptions {
|
|
|
7
8
|
gateway_token?: string;
|
|
8
9
|
[key: string]: unknown;
|
|
9
10
|
}
|
|
10
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Map a raw LLM-decision error string to the user-facing Telegram message.
|
|
13
|
+
*
|
|
14
|
+
* Thin wrapper over {@link classifyDecisionError} — all message wording and the
|
|
15
|
+
* failure-mode lookup live in `decision-errors.ts` (the single source of truth
|
|
16
|
+
* shared with the retry logic in `openclaw-llm-decision.ts`). The runtime catches
|
|
17
|
+
* errors from the LLM-decision call and passes `err.message` here.
|
|
18
|
+
*
|
|
19
|
+
* @param rawMessage - Error message thrown by the decision client (may embed an
|
|
20
|
+
* OpenClaw gateway message).
|
|
21
|
+
* @param ctx - Optional plugin-side context (action name, configured model) for
|
|
22
|
+
* placeholders the gateway error string cannot supply.
|
|
23
|
+
*/
|
|
24
|
+
export declare function formatDecisionErrorNotification(rawMessage: string, ctx?: DecisionErrorContext): string;
|
|
11
25
|
/**
|
|
12
26
|
* IMPORTANT: Do NOT use logger.error(...) anywhere inside this service.
|
|
13
27
|
* NotificationService is itself the Telegram delivery path for error notifications.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../../src/runtime/notifications.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../../src/runtime/notifications.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,OAAO,EAAyB,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAIxF,MAAM,WAAW,0BAA0B;IACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,+BAA+B,CAC7C,UAAU,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,oBAAoB,GACzB,MAAM,CAER;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE,0BAA0B,GAAG,mBAAmB,CAyEnG"}
|
|
@@ -1,62 +1,21 @@
|
|
|
1
1
|
import { createLogger } from "../utils/logger.js";
|
|
2
|
+
import { classifyDecisionError } from "./decision-errors.js";
|
|
2
3
|
const logger = createLogger("[senpi-core][notifications]");
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// 401/403 is unreachable in practice; if it ever fires, the generic fallback
|
|
19
|
-
// at the bottom surfaces the raw message.
|
|
20
|
-
export function formatDecisionErrorNotification(rawMessage) {
|
|
21
|
-
const s = rawMessage;
|
|
22
|
-
// Token missing at config-time — fires before any HTTP call, so check first.
|
|
23
|
-
if (/gateway token not configured|OPENCLAW_GATEWAY_TOKEN/.test(s)) {
|
|
24
|
-
return "Senpi: OpenClaw gateway token not configured. Set OPENCLAW_GATEWAY_TOKEN.";
|
|
25
|
-
}
|
|
26
|
-
// (1) OpenClaw Gateway not reachable — fetch() rejected before getting a
|
|
27
|
-
// response. Node's undici fetch surfaces these as "fetch failed" with the
|
|
28
|
-
// underlying SystemError code in .cause; the action runner stringifies it
|
|
29
|
-
// so the code marker usually lands in the message.
|
|
30
|
-
if (/^fetch failed$|fetch failed:|ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENOTFOUND|EAI_AGAIN/.test(s)) {
|
|
31
|
-
return "Senpi: OpenClaw Gateway is unreachable.";
|
|
32
|
-
}
|
|
33
|
-
// (2) Endpoint missing — plugin not installed at this path, or version mismatch.
|
|
34
|
-
if (/OpenClaw gateway error \(404\)/.test(s)) {
|
|
35
|
-
return "Senpi: OpenClaw is missing the plugin endpoint. Please reinstall the plugin.";
|
|
36
|
-
}
|
|
37
|
-
// (3) AI model timed out — OpenClaw's llm-task tool returns 408 when it hits
|
|
38
|
-
// its own per-call cap; the gateway returns 504 when the upstream call exceeded
|
|
39
|
-
// the gateway's frame timeout. AbortError covers cancellation paths that may
|
|
40
|
-
// be added at the runtime side in the future.
|
|
41
|
-
if (/OpenClaw gateway error \(408\)|OpenClaw gateway error \(504\)|AbortError|operation was aborted/.test(s)) {
|
|
42
|
-
return "Senpi: Signal Dropped - AI model didn't respond in 90 seconds.";
|
|
43
|
-
}
|
|
44
|
-
// (4) AI model failed — HTTP 5xx whose body carries a structured tool_error
|
|
45
|
-
// (today's case: Gemini overload → llm-task wraps as `{"ok":false,
|
|
46
|
-
// "error":{"type":"tool_error","message":"tool execution failed"}}` →
|
|
47
|
-
// gateway returns HTTP 500), OR HTTP 200 with body.ok=false from the same
|
|
48
|
-
// tool layer ("OpenClaw gateway rejected request: ...").
|
|
49
|
-
if ((/OpenClaw gateway error \(5\d\d\)/.test(s) && /"type":\s*"tool_error"/.test(s)) ||
|
|
50
|
-
/OpenClaw gateway rejected request/.test(s)) {
|
|
51
|
-
return "Senpi: Signal Dropped - AI model failed to respond.";
|
|
52
|
-
}
|
|
53
|
-
// (5) Any other 5xx — gateway-frame error or unparseable body.
|
|
54
|
-
if (/OpenClaw gateway error \(5\d\d\)/.test(s)) {
|
|
55
|
-
return "Senpi: Signal Dropped - OpenClaw hit an unexpected error.";
|
|
56
|
-
}
|
|
57
|
-
// Last-resort generic prefix — surfaces the underlying message so operators
|
|
58
|
-
// can still diagnose unknown failure modes from the chat.
|
|
59
|
-
return `Senpi: Trading decision failed — ${s}`;
|
|
4
|
+
/**
|
|
5
|
+
* Map a raw LLM-decision error string to the user-facing Telegram message.
|
|
6
|
+
*
|
|
7
|
+
* Thin wrapper over {@link classifyDecisionError} — all message wording and the
|
|
8
|
+
* failure-mode lookup live in `decision-errors.ts` (the single source of truth
|
|
9
|
+
* shared with the retry logic in `openclaw-llm-decision.ts`). The runtime catches
|
|
10
|
+
* errors from the LLM-decision call and passes `err.message` here.
|
|
11
|
+
*
|
|
12
|
+
* @param rawMessage - Error message thrown by the decision client (may embed an
|
|
13
|
+
* OpenClaw gateway message).
|
|
14
|
+
* @param ctx - Optional plugin-side context (action name, configured model) for
|
|
15
|
+
* placeholders the gateway error string cannot supply.
|
|
16
|
+
*/
|
|
17
|
+
export function formatDecisionErrorNotification(rawMessage, ctx) {
|
|
18
|
+
return classifyDecisionError(rawMessage, ctx).userMessage;
|
|
60
19
|
}
|
|
61
20
|
/**
|
|
62
21
|
* IMPORTANT: Do NOT use logger.error(...) anywhere inside this service.
|