@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.
Files changed (33) hide show
  1. package/dist/core/GthAbstractAgent.d.ts +66 -0
  2. package/dist/core/GthAbstractAgent.js +185 -1
  3. package/dist/core/GthAbstractAgent.js.map +1 -1
  4. package/dist/core/GthAgentRunner.d.ts +82 -1
  5. package/dist/core/GthAgentRunner.js +265 -8
  6. package/dist/core/GthAgentRunner.js.map +1 -1
  7. package/dist/core/GthLangChainAgent.d.ts +3 -2
  8. package/dist/core/GthLangChainAgent.js +24 -4
  9. package/dist/core/GthLangChainAgent.js.map +1 -1
  10. package/dist/core/approvals/approvalRequest.d.ts +142 -0
  11. package/dist/core/approvals/approvalRequest.js +198 -3
  12. package/dist/core/approvals/approvalRequest.js.map +1 -1
  13. package/dist/core/refusal.d.ts +54 -1
  14. package/dist/core/refusal.js +109 -1
  15. package/dist/core/refusal.js.map +1 -1
  16. package/dist/core/terminationNotice.d.ts +111 -0
  17. package/dist/core/terminationNotice.js +209 -0
  18. package/dist/core/terminationNotice.js.map +1 -0
  19. package/dist/core/terminationReason.d.ts +271 -0
  20. package/dist/core/terminationReason.js +405 -0
  21. package/dist/core/terminationReason.js.map +1 -0
  22. package/dist/core/types.d.ts +27 -0
  23. package/dist/core/types.js.map +1 -1
  24. package/dist/runtime/conversation.d.ts +11 -0
  25. package/dist/runtime/conversation.js +13 -1
  26. package/dist/runtime/conversation.js.map +1 -1
  27. package/dist/runtime/singleShot.d.ts +11 -0
  28. package/dist/runtime/singleShot.js +21 -1
  29. package/dist/runtime/singleShot.js.map +1 -1
  30. package/dist/utils/debugDump.d.ts +54 -0
  31. package/dist/utils/debugDump.js +32 -0
  32. package/dist/utils/debugDump.js.map +1 -1
  33. package/package.json +1 -1
