@gaunt-sloth/core 2.0.0-beta.3 → 2.0.0-beta.5
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/core/GthAbstractAgent.d.ts +66 -0
- package/dist/core/GthAbstractAgent.js +185 -1
- package/dist/core/GthAbstractAgent.js.map +1 -1
- package/dist/core/GthAgentRunner.d.ts +82 -1
- package/dist/core/GthAgentRunner.js +265 -8
- package/dist/core/GthAgentRunner.js.map +1 -1
- package/dist/core/GthLangChainAgent.d.ts +3 -2
- package/dist/core/GthLangChainAgent.js +24 -4
- package/dist/core/GthLangChainAgent.js.map +1 -1
- package/dist/core/approvals/approvalRequest.d.ts +142 -0
- package/dist/core/approvals/approvalRequest.js +198 -3
- package/dist/core/approvals/approvalRequest.js.map +1 -1
- package/dist/core/refusal.d.ts +54 -1
- package/dist/core/refusal.js +109 -1
- package/dist/core/refusal.js.map +1 -1
- package/dist/core/terminationNotice.d.ts +111 -0
- package/dist/core/terminationNotice.js +209 -0
- package/dist/core/terminationNotice.js.map +1 -0
- package/dist/core/terminationReason.d.ts +271 -0
- package/dist/core/terminationReason.js +405 -0
- package/dist/core/terminationReason.js.map +1 -0
- package/dist/core/types.d.ts +27 -0
- package/dist/core/types.js.map +1 -1
- package/dist/runtime/conversation.d.ts +11 -0
- package/dist/runtime/conversation.js +13 -1
- package/dist/runtime/conversation.js.map +1 -1
- package/dist/runtime/singleShot.d.ts +11 -0
- package/dist/runtime/singleShot.js +21 -1
- package/dist/runtime/singleShot.js.map +1 -1
- package/dist/utils/debugDump.d.ts +54 -0
- package/dist/utils/debugDump.js +32 -0
- package/dist/utils/debugDump.js.map +1 -1
- package/package.json +1 -1
package/dist/core/refusal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"refusal.js","sourceRoot":"","sources":["../../src/core/refusal.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"refusal.js","sourceRoot":"","sources":["../../src/core/refusal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* [[EXT-159]] — getting the typed termination reason to the person looking at the screen.
|
|
4
|
+
*
|
|
5
|
+
* `core/terminationReason.ts` decides *what* ended a run. This module decides *how that fact is
|
|
6
|
+
* said*, once, for every surface — the Ink TUI, the readline session, the non-interactive verbs,
|
|
7
|
+
* ACP and AG-UI — so a run states why it ended in the same vocabulary wherever it is being watched.
|
|
8
|
+
*
|
|
9
|
+
* **Recording it better is not the deliverable.** Most people never make a debug dump; the ones
|
|
10
|
+
* most likely to hit a silent stop have already quit, taking the live session state with them; and
|
|
11
|
+
* reading a dump is a skill that fails even for a maintainer holding one. So the reason has to
|
|
12
|
+
* reach the SESSION, at the moment it happens, on the surface in front of the user.
|
|
13
|
+
*
|
|
14
|
+
* **The prose is never the carrier.** Every surface commits the {@link GthTerminationReason} value
|
|
15
|
+
* itself and derives the words from it here, so a test — and a consumer — reads the classification
|
|
16
|
+
* structurally instead of matching a sentence. That is also why {@link terminationCode} exists: a
|
|
17
|
+
* user quoting one short token in a bug report hands a maintainer the fact that makes the report
|
|
18
|
+
* tractable, which is precisely what "the agent just stopped" could not.
|
|
19
|
+
*
|
|
20
|
+
* **An ABSENT reason is a signal, not a blank to fill.** The taxonomy's contract is that a turn
|
|
21
|
+
* with no reason is a site nobody classified, which is why an ordinary completion is recorded
|
|
22
|
+
* rather than left empty. Nothing here invents a category, a placeholder or a default for `null`:
|
|
23
|
+
* {@link terminationLogLine} states the absence as an absence, and
|
|
24
|
+
* {@link displayTermination} says nothing to the user, because "we did not classify this" is a
|
|
25
|
+
* defect report aimed at us and not an explanation aimed at them.
|
|
26
|
+
*/
|
|
27
|
+
import type { GthTerminationReason } from '#src/core/terminationReason.js';
|
|
28
|
+
/**
|
|
29
|
+
* The opening of every termination notice title.
|
|
30
|
+
*
|
|
31
|
+
* Exported because two surfaces put the notice into a channel that also carries other messages —
|
|
32
|
+
* ACP's `agent_message`, where the agent speaks about the session — so something has to be able to
|
|
33
|
+
* tell one from the other without transcribing the wording. A consumer keying on this constant
|
|
34
|
+
* moves with the copy; one keying on a copy of the string does not, and finds out when the two have
|
|
35
|
+
* silently disagreed for a while.
|
|
36
|
+
*/
|
|
37
|
+
export declare const TERMINATION_NOTICE_TITLE_PREFIX = "Run ended: ";
|
|
38
|
+
/** A termination reason, rendered for a surface that shows a title and body lines. */
|
|
39
|
+
export interface GthTerminationNotice {
|
|
40
|
+
/**
|
|
41
|
+
* **The carrier.** The classification travels as this value; the strings below are derived from
|
|
42
|
+
* it and are never the only place it exists.
|
|
43
|
+
*/
|
|
44
|
+
reason: GthTerminationReason;
|
|
45
|
+
/** The one line that says what happened. */
|
|
46
|
+
title: string;
|
|
47
|
+
/** The supporting lines: the quotable code, what was seen, and what may help. */
|
|
48
|
+
lines: string[];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The short token a user can quote and a maintainer can act on: the category and the site that
|
|
52
|
+
* classified it.
|
|
53
|
+
*
|
|
54
|
+
* Both halves, always. Several sites share a category and several categories reach one site, so
|
|
55
|
+
* either alone loses the fact that separates two failures which look identical on screen — the
|
|
56
|
+
* standing example being an `empty_response` whose as-is retry has already been spent at
|
|
57
|
+
* `runner.empty-after-fallback` and one where it has not been spent at all.
|
|
58
|
+
*/
|
|
59
|
+
export declare function terminationCode(reason: GthTerminationReason): string;
|
|
60
|
+
/**
|
|
61
|
+
* Is this reason worth telling the person watching?
|
|
62
|
+
*
|
|
63
|
+
* Two categories are not. `completed` is the ordinary end of a turn, and announcing it would put a
|
|
64
|
+
* line under every successful answer. `suspended` is a run that has PAUSED rather than ended — the
|
|
65
|
+
* graph is parked on a tool-approval interrupt and about to continue — so announcing it would
|
|
66
|
+
* report the middle of a working turn as its end.
|
|
67
|
+
*
|
|
68
|
+
* Everything else is announced, deliberately including the ones that look self-explanatory.
|
|
69
|
+
* `cancelled` is announced because a turn cancelled by a stray escape sequence is exactly the shape
|
|
70
|
+
* that gets misattributed to the provider for months ([[TUI-C62]]), and a user who did not knowingly
|
|
71
|
+
* press anything is owed the fact that a cancellation is what happened. `approval_stop` is announced
|
|
72
|
+
* because the gate's own prose explains the DECISION while this states the CLASSIFICATION, which is
|
|
73
|
+
* the half a bug report needs.
|
|
74
|
+
*
|
|
75
|
+
* Announcing is a separate decision from recording: everything is recorded, including the
|
|
76
|
+
* categories this returns `false` for.
|
|
77
|
+
*/
|
|
78
|
+
export declare function shouldAnnounceTermination(reason: GthTerminationReason): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Render a reason for a surface that shows a title and body lines.
|
|
81
|
+
*
|
|
82
|
+
* **The `??` on the label lookup is a runtime floor, and it takes nothing away from the build-time
|
|
83
|
+
* one.** That a 23rd category fails the build comes from the category-label table being a `Record`
|
|
84
|
+
* over the whole union, which this expression cannot weaken. What it covers is the case the type
|
|
85
|
+
* system never saw: a reason that arrived from outside it — read back off an ACP `_meta`, handed
|
|
86
|
+
* over by an embedder, or revived from a dump — whose category is a string the table has no entry
|
|
87
|
+
* for. Without the fallback that renders as the literal word `undefined` in the one line the user
|
|
88
|
+
* is being shown, which is a worse answer than "the cause was not recognised" and is exactly the
|
|
89
|
+
* kind of second failure this module refuses to add to a first one.
|
|
90
|
+
*/
|
|
91
|
+
export declare function terminationNotice(reason: GthTerminationReason): GthTerminationNotice;
|
|
92
|
+
/**
|
|
93
|
+
* One line for the debug log, stating either the whole classification or the absence of one.
|
|
94
|
+
*
|
|
95
|
+
* `null` is the case this function exists for. A turn that ended with nothing classifying it is the
|
|
96
|
+
* taxonomy's own defect signal, and it has to be written down as that — never as `completed`, never
|
|
97
|
+
* as `unknown`, and never omitted, because a missing line is indistinguishable from a session where
|
|
98
|
+
* this was never reached.
|
|
99
|
+
*/
|
|
100
|
+
export declare function terminationLogLine(reason: GthTerminationReason | null): string;
|
|
101
|
+
/**
|
|
102
|
+
* Say why the run ended on a console surface (the readline session and the non-interactive verbs),
|
|
103
|
+
* and write the same fact to the debug log either way.
|
|
104
|
+
*
|
|
105
|
+
* Returns whether anything was shown, so a caller can tell "said nothing because the turn simply
|
|
106
|
+
* finished" from "said nothing because nothing classified it" without re-deriving the rule.
|
|
107
|
+
*
|
|
108
|
+
* Fail-soft in the strongest sense: explaining a failure must never become a second failure, so
|
|
109
|
+
* every path here is wrapped and a throw is swallowed.
|
|
110
|
+
*/
|
|
111
|
+
export declare function displayTermination(reason: GthTerminationReason | null): boolean;
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* [[EXT-159]] — getting the typed termination reason to the person looking at the screen.
|
|
4
|
+
*
|
|
5
|
+
* `core/terminationReason.ts` decides *what* ended a run. This module decides *how that fact is
|
|
6
|
+
* said*, once, for every surface — the Ink TUI, the readline session, the non-interactive verbs,
|
|
7
|
+
* ACP and AG-UI — so a run states why it ended in the same vocabulary wherever it is being watched.
|
|
8
|
+
*
|
|
9
|
+
* **Recording it better is not the deliverable.** Most people never make a debug dump; the ones
|
|
10
|
+
* most likely to hit a silent stop have already quit, taking the live session state with them; and
|
|
11
|
+
* reading a dump is a skill that fails even for a maintainer holding one. So the reason has to
|
|
12
|
+
* reach the SESSION, at the moment it happens, on the surface in front of the user.
|
|
13
|
+
*
|
|
14
|
+
* **The prose is never the carrier.** Every surface commits the {@link GthTerminationReason} value
|
|
15
|
+
* itself and derives the words from it here, so a test — and a consumer — reads the classification
|
|
16
|
+
* structurally instead of matching a sentence. That is also why {@link terminationCode} exists: a
|
|
17
|
+
* user quoting one short token in a bug report hands a maintainer the fact that makes the report
|
|
18
|
+
* tractable, which is precisely what "the agent just stopped" could not.
|
|
19
|
+
*
|
|
20
|
+
* **An ABSENT reason is a signal, not a blank to fill.** The taxonomy's contract is that a turn
|
|
21
|
+
* with no reason is a site nobody classified, which is why an ordinary completion is recorded
|
|
22
|
+
* rather than left empty. Nothing here invents a category, a placeholder or a default for `null`:
|
|
23
|
+
* {@link terminationLogLine} states the absence as an absence, and
|
|
24
|
+
* {@link displayTermination} says nothing to the user, because "we did not classify this" is a
|
|
25
|
+
* defect report aimed at us and not an explanation aimed at them.
|
|
26
|
+
*/
|
|
27
|
+
import { display, displayWarning } from '#src/utils/consoleUtils.js';
|
|
28
|
+
import { debugLog } from '#src/utils/debugUtils.js';
|
|
29
|
+
/**
|
|
30
|
+
* What each category is called in a sentence a user reads.
|
|
31
|
+
*
|
|
32
|
+
* An exhaustive `Record` rather than a `switch` with a default: a 23rd category is then a build
|
|
33
|
+
* failure here instead of a run that silently reports the new cause in the old words.
|
|
34
|
+
*/
|
|
35
|
+
const CATEGORY_LABEL = {
|
|
36
|
+
completed: 'the model finished',
|
|
37
|
+
empty_response: 'the model returned nothing',
|
|
38
|
+
content_refusal: 'the model declined to answer',
|
|
39
|
+
output_truncated: 'the answer hit the output limit',
|
|
40
|
+
context_overflow: 'the conversation outgrew the model input window',
|
|
41
|
+
rate_limited: 'the provider rate-limited the request',
|
|
42
|
+
auth_failed: 'the provider rejected the credentials',
|
|
43
|
+
invalid_request: 'the provider rejected the request',
|
|
44
|
+
provider_error: 'the provider failed',
|
|
45
|
+
network_error: 'the connection failed',
|
|
46
|
+
timeout: 'a deadline elapsed',
|
|
47
|
+
cancelled: 'it was cancelled',
|
|
48
|
+
approval_stop: 'the approvals gate stopped it',
|
|
49
|
+
tool_error_budget: 'the tool-error budget ended the run',
|
|
50
|
+
tool_loop_guard: 'the tool-loop guard ended a repeating call',
|
|
51
|
+
interrupt_drain_guard: 'too many tool approvals in one turn',
|
|
52
|
+
tool_error: 'a tool failed',
|
|
53
|
+
suspended: 'it is waiting to be resumed',
|
|
54
|
+
recursion_limit: 'it hit the graph step limit',
|
|
55
|
+
abandoned: 'the client stopped listening',
|
|
56
|
+
unknown: 'the cause was not recognised',
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* What each remedy asks the user to actually do.
|
|
60
|
+
*
|
|
61
|
+
* Exhaustive for the same reason {@link CATEGORY_LABEL} is: the posture table names a remedy, and a
|
|
62
|
+
* remedy nobody worded is a retry hint the user never gets.
|
|
63
|
+
*/
|
|
64
|
+
const REMEDY_LABEL = {
|
|
65
|
+
'reduce-context': 'Send less — clear or compact the conversation, then try again.',
|
|
66
|
+
'back-off': 'Wait a moment, then send the same request again.',
|
|
67
|
+
'change-request': 'Try a different or narrower request.',
|
|
68
|
+
'change-model': 'Try a different model.',
|
|
69
|
+
'fix-credentials': 'Check the API credentials or configuration, then try again.',
|
|
70
|
+
resume: 'Nothing is wrong — the run is parked and can be continued where it stopped.',
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* The opening of every termination notice title.
|
|
74
|
+
*
|
|
75
|
+
* Exported because two surfaces put the notice into a channel that also carries other messages —
|
|
76
|
+
* ACP's `agent_message`, where the agent speaks about the session — so something has to be able to
|
|
77
|
+
* tell one from the other without transcribing the wording. A consumer keying on this constant
|
|
78
|
+
* moves with the copy; one keying on a copy of the string does not, and finds out when the two have
|
|
79
|
+
* silently disagreed for a while.
|
|
80
|
+
*/
|
|
81
|
+
export const TERMINATION_NOTICE_TITLE_PREFIX = 'Run ended: ';
|
|
82
|
+
/**
|
|
83
|
+
* The short token a user can quote and a maintainer can act on: the category and the site that
|
|
84
|
+
* classified it.
|
|
85
|
+
*
|
|
86
|
+
* Both halves, always. Several sites share a category and several categories reach one site, so
|
|
87
|
+
* either alone loses the fact that separates two failures which look identical on screen — the
|
|
88
|
+
* standing example being an `empty_response` whose as-is retry has already been spent at
|
|
89
|
+
* `runner.empty-after-fallback` and one where it has not been spent at all.
|
|
90
|
+
*/
|
|
91
|
+
export function terminationCode(reason) {
|
|
92
|
+
return `${reason.category}@${reason.site}`;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Is this reason worth telling the person watching?
|
|
96
|
+
*
|
|
97
|
+
* Two categories are not. `completed` is the ordinary end of a turn, and announcing it would put a
|
|
98
|
+
* line under every successful answer. `suspended` is a run that has PAUSED rather than ended — the
|
|
99
|
+
* graph is parked on a tool-approval interrupt and about to continue — so announcing it would
|
|
100
|
+
* report the middle of a working turn as its end.
|
|
101
|
+
*
|
|
102
|
+
* Everything else is announced, deliberately including the ones that look self-explanatory.
|
|
103
|
+
* `cancelled` is announced because a turn cancelled by a stray escape sequence is exactly the shape
|
|
104
|
+
* that gets misattributed to the provider for months ([[TUI-C62]]), and a user who did not knowingly
|
|
105
|
+
* press anything is owed the fact that a cancellation is what happened. `approval_stop` is announced
|
|
106
|
+
* because the gate's own prose explains the DECISION while this states the CLASSIFICATION, which is
|
|
107
|
+
* the half a bug report needs.
|
|
108
|
+
*
|
|
109
|
+
* Announcing is a separate decision from recording: everything is recorded, including the
|
|
110
|
+
* categories this returns `false` for.
|
|
111
|
+
*/
|
|
112
|
+
export function shouldAnnounceTermination(reason) {
|
|
113
|
+
return reason.category !== 'completed' && reason.category !== 'suspended';
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Render a reason for a surface that shows a title and body lines.
|
|
117
|
+
*
|
|
118
|
+
* **The `??` on the label lookup is a runtime floor, and it takes nothing away from the build-time
|
|
119
|
+
* one.** That a 23rd category fails the build comes from the category-label table being a `Record`
|
|
120
|
+
* over the whole union, which this expression cannot weaken. What it covers is the case the type
|
|
121
|
+
* system never saw: a reason that arrived from outside it — read back off an ACP `_meta`, handed
|
|
122
|
+
* over by an embedder, or revived from a dump — whose category is a string the table has no entry
|
|
123
|
+
* for. Without the fallback that renders as the literal word `undefined` in the one line the user
|
|
124
|
+
* is being shown, which is a worse answer than "the cause was not recognised" and is exactly the
|
|
125
|
+
* kind of second failure this module refuses to add to a first one.
|
|
126
|
+
*/
|
|
127
|
+
export function terminationNotice(reason) {
|
|
128
|
+
const lines = [`Reason code: ${terminationCode(reason)}`];
|
|
129
|
+
const seen = [];
|
|
130
|
+
if (reason.provider)
|
|
131
|
+
seen.push(`provider ${reason.provider}`);
|
|
132
|
+
if (reason.detail)
|
|
133
|
+
seen.push(`reported ${reason.detail}`);
|
|
134
|
+
if (seen.length > 0)
|
|
135
|
+
lines.push(`What was seen: ${seen.join(', ')}.`);
|
|
136
|
+
lines.push(retryAdvice(reason));
|
|
137
|
+
return {
|
|
138
|
+
reason,
|
|
139
|
+
title: `${TERMINATION_NOTICE_TITLE_PREFIX}${CATEGORY_LABEL[reason.category] ?? CATEGORY_LABEL.unknown}`,
|
|
140
|
+
lines,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* The retry advice for a reason, read off the posture the taxonomy already decided.
|
|
145
|
+
*
|
|
146
|
+
* Read from the reason's own posture fields rather than re-deriving one from the category, so this
|
|
147
|
+
* cannot come to disagree with the single posture table the taxonomy exists to keep.
|
|
148
|
+
*/
|
|
149
|
+
function retryAdvice(reason) {
|
|
150
|
+
if (reason.retryableAsIs)
|
|
151
|
+
return 'Sending the same request again may work.';
|
|
152
|
+
if (reason.retryableAfterRemedy && reason.remedy)
|
|
153
|
+
return REMEDY_LABEL[reason.remedy];
|
|
154
|
+
return 'Sending the same request again will not help.';
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* One line for the debug log, stating either the whole classification or the absence of one.
|
|
158
|
+
*
|
|
159
|
+
* `null` is the case this function exists for. A turn that ended with nothing classifying it is the
|
|
160
|
+
* taxonomy's own defect signal, and it has to be written down as that — never as `completed`, never
|
|
161
|
+
* as `unknown`, and never omitted, because a missing line is indistinguishable from a session where
|
|
162
|
+
* this was never reached.
|
|
163
|
+
*/
|
|
164
|
+
export function terminationLogLine(reason) {
|
|
165
|
+
if (!reason) {
|
|
166
|
+
return ('EXT-159 termination: UNCLASSIFIED — no site classified how this turn ended. ' +
|
|
167
|
+
'An absent reason means a termination site we missed, not a turn that went well.');
|
|
168
|
+
}
|
|
169
|
+
const parts = [
|
|
170
|
+
`category=${reason.category}`,
|
|
171
|
+
`site=${reason.site}`,
|
|
172
|
+
`source=${reason.source}`,
|
|
173
|
+
`retryableAsIs=${reason.retryableAsIs}`,
|
|
174
|
+
`retryableAfterRemedy=${reason.retryableAfterRemedy}`,
|
|
175
|
+
];
|
|
176
|
+
if (reason.remedy)
|
|
177
|
+
parts.push(`remedy=${reason.remedy}`);
|
|
178
|
+
if (reason.provider)
|
|
179
|
+
parts.push(`provider=${reason.provider}`);
|
|
180
|
+
if (reason.detail)
|
|
181
|
+
parts.push(`detail=${reason.detail}`);
|
|
182
|
+
return `EXT-159 termination: ${parts.join(' ')}`;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Say why the run ended on a console surface (the readline session and the non-interactive verbs),
|
|
186
|
+
* and write the same fact to the debug log either way.
|
|
187
|
+
*
|
|
188
|
+
* Returns whether anything was shown, so a caller can tell "said nothing because the turn simply
|
|
189
|
+
* finished" from "said nothing because nothing classified it" without re-deriving the rule.
|
|
190
|
+
*
|
|
191
|
+
* Fail-soft in the strongest sense: explaining a failure must never become a second failure, so
|
|
192
|
+
* every path here is wrapped and a throw is swallowed.
|
|
193
|
+
*/
|
|
194
|
+
export function displayTermination(reason) {
|
|
195
|
+
try {
|
|
196
|
+
debugLog(terminationLogLine(reason));
|
|
197
|
+
if (!reason || !shouldAnnounceTermination(reason))
|
|
198
|
+
return false;
|
|
199
|
+
const notice = terminationNotice(reason);
|
|
200
|
+
displayWarning(notice.title);
|
|
201
|
+
for (const line of notice.lines)
|
|
202
|
+
display(` ${line}`);
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=terminationNotice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminationNotice.js","sourceRoot":"","sources":["../../src/core/terminationNotice.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* EXT-159 — the typed reason a run ended.
|
|
4
|
+
*
|
|
5
|
+
* A run can stop for a dozen unrelated causes — a rate limit, a provider-side fault, a full context
|
|
6
|
+
* window, a content-policy refusal, the approvals gate, a tool-error budget, the user pressing Esc
|
|
7
|
+
* — and every one of them used to reach the surface as one untyped sentence. This module is the
|
|
8
|
+
* single taxonomy those causes are classified into, so the fact "why did this end" is carried as a
|
|
9
|
+
* value rather than reconstructed from prose.
|
|
10
|
+
*
|
|
11
|
+
* **Two feeders converge here, and they are not interchangeable.**
|
|
12
|
+
*
|
|
13
|
+
* - A **metadata reader** (`detectStopMetadata` in `core/refusal.ts`, called from
|
|
14
|
+
* `GthAbstractAgent`) handles reasons that arrive *on a message* — a stop/finish reason in
|
|
15
|
+
* `response_metadata` or
|
|
16
|
+
* `additional_kwargs`. It sits at that layer because that is the only place the metadata is
|
|
17
|
+
* visible.
|
|
18
|
+
* - An **exception classifier** ({@link classifyThrownTermination}, called from the runner's
|
|
19
|
+
* catches) handles reasons that arrive as a *thrown error*. Those are not in `response_metadata`
|
|
20
|
+
* at all, so no metadata reader can ever see them.
|
|
21
|
+
*
|
|
22
|
+
* Built as one metadata reader the whole thrown-error half of the class falls outside it; built as
|
|
23
|
+
* two taxonomies every consumer grows its own. Hence: two feeders, one taxonomy.
|
|
24
|
+
*
|
|
25
|
+
* **Retryability is two facts, never a boolean.** `@langchain/core` exports a typed
|
|
26
|
+
* `ContextOverflowError` that stamps itself non-retryable in its own constructor. That is right for
|
|
27
|
+
* "send the same prompt again" and exactly backwards for the remedy this cause actually has, which
|
|
28
|
+
* is to send a *smaller* one. {@link GthTerminationReason} therefore carries
|
|
29
|
+
* {@link GthTerminationReason#retryableAsIs} and
|
|
30
|
+
* {@link GthTerminationReason#retryableAfterRemedy} separately, with the remedy named.
|
|
31
|
+
*
|
|
32
|
+
* Classification only. Nothing here surfaces anything, formats anything, or changes what a run
|
|
33
|
+
* does; the user-facing strings stay where they are and keep their own wording.
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* What ended the run, as one closed vocabulary shared by every site and every consumer.
|
|
37
|
+
*
|
|
38
|
+
* Members are causes, not messages: two sites that stop for the same reason report the same
|
|
39
|
+
* category and are told apart by {@link GthTerminationSite}.
|
|
40
|
+
*/
|
|
41
|
+
export type GthTerminationCategory =
|
|
42
|
+
/** The model finished of its own accord — the ordinary end of a turn. */
|
|
43
|
+
'completed'
|
|
44
|
+
/** The turn produced no content at all (no refusal, no error, nothing). */
|
|
45
|
+
| 'empty_response'
|
|
46
|
+
/** The model or the provider's safety system declined to answer. */
|
|
47
|
+
| 'content_refusal'
|
|
48
|
+
/** The answer was cut off against the output cap rather than finished. */
|
|
49
|
+
| 'output_truncated'
|
|
50
|
+
/** Prompt plus history exceeded the model's input window. */
|
|
51
|
+
| 'context_overflow'
|
|
52
|
+
/** The provider refused for rate/quota reasons (HTTP 429). */
|
|
53
|
+
| 'rate_limited'
|
|
54
|
+
/** Credentials were missing, wrong or unauthorised (HTTP 401/403). */
|
|
55
|
+
| 'auth_failed'
|
|
56
|
+
/** The provider rejected the request itself (HTTP 400) for a reason retrying cannot change. */
|
|
57
|
+
| 'invalid_request'
|
|
58
|
+
/** A fault on the provider's side (HTTP 5xx, "internal error during token generation"). */
|
|
59
|
+
| 'provider_error'
|
|
60
|
+
/** The request never completed at the transport level. */
|
|
61
|
+
| 'network_error'
|
|
62
|
+
/** A deadline elapsed before the run finished. */
|
|
63
|
+
| 'timeout'
|
|
64
|
+
/** The user stopped it — Esc, a cancelled signal, a closed client. */
|
|
65
|
+
| 'cancelled'
|
|
66
|
+
/** The approvals gate deliberately ended the run. */
|
|
67
|
+
| 'approval_stop'
|
|
68
|
+
/** The tool-error budget ended the run rather than spend another model call. */
|
|
69
|
+
| 'tool_error_budget'
|
|
70
|
+
/** The tool-loop guard ended a no-progress identical-call loop. */
|
|
71
|
+
| 'tool_loop_guard'
|
|
72
|
+
/**
|
|
73
|
+
* The tool-approval interrupt drain gave up: the graph re-suspended on a gated tool call more
|
|
74
|
+
* times in one turn than the drain loop is willing to resume, so the RUNTIME ended the turn.
|
|
75
|
+
*
|
|
76
|
+
* Deliberately not `recursion_limit`, which states that the graph hit *its* recursion limit — a
|
|
77
|
+
* different bound, owned by LangGraph, with a different knob. Reporting one for the other would
|
|
78
|
+
* be the same false-category defect this taxonomy exists to remove, and `site` is what separates
|
|
79
|
+
* the two surfaces this bound has.
|
|
80
|
+
*/
|
|
81
|
+
| 'interrupt_drain_guard'
|
|
82
|
+
/** A tool threw, and the failure ended the turn. */
|
|
83
|
+
| 'tool_error'
|
|
84
|
+
/** The graph suspended on an `interrupt()` and is waiting to be resumed. */
|
|
85
|
+
| 'suspended'
|
|
86
|
+
/** The graph hit its recursion limit. */
|
|
87
|
+
| 'recursion_limit'
|
|
88
|
+
/** The consumer stopped consuming the turn before it ended. */
|
|
89
|
+
| 'abandoned'
|
|
90
|
+
/** Nothing in the taxonomy matched — recorded as such rather than guessed at. */
|
|
91
|
+
| 'unknown';
|
|
92
|
+
/**
|
|
93
|
+
* Where the classification was made, as a stable identifier per termination site.
|
|
94
|
+
*
|
|
95
|
+
* The site is a distinct fact from the category: several sites classify into the same category (the
|
|
96
|
+
* runner's two exception wrappers both report whatever the classifier says), and several categories
|
|
97
|
+
* can be reported from one site (an aborted stream and a suspended graph leave `streamWithEvents`
|
|
98
|
+
* at the same place). Diagnosis needs both.
|
|
99
|
+
*/
|
|
100
|
+
export type GthTerminationSite =
|
|
101
|
+
/** `GthAgentRunner.processMessages` returned an answer. */
|
|
102
|
+
'runner.completed'
|
|
103
|
+
/** The streamed turn was empty and the non-streaming fallback was empty too. */
|
|
104
|
+
| 'runner.empty-after-fallback'
|
|
105
|
+
/** The non-streaming turn produced no content. */
|
|
106
|
+
| 'runner.empty-invoke'
|
|
107
|
+
/** An approvals stop re-thrown out of the stream drain. */
|
|
108
|
+
| 'runner.stream-approval-stop'
|
|
109
|
+
/** An approvals stop re-thrown out of the turn. */
|
|
110
|
+
| 'runner.turn-approval-stop'
|
|
111
|
+
/** The stream drain threw. */
|
|
112
|
+
| 'runner.stream-error'
|
|
113
|
+
/** The turn threw. */
|
|
114
|
+
| 'runner.turn-error'
|
|
115
|
+
/** The string path's tool-approval drain ran out of resume rounds. */
|
|
116
|
+
| 'runner.interrupt-guard-exhausted'
|
|
117
|
+
/** `GthAgentRunner.processMessagesWithEvents` drained its stream to the end. */
|
|
118
|
+
| 'runner.events-completed'
|
|
119
|
+
/** The typed-event turn ended having yielded no answer text at all. */
|
|
120
|
+
| 'runner.events-empty'
|
|
121
|
+
/** The typed-event turn ended because its signal was aborted. */
|
|
122
|
+
| 'runner.events-cancelled'
|
|
123
|
+
/** The typed-event turn threw. */
|
|
124
|
+
| 'runner.events-error'
|
|
125
|
+
/** The consumer stopped consuming the typed-event turn before it ended. */
|
|
126
|
+
| 'runner.events-abandoned'
|
|
127
|
+
/** The typed-event path's tool-approval drain ran out of resume rounds. */
|
|
128
|
+
| 'runner.events-interrupt-guard-exhausted'
|
|
129
|
+
/** The metadata reader fired on the non-streaming `invoke` path. */
|
|
130
|
+
| 'agent.invoke-stop-metadata'
|
|
131
|
+
/** The metadata reader fired on the string-streaming path. */
|
|
132
|
+
| 'agent.stream-stop-metadata'
|
|
133
|
+
/** The metadata reader fired on the typed-event path. */
|
|
134
|
+
| 'agent.events-stop-metadata'
|
|
135
|
+
/** A `ToolException` was turned into the turn's answer on the `invoke` path. */
|
|
136
|
+
| 'agent.invoke-tool-exception'
|
|
137
|
+
/** The string-streaming path ended on Esc / an abort. */
|
|
138
|
+
| 'agent.stream-cancelled'
|
|
139
|
+
/** `streamWithEvents` ended on a suspend or an abort. */
|
|
140
|
+
| 'agent.events-ended'
|
|
141
|
+
/** `streamWithEventsResume` ended on a suspend or an abort. */
|
|
142
|
+
| 'agent.events-resume-ended'
|
|
143
|
+
/** The tool-error budget's `jumpTo: 'end'`. */
|
|
144
|
+
| 'middleware.tool-error-budget'
|
|
145
|
+
/** The tool-loop guard's `jumpTo: 'end'`. */
|
|
146
|
+
| 'middleware.tool-loop-guard';
|
|
147
|
+
/**
|
|
148
|
+
* Which feeder produced the classification.
|
|
149
|
+
*
|
|
150
|
+
* `metadata` — read off a message's stop/finish reason. `exception` — classified from a thrown
|
|
151
|
+
* error. `control` — the runtime itself decided to end the run (a gate, a middleware, a
|
|
152
|
+
* cancellation, an ordinary completion), so there was nothing to classify.
|
|
153
|
+
*/
|
|
154
|
+
export type GthTerminationSource = 'metadata' | 'exception' | 'control';
|
|
155
|
+
/**
|
|
156
|
+
* What would have to change before a retry is worth making.
|
|
157
|
+
*
|
|
158
|
+
* Named rather than implied, because {@link GthTerminationReason#retryableAfterRemedy} is only
|
|
159
|
+
* actionable if a consumer knows *which* remedy it is being told about.
|
|
160
|
+
*/
|
|
161
|
+
export type GthTerminationRemedy =
|
|
162
|
+
/** Send less: compact the history, drop context, summarise. */
|
|
163
|
+
'reduce-context'
|
|
164
|
+
/** Wait, then send the same thing again. */
|
|
165
|
+
| 'back-off'
|
|
166
|
+
/** Send something different — rephrase, narrow, change approach. */
|
|
167
|
+
| 'change-request'
|
|
168
|
+
/** Send it to a different model. */
|
|
169
|
+
| 'change-model'
|
|
170
|
+
/** Repair credentials or configuration first. */
|
|
171
|
+
| 'fix-credentials'
|
|
172
|
+
/** Nothing is wrong: the run is parked and can be continued where it stopped. */
|
|
173
|
+
| 'resume';
|
|
174
|
+
/**
|
|
175
|
+
* The retry posture of a category — the two facts, plus the remedy the second one refers to.
|
|
176
|
+
*
|
|
177
|
+
* **Posture is per-CATEGORY, so anything acting on a reason must read its `site` as well.** One
|
|
178
|
+
* table decides the posture precisely so no site can invent its own, and the price of that is that
|
|
179
|
+
* the table cannot know how far a given site already got. The standing example is
|
|
180
|
+
* `empty_response`, which is `retryableAsIs: true` because an empty turn usually is worth asking
|
|
181
|
+
* again — yet `runner.empty-after-fallback` is reached only *because* that as-is retry was already
|
|
182
|
+
* spent, while `runner.events-empty` is reached with it never spent at all. A consumer reading the
|
|
183
|
+
* posture flags alone would retry the first of those a second time.
|
|
184
|
+
*/
|
|
185
|
+
export interface GthTerminationPosture {
|
|
186
|
+
/** Is sending the identical request again a sane thing to do? */
|
|
187
|
+
retryableAsIs: boolean;
|
|
188
|
+
/** Is sending it again worthwhile once {@link remedy} has been applied? */
|
|
189
|
+
retryableAfterRemedy: boolean;
|
|
190
|
+
/** The change that makes {@link retryableAfterRemedy} true; absent when it is `false`. */
|
|
191
|
+
remedy?: GthTerminationRemedy;
|
|
192
|
+
}
|
|
193
|
+
/** The retry posture of a category. */
|
|
194
|
+
export declare function terminationPosture(category: GthTerminationCategory): GthTerminationPosture;
|
|
195
|
+
/** Why a run ended, as one value. */
|
|
196
|
+
export interface GthTerminationReason extends GthTerminationPosture {
|
|
197
|
+
/** The taxonomy member. */
|
|
198
|
+
category: GthTerminationCategory;
|
|
199
|
+
/** The site that classified it. */
|
|
200
|
+
site: GthTerminationSite;
|
|
201
|
+
/** Which feeder classified it. */
|
|
202
|
+
source: GthTerminationSource;
|
|
203
|
+
/** Provider family, where the classification knew one. */
|
|
204
|
+
provider?: string;
|
|
205
|
+
/**
|
|
206
|
+
* The raw token the classification was made from — a `finish_reason`, a `stop_reason`, an error
|
|
207
|
+
* name or status. Diagnostic detail, never the carrier of the classification itself.
|
|
208
|
+
*/
|
|
209
|
+
detail?: string;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* [[EXT-159]] — one observation of what the provider said about why a model message stopped.
|
|
213
|
+
*
|
|
214
|
+
* Recorded per finished model message, on every path, whether or not the provider said anything.
|
|
215
|
+
* No `finish_reason` was written to any log anywhere before this, so the artifact a maintainer
|
|
216
|
+
* reaches for first could not answer the question it exists to answer — and a turn where the
|
|
217
|
+
* provider stayed silent looked exactly like one that ended normally.
|
|
218
|
+
*
|
|
219
|
+
* **The absence is the observation.** `token: null` states that the message carried no stop or
|
|
220
|
+
* finish reason at all; it is never a stand-in for one, and no observation is invented for a
|
|
221
|
+
* message that was never seen.
|
|
222
|
+
*/
|
|
223
|
+
export interface GthFinishReasonObservation {
|
|
224
|
+
/** When the message was observed, as an ISO instant with a zone. */
|
|
225
|
+
at: string;
|
|
226
|
+
/** Which of the agent's three paths produced the message. */
|
|
227
|
+
path: 'invoke' | 'stream' | 'events';
|
|
228
|
+
/** The provider's raw token, lower-cased — or `null` when the message carried none. */
|
|
229
|
+
token: string | null;
|
|
230
|
+
}
|
|
231
|
+
/** The classification a feeder produces, before a site is attached to it. */
|
|
232
|
+
export interface GthTerminationClassification {
|
|
233
|
+
category: GthTerminationCategory;
|
|
234
|
+
provider?: string;
|
|
235
|
+
detail?: string;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Build a {@link GthTerminationReason}: attach a site and a feeder to a classification and fill in
|
|
239
|
+
* the posture from the one table. Every site builds through here, so no site can invent a posture.
|
|
240
|
+
*/
|
|
241
|
+
export declare function terminationReason(site: GthTerminationSite, source: GthTerminationSource, classification: GthTerminationCategory | GthTerminationClassification): GthTerminationReason;
|
|
242
|
+
/**
|
|
243
|
+
* Whether a thrown value is a context overflow.
|
|
244
|
+
*
|
|
245
|
+
* The predicate is `ContextOverflowError.isInstance`, never `lc_error_code`: the code is set
|
|
246
|
+
* asymmetrically across providers (Anthropic stamps both the class and the code, OpenAI only the
|
|
247
|
+
* class), so keying on it silently misses most of where the typed class actually works. The
|
|
248
|
+
* substring fallback then covers the providers LangChain does not type at all, and the case where
|
|
249
|
+
* a reworded provider message drops the class.
|
|
250
|
+
*/
|
|
251
|
+
export declare function isContextOverflow(error: unknown): boolean;
|
|
252
|
+
/**
|
|
253
|
+
* The exception feeder: classify a thrown value into the taxonomy.
|
|
254
|
+
*
|
|
255
|
+
* Order matters. The typed and named cases are decided first, because a context overflow is also an
|
|
256
|
+
* HTTP 400 and an abort is also a `DOMException`; only once those are excluded does the status code
|
|
257
|
+
* and then the prose get a say. Never throws: an unclassifiable value is `unknown`, which is a
|
|
258
|
+
* recorded fact rather than a guess.
|
|
259
|
+
*/
|
|
260
|
+
export declare function classifyThrownTermination(error: unknown): GthTerminationClassification;
|
|
261
|
+
/**
|
|
262
|
+
* Attach a reason to a thrown value and return it, so a `throw` site reads as one expression.
|
|
263
|
+
*
|
|
264
|
+
* Non-enumerable, so the reason never widens what an error serialises to (a logged or
|
|
265
|
+
* JSON-stringified error keeps exactly the shape it had), and first-write-wins so a re-throw
|
|
266
|
+
* through an outer wrapper cannot overwrite the inner, truer classification. Fail-soft: a frozen or
|
|
267
|
+
* primitive throw value is returned unchanged rather than turning a failure into a different one.
|
|
268
|
+
*/
|
|
269
|
+
export declare function attachTerminationReason<T>(error: T, reason: GthTerminationReason): T;
|
|
270
|
+
/** The reason attached to a thrown value, following one `cause` link. Undefined when none is. */
|
|
271
|
+
export declare function terminationReasonOf(error: unknown): GthTerminationReason | undefined;
|