@sema-agent/client-core 0.89.0 → 0.90.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 (38) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +18 -13
  3. package/dist/adapter/runStream.d.ts +2 -0
  4. package/dist/adapter/runStream.js +157 -32
  5. package/dist/attachmentsWireCaps.d.ts +13 -2
  6. package/dist/attachmentsWireCaps.js +25 -5
  7. package/dist/coreValuePorts.d.ts +1 -0
  8. package/dist/engineCapReader.js +12 -1
  9. package/dist/engineNoticeCodes.d.ts +2 -9
  10. package/dist/engineNoticeCodes.js +19 -27
  11. package/dist/fileHistoryLegNotice.d.ts +20 -0
  12. package/dist/fileHistoryLegNotice.js +122 -0
  13. package/dist/gateOutcome.d.ts +1 -1
  14. package/dist/gateOutcome.js +1 -0
  15. package/dist/generated/engineFactTables.d.ts +1 -0
  16. package/dist/generated/engineFactTables.js +3 -0
  17. package/dist/generated/engineNoticeTables.js +0 -3
  18. package/dist/hookToolAliases.d.ts +3 -0
  19. package/dist/hookToolAliases.js +10 -0
  20. package/dist/index.d.ts +8 -0
  21. package/dist/index.js +5 -0
  22. package/dist/permissionsAllowGrantCapability.d.ts +17 -0
  23. package/dist/permissionsAllowGrantCapability.js +75 -0
  24. package/dist/permissionsDeclaration.d.ts +25 -0
  25. package/dist/permissionsDeclaration.js +227 -0
  26. package/dist/promptDelivery.d.ts +5 -2
  27. package/dist/promptDelivery.js +8 -3
  28. package/dist/providerRejection.d.ts +2 -1
  29. package/dist/providerRejection.js +7 -1
  30. package/dist/request/taskRequest.js +27 -1
  31. package/dist/sdkRegistryTransit.d.ts +1 -0
  32. package/dist/sdkRegistryTransit.js +1 -0
  33. package/dist/supportFloor.d.ts +10 -0
  34. package/dist/supportFloor.js +42 -0
  35. package/dist/taskRequestWords.d.ts +1 -0
  36. package/dist/taskRequestWords.js +14 -0
  37. package/docs/INTEGRATION-CLIENTS.md +503 -13
  38. package/package.json +2 -2
@@ -140,11 +140,6 @@ function engineSessionIdOf(result) {
140
140
  const v = result?.sessionId;
141
141
  return typeof v === 'string' && v.length > 0 ? v : undefined;
142
142
  }
143
- function providerFailureStatusOf(result) {
144
- const af = result?.apiFailure;
145
- const status = typeof af === 'object' && af !== null ? af.status : undefined;
146
- return typeof status === 'number' && Number.isFinite(status) ? status : undefined;
147
- }
148
143
  export function isOutcomeUnknownRowText(text) {
149
144
  return text.trimStart().startsWith(`${OUTCOME_UNKNOWN_ROW_PREFIX}:`);
150
145
  }
@@ -155,6 +150,157 @@ export function isGovernanceTerminal(input) {
155
150
  return true;
156
151
  return input.errorCode === OUTPUT_INVALID;
157
152
  }