@@ -0,0 +1,405 @@
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
+ import { ContextOverflowError } from '@langchain/core/errors';
36
+ /**
37
+ * The single posture table.
38
+ *
39
+ * One place decides what a category means for retrying, so the three consumers this taxonomy exists
40
+ * for — a retry posture, a "never retry a 400, a 429 is a different case" ruling, a nudge-or-back-off
41
+ * decision — read the same answer instead of each deriving its own.
42
+ */
43
+ const POSTURE = {
44
+ // Nothing went wrong; there is nothing to retry.
45
+ completed: { retryableAsIs: false, retryableAfterRemedy: false },
46
+ // The one cause the runtime already retries as-is, and it is right to: an empty turn is usually
47
+ // transient. A model that keeps returning nothing needs a different model, not another attempt.
48
+ empty_response: { retryableAsIs: true, retryableAfterRemedy: true, remedy: 'change-model' },
49
+ // A refusal is deterministic for the same input, so the same prompt refuses again.
50
+ content_refusal: { retryableAsIs: false, retryableAfterRemedy: true, remedy: 'change-request' },
51
+ // The answer was cut off, not refused: asking for less, or for a continuation, gets the rest.
52
+ output_truncated: { retryableAsIs: false, retryableAfterRemedy: true, remedy: 'change-request' },
53
+ // THE case the two fields exist for. `ContextOverflowError.getRetryable()` is false, which is
54
+ // right for the same prompt and exactly wrong for the smaller one compaction exists to send.
55
+ context_overflow: { retryableAsIs: false, retryableAfterRemedy: true, remedy: 'reduce-context' },
56
+ // A 429 answered immediately is a 429 again; waiting is the whole remedy.
57
+ rate_limited: { retryableAsIs: false, retryableAfterRemedy: true, remedy: 'back-off' },
58
+ auth_failed: { retryableAsIs: false, retryableAfterRemedy: true, remedy: 'fix-credentials' },
59
+ // A rejected request is rejected identically every time, and a repaired request is a new request
60
+ // rather than a retry — so neither field is true and no remedy is named.
61
+ invalid_request: { retryableAsIs: false, retryableAfterRemedy: false },
62
+ // A provider-side fault is the transient case: the same request often succeeds on the next try.
63
+ provider_error: { retryableAsIs: true, retryableAfterRemedy: true, remedy: 'back-off' },
64
+ network_error: { retryableAsIs: true, retryableAfterRemedy: true, remedy: 'back-off' },
65
+ timeout: { retryableAsIs: true, retryableAfterRemedy: true, remedy: 'back-off' },
66
+ // The user chose to stop. Retrying without being asked overrides the one decision they made.
67
+ cancelled: { retryableAsIs: false, retryableAfterRemedy: false },
68
+ // The gate refused. Re-running the refused command automatically is the failure the gate exists
69
+ // to prevent, so neither field offers it.
70
+ approval_stop: { retryableAsIs: false, retryableAfterRemedy: false },
71
+ // Both guards end a loop that is going nowhere. Repeating it goes nowhere again; a changed
72
+ // approach is exactly what each guard's own notice asks the model for.
73
+ tool_error_budget: { retryableAsIs: false, retryableAfterRemedy: true, remedy: 'change-request' },
74
+ tool_loop_guard: { retryableAsIs: false, retryableAfterRemedy: true, remedy: 'change-request' },
75
+ // The same turn re-suspends the same way, so repeating it exhausts the same bound. Asking for
76
+ // less gated work in one turn is what gets under it.
77
+ interrupt_drain_guard: {
78
+ retryableAsIs: false,
79
+ retryableAfterRemedy: true,
80
+ remedy: 'change-request',
81
+ },
82
+ tool_error: { retryableAsIs: false, retryableAfterRemedy: true, remedy: 'change-request' },
83
+ // Not a failure at all: the run is parked mid-flight and continues where it stopped.
84
+ suspended: { retryableAsIs: false, retryableAfterRemedy: true, remedy: 'resume' },
85
+ recursion_limit: { retryableAsIs: false, retryableAfterRemedy: true, remedy: 'change-request' },
86
+ abandoned: { retryableAsIs: false, retryableAfterRemedy: false },
87
+ // Unclassified is not "probably fine": nothing is known, so nothing is offered.
88
+ unknown: { retryableAsIs: false, retryableAfterRemedy: false },
89
+ };
90
+ /** The retry posture of a category. */
91
+ export function terminationPosture(category) {
92
+ return POSTURE[category] ?? POSTURE.unknown;
93
+ }
94
+ /**
95
+ * Build a {@link GthTerminationReason}: attach a site and a feeder to a classification and fill in
96
+ * the posture from the one table. Every site builds through here, so no site can invent a posture.
97
+ */
98
+ export function terminationReason(site, source, classification) {
99
+ const resolved = typeof classification === 'string' ? { category: classification } : classification;
100
+ return {
101
+ category: resolved.category,
102
+ site,
103
+ source,
104
+ ...terminationPosture(resolved.category),
105
+ ...(resolved.provider === undefined ? {} : { provider: resolved.provider }),
106
+ ...(resolved.detail === undefined ? {} : { detail: resolved.detail }),
107
+ };
108
+ }
109
+ /**
110
+ * Substrings providers use when the input exceeds the model's window, matched case-insensitively.
111
+ *
112
+ * These sit **beside** `@langchain/core`'s own detection rather than behind it. LangChain types the
113
+ * error by substring-matching the provider's English prose in each provider package, so a provider
114
+ * rewording its 400 drops the typed class with nothing going red — and it covers only half our
115
+ * providers to begin with. A fallback that repeats the match here is what keeps the classification
116
+ * from quietly un-typing itself on a dependency bump.
117
+ */
118
+ const CONTEXT_OVERFLOW_PATTERNS = [
119
+ 'context_length_exceeded',
120
+ 'context length exceeded',
121
+ 'maximum context length',
122
+ 'exceeds the context window',
123
+ 'exceed the context window',
124
+ 'input tokens exceed the configured limit',
125
+ 'prompt is too long',
126
+ 'too many tokens',
127
+ 'reduce the length of the messages',
128
+ 'request too large',
129
+ ];
130
+ /** Substrings that mean the provider refused for rate or quota reasons. */
131
+ const RATE_LIMIT_PATTERNS = [
132
+ 'rate limit',
133
+ 'rate_limit',
134
+ 'ratelimit',
135
+ 'too many requests',
136
+ 'quota exceeded',
137
+ 'resource_exhausted',
138
+ 'resource exhausted',
139
+ 'overloaded_error',
140
+ ];
141
+ /** Substrings that mean the caller was not authorised. */
142
+ const AUTH_PATTERNS = [
143
+ 'unauthorized',
144
+ 'unauthenticated',
145
+ 'invalid api key',
146
+ 'invalid_api_key',
147
+ 'incorrect api key',
148
+ 'api key not valid',
149
+ 'permission denied',
150
+ 'permission_denied',
151
+ 'authentication_error',
152
+ 'invalid_grant',
153
+ 'forbidden',
154
+ ];
155
+ /** Substrings that mean the fault was on the provider's side. */
156
+ const PROVIDER_ERROR_PATTERNS = [
157
+ 'internal error',
158
+ 'internal server error',
159
+ 'internal_server_error',
160
+ 'service unavailable',
161
+ 'bad gateway',
162
+ 'server_error',
163
+ 'overloaded',
164
+ 'model is overloaded',
165
+ 'try again later',
166
+ ];
167
+ /** Substrings that mean the request never completed at the transport level. */
168
+ const NETWORK_PATTERNS = [
169
+ 'econnreset',
170
+ 'econnrefused',
171
+ 'enotfound',
172
+ 'epipe',
173
+ 'eai_again',
174
+ 'socket hang up',
175
+ 'fetch failed',
176
+ 'network error',
177
+ 'connection error',
178
+ 'terminated',
179
+ ];
180
+ /** Substrings that mean a deadline elapsed. */
181
+ const TIMEOUT_PATTERNS = [
182
+ 'etimedout',
183
+ 'timed out',
184
+ 'timeout',
185
+ 'deadline exceeded',
186
+ 'deadline_exceeded',
187
+ ];
188
+ /** Substrings that mean the provider rejected the request itself. */
189
+ const INVALID_REQUEST_PATTERNS = [
190
+ 'invalid_request_error',
191
+ 'invalid request',
192
+ 'bad request',
193
+ 'invalid argument',
194
+ 'invalid_argument',
195
+ ];
196
+ /** Read a property off an unknown value without asserting anything about its shape. */
197
+ function field(source, key) {
198
+ if (!source || (typeof source !== 'object' && typeof source !== 'function'))
199
+ return undefined;
200
+ return source[key];
201
+ }
202
+ /** Whether `haystack` contains any of `patterns` (both compared lower-cased). */
203
+ function containsAny(haystack, patterns) {
204
+ return patterns.some((pattern) => haystack.includes(pattern));
205
+ }
206
+ /**
207
+ * Every text an error carries that a classification may read: its message, its name, and the
208
+ * nested provider payloads SDKs hang off `error`, `cause`, `body` and `response.data`. Bounded to
209
+ * one nesting level per branch so a self-referential payload cannot spin.
210
+ */
211
+ function errorText(error) {
212
+ const parts = [];
213
+ const push = (value) => {
214
+ if (typeof value === 'string')
215
+ parts.push(value);
216
+ else if (typeof value === 'number')
217
+ parts.push(String(value));
218
+ };
219
+ push(field(error, 'message'));
220
+ push(field(error, 'name'));
221
+ push(field(error, 'code'));
222
+ push(field(error, 'type'));
223
+ if (typeof error === 'string')
224
+ parts.push(error);
225
+ for (const key of ['error', 'cause', 'body', 'data', 'response']) {
226
+ const nested = field(error, key);
227
+ if (typeof nested === 'string') {
228
+ parts.push(nested);
229
+ continue;
230
+ }
231
+ push(field(nested, 'message'));
232
+ push(field(nested, 'type'));
233
+ push(field(nested, 'code'));
234
+ const inner = field(nested, 'error');
235
+ push(field(inner, 'message'));
236
+ push(field(inner, 'type'));
237
+ push(field(inner, 'code'));
238
+ }
239
+ // The separator is deliberately not a plain space: the prose patterns below are multi-word
240
+ // English, and joining two adjacent fragments with a space lets a pattern match ACROSS them —
241
+ // a fragment ending in "rate" beside one starting with "limit" would read as a rate limit.
242
+ // A newline cannot occur mid-pattern, so it breaks that adjacency without hiding anything.
243
+ return parts.join(' \n ').toLowerCase();
244
+ }
245
+ /** The HTTP status an SDK error carries, wherever it hangs it. Undefined when there is none. */
246
+ function httpStatus(error) {
247
+ for (const holder of [error, field(error, 'response'), field(error, 'error')]) {
248
+ for (const key of ['status', 'statusCode', 'code']) {
249
+ const value = field(holder, key);
250
+ if (typeof value === 'number' && value >= 100 && value < 600)
251
+ return value;
252
+ if (typeof value === 'string' && /^[1-5]\d{2}$/.test(value))
253
+ return Number(value);
254
+ }
255
+ }
256
+ return undefined;
257
+ }
258
+ /** The `name` of an error, or `undefined` for anything that carries none. */
259
+ function errorName(error) {
260
+ const name = field(error, 'name');
261
+ return typeof name === 'string' && name.length > 0 ? name : undefined;
262
+ }
263
+ /**
264
+ * Whether a thrown value is a context overflow.
265
+ *
266
+ * The predicate is `ContextOverflowError.isInstance`, never `lc_error_code`: the code is set
267
+ * asymmetrically across providers (Anthropic stamps both the class and the code, OpenAI only the
268
+ * class), so keying on it silently misses most of where the typed class actually works. The
269
+ * substring fallback then covers the providers LangChain does not type at all, and the case where
270
+ * a reworded provider message drops the class.
271
+ */
272
+ export function isContextOverflow(error) {
273
+ try {
274
+ if (ContextOverflowError.isInstance(error))
275
+ return true;
276
+ }
277
+ catch {
278
+ /* fail-soft: a dependency that stops exporting the predicate must not break classification */
279
+ }
280
+ if (errorName(error) === 'ContextOverflowError')
281
+ return true;
282
+ return containsAny(errorText(error), CONTEXT_OVERFLOW_PATTERNS);
283
+ }
284
+ /**
285
+ * The exception feeder: classify a thrown value into the taxonomy.
286
+ *
287
+ * Order matters. The typed and named cases are decided first, because a context overflow is also an
288
+ * HTTP 400 and an abort is also a `DOMException`; only once those are excluded does the status code
289
+ * and then the prose get a say. Never throws: an unclassifiable value is `unknown`, which is a
290
+ * recorded fact rather than a guess.
291
+ */
292
+ export function classifyThrownTermination(error) {
293
+ try {
294
+ const name = errorName(error);
295
+ const text = errorText(error);
296
+ const status = httpStatus(error);
297
+ // Typed / named first — these are unambiguous and several of them also carry a status that
298
+ // would classify them wrongly.
299
+ if (isContextOverflow(error)) {
300
+ return { category: 'context_overflow', detail: name ?? 'ContextOverflowError' };
301
+ }
302
+ if (name === 'AbortError' || name === 'ModelAbortError' || name === 'APIUserAbortError') {
303
+ return { category: 'cancelled', detail: name };
304
+ }
305
+ if (name === 'GraphInterrupt') {
306
+ return { category: 'suspended', detail: name };
307
+ }
308
+ if (name === 'ToolException') {
309
+ return { category: 'tool_error', detail: name };
310
+ }
311
+ if (name === 'GraphRecursionError' || text.includes('recursion limit')) {
312
+ return { category: 'recursion_limit', detail: name ?? 'recursion limit' };
313
+ }
314
+ if (name === 'TimeoutError' || name === 'APITimeoutError') {
315
+ return { category: 'timeout', detail: name };
316
+ }
317
+ if (name === 'APIConnectionError') {
318
+ return { category: 'network_error', detail: name };
319
+ }
320
+ // Status codes next: a number the provider set is stronger evidence than prose we matched.
321
+ if (status === 429)
322
+ return { category: 'rate_limited', detail: '429' };
323
+ if (status === 401 || status === 403)
324
+ return { category: 'auth_failed', detail: String(status) };
325
+ if (status === 408 || status === 504)
326
+ return { category: 'timeout', detail: String(status) };
327
+ if (status !== undefined && status >= 500) {
328
+ return { category: 'provider_error', detail: String(status) };
329
+ }
330
+ // Prose last, and in the order that keeps a specific signal from being eaten by a generic one:
331
+ // "quota exceeded" is a rate limit before it is an invalid request, and an auth failure often
332
+ // arrives as a 400 whose body says `invalid_grant`.
333
+ if (containsAny(text, RATE_LIMIT_PATTERNS))
334
+ return { category: 'rate_limited' };
335
+ if (containsAny(text, AUTH_PATTERNS))
336
+ return { category: 'auth_failed' };
337
+ if (containsAny(text, TIMEOUT_PATTERNS))
338
+ return { category: 'timeout' };
339
+ if (containsAny(text, NETWORK_PATTERNS))
340
+ return { category: 'network_error' };
341
+ if (containsAny(text, PROVIDER_ERROR_PATTERNS))
342
+ return { category: 'provider_error' };
343
+ if (status === 400 || containsAny(text, INVALID_REQUEST_PATTERNS)) {
344
+ // `detail` is the raw token the classification was made from, so it states the status the
345
+ // response ACTUALLY had. A 402 or 409 whose prose matches the patterns reaches this branch
346
+ // too (it is past the 429/401/403/408/5xx arms), and stamping a flat '400' on one would put
347
+ // a false statement in the field that exists to record what was seen.
348
+ return {
349
+ category: 'invalid_request',
350
+ detail: status === undefined ? undefined : String(status),
351
+ };
352
+ }
353
+ return { category: 'unknown', ...(name === undefined ? {} : { detail: name }) };
354
+ }
355
+ catch {
356
+ // Classification must never be the thing that breaks a run that was already failing.
357
+ return { category: 'unknown' };
358
+ }
359
+ }
360
+ /**
361
+ * The property a reason is carried on when it rides a thrown error.
362
+ *
363
+ * A run that ends by throwing crosses layers the runner does not own, and the message is not the
364
+ * carrier — that is the whole defect this taxonomy exists to fix. Attaching the value to the error
365
+ * lets any catcher upstream read the classification without re-deriving it from prose.
366
+ */
367
+ const TERMINATION_REASON_KEY = 'gthTerminationReason';
368
+ /**
369
+ * Attach a reason to a thrown value and return it, so a `throw` site reads as one expression.
370
+ *
371
+ * Non-enumerable, so the reason never widens what an error serialises to (a logged or
372
+ * JSON-stringified error keeps exactly the shape it had), and first-write-wins so a re-throw
373
+ * through an outer wrapper cannot overwrite the inner, truer classification. Fail-soft: a frozen or
374
+ * primitive throw value is returned unchanged rather than turning a failure into a different one.
375
+ */
376
+ export function attachTerminationReason(error, reason) {
377
+ try {
378
+ if (!error || (typeof error !== 'object' && typeof error !== 'function'))
379
+ return error;
380
+ if (field(error, TERMINATION_REASON_KEY) !== undefined)
381
+ return error;
382
+ Object.defineProperty(error, TERMINATION_REASON_KEY, {
383
+ value: reason,
384
+ enumerable: false,
385
+ writable: true,
386
+ configurable: true,
387
+ });
388
+ }
389
+ catch {
390
+ /* fail-soft */
391
+ }
392
+ return error;
393
+ }
394
+ /** The reason attached to a thrown value, following one `cause` link. Undefined when none is. */
395
+ export function terminationReasonOf(error) {
396
+ const own = field(error, TERMINATION_REASON_KEY);
397
+ if (own && typeof own === 'object')
398
+ return own;
399
+ const cause = field(error, 'cause');
400
+ const inherited = field(cause, TERMINATION_REASON_KEY);
401
+ if (inherited && typeof inherited === 'object')
402
+ return inherited;
403
+ return undefined;
404
+ }
405
+ //# sourceMappingURL=terminationReason.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminationReason.js","sourceRoot":"","sources":["../../src/core/terminationReason.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import type { GthConfig } from '#src/config.js';
2
2
  import type { DeclaredToolAnnotations } from '#src/core/approvals/annotations.js';
