@sema-agent/client-core 0.86.0 → 0.88.0

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 (57) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/README.md +18 -12
  3. package/dist/adapt/arms.js +9 -0
  4. package/dist/adapter/downstream/eventToSdkMessage.js +14 -1
  5. package/dist/adapter/downstream/terminalToSdkResult.js +4 -1
  6. package/dist/adapter/downstream/wiringManifestView.d.ts +2 -0
  7. package/dist/adapter/downstream/wiringManifestView.js +1 -0
  8. package/dist/adapter/runStream.js +75 -2
  9. package/dist/adapter/types.d.ts +3 -0
  10. package/dist/decideReceipt.d.ts +1 -1
  11. package/dist/decideReceipt.js +1 -1
  12. package/dist/detachWire.d.ts +6 -1
  13. package/dist/detachWire.js +22 -1
  14. package/dist/displayUntrusted.d.ts +4 -0
  15. package/dist/displayUntrusted.js +1657 -573
  16. package/dist/engineErrorCodes.d.ts +2 -0
  17. package/dist/engineErrorCodes.js +1 -0
  18. package/dist/engineNoticeCodes.d.ts +20 -0
  19. package/dist/engineNoticeCodes.js +71 -0
  20. package/dist/engineWireFor.d.ts +1 -0
  21. package/dist/engineWireFor.js +1 -1
  22. package/dist/gateOutcome.d.ts +4 -0
  23. package/dist/gateOutcome.js +5 -0
  24. package/dist/generated/engineFactTables.d.ts +1 -0
  25. package/dist/generated/engineFactTables.js +3 -0
  26. package/dist/generated/engineNoticeTables.js +2 -0
  27. package/dist/hitl/crashConverged.d.ts +2 -1
  28. package/dist/hitl/crashConverged.js +23 -3
  29. package/dist/hitl/hitlBridge.d.ts +4 -0
  30. package/dist/hitl/hitlBridge.js +6 -0
  31. package/dist/hitl/parkResolver.js +17 -12
  32. package/dist/hitl/planReviewWire.js +30 -10
  33. package/dist/hitl/suspendedReopen.d.ts +7 -0
  34. package/dist/hitl/suspendedReopen.js +38 -7
  35. package/dist/hitl/toolApprovalWire.js +4 -5
  36. package/dist/index.d.ts +7 -0
  37. package/dist/index.js +4 -0
  38. package/dist/promptDelivery.d.ts +20 -0
  39. package/dist/promptDelivery.js +153 -0
  40. package/dist/registryConflict.d.ts +3 -0
  41. package/dist/registryConflict.js +44 -0
  42. package/dist/request/taskRequest.d.ts +1 -0
  43. package/dist/request/taskRequest.js +25 -1
  44. package/dist/resumeRefusalCopy.d.ts +8 -6
  45. package/dist/resumeRefusalCopy.js +41 -19
  46. package/dist/rewindArchiveCapability.js +5 -0
  47. package/dist/seam.d.ts +12 -1
  48. package/dist/seam.js +7 -0
  49. package/dist/sessionMemoryErase.d.ts +76 -0
  50. package/dist/sessionMemoryErase.js +432 -0
  51. package/dist/subagent/engineSubagentResume.js +2 -1
  52. package/dist/toolHistoryMismatch.d.ts +63 -0
  53. package/dist/toolHistoryMismatch.js +425 -0
  54. package/dist/workflowClient.d.ts +3 -2
  55. package/dist/workflowClient.js +3 -3
  56. package/docs/INTEGRATION-CLIENTS.md +598 -13
  57. package/package.json +2 -2