153
+ function terminalRowFacts(ev) {
154
+ const doneTerminal = ev.type === 'done' ? readRunTerminal(ev.result) : null;
155
+ const errTextRaw = ev.type === 'failed'
156
+ ? (ev.errorMessage ?? ev.errorCode ?? 'run failed')
157
+ : doneTerminal?.kind === 'failed'
158
+ ? (doneTerminal.message ?? doneTerminal.code ?? 'run failed')
159
+ : doneTerminal?.kind === 'blocked'
160
+ ? (doneTerminal.reason
161
+ ? `Run blocked: ${doneTerminal.reason}`
162
+ : 'Run blocked (the agent reported it cannot proceed)')
163
+ : null;
164
+ const errText = errTextRaw === '' ? null : errTextRaw;
165
+ if (errText === null)
166
+ return { doneTerminal, errText, activeTaskId: undefined, busy: { busy: false, handle: null }, governance: false };
167
+ const activeTaskId = (ev.type === 'done' ? ev.result?.activeTaskId : undefined) ??
168
+ (ev.type === 'failed' ? ev.activeTaskId : undefined);
169
+ const busy = classifyActiveRunBusy({
170
+ errorCode: ev.type === 'failed' ? ev.errorCode : runTerminalCode(doneTerminal),
171
+ activeTaskId,
172
+ });
173
+ const governance = isGovernanceTerminal({
174
+ status: ev.type === 'failed' ? undefined : doneTerminal?.kind === 'blocked' ? 'blocked' : undefined,
175
+ errorCode: ev.type === 'failed' ? ev.errorCode : runTerminalCode(doneTerminal),
176
+ });
177
+ return { doneTerminal, errText, activeTaskId, busy, governance };
178
+ }
179
+ const PROVIDER_REJECTED_DETAIL = 'provider_rejected';
180
+ function ownDataBit(o, k) {
181
+ try {
182
+ const d = Object.getOwnPropertyDescriptor(o, k);
183
+ return d !== undefined && Object.prototype.hasOwnProperty.call(d, 'value') ? d.value : undefined;
184
+ }
185
+ catch {
186
+ return undefined;
187
+ }
188
+ }
189
+ function plainObjectOf(v) {
190
+ if (typeof v !== 'object' || v === null)
191
+ return undefined;
192
+ try {
193
+ return Array.isArray(v) ? undefined : v;
194
+ }
195
+ catch {
196
+ return undefined;
197
+ }
198
+ }
199
+ function ownDataCopy(o, keys) {
200
+ const out = Object.create(null);
201
+ for (const k of keys) {
202
+ const v = ownDataBit(o, k);
203
+ if (v !== undefined)
204
+ out[k] = v;
205
+ }
206
+ return out;
207
+ }
208
+ function ownTerminalSnapshot(result) {
209
+ let causePlane;
210
+ try {
211
+ causePlane = 'terminal' in result;
212
+ }
213
+ catch {
214
+ return undefined;
215
+ }
216
+ const snap = ownDataCopy(result, ['status', 'errorCode', 'errorMessage']);
217
+ if (!causePlane)
218
+ return snap;
219
+ let d;
220
+ try {
221
+ d = Object.getOwnPropertyDescriptor(result, 'terminal');
222
+ }
223
+ catch {
224
+ return undefined;
225
+ }
226
+ if (d === undefined || !Object.prototype.hasOwnProperty.call(d, 'value'))
227
+ return undefined;
228
+ const t = plainObjectOf(d.value);
229
+ if (t === undefined) {
230
+ snap.terminal = null;
231
+ return snap;
232
+ }
233
+ const ts = ownDataCopy(t, ['kind', 'code', 'message']);
234
+ const detail = plainObjectOf(ownDataBit(t, 'detail'));
235
+ if (detail !== undefined)
236
+ ts.detail = ownDataCopy(detail, ['kind', 'status', 'code']);
237
+ snap.terminal = ts;
238
+ return snap;
239
+ }
240
+ function nonEmptyStr(v) {
241
+ return typeof v === 'string' && v.length > 0 ? v : undefined;
242
+ }
243
+ function mintToolHistoryCarrier(code, status, message) {
244
+ const out = Object.create(null);
245
+ if (code !== undefined)
246
+ out.code = code;
247
+ if (status !== undefined)
248
+ out.status = status;
249
+ if (message !== undefined)
250
+ out.message = message;
251
+ return Object.freeze(out);
252
+ }
253
+ export function toolHistoryErrorCarrierOf(terminalOrResult) {
254
+ const empty = () => mintToolHistoryCarrier(undefined, undefined, undefined);
255
+ try {
256
+ const x = plainObjectOf(terminalOrResult);
257
+ if (x === undefined)
258
+ return empty();
259
+ const type = ownDataBit(x, 'type');
260
+ const frame = (type === 'done' || type === 'failed' ? x : { type: 'done', result: x });
261
+ return toolHistoryCarrierFromFacts(x, terminalRowFacts(frame));
262
+ }
263
+ catch {
264
+ return empty();
265
+ }
266
+ }
267
+ function toolHistoryCarrierFromFacts(x, facts) {
268
+ const empty = () => mintToolHistoryCarrier(undefined, undefined, undefined);
269
+ try {
270
+ const type = ownDataBit(x, 'type');
271
+ if (facts.errText === null || facts.busy.busy || facts.governance || isModelOutputErrorText(facts.errText))
272
+ return empty();
273
+ if (type === 'failed')
274
+ return mintToolHistoryCarrier(nonEmptyStr(ownDataBit(x, 'errorCode')), undefined, nonEmptyStr(ownDataBit(x, 'errorMessage')));
275
+ const result = type === 'done' ? plainObjectOf(ownDataBit(x, 'result')) : x;
276
+ if (result === undefined)
277
+ return empty();
278
+ const snap = ownTerminalSnapshot(result);
279
+ if (snap === undefined)
280
+ return empty();
281
+ const read = readRunTerminal(snap);
282
+ if (read === null || read.kind !== 'failed')
283
+ return empty();
284
+ const detail = plainObjectOf(ownDataBit(read, 'detail'));
285
+ const detailStatus = detail !== undefined && ownDataBit(detail, 'kind') === PROVIDER_REJECTED_DETAIL ? ownDataBit(detail, 'status') : undefined;
286
+ const apiFailure = plainObjectOf(ownDataBit(result, 'apiFailure'));
287
+ const apiStatus = apiFailure === undefined ? undefined : ownDataBit(apiFailure, 'status');
288
+ const status = typeof apiStatus === 'number' && Number.isFinite(apiStatus) ? apiStatus : typeof detailStatus === 'number' ? detailStatus : undefined;
289
+ return mintToolHistoryCarrier(nonEmptyStr(ownDataBit(read, 'code')), status, nonEmptyStr(ownDataBit(read, 'message')));
290
+ }
291
+ catch {
292
+ return empty();
293
+ }
294
+ }
295
+ function toolHistoryCarrierOfRowFrame(ev, facts) {
296
+ try {
297
+ const x = plainObjectOf(ev);
298
+ return x === undefined ? mintToolHistoryCarrier(undefined, undefined, undefined) : toolHistoryCarrierFromFacts(x, facts);
299
+ }
300
+ catch {
301
+ return mintToolHistoryCarrier(undefined, undefined, undefined);
302
+ }
303
+ }
158
304
  const reportedDroppedTypes = new Set();