3
3
  import type { ApprovalSubject } from '#src/core/approvals/matcher.js';
4
4
  import type { RaterNegotiationRound, ShellSafetyVerdict } from '#src/core/shell/rater.js';
5
+ import type { GthFinishReasonObservation, GthTerminationReason } from '#src/core/terminationReason.js';
5
6
  import type { BaseMessage } from '@langchain/core/messages';
6
7
  import type { RunnableConfig } from '@langchain/core/runnables';
7
8
  import type { StructuredToolInterface } from '@langchain/core/tools';
@@ -35,6 +36,7 @@ export type { DeclaredToolAnnotations } from '#src/core/approvals/annotations.js
35
36
  export type { ApprovalSubject, McpToolApprovalSubject, ShellApprovalSubject, ToolApprovalSubject, } from '#src/core/approvals/matcher.js';
36
37
  export type { RaterNegotiationRound, RaterOutcome, ShellSafetyVerdict, } from '#src/core/shell/rater.js';
37
38
  export type { AlignmentDecision, AlignmentDecisionKind } from '#src/core/shell/alignment.js';
39
+ export type { GthFinishReasonObservation, GthTerminationCategory, GthTerminationPosture, GthTerminationReason, GthTerminationRemedy, GthTerminationSite, GthTerminationSource, } from '#src/core/terminationReason.js';
38
40
  export type Message = BaseMessage;
