@sema-agent/client-core 0.90.0 → 0.91.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 (88) hide show
  1. package/CHANGELOG.md +193 -0
  2. package/README.md +24 -18
  3. package/dist/adapt/arms.js +9 -0
  4. package/dist/adapter/downstream/eventToSdkMessage.js +4 -2
  5. package/dist/adapter/downstream/wiringManifestView.d.ts +6 -0
  6. package/dist/adapter/downstream/wiringManifestView.js +16 -0
  7. package/dist/adapter/runStream.js +37 -20
  8. package/dist/askSurvivesStandingAnswer.d.ts +10 -0
  9. package/dist/askSurvivesStandingAnswer.js +40 -0
  10. package/dist/attachmentSeatCapability.d.ts +11 -0
  11. package/dist/attachmentSeatCapability.js +27 -0
  12. package/dist/attachmentsWireCaps.d.ts +9 -0
  13. package/dist/attachmentsWireCaps.js +74 -21
  14. package/dist/compensations.js +10 -14
  15. package/dist/controlRouter.d.ts +9 -2
  16. package/dist/controlRouter.js +66 -28
  17. package/dist/decideFailureNote.js +4 -2
  18. package/dist/disableSkillsWire.d.ts +1 -0
  19. package/dist/disableSkillsWire.js +8 -1
  20. package/dist/displayUntrusted.js +153 -4
  21. package/dist/engineCapsCache.d.ts +3 -1
  22. package/dist/engineCapsCache.js +56 -6
  23. package/dist/engineClosedSets.d.ts +2 -0
  24. package/dist/engineClosedSets.js +3 -0
  25. package/dist/engineErrorCodes.d.ts +3 -0
  26. package/dist/engineErrorCodes.js +10 -0
  27. package/dist/generated/engineFactTables.d.ts +12 -0
  28. package/dist/generated/engineFactTables.js +47 -0
  29. package/dist/hitl/askGateWire.d.ts +1 -1
  30. package/dist/hitl/askGateWire.js +1 -1
  31. package/dist/hitl/crashConverged.d.ts +7 -0
  32. package/dist/hitl/crashConverged.js +57 -0
  33. package/dist/hitl/crashConvergedNotice.d.ts +4 -0
  34. package/dist/hitl/crashConvergedNotice.js +57 -0
  35. package/dist/hitl/hitlBridge.d.ts +7 -4
  36. package/dist/hitl/parkResolver.d.ts +7 -2
  37. package/dist/hitl/parkResolver.js +19 -7
  38. package/dist/hitl/planReviewWire.d.ts +1 -1
  39. package/dist/hitl/planReviewWire.js +18 -4
  40. package/dist/hitl/sessionPolicyWire.d.ts +5 -0
  41. package/dist/hitl/sessionPolicyWire.js +63 -0
  42. package/dist/hitl/suspendedReopen.js +2 -2
  43. package/dist/hitl/toolApprovalWire.d.ts +4 -1
  44. package/dist/hitl/toolApprovalWire.js +20 -0
  45. package/dist/hookMatcherVerdict.d.ts +29 -0
  46. package/dist/hookMatcherVerdict.js +265 -0
  47. package/dist/humanTurn.d.ts +91 -0
  48. package/dist/humanTurn.js +253 -0
  49. package/dist/humanTurnRead.d.ts +105 -0
  50. package/dist/humanTurnRead.js +331 -0
  51. package/dist/index.d.ts +25 -8
  52. package/dist/index.js +16 -6
  53. package/dist/inheritEnvWire.js +1 -1
  54. package/dist/liveModelCatalog.d.ts +2 -0
  55. package/dist/liveModelCatalog.js +51 -3
  56. package/dist/permissionRuleIssue.d.ts +1 -1
  57. package/dist/permissionRuleIssue.js +22 -3
  58. package/dist/permissionsAllowGrantCapability.d.ts +2 -1
  59. package/dist/permissionsAllowGrantCapability.js +11 -2
  60. package/dist/permissionsDeclaration.d.ts +23 -0
  61. package/dist/permissionsDeclaration.js +133 -53
  62. package/dist/promptDelivery.d.ts +11 -3
  63. package/dist/promptDelivery.js +164 -29
  64. package/dist/providerRejection.d.ts +1 -0
  65. package/dist/providerRejection.js +20 -0
  66. package/dist/readDenyTiers.d.ts +10 -0
  67. package/dist/readDenyTiers.js +29 -0
  68. package/dist/request/taskRequest.js +2 -2
  69. package/dist/resumeRefusalCopy.d.ts +11 -0
  70. package/dist/resumeRefusalCopy.js +61 -4
  71. package/dist/rewindArchiveCapability.js +4 -4
  72. package/dist/rewindWireCaps.d.ts +2 -1
  73. package/dist/rewindWireCaps.js +19 -2
  74. package/dist/seam.d.ts +2 -0
  75. package/dist/taskRequestWords.d.ts +7 -1
  76. package/dist/taskRequestWords.js +18 -8
  77. package/dist/toolHistoryBadTurn.d.ts +1 -0
  78. package/dist/toolHistoryBadTurn.js +89 -0
  79. package/dist/toolHistoryMismatch.d.ts +2 -1
  80. package/dist/toolHistoryMismatch.js +19 -6
  81. package/dist/userAttachmentEnvelope.d.ts +61 -0
  82. package/dist/userAttachmentEnvelope.js +204 -0
  83. package/dist/wireFailureShape.d.ts +1 -0
  84. package/dist/wireFailureShape.js +4 -0
  85. package/dist/wireRefusalCopy.d.ts +6 -0
  86. package/dist/wireRefusalCopy.js +26 -3
  87. package/docs/INTEGRATION-CLIENTS.md +875 -14
  88. package/package.json +2 -2