@@ -0,0 +1,432 @@
1
+ import { isLoopbackWireUrl, resolveWireAuth } from './engineWireSdk.js';
2
+ import { INJECTED_WIRE_SETTLE_MS, connectionLostDetail, noAnswerDetailFrom, noAnswerKindOf, provablyNotSent, settleInjected } from './engineWireFor.js';
3
+ import { memoryEraseReplayPosture, readMemoryEraseReceipt } from './memoryEntriesWire.js';
4
+ import { normalizeWirePrincipal } from './principalWire.js';
5
+ const PRINCIPAL_HEADER = 'x-agent-principal';
6
+ const MAX_TIMER_MS = 2_147_483_647;
7
+ const NOT_SENT = ' — nothing was sent';
8
+ const DETAIL_BAD_CALL = `the erase call needs a session id (a non-empty string that is not a dot segment) and a request id (a non-empty string), and allowUnevidenced must be true or false when given${NOT_SENT}`;
9
+ const DETAIL_BAD_OPTIONS = `the options could not be read, or the signal is not an AbortSignal${NOT_SENT}`;
10
+ const DETAIL_BAD_WIRE = `the connection could not be read (it needs a baseUrl string, a known token form, a positive time limit and a fetch function)${NOT_SENT}`;
11
+ const DETAIL_BROWSER = `a connection that carries a token is refused in a browser; use the same-origin relay form${NOT_SENT}`;
12
+ const DETAIL_NO_FETCH = `no fetch function is available${NOT_SENT}`;
13
+ const DETAIL_CANCELLED = `the call was cancelled before the request was sent${NOT_SENT}`;
14
+ const DETAIL_CONNECT_FAILED = `the connection to the engine could not be opened${NOT_SENT}`;
15
+ const DETAIL_ANSWER_UNREADABLE = 'an answer came back but could not be read (the request may still have been received)';
16
+ class AnswerUnreadable extends Error {
17
+ }
18
+ function plainObject(v) {
19
+ return v !== null && typeof v === 'object' && !Array.isArray(v);
20
+ }
21
+ function nonEmptyString(v) {
22
+ return typeof v === 'string' && v !== '';
23
+ }
24
+ function notSent(why, detail) {
25
+ return { kind: 'not_sent', why, detail };
26
+ }
27
+ function tokenFormOf(token, baseUrl) {
28
+ if (token === undefined)
29
+ return { kind: 'value', read: () => undefined };
30
+ if (typeof token === 'string')
31
+ return { kind: 'value', read: () => token };
32
+ if (typeof token === 'function') {
33
+ const fn = token;
34
+ return {
35
+ kind: 'value',
36
+ read: () => {
37
+ try {
38
+ return fn();
39
+ }
40
+ catch {
41
+ return undefined;
42
+ }
43
+ },
44
+ };
45
+ }
46
+ if (plainObject(token)) {
47
+ let mode;
48
+ try {
49
+ mode = token.mode;
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ if (mode === 'same-origin-relay')
55
+ return { kind: 'relay' };
56
+ if (mode === 'loopback-unauthed' && isLoopbackWireUrl(baseUrl))
57
+ return { kind: 'unauthed' };
58
+ return null;
59
+ }
60
+ return null;
61
+ }
62
+ function inBrowser() {
63
+ try {
64
+ return typeof globalThis.window !== 'undefined';
65
+ }
66
+ catch {
67
+ return false;
68
+ }
69
+ }
70
+ function outboundHeaders(baseUrl, form, principal) {
71
+ const h = {};
72
+ if (form.kind === 'value') {
73
+ const resolved = resolveWireAuth(baseUrl, form.read());
74
+ if (typeof resolved === 'string')
75
+ h.authorization = `Bearer ${resolved}`;
76
+ }
77
+ if (principal !== undefined)
78
+ h[PRINCIPAL_HEADER] = principal;
79
+ h['content-type'] = 'application/json';
80
+ return h;
81
+ }
82
+ async function readAnswer(res) {
83
+ let status;
84
+ let textOf;
85
+ try {
86
+ if (res === null || typeof res !== 'object')
87
+ throw new AnswerUnreadable();
88
+ status = res.status;
89
+ textOf = res.text;
90
+ }
91
+ catch {
92
+ throw new AnswerUnreadable();
93
+ }
94
+ if (typeof status !== 'number' || !Number.isInteger(status) || status < 100 || status > 599 || typeof textOf !== 'function')
95
+ throw new AnswerUnreadable();
96
+ let text;
97
+ try {
98
+ text = await textOf.call(res);
99
+ }
100
+ catch (e) {
101
+ if (e !== null && typeof e === 'object' && noAnswerKindOf(e) !== null)
102
+ throw e;
103
+ throw new AnswerUnreadable();
104
+ }
105
+ if (typeof text !== 'string')
106
+ throw new AnswerUnreadable();
107
+ return { status, text };
108
+ }
109
+ export async function eraseSessionMemory(call, wire, opts) {
110
+ let sent;
111
+ let sessionSegment;
112
+ try {
113
+ if (!plainObject(call))
114
+ return notSent('bad_input', DETAIL_BAD_CALL);
115
+ const sessionId = call.sessionId;
116
+ const requestId = call.requestId;
117
+ const allowUnevidenced = Object.hasOwn(call, 'allowUnevidenced') ? call.allowUnevidenced : undefined;
118
+ if (!nonEmptyString(sessionId) || !nonEmptyString(requestId))
119
+ return notSent('bad_input', DETAIL_BAD_CALL);
120
+ if (sessionId === '.' || sessionId === '..')
121
+ return notSent('bad_input', DETAIL_BAD_CALL);
122
+ if (allowUnevidenced !== undefined && typeof allowUnevidenced !== 'boolean')
123
+ return notSent('bad_input', DETAIL_BAD_CALL);
124
+ sessionSegment = encodeURIComponent(sessionId);
125
+ sent = { sessionId, requestId, ...(allowUnevidenced !== undefined ? { allowUnevidenced } : {}) };
126
+ }
127
+ catch {
128
+ return notSent('bad_input', DETAIL_BAD_CALL);
129
+ }
130
+ let signal;
131
+ try {
132
+ if (opts !== undefined && opts !== null && typeof opts !== 'object')
133
+ return notSent('bad_input', DETAIL_BAD_OPTIONS);
134
+ const sig = opts?.signal;
135
+ if (sig !== undefined && sig !== null) {
136
+ if (typeof sig !== 'object' || typeof sig.addEventListener !== 'function' || typeof sig.aborted !== 'boolean') {
137
+ return notSent('bad_input', DETAIL_BAD_OPTIONS);
138
+ }
139
+ if (typeof AbortSignal.any === 'function' && !(sig instanceof AbortSignal))
140
+ return notSent('bad_input', DETAIL_BAD_OPTIONS);
141
+ signal = sig;
142
+ }
143
+ }
144
+ catch {
145
+ return notSent('bad_input', DETAIL_BAD_OPTIONS);
146
+ }
147
+ let baseUrl;
148
+ let form;
149
+ let principal;
150
+ let timeoutMs;
151
+ let fetchImpl;
152
+ try {
153
+ if (!plainObject(wire))
154
+ return notSent('no_connection', DETAIL_BAD_WIRE);
155
+ const rawBase = wire.baseUrl;
156
+ const rawToken = wire.token;
157
+ const rawPrincipal = wire.principal;
158
+ const rawTimeout = wire.timeoutMs;
159
+ const rawFetch = wire.fetchImpl;
160
+ if (typeof rawBase !== 'string')
161
+ return notSent('no_connection', DETAIL_BAD_WIRE);
162
+ const f = tokenFormOf(rawToken, rawBase);
163
+ if (f === null)
164
+ return notSent('no_connection', DETAIL_BAD_WIRE);
165
+ if (rawPrincipal !== undefined && typeof rawPrincipal !== 'string')
166
+ return notSent('no_connection', DETAIL_BAD_WIRE);
167
+ if (rawTimeout !== undefined && (typeof rawTimeout !== 'number' || !Number.isFinite(rawTimeout) || rawTimeout <= 0 || rawTimeout > MAX_TIMER_MS)) {
168
+ return notSent('no_connection', DETAIL_BAD_WIRE);
169
+ }
170
+ if (rawFetch !== undefined && typeof rawFetch !== 'function')
171
+ return notSent('no_connection', DETAIL_BAD_WIRE);
172
+ if (f.kind !== 'relay' && inBrowser())
173
+ return notSent('no_connection', DETAIL_BROWSER);
174
+ const globalFetch = globalThis.fetch;
175
+ const chosen = rawFetch ?? (typeof globalFetch === 'function' ? globalFetch.bind(globalThis) : undefined);
176
+ if (typeof chosen !== 'function')
177
+ return notSent('no_connection', DETAIL_NO_FETCH);
178
+ baseUrl = rawBase;
179
+ form = f;
180
+ principal = normalizeWirePrincipal(rawPrincipal);
181
+ timeoutMs = rawTimeout ?? INJECTED_WIRE_SETTLE_MS;
182
+ fetchImpl = chosen;
183
+ }
184
+ catch {
185
+ return notSent('no_connection', DETAIL_BAD_WIRE);
186
+ }
187
+ try {
188
+ if (signal !== undefined && signal.aborted === true)
189
+ return notSent('cancelled', DETAIL_CANCELLED);
190
+ }
191
+ catch {
192
+ return notSent('bad_input', DETAIL_BAD_OPTIONS);
193
+ }
194
+ const url = `${baseUrl.replace(/\/+$/, '')}/v1/sessions/${sessionSegment}/memory/erase`;
195
+ const payload = JSON.stringify(sent.allowUnevidenced !== undefined ? { requestId: sent.requestId, allowUnevidenced: sent.allowUnevidenced } : { requestId: sent.requestId });
196
+ const once = (s) => fetchImpl(url, { method: 'POST', headers: outboundHeaders(baseUrl, form, principal), body: payload, signal: s }).then(readAnswer);
197
+ let got;
198
+ try {
199
+ got = await settleInjected(timeoutMs, signal, once);
200
+ }
201
+ catch (e) {
202
+ if (e instanceof AnswerUnreadable)
203
+ return { kind: 'no_answer', sent, detail: DETAIL_ANSWER_UNREADABLE };
204
+ const obj = e !== null && typeof e === 'object' ? e : null;
205
+ if (obj !== null && noAnswerKindOf(obj) !== null)
206
+ return { kind: 'no_answer', sent, detail: noAnswerDetailFrom(obj) ?? connectionLostDetail() };
207
+ if (obj !== null && provablyNotSent(obj))
208
+ return notSent('no_connection', DETAIL_CONNECT_FAILED);
209
+ return { kind: 'no_answer', sent, detail: connectionLostDetail() };
210
+ }
211
+ let body;
212
+ try {
213
+ body = got.text === '' ? undefined : JSON.parse(got.text);
214
+ }
215
+ catch {
216
+ body = undefined;
217
+ }
218
+ return { kind: 'answered', sent, status: got.status, body };
219
+ }
220
+ const REFUSAL_TEXT = {
221
+ bad_input: 'Nothing was erased: the erase request could not be built from what was given, so it was not sent.',
222
+ no_connection: 'Nothing was erased: there is no usable connection to the engine, so the request was not sent.',
223
+ cancelled_before_send: 'Nothing was erased: the erase was cancelled before the request was sent.',
224
+ credentials_required: 'Nothing was erased: this deployment does not accept memory erase requests until its operator configures a service credential.',
225
+ not_permitted: "Nothing was erased: this caller is not permitted to erase this session's memory.",
226
+ memory_engine_absent: "Nothing was erased: this deployment has no memory store that supports erasing a session's memory.",
227
+ session_not_found: 'Nothing was erased: the engine does not recognise this session for this caller.',
228
+ route_absent: "Nothing was erased: this engine version cannot erase a session's memory.",
229
+ request_rejected: 'Nothing was erased: the engine rejected the erase request as malformed.',
230
+ evidence_unavailable: "Nothing was erased: this deployment's memory store cannot keep proof of an erase, so the erase was declined.",
231
+ request_id_reused: 'Nothing was erased: this request id was already used for a different erase.',
232
+ store_unreadable: 'Nothing was erased: part of the memory store could not be read, so the erase was declined before anything changed; the deployment operator needs to repair the store.',
233
+ };
234
+ const UNCONFIRMED_TEXT = {
235
+ no_answer: 'Not confirmed: no answer came back from the engine, so the erase may or may not have happened.',
236
+ receipt_unreadable: 'Not confirmed: the engine answered, but its erase receipt could not be read.',
237
+ receipt_mismatch: 'Not confirmed: the engine answered with a receipt for a different erase request.',
238
+ store_link_outside: 'Not confirmed: the memory store contains a link that leads outside it, so the erase stopped before finishing and may have erased part of this memory or none of it; the deployment operator needs to remove the link.',
239
+ control_plane_corrupt: "Not confirmed: the memory store's own records could not be read cleanly, so the erase may have changed nothing or may already be committed; the deployment operator needs to repair them.",
240
+ engine_fault: 'Not confirmed: the engine reported an internal failure during the erase, so it may or may not have happened.',
241
+ unclassified: 'Not confirmed: the engine refused in a way this client does not recognise.',
242
+ no_code: 'Not confirmed: an answer came back without the engine error code, so it may not have come from the engine at all.',
243
+ unreadable: 'Not confirmed: the result of this erase could not be read.',
244
+ };
245
+ const ERASED_TEXT = 'Erased: every memory entry recorded as coming from this session was erased or was already gone.';
246
+ const PARTIAL_TEXT = 'Partly erased: some memory entries from this session were not erased yet.';
247
+ const RETRY_TEXT = {
248
+ not_needed: 'Nothing more to do.',
249
+ same_request_id: 'It is safe to try again with the same request id.',
250
+ new_request_id: "To erase this session's memory, try again with a new request id.",
251
+ needs_operator: 'Ask the deployment operator to fix this first, then try again with the same request id.',
252
+ needs_consent: 'It can only go ahead if someone explicitly agrees to erase without keeping proof.',
253
+ human_decision: 'Trying again may erase a second time, so a person should decide whether to retry.',
254
+ pointless: 'Trying again as it is will not help.',
255
+ };
256
+ const REFUSAL_RETRY = {
257
+ bad_input: 'pointless',
258
+ no_connection: 'same_request_id',
259
+ cancelled_before_send: 'same_request_id',
260
+ credentials_required: 'needs_operator',
261
+ not_permitted: 'pointless',
262
+ memory_engine_absent: 'pointless',
263
+ session_not_found: 'pointless',
264
+ route_absent: 'pointless',
265
+ request_rejected: 'pointless',
266
+ evidence_unavailable: 'needs_consent',
267
+ request_id_reused: 'new_request_id',
268
+ store_unreadable: 'needs_operator',
269
+ };
270
+ export const SESSION_MEMORY_ERASE_REFUSAL_REASONS = Object.freeze(Object.keys(REFUSAL_TEXT));
271
+ export const SESSION_MEMORY_ERASE_UNCONFIRMED_WHYS = Object.freeze(Object.keys(UNCONFIRMED_TEXT));
272
+ export const SESSION_MEMORY_ERASE_RETRIES = Object.freeze(Object.keys(RETRY_TEXT));
273
+ const MAX_DIAG_CODE = 200;
274
+ function refusedBy(reason, diag) {
275
+ const retry = REFUSAL_RETRY[reason];
276
+ return { kind: 'refused', reason, text: REFUSAL_TEXT[reason], retry, retryText: RETRY_TEXT[retry], ...diag };
277
+ }
278
+ function unconfirmedRetry(why, allowUnevidenced) {
279
+ if (allowUnevidenced === true)
280
+ return 'human_decision';
281
+ switch (why) {
282
+ case 'store_link_outside':
283
+ case 'control_plane_corrupt':
284
+ return 'needs_operator';
285
+ case 'receipt_mismatch':
286
+ case 'unreadable':
287
+ return 'human_decision';
288
+ case 'no_answer':
289
+ case 'receipt_unreadable':
290
+ case 'engine_fault':
291
+ case 'unclassified':
292
+ case 'no_code':
293
+ return 'same_request_id';
294
+ default: {
295
+ const unreachable = why;
296
+ return unreachable;
297
+ }
298
+ }
299
+ }
300
+ function unconfirmedBy(why, allowUnevidenced, diag) {
301
+ const retry = unconfirmedRetry(why, allowUnevidenced);
302
+ return { kind: 'unconfirmed', why, text: UNCONFIRMED_TEXT[why], retry, retryText: RETRY_TEXT[retry], ...diag };
303
+ }
304
+ function bandOfCode(code) {
305
+ switch (code) {
306
+ case 'auth.service_token_required':
307
+ return { refused: 'credentials_required' };
308
+ case 'principal_unverified':
309
+ return { refused: 'not_permitted' };
310
+ case 'capability.memory_engine_required':
311
+ return { refused: 'memory_engine_absent' };
312
+ case 'not_found.session':
313
+ return { refused: 'session_not_found' };
314
+ case 'not_found.route':
315
+ return { refused: 'route_absent' };
316
+ case 'request.path_malformed':
317
+ case 'request.body_shape':
318
+ case 'request.request_id_required':
319
+ case 'request.field_invalid':
320
+ case 'config.memory_erasure_request':
321
+ return { refused: 'request_rejected' };
322
+ case 'memory.erasure_evidence_unavailable':
323
+ return { refused: 'evidence_unavailable' };
324
+ case 'memory.erasure_selector_mismatch':
325
+ return { refused: 'request_id_reused' };
326
+ case 'memory.erasure_census_incomplete':
327
+ return { refused: 'store_unreadable' };
328
+ case 'memory.target_outside_store':
329
+ return { unconfirmed: 'store_link_outside' };
330
+ case 'internal.memory_control_plane_corrupt':
331
+ return { unconfirmed: 'control_plane_corrupt' };
332
+ case 'internal.error':
333
+ return { unconfirmed: 'engine_fault' };
334
+ default:
335
+ return code.startsWith('auth.') ? { refused: 'not_permitted' } : undefined;
336
+ }
337
+ }
338
+ function selectEchoMatches(body, sessionId) {
339
+ try {
340
+ if (!plainObject(body) || !Object.hasOwn(body, 'select'))
341
+ return true;
342
+ const sel = body.select;
343
+ if (!plainObject(sel) || !Object.hasOwn(sel, 'sessionId'))
344
+ return false;
345
+ return sel.sessionId === sessionId;
346
+ }
347
+ catch {
348
+ return false;
349
+ }
350
+ }
351
+ export function sessionMemoryEraseOutcome(answer) {
352
+ let kind;
353
+ let why;
354
+ let sessionId;
355
+ let requestId;
356
+ let allowUnevidenced;
357
+ let status;
358
+ let body;
359
+ const a = answer;
360
+ try {
361
+ if (!plainObject(a))
362
+ return unconfirmedBy('unreadable', undefined, {});
363
+ kind = a.kind;
364
+ if (kind === 'not_sent') {
365
+ why = a.why;
366
+ }
367
+ else if (kind === 'answered' || kind === 'no_answer') {
368
+ const sent = a.sent;
369
+ if (!plainObject(sent))
370
+ return unconfirmedBy('unreadable', undefined, {});
371
+ sessionId = sent.sessionId;
372
+ requestId = sent.requestId;
373
+ allowUnevidenced = Object.hasOwn(sent, 'allowUnevidenced') ? sent.allowUnevidenced : undefined;
374
+ if (kind === 'answered') {
375
+ status = a.status;
376
+ body = a.body;
377
+ }
378
+ }
379
+ }
380
+ catch {
381
+ return unconfirmedBy('unreadable', undefined, {});
382
+ }
383
+ if (kind === 'not_sent') {
384
+ if (why === 'bad_input')
385
+ return refusedBy('bad_input', {});
386
+ if (why === 'no_connection')
387
+ return refusedBy('no_connection', {});
388
+ if (why === 'cancelled')
389
+ return refusedBy('cancelled_before_send', {});
390
+ return unconfirmedBy('unreadable', undefined, {});
391
+ }
392
+ if (kind !== 'answered' && kind !== 'no_answer')
393
+ return unconfirmedBy('unreadable', undefined, {});
394
+ if (!nonEmptyString(sessionId) || !nonEmptyString(requestId) || (allowUnevidenced !== undefined && typeof allowUnevidenced !== 'boolean')) {
395
+ return unconfirmedBy('unreadable', undefined, {});
396
+ }
397
+ const signed = allowUnevidenced;
398
+ if (kind === 'no_answer')
399
+ return unconfirmedBy('no_answer', signed, {});
400
+ if (typeof status !== 'number' || !Number.isInteger(status) || status < 100 || status > 599)
401
+ return unconfirmedBy('unreadable', undefined, {});
402
+ if (status >= 200 && status < 300) {
403
+ const reading = readMemoryEraseReceipt(body);
404
+ if (reading.kind !== 'present')
405
+ return unconfirmedBy('receipt_unreadable', signed, { status, receiptWhy: reading.why });
406
+ if (reading.requestId !== requestId || !selectEchoMatches(body, sessionId))
407
+ return unconfirmedBy('receipt_mismatch', signed, { status });
408
+ if (reading.status === 'complete') {
409
+ return { kind: 'erased', receipt: reading, text: ERASED_TEXT, retry: 'not_needed', retryText: RETRY_TEXT.not_needed };
410
+ }
411
+ const retry = memoryEraseReplayPosture(reading) === 'idempotent' ? 'same_request_id' : 'human_decision';
412
+ return { kind: 'partial', receipt: reading, text: PARTIAL_TEXT, retry, retryText: RETRY_TEXT[retry] };
413
+ }
414
+ if (status < 400)
415
+ return unconfirmedBy('unclassified', signed, { status });
416
+ let rawCode;
417
+ try {
418
+ rawCode = plainObject(body) && Object.hasOwn(body, 'errorCode') ? body.errorCode : undefined;
419
+ }
420
+ catch {
421
+ return unconfirmedBy('unreadable', undefined, {});
422
+ }
423
+ if (!nonEmptyString(rawCode))
424
+ return unconfirmedBy('no_code', signed, { status });
425
+ const diag = { status, ...(rawCode.length <= MAX_DIAG_CODE ? { errorCode: rawCode } : {}) };
426
+ const band = bandOfCode(rawCode);
427
+ if (band !== undefined && 'refused' in band)
428
+ return refusedBy(band.refused, diag);
429
+ if (band !== undefined)
430
+ return unconfirmedBy(band.unconfirmed, signed, diag);
431
+ return unconfirmedBy(status >= 500 ? 'engine_fault' : 'unclassified', signed, diag);
432
+ }
@@ -1,5 +1,6 @@
1
1
  import { hostLog } from '../host.js';
2
2
  import { resumeRetryLaterFromError } from '../wireErrorTriage.js';
3
+ import { RESUME_SESSION_NOT_FOUND } from '../engineErrorCodes.js';
3
4
  import { displayUntrusted } from '../displayUntrusted.js';
4
5
  import { engineCapTrue } from '../engineCapsCache.js';
5
6
  import { clientHasVerbs, engineWireFor, INJECTED_WIRE_SETTLE_MS, noAnswerDetailFrom, sendOnce, settleInjected } from '../engineWireFor.js';
@@ -21,7 +22,7 @@ export function resolveSubagentContinueTarget(rowId, displayName) {
21
22
  const RETENTION_LAPSED_CODES = new Set([
22
23
  'resume.evicted',
23
24
  'resume.cap',
24
- 'resume.session_not_found',
25
+ RESUME_SESSION_NOT_FOUND,
25
26
  ]);
26
27
  function shapeOf(e) {
27
28
  const o = (e ?? {});
@@ -0,0 +1,63 @@
1
+ export declare const TOOL_HISTORY_MISMATCH_KINDS: readonly ["tool_use_without_result", "orphan_tool_result", "duplicate_tool_use_id"];
2
+ export type ToolHistoryMismatchKind = (typeof TOOL_HISTORY_MISMATCH_KINDS)[number];
3
+ export interface ToolHistoryMismatch {
4
+ kind: ToolHistoryMismatchKind;
5
+ toolUseId?: string;
6
+ }
7
+ export interface ToolHistoryErrorCarrier {
8
+ readonly code?: string | null | undefined;
9
+ readonly status?: number | null | undefined;
10
+ readonly message?: string | null | undefined;
11
+ }
12
+ export interface ToolHistoryMismatchAnchor {
13
+ readonly kind: ToolHistoryMismatchKind;
14
+ readonly template: RegExp;
15
+ readonly idAfter: RegExp | null;
16
+ }
17
+ export declare const TOOL_HISTORY_MISMATCH_ANCHORS: readonly ToolHistoryMismatchAnchor[];
18
+ export declare function classifyToolHistoryMismatch(err: ToolHistoryErrorCarrier | null | undefined): ToolHistoryMismatch | null;
19
+ export declare const TOOL_HISTORY_REWIND_SENTENCE = "Run /rewind to recover the conversation.";
20
+ export declare function toolHistoryRepeatSentence(n: number): string;
21
+ type ConversationRewindVerdict = 'available' | 'unavailable' | 'unknown';
22
+ export declare function _wireConversationRewindJudge(judge: (engineBaseUrl: string) => ConversationRewindVerdict): void;
23
+ export declare function toolHistoryRewindOffered(declared: boolean | {
24
+ readonly engineBaseUrl: string;
25
+ } | undefined): boolean;
26
+ export interface ToolHistoryMismatchRowKey {
27
+ kind: ToolHistoryMismatchKind;
28
+ toolUseId?: string;
29
+ consecutive?: number;
30
+ }
31
+ export declare function toolHistoryMismatchRowKey(m: ToolHistoryMismatch, consecutive: number | undefined): ToolHistoryMismatchRowKey;
32
+ export declare function readToolHistoryMismatchKey(v: object | null | undefined): ToolHistoryMismatchRowKey | undefined;
33
+ export declare const TOOL_HISTORY_LEDGER_CAP = 64;
34
+ export declare function openToolHistoryGeneration(): number;
35
+ export interface ToolHistoryTerminalNote {
36
+ sessionId: string | undefined;
37
+ sessionEchoed: boolean;
38
+ generation: number;
39
+ runId: string | undefined;
40
+ replay: boolean;
41
+ mismatch: ToolHistoryMismatch | null;
42
+ }
43
+ export declare function noteToolHistoryTerminal(note: ToolHistoryTerminalNote): number | undefined;
44
+ export declare function _resetToolHistoryLedgerForTest(): void;
45
+ export interface ToolHistoryRow {
46
+ readonly toolUseIds?: readonly string[];
47
+ readonly toolResultIds?: readonly string[];
48
+ readonly prompt?: boolean;
49
+ readonly engineHandle?: string;
50
+ readonly local?: boolean;
51
+ readonly sessionRoot?: boolean;
52
+ }
53
+ export declare const TOOL_HISTORY_UNDECIDABLE_REASONS: readonly ["no-mismatch", "no-tool-use-id", "id-not-found", "prompt-not-found", "prompt-without-handle", "unreadable-rows"];
54
+ export type ToolHistoryUndecidableReason = (typeof TOOL_HISTORY_UNDECIDABLE_REASONS)[number];
55
+ export type ToolHistoryRewindResolution<R> = {
56
+ target: R;
57
+ } | {
58
+ rootReopen: true;
59
+ } | {
60
+ undecidable: ToolHistoryUndecidableReason;
61
+ };
62
+ export declare function resolveToolHistoryRewindTarget<R extends ToolHistoryRow>(rows: readonly (R | null | undefined)[] | null | undefined, mismatch: ToolHistoryMismatch | null | undefined): ToolHistoryRewindResolution<R>;
63
+ export {};