39
41
  export type StatusUpdateCallback = (level: StatusLevel, message: string) => void;
40
42
  /**
@@ -595,6 +597,31 @@ export interface GthAgentInterface {
595
597
  * the runner records no analytics for that turn. Reading must never throw.
596
598
  */
597
599
  getRunStats?(): GthRunStats;
600
+ /**
601
+ * [[EXT-159]] — forget the previous turn's termination reason so the next turn starts with none.
602
+ * Called by the runner at each turn boundary, alongside {@link resetRunStats}.
603
+ * Optional: an agent that classifies nothing simply omits it.
604
+ */
605
+ resetTerminationReason?(): void;
606
+ /**
607
+ * [[EXT-159]] — why the current turn ended, as classified by the sites inside the agent (the
608
+ * metadata reader, the cancellation and suspend paths, the run-ending middlewares), or `null`
609
+ * when none of them fired.
610
+ *
611
+ * **First-write-wins**, and the agent's answer outranks the runner's: these sites sit inside the
612
+ * runner's own catches, so the innermost classification is the true one and an outer wrapper must
613
+ * not overwrite it. Optional; reading must never throw.
614
+ */
615
+ getTerminationReason?(): GthTerminationReason | null;
616
+ /**
617
+ * [[EXT-159]] — what the provider said about why each finished model message stopped, this turn.
618
+ *
619
+ * The raw `finish_reason` / `stop_reason` / `done_reason` token per observed message, or `null`
620
+ * on an entry whose message carried none — the absence recorded as an absence rather than left to
621
+ * look like an ordinary end. An EMPTY list is a third thing again: no finished model message was
622
+ * observed at all. Optional; reading must never throw.
623
+ */
624
+ getFinishReasonObservations?(): readonly GthFinishReasonObservation[];
598
625
  cleanup?(): Promise<void>;