@@ -1,8 +1,43 @@
1
1
  import { activeRunBusySignal } from './adapter/runStream.js';
2
2
  import { selfHealSubmissionDisposition } from './adapter/activeRunSelfHeal.js';
3
3
  const uuidOk = (u) => typeof u === 'string' && u.length > 0;
4
+ function isArraySafe(v) {
5
+ try {
6
+ return Array.isArray(v);
7
+ }
8
+ catch {
9
+ return false;
10
+ }
11
+ }
12
+ const generationOk = (g) => typeof g === 'number' && Number.isSafeInteger(g) && g >= 0;
13
+ function generationOpt(opts) {
14
+ if (typeof opts !== 'object' || opts === null)
15
+ return undefined;
16
+ try {
17
+ const g = opts.generation;
18
+ return generationOk(g) ? g : undefined;
19
+ }
20
+ catch {
21
+ return undefined;
22
+ }
23
+ }
24
+ const levelOf = (rec) => !Object.hasOwn(rec, 'by') ? 1 : Object.hasOwn(rec, 'share') && rec.share === 'partial' ? 2 : 3;
25
+ function deliveredByWord(v) {
26
+ const words = { frame: true, steer_receipt: true };
27
+ return typeof v === 'string' && Object.hasOwn(words, v) ? v : undefined;
28
+ }
29
+ function deliveredShareWord(v) {
30
+ const words = { full: true, partial: true };
31
+ return typeof v === 'string' && Object.hasOwn(words, v) ? v : undefined;
32
+ }
33
+ function ownExtension(rec, key) {
34
+ const desc = Object.getOwnPropertyDescriptor(rec, key);
35
+ if (desc === undefined)
36
+ return key in rec ? { kind: 'unreadable' } : { kind: 'absent' };
37
+ return 'value' in desc ? { kind: 'value', value: desc.value } : { kind: 'unreadable' };
38
+ }
4
39
  function cleanBatch(batch) {
5
- if (!Array.isArray(batch))
40
+ if (!isArraySafe(batch))
6
41
  return [];
7
42
  const out = [];
8
43
  const seen = new Set();
@@ -19,17 +54,73 @@ function cleanBatch(batch) {
19
54
  }
20
55
  return out;
21
56
  }