159
305
  const DROPPED_TYPE_MEMO_CAP = 64;
160
306
  const DROPPED_TYPE_DISPLAY_CAP = 60;
@@ -477,30 +623,15 @@ async function* runStreamInner(events, ctx, handle = {}) {
477
623
  continue;
478
624
  }
479
625
  if (ev.type === 'done' || ev.type === 'failed') {
480
- const doneTerminal = ev.type === 'done' ? readRunTerminal(ev.result) : null;
481
- const errText = ev.type === 'failed'
482
- ? (ev.errorMessage ?? ev.errorCode ?? 'run failed')
483
- : doneTerminal?.kind === 'failed'
484
- ? (doneTerminal.message ?? doneTerminal.code ?? 'run failed')
485
- : doneTerminal?.kind === 'blocked'
486
- ? (doneTerminal.reason
487
- ? `Run blocked: ${doneTerminal.reason}`
488
- : 'Run blocked (the agent reported it cannot proceed)')
489
- : null;
626
+ const rowFacts = terminalRowFacts(ev);
627
+ const doneTerminal = rowFacts.doneTerminal;
628
+ const errText = rowFacts.errText;
490
629
  if (errText) {
491
- const activeTaskId = (ev.type === 'done' ? ev.result?.activeTaskId : undefined) ??
492
- (ev.type === 'failed' ? ev.activeTaskId : undefined);
493
- const busy = classifyActiveRunBusy({
494
- errorCode: ev.type === 'failed' ? ev.errorCode : runTerminalCode(doneTerminal),
495
- activeTaskId,
496
- });
630
+ const busy = rowFacts.busy;
497
631
  const maxTokHint = /max_tokens/i.test(errText)
498
632
  ? '\nyour provider caps max_tokens lower — lower it in /model (press m, or M to type a value)'
499
633
  : '';
500
- const governance = isGovernanceTerminal({
501
- status: ev.type === 'failed' ? undefined : doneTerminal?.kind === 'blocked' ? 'blocked' : undefined,
502
- errorCode: ev.type === 'failed' ? ev.errorCode : runTerminalCode(doneTerminal),
503
- });
634
+ const governance = rowFacts.governance;
504
635
  const busyHandle = busy.handle;
505
636
  const rowText = busy.busy
506
637
  ? `This session is locked by an earlier run${busyHandle ? ` (run ${busyHandle})` : ''} that ` +
@@ -516,13 +647,7 @@ async function* runStreamInner(events, ctx, handle = {}) {
516
647
  const sessionUnreadable = ev.type === 'failed'
517
648
  ? isCorruptSessionCode(ev.errorCode)
518
649
  : doneTerminal?.kind === 'failed' && isCorruptSessionCode(doneTerminal.code);
519
- const historyMismatch = !busy.busy && !governance && !isModelOutputErrorText(errText)
520
- ? classifyToolHistoryMismatch({
521
- code: ev.type === 'failed' ? ev.errorCode : runTerminalCode(doneTerminal),
522
- status: ev.type === 'done' ? providerFailureStatusOf(ev.result) : undefined,
523
- message: errText,
524
- })
525
- : null;
650
+ const historyMismatch = classifyToolHistoryMismatch(toolHistoryCarrierOfRowFrame(ev, rowFacts));
526
651
  const historyEchoedSid = ev.type === 'done' ? engineSessionIdOf(ev.result) : undefined;
527
652
  const historyRepeat = noteToolHistoryTerminal({
528
653
  sessionId: historyEchoedSid ?? ctx.sessionId,
@@ -6,7 +6,6 @@ export interface AttachmentsSpec {
6
6
  changedFiles?: true | {
7
7
  maxFiles?: number;
8
8
  };
9
- planModeReminder?: true;
10
9
  budgetUsd?: true;
11
10
  backgroundTasks?: boolean;
12
11
  toolsDelta?: true;
@@ -14,6 +13,18 @@ export interface AttachmentsSpec {
14
13
  agentListing?: boolean;
15
14
  skillsListing?: boolean;
16
15
  }
17
- export declare const ATTACHMENTS_SPEC_KEYS: readonly ["todoReminder", "todoReminderMode", "changedFiles", "planModeReminder", "budgetUsd", "backgroundTasks", "toolsDelta", "mcpInstructions", "agentListing", "skillsListing"];
16
+ export declare const ATTACHMENTS_SPEC_KEYS: readonly ["todoReminder", "todoReminderMode", "changedFiles", "budgetUsd", "backgroundTasks", "toolsDelta", "mcpInstructions", "agentListing", "skillsListing"];
18
17
  export declare const ATTACHMENTS_DEFAULT_ON_KEYS: readonly ["agentListing", "skillsListing", "backgroundTasks"];
18
+ export declare class AttachmentsSpecRefused extends TypeError {
19
+ readonly token: string;
20
+ readonly why: string;
21
+ readonly allowed: readonly string[];
22
+ readonly key?: string;
23
+ constructor(message: string, init: {
24
+ readonly token: string;
25
+ readonly why: string;
26
+ readonly allowed: readonly string[];
27
+ readonly key?: string;
28
+ });
29
+ }
19
30
  export declare function attachmentsForRequest(env?: EnvLike): AttachmentsSpec | undefined;
@@ -1,11 +1,11 @@
1
1
  import { hostEnv } from './hostEnv.js';
2
2
  import { envFlagOff } from './envFlag.js';
3
+ import { retiredAttachmentsKeyWhy } from './taskRequestWords.js';
3
4
  export const ATTACHMENTS_ENV = 'SEMA_ATTACHMENTS';
4
5
  export const ATTACHMENTS_SPEC_KEYS = Object.freeze([
5
6
  'todoReminder',
6
7
  'todoReminderMode',
7
8
  'changedFiles',
8
- 'planModeReminder',
9
9
  'budgetUsd',
10
10
  'backgroundTasks',
11
11
  'toolsDelta',
@@ -17,7 +17,6 @@ export const ATTACHMENTS_DEFAULT_ON_KEYS = ['agentListing', 'skillsListing', 'ba
17
17
  const OPT_IN_KEYS = [
18
18
  'todoReminder',
19
19
  'changedFiles',
20
- 'planModeReminder',
21
20
  'budgetUsd',
22
21
  'backgroundTasks',
23
22
  'toolsDelta',
@@ -28,16 +27,37 @@ const DEFAULT_PAIR = ['backgroundTasks', 'toolsDelta'];
28
27
  const _attachmentsKeyPins = [true, true, true, true, true, true];
29
28
  void _attachmentsKeyPins;
30
29
  const TODO_REMINDER_MODES = ['baseline', 'off'];
30
+ export class AttachmentsSpecRefused extends TypeError {
31
+ token;
32
+ why;
33
+ allowed;
34
+ constructor(message, init) {
35
+ super(message);
36
+ this.name = 'AttachmentsSpecRefused';
37
+ this.token = init.token;
38
+ this.why = init.why;
39
+ this.allowed = Object.freeze([...init.allowed]);
40
+ if (init.key !== undefined)
41
+ this.key = init.key;
42
+ }
43
+ }
44
+ function legalAttachmentsTokens() {
45
+ return ['off', 'full', 'all', 'default', '<literalTrueKey>', '+<literalTrueKey>', ...DEFAULT_ON_ONLY_KEYS.map(k => `-${k}`), `todoReminderMode=${TODO_REMINDER_MODES.join('|')}`];
46
+ }
31
47
  function refuse(token, why) {
32
- throw new Error(`${ATTACHMENTS_ENV}: refusing token "${token}" — ${why}. ` +
33
- `Legal tokens: off | full | all | default | <literalTrueKey> | +<literalTrueKey> | ` +
34
- `-agentListing | -skillsListing | todoReminderMode=baseline|off`);
48
+ const allowed = legalAttachmentsTokens();
49
+ throw new AttachmentsSpecRefused(`${ATTACHMENTS_ENV}: refusing token "${token}" — ${why}. Legal tokens: ${allowed.join(' | ')}`, { token, why, allowed });
35
50
  }
36
51
  export function attachmentsForRequest(env = hostEnv()) {
37
52
  const raw = (env[ATTACHMENTS_ENV] ?? '').trim().toLowerCase();
38
53
  if (envFlagOff(raw))
39
54
  return allDefaultOnOff();
40
55
  const tokens = raw.split(',').map(t => t.trim()).filter(t => t.length > 0);
56
+ for (const token of tokens) {
57
+ const named = retiredAttachmentsKeyWhy(token);
58
+ if (named !== undefined)
59
+ refuse(token, named);
60
+ }
41
61
  if (tokens.length === 0)
42
62
  return { backgroundTasks: true, toolsDelta: true };
43
63
  if (tokens.length === 1 && (tokens[0] === 'full' || tokens[0] === 'all'))
@@ -16,6 +16,7 @@ export interface AllowDenyPolicyOptionsView {
16
16
  deny?: string[];
17
17
  onInvalidName?: 'throw' | 'skip';
18
18
  onInvalidNameIssue?: (issue: AllowDenyInvalidNameIssueView) => void;
19
+ scope?: 'grant' | 'narrowing';
19
20
  }
20
21
  export interface AllowDenyInvalidNameIssueView {
21
22
  entry: string;
@@ -1,4 +1,15 @@
1
1
  import { snapshotCapsGeneration, capsGenerationStillCurrent } from './engineCapsGenerationGuard.js';
2
+ function freezeReading(reading, seen = new Set()) {
3
+ if (reading === null || typeof reading !== 'object' || seen.has(reading))
4
+ return reading;
5
+ seen.add(reading);
6
+ Object.freeze(reading);
7
+ for (const d of Object.values(Object.getOwnPropertyDescriptors(reading))) {
8
+ if ('value' in d)
9
+ freezeReading(d.value, seen);
10
+ }
11
+ return reading;
12
+ }
2
13
  export function createEngineCapReader(spec) {
3
14
  const readingByBase = new Map();
4
15
  return {
@@ -23,7 +34,7 @@ export function createEngineCapReader(spec) {
23
34
  readingByBase.delete(baseUrl);
24
35
  return;
25
36
  }
26
- readingByBase.set(baseUrl, reading);
37
+ readingByBase.set(baseUrl, freezeReading(reading));
27
38
  },
28
39
  observed(baseUrl) {
29
40
  if (typeof baseUrl !== 'string' || baseUrl === '')
@@ -130,13 +130,6 @@ export interface ReadDenyUnappliedFactsView {
130
130
  runId: string;
131
131
  }
132
132
  export declare function readReadDenyUnapplied(notice: unknown): ReadDenyUnappliedFactsView | undefined;
133
- export interface LegacyModeSeedMigratedFactsView {
134
- permissionMode?: string;
135
- droppedKeys: readonly string[];
136
- sessionId: string;
137
- runId: string;
138
- }
139
- export declare function readLegacyModeSeedMigrated(notice: unknown): LegacyModeSeedMigratedFactsView | undefined;
140
133
  export interface ToolTurnRepairView {
141
134
  form: string;
142
135
  toolCallIds: readonly string[];
@@ -172,9 +165,10 @@ export interface UserInputUndrainedFactsView {
172
165
  taskId?: string;
173
166
  sessionId?: string;
174
167
  runId?: string;
168
+ childSessionId?: string;
175
169
  }
176
170
  export declare function readUserInputUndrained(notice: unknown): UserInputUndrainedFactsView | undefined;
177
- export declare function userInputUndrainedNotice(facts: Pick<UserInputUndrainedFactsView, 'kind' | 'count' | 'cause'>): string | undefined;
171
+ export declare function userInputUndrainedNotice(facts: Pick<UserInputUndrainedFactsView, 'kind' | 'count' | 'cause' | 'childSessionId'>): string | undefined;
178
172
  export declare const ENGINE_NOTICE_FACT_READERS: Readonly<{
179
173
  readonly 'mcp.injection_dropped': typeof readMcpInjectionDrop;
180
174
  readonly 'delegation.ask_unresolvable': typeof readAskUnresolvable;
@@ -190,7 +184,6 @@ export declare const ENGINE_NOTICE_FACT_READERS: Readonly<{
190
184
  readonly 'config.permission_mode_unavailable': typeof readPermissionModeUnavailable;
191
185
  readonly 'permission.ask_released_by_mode': typeof readAskReleasedByMode;
192
186
  readonly 'permission.read_deny_unapplied': typeof readReadDenyUnapplied;
193
- readonly 'resume.legacy_mode_seed_migrated': typeof readLegacyModeSeedMigrated;
194
187
  readonly 'context.tool_turn_repaired': typeof readToolTurnRepaired;
195
188
  readonly 'server.model_unreachable': typeof readModelUnreachable;
196
189
  readonly 'task.tool_turn_incomplete': typeof readToolTurnIncomplete;
@@ -454,31 +454,6 @@ function readReadDenyUnappliedOf(notice) {
454
454
  }
455
455
  return { mode, toolCallId, tiers, rows, rowCount, sessionId, runId };
456
456
  }
457
- export function readLegacyModeSeedMigrated(notice) {
458
- return guardedRead(notice, readLegacyModeSeedMigratedOf);
459
- }
460
- function readLegacyModeSeedMigratedOf(notice) {
461
- if (typeof notice !== 'object' || notice === null || Array.isArray(notice))
462
- return undefined;
463
- const n = notice;
464
- if (ownSeat(n, 'code') !== 'resume.legacy_mode_seed_migrated')
465
- return undefined;
466
- const d = ownSeat(n, 'detail');
467
- if (typeof d !== 'object' || d === null || Array.isArray(d))
468
- return undefined;
469
- const o = d;
470
- const m = o.migrated;
471
- if (typeof m !== 'object' || m === null || Array.isArray(m))
472
- return undefined;
473
- const mo = m;
474
- const droppedKeys = nameList(mo.droppedKeys);
475
- const sessionId = nonEmpty(o.sessionId);
476
- const runId = nonEmpty(o.runId);
477
- if (droppedKeys === undefined || sessionId === undefined || runId === undefined)
478
- return undefined;
479
- const permissionMode = nonEmpty(optionalSeat(mo, 'permissionMode'));
480
- return { ...(permissionMode !== undefined ? { permissionMode } : {}), droppedKeys, sessionId, runId };
481
- }
482
457
  export function readToolTurnRepaired(notice) {
483
458
  return guardedRead(notice, readToolTurnRepairedOf);
484
459
  }
@@ -601,6 +576,7 @@ function readUserInputUndrainedOf(notice) {
601
576
  const taskId = nonEmpty(optionalOwnSeat(o, 'taskId'));
602
577
  const sessionId = nonEmpty(optionalOwnSeat(o, 'sessionId'));
603
578
  const runId = nonEmpty(optionalOwnSeat(o, 'runId'));
579
+ const childSessionId = nonEmpty(optionalOwnSeat(o, 'childSessionId'));
604
580
  return {
605
581
  kind,
606
582
  count,
@@ -608,6 +584,7 @@ function readUserInputUndrainedOf(notice) {
608
584
  ...(taskId !== undefined ? { taskId } : {}),
609
585
  ...(sessionId !== undefined ? { sessionId } : {}),
610
586
  ...(runId !== undefined ? { runId } : {}),
587
+ ...(childSessionId !== undefined ? { childSessionId } : {}),
611
588
  };
612
589
  }
613
590
  const messagesPhrase = (n, noun) => `${n} ${noun}${n === 1 ? '' : 's'}`;
@@ -623,12 +600,17 @@ const _steerSentencePin = true;
623
600
  void _steerSentencePin;
624
601
  const followUpUndrainedSentence = (n) => `${messagesPhrase(n, 'follow-up message')} you queued ${verbBe(n)} not used before the run ended and will not be delivered. ` +
625
602
  `Send ${itThem(n)} again if you still need ${itThem(n)}.`;
603
+ const subagentSteerUndrainedSentence = (n) => `${messagesPhrase(n, 'message')} you sent while the run was in progress ${verbBe(n)} not used before a sub-agent's run ended and will not be delivered. ` +
604
+ `Send ${itThem(n)} again if you still need ${itThem(n)}.`;
605
+ const subagentFollowUpUndrainedSentence = (n) => `${messagesPhrase(n, 'follow-up message')} you queued ${verbBe(n)} not used before a sub-agent's run ended and will not be delivered. ` +
606
+ `Send ${itThem(n)} again if you still need ${itThem(n)}.`;
626
607
  const unrecognizedUndrainedSentence = (n, kind) => `${kind === 'followUp' ? `${messagesPhrase(n, 'follow-up message')} you queued` : `${messagesPhrase(n, 'message')} you sent during a run`} may not have been used, ` +
627
608
  `and the reason was not reported in a form this version recognizes. Check the run's transcript before sending ${itThem(n)} again.`;
628
609
  export function userInputUndrainedNotice(facts) {
629
610
  let kind;
630
611
  let count;
631
612
  let cause;
613
+ let child;
632
614
  try {
633
615
  if (facts === null || typeof facts !== 'object' || Array.isArray(facts))
634
616
  return undefined;
@@ -639,12 +621,23 @@ export function userInputUndrainedNotice(facts) {
639
621
  catch {
640
622
  return undefined;
641
623
  }
624
+ try {
625
+ child = Object.hasOwn(facts, 'childSessionId') ? facts.childSessionId : undefined;
626
+ }
627
+ catch {
628
+ child = undefined;
629
+ }
630
+ const fromSubagent = typeof child === 'string' && child.length > 0;
642
631
  const n = positiveInt(count);
643
632
  if (n === undefined || (kind !== 'steer' && kind !== 'followUp'))
644
633
  return undefined;
645
634
  if (kind === 'followUp') {
646
- return cause === undefined || cause === 'leg_end' ? followUpUndrainedSentence(n) : unrecognizedUndrainedSentence(n, kind);
635
+ if (cause === undefined || cause === 'leg_end')
636
+ return fromSubagent ? subagentFollowUpUndrainedSentence(n) : followUpUndrainedSentence(n);
637
+ return unrecognizedUndrainedSentence(n, kind);
647
638
  }
639
+ if (cause === 'leg_end' && fromSubagent)
640
+ return subagentSteerUndrainedSentence(n);
648
641
  if (typeof cause === 'string' && Object.hasOwn(STEER_UNDRAINED_SENTENCE, cause)) {
649
642
  return STEER_UNDRAINED_SENTENCE[cause](n);
650
643
  }
@@ -665,7 +658,6 @@ export const ENGINE_NOTICE_FACT_READERS = Object.freeze({
665
658
  'config.permission_mode_unavailable': readPermissionModeUnavailable,
666
659
  'permission.ask_released_by_mode': readAskReleasedByMode,
667
660
  'permission.read_deny_unapplied': readReadDenyUnapplied,
668
- 'resume.legacy_mode_seed_migrated': readLegacyModeSeedMigrated,
669
661
  'context.tool_turn_repaired': readToolTurnRepaired,
670
662
  'server.model_unreachable': readModelUnreachable,
671
663
  'task.tool_turn_incomplete': readToolTurnIncomplete,
@@ -0,0 +1,20 @@
1
+ import type { AgentEvent } from '@sema-agent/sdk';
2
+ import { type FileHistoryCaptureReading } from './fileHistoryCaptureCapability.js';
3
+ export type FileHistoryDeclarationFact = 'sent' | 'not_sent' | 'unknown';
4
+ export declare const FILE_HISTORY_LEG_VERDICTS: readonly ["not_honored", "not_offered", "honored", "not_declared", "unknown"];
5
+ export type FileHistoryLegNoticeVerdict = (typeof FILE_HISTORY_LEG_VERDICTS)[number];
6
+ export interface FileHistoryLegNoticeInput {
7
+ readonly capability: FileHistoryCaptureReading;
8
+ readonly declaration: FileHistoryDeclarationFact;
9
+ readonly captureOffByUser: boolean;
10
+ readonly legWord: string | undefined;
11
+ }
12
+ export declare function fileHistoryLegNoticeVerdict(input: FileHistoryLegNoticeInput): FileHistoryLegNoticeVerdict;
13
+ export declare function fileHistoryLegNoticeSentence(verdict: FileHistoryLegNoticeVerdict): string | null;
14
+ export declare function fileHistoryDeclarationOfBody(body: object | null | undefined): FileHistoryDeclarationFact;
15
+ export interface FileHistoryDeclarationLedger {
16
+ declared(requestKey: string, fact: FileHistoryDeclarationFact): boolean;
17
+ boundToRun(requestKey: string, runId: string, event: AgentEvent): boolean;
18
+ declarationOf(runId: string): FileHistoryDeclarationFact;
19
+ }
20
+ export declare function createFileHistoryDeclarationLedger(): FileHistoryDeclarationLedger;
@@ -0,0 +1,122 @@
1
+ import { fileHistoryCaptureMode } from './fileHistoryCaptureCapability.js';
2
+ import { frameProvesDelivery } from './promptDelivery.js';
3
+ import { FILE_HISTORY_DISABLED } from './request/taskRequest.js';
4
+ export const FILE_HISTORY_LEG_VERDICTS = Object.freeze(['not_honored', 'not_offered', 'honored', 'not_declared', 'unknown']);
5
+ function ownRead(o, key) {
6
+ return Object.hasOwn(o, key) ? o[key] : undefined;
7
+ }
8
+ function declarationOfInput(v) {
9
+ return v === 'sent' || v === 'not_sent' ? v : 'unknown';
10
+ }
11
+ export function fileHistoryLegNoticeVerdict(input) {
12
+ if (typeof input !== 'object' || input === null)
13
+ return 'unknown';
14
+ let capability;
15
+ let declaration;
16
+ let offByUser;
17
+ let legWord;
18
+ try {
19
+ capability = ownRead(input, 'capability');
20
+ declaration = declarationOfInput(ownRead(input, 'declaration'));
21
+ offByUser = ownRead(input, 'captureOffByUser') === true;
22
+ legWord = ownRead(input, 'legWord');
23
+ }
24
+ catch {
25
+ return 'unknown';
26
+ }
27
+ if (legWord !== 'on' && legWord !== FILE_HISTORY_DISABLED)
28
+ return 'unknown';
29
+ if (declaration === 'sent')
30
+ return legWord === 'on' ? 'not_honored' : 'honored';
31
+ if (!offByUser)
32
+ return 'not_declared';
33
+ const mode = fileHistoryCaptureMode(capability);
34
+ if (legWord === 'on' && mode === 'on-always')
35
+ return 'not_offered';
36
+ if (declaration === 'not_sent' && (legWord === FILE_HISTORY_DISABLED || mode === 'on-optional'))
37
+ return 'not_declared';
38
+ return 'unknown';
39
+ }
40
+ const NOT_HONORED_SENTENCE = 'The engine is still capturing file history for this session, though your settings asked it not to.';
41
+ const NOT_OFFERED_SENTENCE = 'This engine always captures file history and cannot turn it off per session, even though file checkpoints are off in your settings.';
42
+ export function fileHistoryLegNoticeSentence(verdict) {
43
+ if (verdict === 'not_honored')
44
+ return NOT_HONORED_SENTENCE;
45
+ if (verdict === 'not_offered')
46
+ return NOT_OFFERED_SENTENCE;
47
+ return null;
48
+ }
49
+ export function fileHistoryDeclarationOfBody(body) {
50
+ if (typeof body !== 'object' || body === null)
51
+ return 'unknown';
52
+ try {
53
+ if (Array.isArray(body))
54
+ return 'unknown';
55
+ if (typeof body.toJSON === 'function')
56
+ return 'unknown';
57
+ const d = Object.getOwnPropertyDescriptor(body, 'fileHistory');
58
+ const all = Object.getOwnPropertyDescriptors(body);
59
+ for (const k of Reflect.ownKeys(all)) {
60
+ const x = all[k];
61
+ if (x !== undefined && x.enumerable === true && !Object.hasOwn(x, 'value'))
62
+ return 'unknown';
63
+ }
64
+ const listed = Object.hasOwn(all, 'fileHistory') ? all.fileHistory : undefined;
65
+ const listedOn = listed !== undefined && listed.enumerable === true;
66
+ if (d === undefined || d.enumerable !== true)
67
+ return listedOn ? 'unknown' : 'not_sent';
68
+ if (!Object.hasOwn(d, 'value') || !listedOn || !Object.hasOwn(listed, 'value') || !Object.is(listed.value, d.value))
69
+ return 'unknown';
70
+ if (!Object.is(body.fileHistory, d.value))
71
+ return 'unknown';
72
+ if (typeof d.value === 'object' && d.value !== null)
73
+ return 'unknown';
74
+ return d.value === FILE_HISTORY_DISABLED ? 'sent' : 'not_sent';
75
+ }
76
+ catch {
77
+ return 'unknown';
78
+ }
79
+ }
80
+ const idOk = (v) => typeof v === 'string' && v.length > 0;
81
+ export function createFileHistoryDeclarationLedger() {
82
+ const factsByKey = new Map();
83
+ const keysByRun = new Map();
84
+ return Object.freeze({
85
+ declared(requestKey, fact) {
86
+ if (!idOk(requestKey) || (fact !== 'sent' && fact !== 'not_sent'))
87
+ return false;
88
+ let set = factsByKey.get(requestKey);
89
+ if (set === undefined)
90
+ factsByKey.set(requestKey, (set = new Set()));
91
+ set.add(fact);
92
+ return true;
93
+ },
94
+ boundToRun(requestKey, runId, event) {
95
+ if (!idOk(requestKey) || !idOk(runId) || !frameProvesDelivery(event))
96
+ return false;
97
+ let set = keysByRun.get(runId);
98
+ if (set === undefined)
99
+ keysByRun.set(runId, (set = new Set()));
100
+ set.add(requestKey);
101
+ return true;
102
+ },
103
+ declarationOf(runId) {
104
+ if (!idOk(runId))
105
+ return 'unknown';
106
+ const keys = keysByRun.get(runId);
107
+ if (keys === undefined)
108
+ return 'unknown';
109
+ let agreed;
110
+ for (const k of keys) {
111
+ const facts = factsByKey.get(k);
112
+ if (facts === undefined || facts.size !== 1)
113
+ return 'unknown';
114
+ const [only] = facts;
115
+ if (only === undefined || (agreed !== undefined && agreed !== only))
116
+ return 'unknown';
117
+ agreed = only;
118
+ }
119
+ return agreed ?? 'unknown';
120
+ },
121
+ });
122
+ }
@@ -40,7 +40,7 @@ export interface SettlementWhoView {
40
40
  approver?: string;
41
41
  window?: string;
42
42
  }
43
- export declare const SETTLEMENT_KIND_WORDS: readonly ["human_allowed", "human_refused", "approval_window_expired", "denial_limit_window_expired", "park_sla_expired", "no_approver", "approver_unavailable", "approver_error", "approver_contract", "presentation_failed", "blanket_allow_refused", "policy_refused", "task_aborted"];
43
+ export declare const SETTLEMENT_KIND_WORDS: readonly ["human_allowed", "human_refused", "approval_window_expired", "denial_limit_window_expired", "park_sla_expired", "no_approver", "approver_unavailable", "approver_error", "approver_contract", "presentation_failed", "blanket_allow_refused", "policy_allowed", "policy_refused", "task_aborted"];
44
44
  export interface SettlementView {
45
45
  kind: Settlement['kind'] | (string & {});
46
46
  who: SettlementWhoView;
@@ -20,6 +20,7 @@ export const SETTLEMENT_KIND_WORDS = Object.freeze([
20
20
  'approver_contract',
21
21
  'presentation_failed',
22
22
  'blanket_allow_refused',
23
+ 'policy_allowed',
23
24
  'policy_refused',
24
25
  'task_aborted',
25
26
  ]);
@@ -9,3 +9,4 @@ export declare const CORE_PERMISSION_MODE_FOLD_REASONS: readonly string[];
9
9
  export declare const CORE_ENGINE_CARD_TYPES: readonly string[];
10
10
  export declare const CORE_TOOL_DECLARATION_CODES: readonly string[];
11
11
  export declare const CORE_MODE_RELEASE_REASONS: readonly ["rule_unreadable"];
12
+ export declare const CORE_HOOK_TOOL_ALIASES: Readonly<Record<string, string>>;
@@ -114,3 +114,6 @@ export const CORE_TOOL_DECLARATION_CODES = Object.freeze([
114
114
  export const CORE_MODE_RELEASE_REASONS = Object.freeze([
115
115
  "rule_unreadable",
116
116
  ]);
117
+ export const CORE_HOOK_TOOL_ALIASES = Object.freeze(Object.assign(Object.create(null), Object.fromEntries([
118
+ ["ListPeers", "ListAgents"],
119
+ ])));