599
626
  }
600
627
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAuDA;;;;GAIG;AACH,MAAM,CAAN,IAAY,WAQX;AARD,WAAY,WAAW;IACrB,+CAAS,CAAA;IACT,6CAAQ,CAAA;IACR,mDAAW,CAAA;IACX,mDAAW,CAAA;IACX,mDAAW,CAAA;IACX,+CAAS,CAAA;IACT,iDAAU,CAAA;AACZ,CAAC,EARW,WAAW,KAAX,WAAW,QAQtB"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAwEA;;;;GAIG;AACH,MAAM,CAAN,IAAY,WAQX;AARD,WAAY,WAAW;IACrB,+CAAS,CAAA;IACT,6CAAQ,CAAA;IACR,mDAAW,CAAA;IACX,mDAAW,CAAA;IACX,mDAAW,CAAA;IACX,+CAAS,CAAA;IACT,iDAAU,CAAA;AACZ,CAAC,EARW,WAAW,KAAX,WAAW,QAQtB"}
@@ -2,6 +2,7 @@ import type { GthConfig } from '#src/config.js';
2
2
  import type { AgentResolvers, GthAgentFactory, GthCommand } from '#src/core/types.js';
3
3
  import type { SingleShotOptions } from '#src/runtime/singleShot.js';