22
- function readRecord(rec) {
23
- if (typeof rec !== 'object' || rec === null || Array.isArray(rec))
57
+ function readRecord(raw) {
58
+ if (typeof raw !== 'object' || raw === null)
24
59
  return undefined;
60
+ let u;
61
+ let s;
25
62
  try {
26
- const u = rec.u;
27
- const s = rec.s;
28
- return uuidOk(u) && (s === 'a' || s === 'd') ? { u, s } : undefined;
63
+ if (Array.isArray(raw))
64
+ return undefined;
65
+ u = raw.u;
66
+ s = raw.s;
29
67
  }
30
68
  catch {
31
69
  return undefined;
32
70
  }
71
+ if (!uuidOk(u) || (s !== 'a' && s !== 'd'))
72
+ return undefined;
73
+ const legacy = { u, s };
74
+ try {
75
+ let rec = legacy;
76
+ if (s === 'd') {
77
+ const byExt = ownExtension(raw, 'by');
78
+ const shareExt = ownExtension(raw, 'share');
79
+ const by = byExt.kind === 'value' ? deliveredByWord(byExt.value) : undefined;
80
+ const share = shareExt.kind === 'value' ? deliveredShareWord(shareExt.value) : undefined;
81
+ if (by !== undefined && (shareExt.kind === 'absent' || share !== undefined))
82
+ rec = share === undefined ? { u, s, by } : { u, s, by, share };
83
+ }
84
+ const dropped = Object.keys(raw).filter((k) => !Object.hasOwn(rec, k)).length;
85
+ return { rec, dropped };
86
+ }
87
+ catch {
88
+ return { rec: legacy, dropped: 0 };
89
+ }
90
+ }
91
+ function readTierStrict(holder, key) {
92
+ try {
93
+ if (!Object.hasOwn(holder, key))
94
+ return [];
95
+ const raw = holder[key];
96
+ if (!Array.isArray(raw))
97
+ return null;
98
+ const out = [];
99
+ const seen = new Set();
100
+ for (const u of raw) {
101
+ if (!uuidOk(u) || seen.has(u))
102
+ continue;
103
+ seen.add(u);
104
+ out.push(u);
105
+ }
106
+ return out;
107
+ }
108
+ catch {
109
+ return null;
110
+ }
111
+ }
112
+ function batchTiers(batch) {
113
+ const none = { full: [], partial: [] };
114
+ if (isArraySafe(batch))
115
+ return { full: cleanBatch(batch), partial: [] };
116
+ if (typeof batch !== 'object' || batch === null)
117
+ return none;
118
+ const full = readTierStrict(batch, 'full');
119
+ const partial = readTierStrict(batch, 'partial');
120
+ if (full === null || partial === null)
121
+ return none;
122
+ const inFull = new Set(full);
123
+ return { full, partial: partial.filter((u) => !inFull.has(u)) };
33
124
  }
34
125
  const PRE_RUN_FRAME_TYPES = new Set(['meta', 'approval_request', 'approval_revoke']);
35
126
  export function frameProvesDelivery(event) {
@@ -56,9 +147,11 @@ export function frameProvesDelivery(event) {
56
147
  }
57
148
  }
58
149
  function outcomeProvesDelivery(outcome) {
59
- if (typeof outcome !== 'object' || outcome === null || Array.isArray(outcome))
150
+ if (typeof outcome !== 'object' || outcome === null)
60
151
  return false;
61
152
  try {
153
+ if (Array.isArray(outcome))
154
+ return false;
62
155
  return selfHealSubmissionDisposition(outcome) === 'handed-off';
63
156
  }
64
157
  catch {
@@ -67,48 +160,88 @@ function outcomeProvesDelivery(outcome) {
67
160
  }
68
161
  export function createPromptDeliveryLedger() {
69
162
  const attemptedSet = new Set();
70
- const deliveredSet = new Set();
163
+ const deliveredLevel = new Map();
71
164
  const batchOfMap = new Map();
165
+ const generationOf = new Map();
72
166
  const log = [];
73
- const add = (u, s, into) => {
74
- const set = s === 'a' ? attemptedSet : deliveredSet;
75
- if (set.has(u))
167
+ let droppedKeys = 0;
168
+ const keep = (rec, into) => {
169
+ const frozen = Object.freeze(rec);
170
+ log.push(frozen);
171
+ into.push(frozen);
172
+ };
173
+ const add = (rec, into) => {
174
+ if (rec.s === 'a') {
175
+ if (attemptedSet.has(rec.u))
176
+ return;
177
+ attemptedSet.add(rec.u);
178
+ keep(rec, into);
76
179
  return;
77
- set.add(u);
78
- const rec = Object.freeze({ u, s });
79
- log.push(rec);
80
- into.push(rec);
180
+ }
181
+ const level = levelOf(rec);
182
+ if ((deliveredLevel.get(rec.u) ?? 0) >= level)
183
+ return;
184
+ deliveredLevel.set(rec.u, level);
185
+ keep(rec, into);
81
186
  };
82
- const markDelivered = (batchUuids) => {
187
+ const markDelivered = (batch, by) => {
83
188
  const added = [];
84
- for (const u of cleanBatch(batchUuids))
85
- add(u, 'd', added);
189
+ const { full, partial } = batchTiers(batch);
190
+ for (const u of full)
191
+ add({ u, s: 'd', by }, added);
192
+ for (const u of partial)
193
+ add({ u, s: 'd', by, share: 'partial' }, added);
86
194
  return Object.freeze(added);
87
195
  };
196
+ const levelNow = (uuid) => (uuidOk(uuid) ? (deliveredLevel.get(uuid) ?? 0) : 0);
197
+ const knownUndeliveredUuid = (uuid) => uuidOk(uuid) && attemptedSet.has(uuid) && !deliveredLevel.has(uuid);
88
198
  const ledger = Object.freeze({
89
- attempted(batchUuids) {
199
+ attempted(batchUuids, opts) {
200
+ const generation = generationOpt(opts);
90
201
  const batch = Object.freeze(cleanBatch(batchUuids));
91
202
  const added = [];
92
203
  for (const u of batch) {
93
204
  batchOfMap.set(u, batch);
94
- add(u, 'a', added);
205
+ if (generation !== undefined) {
206
+ generationOf.delete(u);
207
+ generationOf.set(u, generation);
208
+ }
209
+ add({ u, s: 'a' }, added);
95
210
  }
96
211
  return Object.freeze(added);
97
212
  },
98
213
  deliveredByFrame(batchUuids, event) {
99
- return frameProvesDelivery(event) ? markDelivered(batchUuids) : Object.freeze([]);
214
+ return frameProvesDelivery(event) ? markDelivered(batchUuids, 'frame') : Object.freeze([]);
100
215
  },
101
216
  deliveredBySteerReceipt(batchUuids, outcome) {
102
- return outcomeProvesDelivery(outcome) ? markDelivered(batchUuids) : Object.freeze([]);
217
+ return outcomeProvesDelivery(outcome) ? markDelivered(batchUuids, 'steer_receipt') : Object.freeze([]);
103
218
  },
104
219
  isAttempted: (uuid) => uuidOk(uuid) && attemptedSet.has(uuid),
105
- isDelivered: (uuid) => uuidOk(uuid) && deliveredSet.has(uuid),
106
- knownUndelivered: (uuid) => uuidOk(uuid) && attemptedSet.has(uuid) && !deliveredSet.has(uuid),
220
+ isDelivered: (uuid) => levelNow(uuid) >= 2,
221
+ knownUndelivered: (uuid) => knownUndeliveredUuid(uuid),
222
+ deliveryShareOf: (uuid) => {
223
+ const level = levelNow(uuid);
224
+ return level === 3 ? 'full' : level === 2 ? 'partial' : undefined;
225
+ },
226
+ undeliveredInGeneration(generation) {
227
+ const out = [];
228
+ if (!generationOk(generation))
229
+ return Object.freeze(out);
230
+ for (const [u, g] of generationOf) {
231
+ if (g !== generation)
232
+ continue;
233
+ generationOf.delete(u);
234
+ if (knownUndeliveredUuid(u))
235
+ out.push(u);
236
+ }
237
+ return Object.freeze(out);
238
+ },
239
+ droppedKeyCount: () => droppedKeys,
107
240
  batchOf: (uuid) => (uuidOk(uuid) ? batchOfMap.get(uuid) : undefined),
108
241
  toRecords: () => Object.freeze([...log]),
109
242
  fromRecords(records) {
110
243
  const added = [];
111
- if (!Array.isArray(records))
244
+ if (!isArraySafe(records))
112
245
  return Object.freeze(added);
113
246
  let list;
114
247
  try {
@@ -118,9 +251,11 @@ export function createPromptDeliveryLedger() {
118
251
  return Object.freeze(added);
119
252
  }
120
253
  for (const raw of list) {
121
- const rec = readRecord(raw);
122
- if (rec !== undefined)
123
- add(rec.u, rec.s, added);
254
+ const read = readRecord(raw);
255
+ if (read === undefined)
256
+ continue;
257
+ droppedKeys += read.dropped;
258
+ add(read.rec, added);
124
259
  }
125
260
  return Object.freeze(added);
126
261
  },
@@ -152,7 +287,7 @@ export function promptKnownUndelivered(ledger, message) {
152
287
  }
153
288
  export function withoutUndeliveredPrompts(ledger, messages) {
154
289
  assertLedger(ledger, 'withoutUndeliveredPrompts');
155
- if (!Array.isArray(messages))
290
+ if (!isArraySafe(messages))
156
291
  return [];
157
292
  return messages.filter((m) => !promptKnownUndelivered(ledger, m));
158
293
  }
@@ -4,4 +4,5 @@ export interface ProviderRejectionResend {
4
4
  resend: 'edit_and_resend' | 'not_by_editing';
5
5
  text: string;
6
6
  }
7
+ export declare function readProviderRejectionResendKey(v: object | null | undefined): ProviderRejectionResend | undefined;
7
8
  export declare function providerRejectionResend(detail: RunTerminalFailureDetail | null | undefined, carrier?: ToolHistoryErrorCarrier | null): ProviderRejectionResend | undefined;
@@ -29,6 +29,26 @@ function toolHistoryMismatchCarrier(carrier) {
29
29
  return carrier !== undefined && carrier !== null && classifyToolHistoryMismatch(carrier) !== null;
30
30
  }
31
31
  const PROVIDER_REJECTED = 'provider_rejected';
32
+ const RESEND_TEXT_STATUS = /\(HTTP (\d{3})\)/;
33
+ export function readProviderRejectionResendKey(v) {
34
+ try {
35
+ if (typeof v !== 'object' || v === null || Array.isArray(v))
36
+ return undefined;
37
+ const got = v;
38
+ const resend = got.resend;
39
+ const text = got.text;
40
+ if (typeof text !== 'string' || (resend !== 'edit_and_resend' && resend !== 'not_by_editing'))
41
+ return undefined;
42
+ const hit = RESEND_TEXT_STATUS.exec(text);
43
+ if (hit === null || hit[1] === undefined)
44
+ return undefined;
45
+ const again = providerRejectionResend({ kind: PROVIDER_REJECTED, status: Number(hit[1]) });
46
+ return again !== undefined && again.resend === resend && again.text === text ? { resend: again.resend, text: again.text } : undefined;
47
+ }
48
+ catch {
49
+ return undefined;
50
+ }
51
+ }
32
52
  export function providerRejectionResend(detail, carrier) {
33
53
  let read;
34
54
  try {
@@ -0,0 +1,10 @@
1
+ export declare const READ_DENY_BUILTIN_TIERS: readonly ["credentials", "shell-history", "browser", "wallet", "agent-config"];
2
+ export declare const READ_DENY_DEFAULT_TIERS: readonly ["credentials", "agent-config"];
3
+ export type ReadDenyBuiltinTiersParse = {
4
+ readonly ok: true;
5
+ readonly value: string | null;
6
+ } | {
7
+ readonly ok: false;
8
+ readonly why: string;
9
+ };
10
+ export declare function parseReadDenyBuiltinTiers(input: string): ReadDenyBuiltinTiersParse;
@@ -0,0 +1,29 @@
1
+ import { displayUntrusted } from './displayUntrusted.js';
2
+ import { CORE_READ_DENY_BUILTIN_TIERS, CORE_READ_DENY_DEFAULT_TIERS } from './generated/engineFactTables.js';
3
+ export const READ_DENY_BUILTIN_TIERS = CORE_READ_DENY_BUILTIN_TIERS;
4
+ export const READ_DENY_DEFAULT_TIERS = CORE_READ_DENY_DEFAULT_TIERS;
5
+ const NONE_WORD = 'none';
6
+ const SHOWN_ITEM_MAX = 40;
7
+ const SHOWN_ITEM_WINDOW = 2000;
8
+ const refuse = (why) => ({ ok: false, why });
9
+ const shown = (item) => displayUntrusted(item.length > SHOWN_ITEM_WINDOW ? item.slice(0, SHOWN_ITEM_WINDOW) : item, { max: SHOWN_ITEM_MAX });
10
+ export function parseReadDenyBuiltinTiers(input) {
11
+ if (typeof input !== 'string')
12
+ return refuse(`expected a string: comma-separated tier names, or the single word "${NONE_WORD}"`);
13
+ const text = input.trim();
14
+ if (text === '')
15
+ return { ok: true, value: null };
16
+ const parts = text.split(',').map((p) => p.trim());
17
+ if (parts.some((p) => p === ''))
18
+ return refuse('an empty entry between commas');
19
+ if (parts.includes(NONE_WORD)) {
20
+ return parts.length === 1 ? { ok: true, value: NONE_WORD } : refuse(`"${NONE_WORD}" must be the only entry`);
21
+ }
22
+ const known = READ_DENY_BUILTIN_TIERS;
23
+ const unknown = parts.filter((p) => !known.includes(p));
24
+ if (unknown.length > 0) {
25
+ const more = unknown.length > 1 ? ` (and ${unknown.length - 1} more)` : '';
26
+ return refuse(`unknown tier "${shown(unknown[0])}"${more}; known tiers: ${known.join(', ')}`);
27
+ }
28
+ return { ok: true, value: parts.join(',') };
29
+ }
@@ -1,6 +1,6 @@
1
1
  import { refuseBadDisableSkills, refuseDisableSkillsConflict } from '../disableSkillsWire.js';
2
2
  import { ATTACHMENTS_SPEC_KEYS, AttachmentsSpecRefused } from '../attachmentsWireCaps.js';
3
- import { retiredAttachmentsKeyWhy } from '../taskRequestWords.js';
3
+ import { retiredAttachmentsKeyOf } from '../taskRequestWords.js';
4
4
  export const REQUEST_FIELD_MATRIX = [
5
5
  { field: 'objective', lanes: ['interactive', 'print', 'utility'], live: false, why: '本 turn 的真实输入,每条车道都必需', whyUtilityLane: '恒座:三条车道都必须带上本次提交的模型面输入' },
6
6
  { field: 'sessionId', lanes: ['interactive', 'print', 'utility'], live: false, why: '单命名空间(TP-A Step 2):壳 id 即引擎 session id;print 在非 live 下保留 mock 常量', whyUtilityLane: '恒座:side-channel 提交走**独立**会话 id(壳另铸一个,绝不复用用户会话那一个 —— 复用会把这次提交的转录混进用户会话)' },
@@ -458,7 +458,7 @@ const withoutRetiredAttachmentsKeys = (v) => {
458
458
  return v;
459
459
  let drop;
460
460
  for (const key of Object.keys(v)) {
461
- const why = retiredAttachmentsKeyWhy(key, 'key');
461
+ const why = retiredAttachmentsKeyOf(key, 'key')?.why;
462
462
  if (why === undefined)
463
463
  continue;
464
464
  const d = Object.getOwnPropertyDescriptor(v, key);
@@ -46,3 +46,14 @@ export interface ParkedResumeStartupDetail {
46
46
  }
47
47
  export declare function parkedResumeStartupFromError(err: unknown, decision?: ParkedResumeStartupDecision): ParkedResumeStartupDetail | null;
48
48
  export declare function parkedResumeStartupContent(detail: ParkedResumeStartupDetail): string;
49
+ export interface CheckpointUnsupportedVersionDetail {
50
+ code: 'checkpoint.unsupported_version';
51
+ retriable: true;
52
+ }
53
+ export declare function checkpointUnsupportedVersionFromError(err: unknown): CheckpointUnsupportedVersionDetail | null;
54
+ export type CheckpointUnsupportedParkKind = 'tool_approval' | 'plan_review' | 'resource_park' | 'wake' | 'workflow_host';
55
+ export interface CheckpointUnsupportedVersionContext {
56
+ readonly parkKind?: CheckpointUnsupportedParkKind;
57
+ readonly runId?: string;
58
+ }
59
+ export declare function checkpointUnsupportedVersionContent(detail: CheckpointUnsupportedVersionDetail, ctx?: CheckpointUnsupportedVersionContext): string;
@@ -1,5 +1,5 @@
1
1
  import { readErrorNumber, readErrorString } from './wireFailureShape.js';
2
- import { RESUME_CONTEXT_UNAVAILABLE, RESUME_PLACEMENT_MISMATCH, RESUME_PREFLIGHT_REJECTED, RESUME_POLICY_FOLD_CODE, PARKED_RESUME_STARTUP_FAILED, isStatusDerivedCoarseCode, RESUME_SESSION_NOT_FOUND, } from './engineErrorCodes.js';
2
+ import { RESUME_CONTEXT_UNAVAILABLE, RESUME_PLACEMENT_MISMATCH, RESUME_PREFLIGHT_REJECTED, RESUME_POLICY_FOLD_CODE, PARKED_RESUME_STARTUP_FAILED, isStatusDerivedCoarseCode, RESUME_SESSION_NOT_FOUND, REQUEST_FIELD_CONFLICT, CHECKPOINT_UNSUPPORTED_VERSION, refusalSaysStillPending, } from './engineErrorCodes.js';
3
3
  import { capForDisplay } from './fleetTaskDesc.js';
4
4
  import { resumeRetryLaterFromError } from './wireErrorTriage.js';
5
5
  export const RESUME_REFUSAL_CODES = Object.freeze([
@@ -88,8 +88,8 @@ export function resumePolicyBlockContent(detail) {
88
88
  export { resumeReopenFromError, resumeReopenContent } from './hitl/suspendedReopen.js';
89
89
  const RESUME_SESSION_NOT_FOUND_TAIL = `(${RESUME_SESSION_NOT_FOUND})`;
90
90
  const REQUIRE_EXISTING_BY_STATUS = new Map([
91
- [404, Object.freeze({ form: 'session_not_found', coarse: 'not_found.resource' })],
92
- [422, Object.freeze({ form: 'session_id_missing', coarse: 'request.unprocessable' })],
91
+ [404, Object.freeze({ form: 'session_not_found', code: RESUME_SESSION_NOT_FOUND, coarse: 'not_found.resource' })],
92
+ [422, Object.freeze({ form: 'session_id_missing', code: REQUEST_FIELD_CONFLICT, coarse: 'request.unprocessable' })],
93
93
  ]);
94
94
  export function requireExistingSessionRefusalFromError(err) {
95
95
  if (typeof err !== 'object' || err === null)
@@ -101,7 +101,7 @@ export function requireExistingSessionRefusalFromError(err) {
101
101
  if (byStatus === undefined)
102
102
  return null;
103
103
  const code = o['errorCode'];
104
- if (code === RESUME_SESSION_NOT_FOUND)
104
+ if (code === byStatus.code)
105
105
  return Object.freeze({ code: RESUME_SESSION_NOT_FOUND, form: byStatus.form });
106
106
  if (code !== byStatus.coarse)
107
107
  return null;
@@ -196,3 +196,60 @@ const PARKED_RESUME_STARTUP_SENTENCES = Object.freeze({
196
196
  export function parkedResumeStartupContent(detail) {
197
197
  return detail.cause === 'resume_rejected' ? PARKED_RESUME_STARTUP_SENTENCES.resume_rejected : PARKED_RESUME_STARTUP_SENTENCES.undetermined;
198
198
  }
199
+ export function checkpointUnsupportedVersionFromError(err) {
200
+ if (typeof err !== 'object' || err === null)
201
+ return null;
202
+ let code;
203
+ try {
204
+ code = err.errorCode;
205
+ }
206
+ catch {
207
+ return null;
208
+ }
209
+ if (code !== CHECKPOINT_UNSUPPORTED_VERSION || !refusalSaysStillPending(err))
210
+ return null;
211
+ return { code: CHECKPOINT_UNSUPPORTED_VERSION, retriable: true };
212
+ }
213
+ function checkpointUnsupportedHasRun(kind) {
214
+ switch (kind) {
215
+ case 'tool_approval':
216
+ case 'plan_review':
217
+ case 'resource_park':
218
+ return true;
219
+ case 'wake':
220
+ case 'workflow_host':
221
+ return false;
222
+ default:
223
+ return undefined;
224
+ }
225
+ }
226
+ const CHECKPOINT_UNSUPPORTED_WHAT = 'This server cannot continue this parked work in the form it was stored in';
227
+ const CHECKPOINT_UNSUPPORTED_STILL_WAITING = 'Nothing was decided or resumed and the parked work is still waiting, but sending the same request again will not by itself continue it';
228
+ const CHECKPOINT_UNSUPPORTED_DRAIN = " — whoever operates this deployment can find such parked work before an upgrade with the server's pre-upgrade drain check";
229
+ export function checkpointUnsupportedVersionContent(detail, ctx) {
230
+ void detail;
231
+ let kind;
232
+ let runId;
233
+ try {
234
+ kind = ctx?.parkKind;
235
+ runId = ctx?.runId;
236
+ }
237
+ catch {
238
+ kind = undefined;
239
+ runId = undefined;
240
+ }
241
+ const hasRun = checkpointUnsupportedHasRun(kind);
242
+ let outlet;
243
+ if (hasRun === true) {
244
+ const named = typeof runId === 'string' && runId.length > 0 ? ` (${capForDisplay(runId, 80)})` : '';
245
+ outlet = `If it was parked before this server was upgraded, cancel its run${named} first, then submit the work again as a new task`;
246
+ }
247
+ else if (hasRun === false) {
248
+ outlet = 'If it was parked before this server was upgraded, there is no run to cancel: start a new task in that conversation';
249
+ }
250
+ else {
251
+ outlet =
252
+ 'If it was parked before this server was upgraded, submit the work again as a new task: when it still has a run waiting on it (a tool approval, a plan review or a paused resource), cancel that run first; when it was waiting to be woken up, or was a workflow step continuing its host conversation, there is no run to cancel, so start a new task in that conversation';
253
+ }
254
+ return [CHECKPOINT_UNSUPPORTED_WHAT, CHECKPOINT_UNSUPPORTED_STILL_WAITING, outlet + CHECKPOINT_UNSUPPORTED_DRAIN].join(' · ');
255
+ }
@@ -109,17 +109,17 @@ export function rewindConversationDoctorDetail(reading) {
109
109
  }
110
110
  export function rewindRestoreFilesEpochDetail(reading) {
111
111
  if (reading.kind !== 'present') {
112
- return 'restore-files request spelling not observed — this process has no usable capabilities reading for this engine';
112
+ return 'restore-files request key not observed — this process has no usable capabilities reading for this engine, so a rewind with code restore still sends the key';
113
113
  }
114
114
  switch (reading.restoreFilesEpoch) {
115
115
  case 'on':
116
116
  return 'this engine announces the restore-files request key (tracked-set restore semantics)';
117
117
  case 'off':
118
- return 'this engine does not announce the restore-files request key; the client sends it anyway because the older spelling is rejected outright by current engines';
118
+ return 'this engine does not announce the restore-files request key, so a rewind with code restore built from this reading leaves the key out and moves the conversation only';
119
119
  case 'not_reported':
120
- return 'this engine does not report the restore-files epoch bit (older engine); the client sends the current spelling regardless';
120
+ return 'this engine does not report the restore-files epoch bit (older engine), so a rewind with code restore built from this reading leaves the key out and moves the conversation only';
121
121
  case 'malformed':
122
- return 'the restore-files epoch bit is reported in an unreadable shape; the client sends the current spelling regardless';
122
+ return 'the restore-files epoch bit is reported in an unreadable shape, so a rewind with code restore built from this reading leaves the key out and moves the conversation only';
123
123
  }
124
124
  }
125
125
  export function forgetRewindArchiveReading(baseUrl) {
@@ -1,3 +1,4 @@
1
+ import type { RewindArchiveReading } from './rewindArchiveCapability.js';
1
2
  export interface SeamRewindSpec {
2
3
  resumeAt?: string;
3
4
  resumeAtMode?: 'at' | 'before';
@@ -5,5 +6,5 @@ export interface SeamRewindSpec {
5
6
  rewindFilesTo?: string;
6
7
  }
7
8
  export type RewindMode = 'both' | 'conversation' | 'code';
8
- export declare function rewindSpecForMode(mode: RewindMode, targetEntryId: string): SeamRewindSpec;
9
+ export declare function rewindSpecForMode(mode: RewindMode, targetEntryId: string, reading?: RewindArchiveReading): SeamRewindSpec;
9
10
  export declare function projectRewind(pending: SeamRewindSpec | undefined): SeamRewindSpec;
@@ -1,7 +1,24 @@
1
- export function rewindSpecForMode(mode, targetEntryId) {
1
+ function restoreFilesKeyMaySend(reading) {
2
+ if (reading === undefined)
3
+ return true;
4
+ try {
5
+ if (reading === null || typeof reading !== 'object')
6
+ return false;
7
+ const kind = reading.kind;
8
+ if (kind === 'unobserved')
9
+ return true;
10
+ return kind === 'present' && reading.restoreFilesEpoch === 'on';
11
+ }
12
+ catch {
13
+ return false;
14
+ }
15
+ }
16
+ export function rewindSpecForMode(mode, targetEntryId, reading) {
2
17
  switch (mode) {
3
18
  case 'both':
4
- return { resumeAt: targetEntryId, resumeAtMode: 'before', restoreFiles: true };
19
+ return restoreFilesKeyMaySend(reading)
20
+ ? { resumeAt: targetEntryId, resumeAtMode: 'before', restoreFiles: true }
21
+ : { resumeAt: targetEntryId, resumeAtMode: 'before' };
5
22
  case 'conversation':
6
23
  return { resumeAt: targetEntryId, resumeAtMode: 'before' };
7
24
  case 'code':
package/dist/seam.d.ts CHANGED
@@ -3,6 +3,7 @@ import type { EngineTurnUsage } from './adapter/downstream/turnUsageToModelUsage
3
3
  import type { RunCostReconcile } from './adapter/downstream/terminalToSdkResult.js';
4
4
  import type { WiringManifestAutoMode, WiringManifestModelGate, WiringManifestMcpEntryView, WiringManifestHookEntryView, WiringManifestLspSeamView, WiringManifestWriteProtectionView, WiringManifestAutoConsolidationView, WiringManifestReadDenyView, ContextUsageSectionView, PromptManifestSectionView, PromptManifestBlockView, PromptManifestToolView } from './adapter/downstream/eventToSdkMessage.js';
5
5
  import type { ToolRosterView, ToolRosterDeltaView, WiringManifestHandsView } from './toolRoster.js';
6
+ import type { WiringManifestFleetView } from './adapter/downstream/wiringManifestView.js';
6
7
  import type { SuspendedReopenReading } from './hitl/suspendedReopen.js';
7
8
  export interface AbortSignalLike {
8
9
  readonly aborted: boolean;
@@ -177,6 +178,7 @@ export interface WiringManifestChromeEvent {
177
178
  instructions?: {
178
179
  readonly selected: readonly string[];
179
180
  };
181
+ fleet?: WiringManifestFleetView;
180
182
  eventId?: string;
181
183
  }
182
184
  export interface TextSegmentCommittedRow {
@@ -3,4 +3,10 @@ export type TaskRequestReasoningEffort = NonNullable<TaskRequest['reasoningEffor
3
3
  export declare const REASONING_EFFORT_WORDS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
4
4
  export type TaskRequestPermissionMode = NonNullable<TaskRequest['permissionMode']>;
5
5
  export declare const PERMISSION_MODE_WORDS: readonly ["default", "plan", "acceptEdits", "bypassPermissions", "auto"];
6
- export declare function retiredAttachmentsKeyWhy(nameOrToken: string, form?: 'token' | 'key'): string | undefined;
6
+ export interface RetiredAttachmentsKey {
7
+ readonly key: string;
8
+ readonly why: string;
9
+ readonly because: string;
10
+ readonly remedy: string;
11
+ }
12
+ export declare function retiredAttachmentsKeyOf(nameOrToken: string, form?: 'token' | 'key'): RetiredAttachmentsKey | undefined;
@@ -2,17 +2,27 @@ export const REASONING_EFFORT_WORDS = ['off', 'minimal', 'low', 'medium', 'high'
2
2
  export const PERMISSION_MODE_WORDS = ['default', 'plan', 'acceptEdits', 'bypassPermissions', 'auto'];
3
3
  const _taskRequestWordPins = [true, true, true, true];
4
4
  void _taskRequestWordPins;
5
- export function retiredAttachmentsKeyWhy(nameOrToken, form = 'token') {
5
+ export function retiredAttachmentsKeyOf(nameOrToken, form = 'token') {
6
6
  const lower = nameOrToken.trim().toLowerCase();
7
7
  const eq = lower.indexOf('=');
8
8
  const lhs = (eq < 0 ? lower : lower.slice(0, eq)).trim();
9
9
  const bare = lhs.startsWith('+') || lhs.startsWith('-') ? lhs.slice(1).trim() : lhs;
10
- if (bare !== 'planmodereminder')
10
+ const retiredRows = [
11
+ {
12
+ key: 'planModeReminder',
13
+ since: 'core 7.39.0 / server 7.113.0',
14
+ because: 'the engine now states the plan posture itself whenever the permission mode is "plan"',
15
+ remedy: 'send permissionMode "plan" for the plan posture',
16
+ },
17
+ ];
18
+ const row = retiredRows.find((r) => r.key.toLowerCase() === bare);
19
+ if (row === undefined)
11
20
  return undefined;
12
- const head = '"planModeReminder" was retired with core 7.39.0 / server 7.113.0 — the engine now states the plan posture itself whenever the permission mode is "plan"';
13
- if (form === 'token')
14
- return `${head}, and a request carrying the key is answered 400 request.field_invalid; remove the token (send permissionMode "plan" for the plan posture)`;
15
- return nameOrToken === 'planModeReminder'
16
- ? `${head}, and a request carrying this key is answered 400 request.field_invalid; remove the key (send permissionMode "plan" for the plan posture)`
17
- : `${head}; the server ignores this spelling of it, but it is refused here so the retired key is not sent under any spelling; remove the key (send permissionMode "plan" for the plan posture)`;
21
+ const head = `"${row.key}" was retired with ${row.since} — ${row.because}`;
22
+ const why = form === 'token'
23
+ ? `${head}, and a request carrying the key is answered 400 request.field_invalid; remove the token (${row.remedy})`
24
+ : nameOrToken === row.key
25
+ ? `${head}, and a request carrying this key is answered 400 request.field_invalid; remove the key (${row.remedy})`
26
+ : `${head}; the server ignores this spelling of it, but it is refused here so the retired key is not sent under any spelling; remove the key (${row.remedy})`;
27
+ return { key: row.key, why, because: row.because, remedy: row.remedy };
18
28
  }
@@ -0,0 +1 @@
1
+ export declare function toolHistoryBadTurnSentence(promptText: unknown): string;