@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
@@ -0,0 +1,265 @@
1
+ import { HOOK_TOOL_ALIASES } from './hookToolAliases.js';
2
+ const TOOL_NAME_MATCHER_EVENTS = new Set(['PreToolUse', 'PostToolUse', 'PostToolUseFailure']);
3
+ const MATCHER_MAX_CHARS = 8192;
4
+ const PATTERN_WORK_BUDGET = 10_000_000;
5
+ const PATTERN_MAX_DEPTH = 32;
6
+ const COUNTED_REPEAT_MAX = 1000;
7
+ function unjudged(reason) {
8
+ return Object.freeze({ kind: 'unjudged', reason });
9
+ }
10
+ function refused(reading, p) {
11
+ return Object.freeze({ kind: 'refused', reason: 'alias_without_canonical', reading, alias: p.alias, canonical: p.canonical });
12
+ }
13
+ function aliasPairs() {
14
+ return Object.entries(HOOK_TOOL_ALIASES).map(([alias, canonical]) => ({ alias, canonical }));
15
+ }
16
+ function literalNames(matcher) {
17
+ const names = [];
18
+ let first = -1;
19
+ let last = -1;
20
+ for (let i = 0; i <= matcher.length; i++) {
21
+ const c = i < matcher.length ? matcher.charCodeAt(i) : 0x7c;
22
+ if (c === 0x7c || c === 0x2c) {
23
+ if (first !== -1)
24
+ names.push(matcher.slice(first, last + 1));
25
+ first = -1;
26
+ last = -1;
27
+ continue;
28
+ }
29
+ if (c === 0x20)
30
+ continue;
31
+ const nameChar = (c >= 0x30 && c <= 0x39) || (c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a) || c === 0x5f || c === 0x2d;
32
+ if (!nameChar)
33
+ return undefined;
34
+ if (first === -1)
35
+ first = i;
36
+ last = i;
37
+ }
38
+ return names;
39
+ }
40
+ const UNSUPPORTED = Symbol('unsupported');
41
+ const SINGLE_CHAR_ESCAPES = 'dDwWsStnrfv^$\\.*+?()[]{}|/-';
42
+ function patternBound(src, n) {
43
+ let i = 0;
44
+ let depth = 0;
45
+ const fail = () => {
46
+ throw UNSUPPORTED;
47
+ };
48
+ const quantifier = () => {
49
+ const c = src[i];
50
+ let lo;
51
+ let hi;
52
+ if (c === '*') {
53
+ lo = 0;
54
+ hi = Infinity;
55
+ i++;
56
+ }
57
+ else if (c === '+') {
58
+ lo = 1;
59
+ hi = Infinity;
60
+ i++;
61
+ }
62
+ else if (c === '?') {
63
+ lo = 0;
64
+ hi = 1;
65
+ i++;
66
+ }
67
+ else if (c === '{') {
68
+ const q = /^\{(\d{1,4})(?:(,)(\d{0,4}))?\}/.exec(src.slice(i, i + 12));
69
+ if (q === null)
70
+ return fail();
71
+ lo = Number(q[1]);
72
+ hi = q[2] === undefined ? lo : q[3] === '' || q[3] === undefined ? Infinity : Number(q[3]);
73
+ if (lo > COUNTED_REPEAT_MAX || (hi !== Infinity && hi > COUNTED_REPEAT_MAX) || hi < lo)
74
+ fail();
75
+ i += q[0].length;
76
+ }
77
+ else
78
+ return undefined;
79
+ if (src[i] === '?')
80
+ i++;
81
+ return { lo, hi };
82
+ };
83
+ const term = () => {
84
+ const c = src[i];
85
+ if (c === '^' || c === '$') {
86
+ i++;
87
+ if (quantifier() !== undefined)
88
+ fail();
89
+ return [1, 1];
90
+ }
91
+ if (c === '\\' && (src[i + 1] === 'b' || src[i + 1] === 'B')) {
92
+ i += 2;
93
+ if (quantifier() !== undefined)
94
+ fail();
95
+ return [1, 1];
96
+ }
97
+ let paths = 1;
98
+ let steps = 1;
99
+ let single = true;
100
+ if (c === '(') {
101
+ i++;
102
+ if (src[i] === '?') {
103
+ if (src[i + 1] === ':')
104
+ i += 2;
105
+ else {
106
+ const named = /^\?<([A-Za-z_$][A-Za-z0-9_$]*)>/.exec(src.slice(i, i + 80));
107
+ if (named === null)
108
+ fail();
109
+ else
110
+ i += named[0].length;
111
+ }
112
+ }
113
+ if (++depth > PATTERN_MAX_DEPTH)
114
+ fail();
115
+ [paths, steps] = disjunction();
116
+ if (src[i] !== ')')
117
+ fail();
118
+ i++;
119
+ depth--;
120
+ single = false;
121
+ }
122
+ else if (c === '[') {
123
+ i++;
124
+ if (src[i] === '^')
125
+ i++;
126
+ for (;;) {
127
+ if (i >= src.length)
128
+ fail();
129
+ const d = src[i];
130
+ if (d === ']') {
131
+ i++;
132
+ break;
133
+ }
134
+ i += d === '\\' ? 2 : 1;
135
+ }
136
+ }
137
+ else if (c === '.')
138
+ i++;
139
+ else if (c === '\\') {
140
+ const e = src[i + 1];
141
+ if (e === undefined || !SINGLE_CHAR_ESCAPES.includes(e))
142
+ fail();
143
+ i += 2;
144
+ }
145
+ else if (c === undefined || '*+?{}]|)'.includes(c))
146
+ fail();
147
+ else
148
+ i++;
149
+ const q = quantifier();
150
+ if (q === undefined)
151
+ return [paths, steps];
152
+ if (single) {
153
+ if (q.lo > n)
154
+ return [1, n + 1];
155
+ const top = Math.min(q.hi, n);
156
+ return [top - q.lo + 1, top + 1];
157
+ }
158
+ const hiEff = Math.min(q.hi, Math.max(q.lo, n + 1));
159
+ if (q.lo > hiEff)
160
+ return [1, 1];
161
+ let sum = 0;
162
+ for (let k = q.lo; k <= hiEff; k++) {
163
+ sum += paths ** k;
164
+ if (sum === Infinity)
165
+ break;
166
+ }
167
+ return [sum, hiEff * steps + 1];
168
+ };
169
+ const alternative = () => {
170
+ let paths = 1;
171
+ let steps = 0;
172
+ while (i < src.length && src[i] !== '|' && src[i] !== ')') {
173
+ const [p, s] = term();
174
+ paths *= p;
175
+ steps += s;
176
+ }
177
+ return [paths, steps];
178
+ };
179
+ const disjunction = () => {
180
+ let [paths, steps] = alternative();
181
+ while (src[i] === '|') {
182
+ i++;
183
+ const [p, s] = alternative();
184
+ paths += p;
185
+ steps = Math.max(steps, s);
186
+ }
187
+ return [paths, steps];
188
+ };
189
+ try {
190
+ const r = disjunction();
191
+ if (i !== src.length)
192
+ fail();
193
+ return r;
194
+ }
195
+ catch (e) {
196
+ if (e === UNSUPPORTED)
197
+ return undefined;
198
+ throw e;
199
+ }
200
+ }
201
+ export function hookMatcherVerdict(event, matcher) {
202
+ if (typeof event !== 'string' || !TOOL_NAME_MATCHER_EVENTS.has(event))
203
+ return unjudged('event_out_of_scope');
204
+ if (matcher === undefined)
205
+ return Object.freeze({ kind: 'matchesAll' });
206
+ if (typeof matcher !== 'string')
207
+ return unjudged('not_a_string');
208
+ if (matcher.length > MATCHER_MAX_CHARS)
209
+ return unjudged('oversize');
210
+ if (matcher === '' || matcher === '*')
211
+ return Object.freeze({ kind: 'matchesAll' });
212
+ const pairs = aliasPairs();
213
+ const names = literalNames(matcher);
214
+ if (names !== undefined) {
215
+ const dead = pairs.find((p) => names.includes(p.alias) && !names.includes(p.canonical));
216
+ if (dead !== undefined)
217
+ return refused('names', dead);
218
+ return Object.freeze({
219
+ kind: 'accepted',
220
+ reading: 'names',
221
+ names: Object.freeze([...names]),
222
+ aliases: Object.freeze(pairs.filter((p) => names.includes(p.alias)).map((p) => Object.freeze({ ...p }))),
223
+ });
224
+ }
225
+ const src = `^(?:${matcher})$`;
226
+ let re;
227
+ try {
228
+ re = new RegExp(src);
229
+ }
230
+ catch {
231
+ return unjudged('invalid_pattern');
232
+ }
233
+ const subjects = pairs.flatMap((p) => [p.alias, p.canonical]);
234
+ const n = subjects.reduce((a, s) => Math.max(a, s.length), 0);
235
+ const bound = patternBound(src, n);
236
+ if (bound === undefined)
237
+ return unjudged('pattern_unsupported');
238
+ if (bound[0] * bound[1] * (n + 1) * subjects.length > PATTERN_WORK_BUDGET)
239
+ return unjudged('over_budget');
240
+ const selects = (name) => {
241
+ if (matcher === name)
242
+ return true;
243
+ try {
244
+ return re.test(name);
245
+ }
246
+ catch {
247
+ return undefined;
248
+ }
249
+ };
250
+ const selected = [];
251
+ for (const p of pairs) {
252
+ const a = selects(p.alias);
253
+ if (a === undefined)
254
+ return unjudged('invalid_pattern');
255
+ if (!a)
256
+ continue;
257
+ const c = selects(p.canonical);
258
+ if (c === undefined)
259
+ return unjudged('invalid_pattern');
260
+ if (!c)
261
+ return refused('pattern', p);
262
+ selected.push(p);
263
+ }
264
+ return Object.freeze({ kind: 'accepted', reading: 'pattern', aliases: Object.freeze(selected.map((p) => Object.freeze({ ...p }))) });
265
+ }
@@ -0,0 +1,91 @@
1
+ import type { ImageInput } from './imagesWireCaps.js';
2
+ import type { ImageVision } from './liveModelCatalog.js';
3
+ import { type UserAttachmentDescriptor } from './userAttachmentEnvelope.js';
4
+ export interface HumanTurnRider {
5
+ readonly uuid: string;
6
+ readonly role: 'image_meta' | 'attachment' | 'skill_body' | (string & {});
7
+ readonly textIncluded: boolean;
8
+ }
9
+ export interface HumanTurnMissing {
10
+ readonly kind: 'rider_text' | 'image' | 'attachment_ref' | 'non_text';
11
+ readonly ref?: string;
12
+ readonly reason: 'not_persisted' | 'not_found' | 'non_text_filtered' | 'projection_failed';
13
+ }
14
+ export interface HumanTurnAttachment {
15
+ readonly uuid: string;
16
+ readonly descriptor: Exclude<UserAttachmentDescriptor, {
17
+ kind: 'image';
18
+ }> | Omit<Extract<UserAttachmentDescriptor, {
19
+ kind: 'image';
20
+ }>, 'delivery'>;
21
+ readonly pixels?: {
22
+ readonly data: string;
23
+ readonly mimeType: string;
24
+ };
25
+ }
26
+ export interface HumanTurnItem {
27
+ readonly uuid: string;
28
+ readonly text: string;
29
+ readonly images?: readonly ImageInput[];
30
+ readonly attachments?: readonly HumanTurnAttachment[];
31
+ readonly attachmentIds?: readonly string[];
32
+ readonly riders?: readonly HumanTurnRider[];
33
+ readonly missing?: readonly HumanTurnMissing[];
34
+ }
35
+ export interface HumanTurnCaps {
36
+ readonly vision?: ImageVision;
37
+ }
38
+ export interface HumanTurnInput {
39
+ readonly items: readonly HumanTurnItem[];
40
+ readonly lane: 'objective' | 'steer';
41
+ readonly caps: HumanTurnCaps;
42
+ readonly otherBodyBytes?: number;
43
+ }
44
+ type EncoderShortfall = 'body_budget' | 'steer_admission' | 'vision_no' | 'vision_unknown' | 'image_too_large' | 'seat_count' | 'seat_total' | 'lane_has_no_image_seat' | 'lane_has_no_attachment_id_seat';
45
+ export interface HumanTurnSharePart {
46
+ readonly kind: 'text' | 'image' | 'attachment_ref' | 'attachment_id' | 'rider_text' | 'non_text';
47
+ readonly ref?: string | number;
48
+ readonly status: 'full' | 'none';
49
+ readonly why?: EncoderShortfall | 'text_not_included' | HumanTurnMissing['reason'];
50
+ }
51
+ export interface HumanTurnShare {
52
+ readonly uuid: string;
53
+ readonly riderUuids: readonly string[];
54
+ readonly level: 'full' | 'partial' | 'none';
55
+ readonly parts: readonly HumanTurnSharePart[];
56
+ }
57
+ export interface HumanTurnDegraded {
58
+ readonly uuid: string;
59
+ readonly kind: HumanTurnSharePart['kind'];
60
+ readonly ref?: string | number;
61
+ readonly why: EncoderShortfall;
62
+ }
63
+ export type HumanTurnEncoding = {
64
+ readonly ok: true;
65
+ readonly lane: 'objective';
66
+ readonly text: string;
67
+ readonly images: readonly ImageInput[];
68
+ readonly attachmentIds: readonly string[];
69
+ readonly shares: readonly HumanTurnShare[];
70
+ readonly degraded: readonly HumanTurnDegraded[];
71
+ } | {
72
+ readonly ok: true;
73
+ readonly lane: 'steer';
74
+ readonly text: string;
75
+ readonly shares: readonly HumanTurnShare[];
76
+ readonly degraded: readonly HumanTurnDegraded[];
77
+ } | {
78
+ readonly ok: false;
79
+ readonly reason: 'steer_hold';
80
+ readonly why: 'no_text' | 'reminder_tag' | 'control_char' | 'too_long';
81
+ } | {
82
+ readonly ok: false;
83
+ readonly reason: 'empty' | 'malformed_input' | 'malformed_item' | 'duplicate_uuid' | 'rider_collides_primary' | 'oversize';
84
+ };
85
+ export interface HumanTurnDelivered {
86
+ readonly full: readonly string[];
87
+ readonly partial: readonly string[];
88
+ }
89
+ export declare function encodeHumanTurn(input: HumanTurnInput): HumanTurnEncoding;
90
+ export declare function deliveredUuidsOf(encoding: HumanTurnEncoding): HumanTurnDelivered;
91
+ export {};
@@ -0,0 +1,253 @@
1
+ import { mintEnvelopeOf, mintReferenceOf } from './userAttachmentEnvelope.js';
2
+ import { readEncodingShares, readHumanTurnInput } from './humanTurnRead.js';
3
+ const REQUEST_BODY_MAX_BYTES = 8 * 1024 * 1024;
4
+ const IMAGE_SEAT_MAX_COUNT = 20;
5
+ const IMAGE_MAX_BASE64_CHARS_EACH = 6 * 1024 * 1024;
6
+ const IMAGE_SEAT_MAX_BASE64_CHARS_TOTAL = 6 * 1024 * 1024;
7
+ const STEER_MAX_CHARS = 16000;
8
+ const STEER_REMINDER_TAG = /<\s*\/?\s*system-reminder(?:\s[^>]*)?>/i;
9
+ const STEER_CONTROL_CHARS = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/;
10
+ function utf8Length(s) {
11
+ let n = 0;
12
+ for (let i = 0; i < s.length; i += 1) {
13
+ const c = s.charCodeAt(i);
14
+ if (c < 0x80)
15
+ n += 1;
16
+ else if (c < 0x800)
17
+ n += 2;
18
+ else if (c >= 0xd800 && c <= 0xdbff && i + 1 < s.length) {
19
+ const d = s.charCodeAt(i + 1);
20
+ if (d >= 0xdc00 && d <= 0xdfff) {
21
+ n += 4;
22
+ i += 1;
23
+ }
24
+ else
25
+ n += 3;
26
+ }
27
+ else
28
+ n += 3;
29
+ }
30
+ return n;
31
+ }
32
+ const jsonBytes = (v) => utf8Length(JSON.stringify(v));
33
+ function bodyBytes(other, objective, images, ids) {
34
+ const keys = [jsonBytes('objective') + 1 + jsonBytes(objective)];
35
+ if (images.length > 0)
36
+ keys.push(jsonBytes('images') + 1 + jsonBytes(images));
37
+ if (ids.length > 0)
38
+ keys.push(jsonBytes('attachmentIds') + 1 + jsonBytes(ids));
39
+ const commas = other === 2 ? keys.length - 1 : keys.length;
40
+ return other + keys.reduce((a, b) => a + b, 0) + commas;
41
+ }
42
+ function steerRefusal(text) {
43
+ if (text.length === 0)
44
+ return 'no_text';
45
+ if (STEER_REMINDER_TAG.test(text))
46
+ return 'reminder_tag';
47
+ if (STEER_CONTROL_CHARS.test(text))
48
+ return 'control_char';
49
+ if (text.length > STEER_MAX_CHARS)
50
+ return 'too_long';
51
+ return undefined;
52
+ }
53
+ function textOf(p) {
54
+ if (p.form === 'reference')
55
+ return mintReferenceOf(p.a.d);
56
+ return mintEnvelopeOf(p.a.d, p.seated ? 'attached' : 'not-attached');
57
+ }
58
+ function compose(items, placements) {
59
+ const perItem = items.map((it, i) => [...placements.filter((p) => p.item === i).map(textOf), it.text].filter((s) => s.length > 0).join('\n\n'));
60
+ return perItem.filter((s) => s.length > 0).join('\n');
61
+ }
62
+ function seatedImages(items, pastedWhy, placements) {
63
+ const out = [];
64
+ items.forEach((it, i) => {
65
+ it.images.forEach((img, k) => {
66
+ if (pastedWhy[i]?.[k] === undefined)
67
+ out.push('url' in img ? { url: img.url } : { data: img.data, mimeType: img.mimeType });
68
+ });
69
+ for (const p of placements) {
70
+ if (p.item === i && p.form === 'content' && p.seated && p.a.pixels !== undefined)
71
+ out.push({ data: p.a.pixels.data, mimeType: p.a.pixels.mimeType });
72
+ }
73
+ });
74
+ return out;
75
+ }
76
+ const ENCODER_WHY_LIST = [
77
+ 'body_budget',
78
+ 'steer_admission',
79
+ 'vision_no',
80
+ 'vision_unknown',
81
+ 'image_too_large',
82
+ 'seat_count',
83
+ 'seat_total',
84
+ 'lane_has_no_image_seat',
85
+ 'lane_has_no_attachment_id_seat',
86
+ ];
87
+ const encoderWhysExhaustive = true;
88
+ void encoderWhysExhaustive;
89
+ const ENCODER_WHYS = new Set(ENCODER_WHY_LIST);
90
+ function records(lane, items, pastedWhy, placements) {
91
+ const recs = [];
92
+ const push = (item, part) => {
93
+ recs.push({ item, part });
94
+ };
95
+ items.forEach((it, i) => {
96
+ if (it.text.length > 0)
97
+ push(i, { kind: 'text', status: 'full' });
98
+ it.images.forEach((_, k) => {
99
+ const why = pastedWhy[i]?.[k];
100
+ push(i, lane !== 'objective' ? { kind: 'image', ref: k, status: 'none', why: 'lane_has_no_image_seat' } : why === undefined ? { kind: 'image', ref: k, status: 'full' } : { kind: 'image', ref: k, status: 'none', why });
101
+ });
102
+ for (const p of placements) {
103
+ if (p.item !== i)
104
+ continue;
105
+ push(i, p.form === 'content' ? { kind: 'attachment_ref', ref: p.a.uuid, status: 'full' } : { kind: 'attachment_ref', ref: p.a.uuid, status: 'none', why: lane === 'objective' ? 'body_budget' : 'steer_admission' });
106
+ if (p.a.pixels !== undefined) {
107
+ if (lane === 'steer')
108
+ push(i, { kind: 'image', ref: p.a.uuid, status: 'none', why: 'lane_has_no_image_seat' });
109
+ else if (p.seated && p.form === 'content')
110
+ push(i, { kind: 'image', ref: p.a.uuid, status: 'full' });
111
+ else
112
+ push(i, { kind: 'image', ref: p.a.uuid, status: 'none', why: p.seated ? 'body_budget' : p.seatWhy });
113
+ }
114
+ }
115
+ for (const id of it.attachmentIds) {
116
+ push(i, lane === 'objective' ? { kind: 'attachment_id', ref: id, status: 'full' } : { kind: 'attachment_id', ref: id, status: 'none', why: 'lane_has_no_attachment_id_seat' });
117
+ }
118
+ for (const r of it.riders) {
119
+ if (r.role === 'image_meta')
120
+ continue;
121
+ push(i, r.textIncluded ? { kind: 'rider_text', ref: r.uuid, status: 'full' } : { kind: 'rider_text', ref: r.uuid, status: 'none', why: 'text_not_included' });
122
+ }
123
+ for (const m of it.missing) {
124
+ push(i, m.ref === undefined ? { kind: m.kind, status: 'none', why: m.reason } : { kind: m.kind, ref: m.ref, status: 'none', why: m.reason });
125
+ }
126
+ });
127
+ return recs;
128
+ }
129
+ function sharesAndDegraded(items, recs) {
130
+ const shares = items.map((it, i) => {
131
+ const parts = recs.filter((r) => r.item === i).map((r) => r.part);
132
+ const full = parts.filter((p) => p.status === 'full').length;
133
+ const level = parts.length > 0 && full === parts.length ? 'full' : full === 0 ? 'none' : 'partial';
134
+ return { uuid: it.uuid, riderUuids: [...it.riders.map((r) => r.uuid), ...it.attachments.map((a) => a.uuid)], level, parts };
135
+ });
136
+ const degraded = [];
137
+ for (const r of recs) {
138
+ const w = r.part.why;
139
+ if (r.part.status === 'none' && w !== undefined && ENCODER_WHYS.has(w)) {
140
+ const uuid = items[r.item].uuid;
141
+ degraded.push(r.part.ref === undefined ? { uuid, kind: r.part.kind, why: w } : { uuid, kind: r.part.kind, ref: r.part.ref, why: w });
142
+ }
143
+ }
144
+ return { shares, degraded };
145
+ }
146
+ function encodeObjective(v) {
147
+ const { items } = v;
148
+ let count = 0;
149
+ let total = 0;
150
+ const seat = (len) => {
151
+ if (len > IMAGE_MAX_BASE64_CHARS_EACH)
152
+ return 'image_too_large';
153
+ if (count + 1 > IMAGE_SEAT_MAX_COUNT)
154
+ return 'seat_count';
155
+ if (total + len > IMAGE_SEAT_MAX_BASE64_CHARS_TOTAL)
156
+ return 'seat_total';
157
+ count += 1;
158
+ total += len;
159
+ return undefined;
160
+ };
161
+ const pastedWhy = items.map((it) => it.images.map((img) => seat('data' in img ? img.data.length : 0)));
162
+ const placements = [];
163
+ items.forEach((it, i) => {
164
+ for (const a of it.attachments) {
165
+ const p = { item: i, a, form: 'content', seated: false, seatWhy: undefined };
166
+ if (a.pixels !== undefined) {
167
+ if (v.vision !== 'yes')
168
+ p.seatWhy = v.vision === 'no' ? 'vision_no' : 'vision_unknown';
169
+ else {
170
+ p.seatWhy = seat(a.pixels.data.length);
171
+ p.seated = p.seatWhy === undefined;
172
+ }
173
+ }
174
+ placements.push(p);
175
+ }
176
+ });
177
+ const ids = items.flatMap((it) => it.attachmentIds);
178
+ for (;;) {
179
+ const text = compose(items, placements);
180
+ const images = seatedImages(items, pastedWhy, placements);
181
+ if (bodyBytes(v.otherBodyBytes, text, images, ids) <= REQUEST_BODY_MAX_BYTES) {
182
+ if (text.trim() === '')
183
+ return { ok: false, reason: 'empty' };
184
+ const { shares, degraded } = sharesAndDegraded(items, records('objective', items, pastedWhy, placements));
185
+ return { ok: true, lane: 'objective', text, images, attachmentIds: [...ids], shares, degraded };
186
+ }
187
+ let pick = -1;
188
+ let pickCost = -1;
189
+ placements.forEach((p, k) => {
190
+ if (p.form !== 'content')
191
+ return;
192
+ const cost = jsonBytes(textOf(p)) + (p.seated && p.a.pixels !== undefined ? jsonBytes({ data: p.a.pixels.data, mimeType: p.a.pixels.mimeType }) : 0);
193
+ if (cost >= pickCost) {
194
+ pick = k;
195
+ pickCost = cost;
196
+ }
197
+ });
198
+ if (pick < 0)
199
+ return { ok: false, reason: 'oversize' };
200
+ placements[pick].form = 'reference';
201
+ }
202
+ }
203
+ function encodeSteer(v) {
204
+ const { items } = v;
205
+ const placements = [];
206
+ items.forEach((it, i) => {
207
+ for (const a of it.attachments)
208
+ placements.push({ item: i, a, form: 'content', seated: false, seatWhy: undefined });
209
+ });
210
+ let text = compose(items, placements);
211
+ let refusal = steerRefusal(text);
212
+ if (refusal !== undefined && refusal !== 'no_text' && placements.length > 0) {
213
+ for (const p of placements)
214
+ p.form = 'reference';
215
+ text = compose(items, placements);
216
+ refusal = steerRefusal(text);
217
+ }
218
+ if (refusal !== undefined)
219
+ return { ok: false, reason: 'steer_hold', why: refusal };
220
+ const { shares, degraded } = sharesAndDegraded(items, records('steer', items, [], placements));
221
+ return { ok: true, lane: 'steer', text, shares, degraded };
222
+ }
223
+ export function encodeHumanTurn(input) {
224
+ let v;
225
+ try {
226
+ v = readHumanTurnInput(input);
227
+ }
228
+ catch {
229
+ return { ok: false, reason: 'malformed_input' };
230
+ }
231
+ if ('reject' in v)
232
+ return { ok: false, reason: v.reject };
233
+ try {
234
+ return v.lane === 'objective' ? encodeObjective(v) : encodeSteer(v);
235
+ }
236
+ catch {
237
+ return { ok: false, reason: 'malformed_input' };
238
+ }
239
+ }
240
+ export function deliveredUuidsOf(encoding) {
241
+ const shares = readEncodingShares(encoding);
242
+ const full = [];
243
+ const partial = [];
244
+ for (const s of shares ?? []) {
245
+ const into = s.level === 'full' ? full : s.level === 'partial' ? partial : undefined;
246
+ if (into === undefined)
247
+ continue;
248
+ into.push(s.uuid);
249
+ for (const r of s.riderUuids)
250
+ into.push(r);
251
+ }
252
+ return { full, partial };
253
+ }