4
4
  import type { GthRunStats } from '#src/core/types.js';
5
+ import type { GthTerminationReason } from '#src/core/terminationReason.js';
5
6
  /**
6
7
  * One turn's result inside a {@link runConversation} run: the per-turn `ok`/`answer` plus that
7
8
  * turn's run stats (GS2-16 {@link GthRunStats} — token usage + invoked tools), captured PER TURN (a
@@ -16,6 +17,16 @@ export interface ConversationTurnResult extends GthRunStats {
16
17
  answer: string;
17
18
  /** Set when `ok` is `false`: why this turn failed. */
18
19
  error?: string;
20
+ /**
21
+ * [[EXT-159]] — why this turn ended, as a value.
22
+ *
23
+ * Per turn, like every other field here, and read before the next turn resets it. It is the
24
+ * structural carrier the console line beside it is rendered FROM, so a consumer — a multi-turn
25
+ * eval asking whether a run stopped because the provider faulted or because the model handed back
26
+ * — reads the classification instead of parsing `error`. `null` means no site classified the
27
+ * ending, which is a site we missed rather than a turn that went well.
28
+ */
29
+ terminationReason: GthTerminationReason | null;
19
30
  }
20
31
  /**
21
32
  * Run a scripted MULTI-TURN conversation and return one {@link ConversationTurnResult} per turn.
@@ -7,6 +7,7 @@ import { ProgressIndicator } from '#src/utils/ProgressIndicator.js';
7
7
  import { recordSessionSafe } from '#src/history/recordSession.js';
8
8
  import { getProjectDir, stdout } from '#src/utils/systemUtils.js';
9
9
  import { ApprovalStopError, approvalStopRows } from '#src/core/shell/approvalStop.js';
10
+ import { displayTermination } from '#src/core/terminationNotice.js';
10
11
  /**
11
12
  * Run a scripted MULTI-TURN conversation and return one {@link ConversationTurnResult} per turn.
12
13
  *
@@ -132,7 +133,18 @@ export async function runConversation(source, _preamble, userMessages, config, r
132
133
  tools: runStats.tools.length > 0 ? runStats.tools : undefined,
133
134
  durationMs: Date.now() - startedAt,
134
135
  });
135
- results.push({ ok, answer, error, ...runStats });
136
+ // [[EXT-159]] — why THIS turn ended, read before the next turn's reset clears it, and said
137
+ // on the same console this surface already reports a failed turn on. A scripted multi-turn
138
+ // run is watched like any other: the turn that stops is the one the person is looking at.
139
+ let terminationReason = null;
140
+ try {
141
+ terminationReason = runner.getTerminationReason();
142
+ }
143
+ catch {
144
+ /* fail-soft: explaining a turn must never be what ends the conversation */
145
+ }
146
+ displayTermination(terminationReason);
147
+ results.push({ ok, answer, error, terminationReason, ...runStats });
136
148
  // A failed turn breaks the conversation's context — stop rather than run later turns on it.
137
149
  if (!ok)
138
150
  break;
@@ -1 +1 @@
1
- {"version":3,"file":"conversation.js","sourceRoot":"","sources":["../../src/runtime/conversation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"conversation.js","sourceRoot":"","sources":["../../src/runtime/conversation.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import type { GthConfig } from '#src/config.js';
2
2
  import type { AgentResolvers, GthAgentFactory, GthCommand } from '#src/core/types.js';
3
3
  import type { GthRunStats } from '#src/core/types.js';
4
+ import type { GthTerminationReason } from '#src/core/terminationReason.js';
4
5
  /**
5
6
  * Result of a {@link runSingleShot} run: the pass/fail contract callers such as `ask`/`exec` have
6
7
  * always used (`ok`), plus the SUT's answer text and run stats (GS2-16's {@link GthRunStats}) that
@@ -12,6 +13,16 @@ export interface SingleShotResult extends GthRunStats {
12
13
  ok: boolean;
13
14
  /** The SUT's full answer text (`runner.processMessages()`'s return value). Empty on failure. */
14
15
  answer: string;
16
+ /**
17
+ * [[EXT-159]] — why the run ended, as a value.
18
+ *
19
+ * The structural twin of the line this runtime prints: a caller (and a test) reads the
20
+ * classification here rather than matching the prose on the console, so no user-facing string is
21
+ * the only carrier of it. `null` means no site classified the ending, which by this taxonomy's
22
+ * contract is a site we missed — never a stand-in for an ordinary finish, which is recorded as
23
+ * `completed`.
24
+ */
25
+ terminationReason: GthTerminationReason | null;
15
26
  }
16
27
  /** Options that qualify a {@link runSingleShot} run without changing how it behaves. */
17
28
  export interface SingleShotOptions {
@@ -7,6 +7,7 @@ import { ProgressIndicator } from '#src/utils/ProgressIndicator.js';
7
7
  import { recordSessionSafe } from '#src/history/recordSession.js';
8
8
  import { getProjectDir, stdout } from '#src/utils/systemUtils.js';
9
9
  import { ApprovalStopError, approvalStopRows } from '#src/core/shell/approvalStop.js';
10
+ import { displayTermination } from '#src/core/terminationNotice.js';
10
11
  /**
11
12
  * Ask a question and get an answer from the LLM.
12
13
  *
@@ -85,6 +86,25 @@ _preamble, content, config, resolvers, command = 'ask', agentFactory, options) {
85
86
  finally {
86
87
  await runner.cleanup();
87
88
  }
89
+ // [[EXT-159]] — say why the run ended, on the surface the user is actually looking at.
90
+ //
91
+ // This is the non-interactive verb's live session: there is no prompt to come back to and no
92
+ // transcript to scroll, so the moment the process is about to print its last line is the only
93
+ // moment this can reach anyone. Read post-cleanup for the same reason the stats below are — the
94
+ // runner snapshots the agent's answer at `cleanup()`, which the `finally` above has already run.
95
+ //
96
+ // Placed AFTER the catch's own message rather than instead of it: [[EXT-92]] owns rendering a
97
+ // provider error as prose, and this node supplies the classification that prose has never
98
+ // carried. On the success path an ordinary completion says nothing, so a run that worked looks
99
+ // exactly as it did.
100
+ let terminationReason = null;
101
+ try {
102
+ terminationReason = runner.getTerminationReason();
103
+ }
104
+ catch {
105
+ /* fail-soft: explaining a run must never be what breaks it */
106
+ }
107
+ displayTermination(terminationReason);
88
108
  // GS2-16: live token usage + invoked tool names for this run (fail-soft; empty when the
89
109
  // provider reported no usage / the runner has no stats). Read post-cleanup — the runner
90
110
  // snapshots stats at cleanup() so this still reflects the finished run.
@@ -126,7 +146,7 @@ _preamble, content, config, resolvers, command = 'ask', agentFactory, options) {
126
146
  displayError(error instanceof Error ? error.message : String(error));
127
147
  }
128
148
  }
129
- return { ok: succeeded, answer: responseText, ...runStats };
149
+ return { ok: succeeded, answer: responseText, terminationReason, ...runStats };
130
150
  }
131
151
  finally {
132
152
  // EXT-53: the indicator owns a 1s setInterval — an active libuv handle that keeps Node's event
@@ -1 +1 @@
1
- {"version":3,"file":"singleShot.js","sourceRoot":"","sources":["../../src/runtime/singleShot.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"singleShot.js","sourceRoot":"","sources":["../../src/runtime/singleShot.ts"],"names":[],"mappings":"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"}