@sema-agent/client-core 0.87.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 (37) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/README.md +14 -10
  3. package/dist/adapter/downstream/eventToSdkMessage.js +14 -1
  4. package/dist/adapter/downstream/wiringManifestView.d.ts +2 -0
  5. package/dist/adapter/downstream/wiringManifestView.js +1 -0
  6. package/dist/displayUntrusted.d.ts +4 -0
  7. package/dist/displayUntrusted.js +1657 -573
  8. package/dist/engineErrorCodes.d.ts +2 -0
  9. package/dist/engineErrorCodes.js +1 -0
  10. package/dist/engineNoticeCodes.d.ts +20 -0
  11. package/dist/engineNoticeCodes.js +71 -0
  12. package/dist/engineWireFor.d.ts +1 -0
  13. package/dist/engineWireFor.js +1 -1
  14. package/dist/gateOutcome.d.ts +4 -0
  15. package/dist/gateOutcome.js +5 -0
  16. package/dist/generated/engineFactTables.d.ts +1 -0
  17. package/dist/generated/engineFactTables.js +3 -0
  18. package/dist/generated/engineNoticeTables.js +2 -0
  19. package/dist/hitl/crashConverged.d.ts +1 -1
  20. package/dist/hitl/crashConverged.js +6 -1
  21. package/dist/hitl/planReviewWire.js +30 -10
  22. package/dist/index.d.ts +6 -0
  23. package/dist/index.js +3 -0
  24. package/dist/promptDelivery.d.ts +20 -0
  25. package/dist/promptDelivery.js +153 -0
  26. package/dist/registryConflict.d.ts +3 -0
  27. package/dist/registryConflict.js +44 -0
  28. package/dist/request/taskRequest.d.ts +1 -0
  29. package/dist/request/taskRequest.js +25 -1
  30. package/dist/resumeRefusalCopy.d.ts +7 -0
  31. package/dist/resumeRefusalCopy.js +45 -1
  32. package/dist/seam.d.ts +1 -0
  33. package/dist/sessionMemoryErase.d.ts +76 -0
  34. package/dist/sessionMemoryErase.js +432 -0
  35. package/dist/subagent/engineSubagentResume.js +2 -1
  36. package/docs/INTEGRATION-CLIENTS.md +372 -11
  37. package/package.json +2 -2
@@ -48,6 +48,8 @@ export declare const REWIND_ERROR_CODE_PREFIXES: readonly ["resume_at.", "rewind
48
48
  export declare function isRewindFamilyCode(code: string | undefined): boolean;
49
49
  export declare const RESUME_USAGE_WINDOW_EXHAUSTED = "resume.usage_window_exhausted";
50
50
  export declare const RESUME_PREFLIGHT_REJECTED = "resume.preflight_rejected";
51
+ export declare const RESUME_SESSION_NOT_FOUND = "resume.session_not_found";
52
+ export type StatusDerivedCoarseCode = (typeof STATUS_DERIVED_COARSE_CODES)[number];
51
53
  export declare const RESUME_PLACEMENT_MISMATCH = "resume.placement_mismatch";
52
54
  export declare const RESUME_CONTEXT_UNAVAILABLE = "conflict.resume_context_unavailable";
53
55
  export declare const RESUME_RETRY_LATER_CODES: readonly string[];
@@ -106,6 +106,7 @@ export function isRewindFamilyCode(code) {
106
106
  }
107
107
  export const RESUME_USAGE_WINDOW_EXHAUSTED = 'resume.usage_window_exhausted';
108
108
  export const RESUME_PREFLIGHT_REJECTED = 'resume.preflight_rejected';
109
+ export const RESUME_SESSION_NOT_FOUND = 'resume.session_not_found';
109
110
  export const RESUME_PLACEMENT_MISMATCH = 'resume.placement_mismatch';
110
111
  export const RESUME_CONTEXT_UNAVAILABLE = 'conflict.resume_context_unavailable';
111
112
  export const RESUME_RETRY_LATER_CODES = Object.freeze([
@@ -137,6 +137,24 @@ export interface LegacyModeSeedMigratedFactsView {
137
137
  runId: string;
138
138
  }
139
139
  export declare function readLegacyModeSeedMigrated(notice: unknown): LegacyModeSeedMigratedFactsView | undefined;
140
+ export interface ToolTurnRepairView {
141
+ form: string;
142
+ toolCallIds: readonly string[];
143
+ }
144
+ export interface ToolTurnRepairedFactsView {
145
+ repairs: readonly ToolTurnRepairView[];
146
+ sessionId: string;
147
+ runId: string;
148
+ }
149
+ export declare function readToolTurnRepaired(notice: unknown): ToolTurnRepairedFactsView | undefined;
150
+ export interface ModelUnreachableFactsView {
151
+ code: string;
152
+ targetHost: string;
153
+ proxyInUse?: string;
154
+ remedy: string;
155
+ sessionId: string;
156
+ }
157
+ export declare function readModelUnreachable(notice: unknown): ModelUnreachableFactsView | undefined;
140
158
  export declare const ENGINE_NOTICE_FACT_READERS: Readonly<{
141
159
  readonly 'mcp.injection_dropped': typeof readMcpInjectionDrop;
142
160
  readonly 'delegation.ask_unresolvable': typeof readAskUnresolvable;
@@ -153,6 +171,8 @@ export declare const ENGINE_NOTICE_FACT_READERS: Readonly<{
153
171
  readonly 'permission.ask_released_by_mode': typeof readAskReleasedByMode;
154
172
  readonly 'permission.read_deny_unapplied': typeof readReadDenyUnapplied;
155
173
  readonly 'resume.legacy_mode_seed_migrated': typeof readLegacyModeSeedMigrated;
174
+ readonly 'context.tool_turn_repaired': typeof readToolTurnRepaired;
175
+ readonly 'server.model_unreachable': typeof readModelUnreachable;
156
176
  }>;
157
177
  type FactReaderTable = typeof ENGINE_NOTICE_FACT_READERS;
158
178
  export type EngineNoticeFactsCode = keyof FactReaderTable;
@@ -7,6 +7,7 @@ export const SERVER_NOTICE_AUDIENCE = Object.freeze({
7
7
  'instructions.source_changed': 'user',
8
8
  'memory.layer_locked_legacy': 'operator',
9
9
  'session.records_skipped': 'user',
10
+ 'server.model_unreachable': 'user',
10
11
  });
11
12
  function audienceRow(table, code) {
12
13
  return Object.hasOwn(table, code) ? table[code] : undefined;
@@ -483,6 +484,74 @@ function readLegacyModeSeedMigratedOf(notice) {
483
484
  const permissionMode = nonEmpty(optionalSeat(mo, 'permissionMode'));
484
485
  return { ...(permissionMode !== undefined ? { permissionMode } : {}), droppedKeys, sessionId, runId };
485
486
  }
487
+ export function readToolTurnRepaired(notice) {
488
+ return guardedRead(notice, readToolTurnRepairedOf);
489
+ }
490
+ function ownSeat(o, key) {
491
+ return Object.hasOwn(o, key) ? o[key] : undefined;
492
+ }
493
+ function optionalOwnSeat(o, key) {
494
+ try {
495
+ return ownSeat(o, key);
496
+ }
497
+ catch {
498
+ return undefined;
499
+ }
500
+ }
501
+ function readToolTurnRepairedOf(notice) {
502
+ if (typeof notice !== 'object' || notice === null || Array.isArray(notice))
503
+ return undefined;
504
+ const n = notice;
505
+ if (n.code !== 'context.tool_turn_repaired')
506
+ return undefined;
507
+ const d = n.detail;
508
+ if (typeof d !== 'object' || d === null || Array.isArray(d))
509
+ return undefined;
510
+ const o = d;
511
+ const rawRepairs = ownSeat(o, 'repairs');
512
+ if (!Array.isArray(rawRepairs) || rawRepairs.length === 0)
513
+ return undefined;
514
+ const count = rawRepairs.length;
515
+ const repairs = [];
516
+ for (let i = 0; i < count; i++) {
517
+ const item = rawRepairs[i];
518
+ if (typeof item !== 'object' || item === null || Array.isArray(item))
519
+ return undefined;
520
+ const it = item;
521
+ const form = nonEmpty(ownSeat(it, 'form'));
522
+ const toolCallIds = nameList(ownSeat(it, 'toolCallIds'));
523
+ if (form === undefined || toolCallIds === undefined || toolCallIds.length === 0)
524
+ return undefined;
525
+ repairs.push(Object.freeze({ form, toolCallIds }));
526
+ }
527
+ const sessionId = nonEmpty(ownSeat(o, 'sessionId'));
528
+ const runId = nonEmpty(ownSeat(o, 'runId'));
529
+ if (sessionId === undefined || runId === undefined)
530
+ return undefined;
531
+ return { repairs: Object.freeze(repairs), sessionId, runId };
532
+ }
533
+ export function readModelUnreachable(notice) {
534
+ return guardedRead(notice, readModelUnreachableOf);
535
+ }
536
+ function readModelUnreachableOf(notice) {
537
+ if (typeof notice !== 'object' || notice === null || Array.isArray(notice))
538
+ return undefined;
539
+ const n = notice;
540
+ if (n.code !== 'server.model_unreachable')
541
+ return undefined;
542
+ const d = n.detail;
543
+ if (typeof d !== 'object' || d === null || Array.isArray(d))
544
+ return undefined;
545
+ const o = d;
546
+ const code = nonEmpty(ownSeat(o, 'code'));
547
+ const targetHost = nonEmpty(ownSeat(o, 'targetHost'));
548
+ const remedy = nonEmpty(ownSeat(o, 'remedy'));
549
+ const sessionId = nonEmpty(ownSeat(o, 'sessionId'));
550
+ if (code === undefined || targetHost === undefined || remedy === undefined || sessionId === undefined)
551
+ return undefined;
552
+ const proxyInUse = nonEmpty(optionalOwnSeat(o, 'proxyInUse'));
553
+ return { code, targetHost, ...(proxyInUse !== undefined ? { proxyInUse } : {}), remedy, sessionId };
554
+ }
486
555
  export const ENGINE_NOTICE_FACT_READERS = Object.freeze({
487
556
  'mcp.injection_dropped': readMcpInjectionDrop,
488
557
  'delegation.ask_unresolvable': readAskUnresolvable,
@@ -499,6 +568,8 @@ export const ENGINE_NOTICE_FACT_READERS = Object.freeze({
499
568
  'permission.ask_released_by_mode': readAskReleasedByMode,
500
569
  'permission.read_deny_unapplied': readReadDenyUnapplied,
501
570
  'resume.legacy_mode_seed_migrated': readLegacyModeSeedMigrated,
571
+ 'context.tool_turn_repaired': readToolTurnRepaired,
572
+ 'server.model_unreachable': readModelUnreachable,
502
573
  });
503
574
  const _factReaderTablePin = true;
504
575
  void _factReaderTablePin;
@@ -40,4 +40,5 @@ export type EngineWireNoAnswerKind = 'deadline' | 'time-limit' | 'connection-los
40
40
  export declare function noAnswerKindOf(e: object | null | undefined): EngineWireNoAnswerKind | null;
41
41
  export declare function noAnswerDetailFrom(e: object | null | undefined): string | null;
42
42
  export declare function sendOnce<T>(signal: AbortSignal | null | undefined, run: () => Promise<T>): Promise<T>;
43
+ export declare function provablyNotSent(e: object | null | undefined): boolean;
43
44
  export declare function settleInjected<T>(ms: number, signal: AbortSignal | null | undefined, run: (signal: AbortSignal) => Promise<T>): Promise<T>;
@@ -331,7 +331,7 @@ export async function sendOnce(signal, run) {
331
331
  throw error;
332
332
  }
333
333
  }
334
- function provablyNotSent(e) {
334
+ export function provablyNotSent(e) {
335
335
  try {
336
336
  return e !== null && typeof e === 'object' && (markedNotSent(e) || failedBeforeSend(e));
337
337
  }
@@ -1,5 +1,6 @@
1
1
  import type { DeniedBy, Settlement } from '@sema-agent/sdk';
2
2
  import { type CcToolDenialKind } from './gateVocabulary.js';
3
+ import { CORE_MODE_RELEASE_REASONS } from './generated/engineFactTables.js';
3
4
  export type GateDispositionView = {
4
5
  kind: 'allowed';
5
6
  classifier?: GateClassifierRoundView;
@@ -31,6 +32,9 @@ export interface GateModeReleaseView {
31
32
  mandate?: string;
32
33
  reason?: string;
33
34
  }
35
+ export declare const GATE_MODE_RELEASE_REASON_WORDS: readonly ["rule_unreadable"];
36
+ export type GateModeReleaseReasonWord = (typeof CORE_MODE_RELEASE_REASONS)[number];
37
+ export declare function isGateModeReleaseReasonWord(v: unknown): v is GateModeReleaseReasonWord;
34
38
  export interface SettlementWhoView {
35
39
  party: string;
36
40
  approver?: string;
@@ -1,8 +1,13 @@
1
1
  import { ccToolDenialKindForSettledBy, isCcToolDenialKind, isGateDeniedByWord } from './gateVocabulary.js';
2
2
  import { GATE_PARKED_ERROR_CODE } from './engineErrorCodes.js';
3
+ import { CORE_MODE_RELEASE_REASONS } from './generated/engineFactTables.js';
3
4
  function str(v) {
4
5
  return typeof v === 'string' && v.length > 0 ? v : undefined;
5
6
  }
7
+ export const GATE_MODE_RELEASE_REASON_WORDS = CORE_MODE_RELEASE_REASONS;
8
+ export function isGateModeReleaseReasonWord(v) {
9
+ return typeof v === 'string' && CORE_MODE_RELEASE_REASONS.includes(v);
10
+ }
6
11
  export const SETTLEMENT_KIND_WORDS = Object.freeze([
7
12
  'human_allowed',
8
13
  'human_refused',
@@ -8,3 +8,4 @@ export declare const CORE_DENIED_BY_VALUES: readonly ["policy", "hook", "org", "
8
8
  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
+ export declare const CORE_MODE_RELEASE_REASONS: readonly ["rule_unreadable"];
@@ -111,3 +111,6 @@ export const CORE_TOOL_DECLARATION_CODES = Object.freeze([
111
111
  "config.egress_requires_write_effect",
112
112
  "config.reserved_tool_name",
113
113
  ]);
114
+ export const CORE_MODE_RELEASE_REASONS = Object.freeze([
115
+ "rule_unreadable",
116
+ ]);
@@ -65,6 +65,7 @@ export const CORE_ENGINE_NOTICE_CODES = Object.freeze([
65
65
  "route.base_url_changed_key_unchanged",
66
66
  "route.response_model_mismatch",
67
67
  "route.tool_call_id_folded",
68
+ "context.tool_turn_repaired",
68
69
  "task.user_steer_undrained",
69
70
  "task.user_followup_undrained",
70
71
  "steering.parked_input_blocked",
@@ -147,6 +148,7 @@ export const CORE_NOTICE_AUDIENCE = Object.freeze({
147
148
  "route.base_url_changed_key_unchanged": "operator",
148
149
  "route.response_model_mismatch": "operator",
149
150
  "route.tool_call_id_folded": "operator",
151
+ "context.tool_turn_repaired": "operator",
150
152
  "task.user_steer_undrained": "user",
151
153
  "task.user_followup_undrained": "user",
152
154
  "steering.parked_input_blocked": "user",
@@ -8,7 +8,7 @@ export interface CrashConvergedRow {
8
8
  readonly ts: number;
9
9
  readonly expiresAtMs: number;
10
10
  readonly decision: 'denied';
11
- readonly cause: 'crashed_before_park';
11
+ readonly cause: 'crashed_before_park' | 'shutdown_before_park';
12
12
  readonly orphanState: CrashConvergedOrphanState;
13
13
  readonly originalDecision?: 'approve';
14
14
  readonly decidedAtMs?: number;
@@ -1,3 +1,8 @@
1
+ const CRASH_CONVERGED_CAUSE_WORDS = Object.freeze({
2
+ crashed_before_park: true,
3
+ shutdown_before_park: true,
4
+ });
5
+ const isCrashConvergedCause = (v) => typeof v === 'string' && Object.hasOwn(CRASH_CONVERGED_CAUSE_WORDS, v);
1
6
  const isString = (v) => typeof v === 'string';
2
7
  const isNumber = (v) => typeof v === 'number';
3
8
  const nonEmptyString = (v) => typeof v === 'string' && v.length > 0;
@@ -45,7 +50,7 @@ function readCrashConvergedRow(v) {
45
50
  return null;
46
51
  if (o.decision !== 'denied')
47
52
  return null;
48
- if (o.cause !== 'crashed_before_park')
53
+ if (!isCrashConvergedCause(o.cause))
49
54
  return null;
50
55
  if (o.orphanState !== 'pending' && o.orphanState !== 'decided')
51
56
  return null;
@@ -346,9 +346,11 @@ let decideDispatchSeq = 0;
346
346
  function planReviewOpsOf(taskId) {
347
347
  return planReviewDecisionOps.filter((op) => op.taskId === taskId);
348
348
  }
349
- function settlePlanReviewDecisionOp(op) {
350
- if (op.settledAt === null)
351
- op.settledAt = ++planReviewLedgerClock;
349
+ function settlePlanReviewDecisionOp(op, by) {
350
+ if (op.settledAt !== null)
351
+ return;
352
+ op.settledAt = ++planReviewLedgerClock;
353
+ op.settler = by;
352
354
  }
353
355
  function pushPlanReviewDecisionOp(op) {
354
356
  planReviewDecisionOps.push(op);
@@ -366,18 +368,31 @@ function dropPlanReviewDecisionOp(op) {
366
368
  if (i >= 0)
367
369
  planReviewDecisionOps.splice(i, 1);
368
370
  }
371
+ function claimablePlanReviewDecisionOp(taskId) {
372
+ const open = planReviewDecisionOps.find((o) => o.taskId === taskId && o.phase === 'handed' && o.settledAt === null);
373
+ if (open !== undefined)
374
+ return open;
375
+ let leftBehind;
376
+ for (const o of planReviewDecisionOps) {
377
+ if (o.taskId !== taskId || o.phase !== 'handed' || o.settler !== 'gate_left')
378
+ continue;
379
+ if (leftBehind === undefined || o.handedAt > leftBehind.handedAt)
380
+ leftBehind = o;
381
+ }
382
+ return leftBehind;
383
+ }
369
384
  export function notePlanReviewDecisionHandedOver(taskId) {
370
385
  if (typeof taskId !== 'string' || taskId === '')
371
386
  return null;
372
387
  if (planReviewDecisionOps.some((op) => op.taskId === taskId && op.settledAt === null))
373
388
  return null;
374
- const op = { taskId, handedAt: ++planReviewLedgerClock, settledAt: null, phase: 'handed', notApplied: false, ownCallOpen: false };
389
+ const op = { taskId, handedAt: ++planReviewLedgerClock, settledAt: null, settler: null, phase: 'handed', notApplied: false, ownCallOpen: false };
375
390
  pushPlanReviewDecisionOp(op);
376
391
  return {
377
392
  settle: () => {
378
393
  if (op.phase !== 'handed')
379
394
  return;
380
- settlePlanReviewDecisionOp(op);
395
+ settlePlanReviewDecisionOp(op, 'host');
381
396
  },
382
397
  revoke: () => {
383
398
  if (op.phase !== 'handed' || op.settledAt !== null)
@@ -412,7 +427,7 @@ export function notePlanReviewGateLeft(taskId) {
412
427
  if (typeof taskId !== 'string' || taskId === '')
413
428
  return;
414
429
  for (const op of planReviewOpsOf(taskId))
415
- settlePlanReviewDecisionOp(op);
430
+ settlePlanReviewDecisionOp(op, 'gate_left');
416
431
  }
417
432
  export function latestPlanReviewDecisionMark(taskId) {
418
433
  if (typeof taskId !== 'string' || taskId === '')
@@ -434,12 +449,17 @@ async function decidePlanReviewFor(taskId, decision, permissionModeAfter, opts,
434
449
  hostLog('error', `planReviewWire: decidePlanReview(${decision}) for task ${taskId} REFUSED — a decision for this task is already in flight (duplicate delivery latched)`);
435
450
  return;
436
451
  }
437
- let op = planReviewDecisionOps.find((o) => o.taskId === taskId && o.phase === 'handed' && o.settledAt === null);
452
+ let op = claimablePlanReviewDecisionOp(taskId);
438
453
  if (op === undefined) {
439
- op = { taskId: typeof taskId === 'string' ? taskId : String(taskId), handedAt: ++planReviewLedgerClock, settledAt: null, phase: 'posting', notApplied: false, ownCallOpen: true };
454
+ op = { taskId: typeof taskId === 'string' ? taskId : String(taskId), handedAt: ++planReviewLedgerClock, settledAt: null, settler: null, phase: 'posting', notApplied: false, ownCallOpen: true };
440
455
  pushPlanReviewDecisionOp(op);
441
456
  }
442
457
  else {
458
+ if (op.settler === 'gate_left') {
459
+ op.settledAt = null;
460
+ op.settler = null;
461
+ op.notApplied = false;
462
+ }
443
463
  op.phase = 'posting';
444
464
  op.ownCallOpen = true;
445
465
  }
@@ -451,7 +471,7 @@ async function decidePlanReviewFor(taskId, decision, permissionModeAfter, opts,
451
471
  const settle = (notApplied) => {
452
472
  if (notApplied)
453
473
  mine.notApplied = true;
454
- settlePlanReviewDecisionOp(mine);
474
+ settlePlanReviewDecisionOp(mine, 'package');
455
475
  };
456
476
  const dispatchNo = ++decideDispatchSeq;
457
477
  hostLog('debug', `planReviewWire: decidePlanReview dispatch #${dispatchNo} task=${taskId} decision=${decision} mode=${permissionModeAfter ?? '-'}`);
@@ -460,7 +480,7 @@ async function decidePlanReviewFor(taskId, decision, permissionModeAfter, opts,
460
480
  }
461
481
  finally {
462
482
  releaseLatch();
463
- settlePlanReviewDecisionOp(mine);
483
+ settlePlanReviewDecisionOp(mine, 'package');
464
484
  mine.ownCallOpen = false;
465
485
  }
466
486
  }
package/dist/index.d.ts CHANGED
@@ -146,6 +146,10 @@ export * from './wireErrorTriage.js';
146
146
  export * from './corruptSession.js';
147
147
  export * from './corruptSessionCopy.js';
148
148
  export { classifyToolHistoryMismatch, resolveToolHistoryRewindTarget } from './toolHistoryMismatch.js';
149
+ export { classifyRegistryConflict, isScopeGoneError } from './registryConflict.js';
150
+ export type { RegistryConflictClass } from './registryConflict.js';
151
+ export { createPromptDeliveryLedger, promptKnownUndelivered, withoutUndeliveredPrompts } from './promptDelivery.js';
152
+ export type { PromptDeliveryLedger, PromptDeliveryRecord } from './promptDelivery.js';
149
153
  export * from './resumeRefusalCopy.js';
150
154
  export * from './wireRefusalCopy.js';
151
155
  export * from './inheritEnvWire.js';
@@ -198,6 +202,8 @@ export { readEngineAgentRegistry, isEngineAgentRegistryListingSuperseded } from
198
202
  export type { EngineAgentRegistryClient } from './agentSession/engineAgentRegistryRead.js';
199
203
  export { stopEngineSessionBackground, engineSessionBackgroundReceiptReadingOf, ENGINE_SESSION_BACKGROUND_RECEIPT_STATES, } from './agentSession/engineSessionBackgroundStop.js';
200
204
  export type { EngineSessionBackgroundStopClient, EngineSessionBackgroundStopWire, EngineSessionBackgroundStopOptions, EngineSessionBackgroundReceipt, EngineSessionBackgroundStopUnavailableWhy, EngineSessionBackgroundStopRefusedWhy, EngineSessionBackgroundStopResult, EngineSessionBackgroundReceiptState, EngineSessionBackgroundReceiptReading, } from './agentSession/engineSessionBackgroundStop.js';
205
+ export { eraseSessionMemory, sessionMemoryEraseOutcome, SESSION_MEMORY_ERASE_REFUSAL_REASONS, SESSION_MEMORY_ERASE_UNCONFIRMED_WHYS, SESSION_MEMORY_ERASE_RETRIES, } from './sessionMemoryErase.js';
206
+ export type { SessionMemoryEraseWire, SessionMemoryEraseCall, SessionMemoryEraseOptions, SessionMemoryEraseSent, SessionMemoryEraseNotSentWhy, SessionMemoryEraseJson, SessionMemoryEraseAnswer, SessionMemoryEraseRefusalReason, SessionMemoryEraseUnconfirmedWhy, SessionMemoryEraseRetry, SessionMemoryEraseOutcome, } from './sessionMemoryErase.js';
201
207
  export * from './model/catalog.js';
202
208
  export * from './model/catalogLoader.js';
203
209
  export * from './model/providerAuth.js';
package/dist/index.js CHANGED
@@ -134,6 +134,8 @@ export * from './wireErrorTriage.js';
134
134
  export * from './corruptSession.js';
135
135
  export * from './corruptSessionCopy.js';
136
136
  export { classifyToolHistoryMismatch, resolveToolHistoryRewindTarget } from './toolHistoryMismatch.js';
137
+ export { classifyRegistryConflict, isScopeGoneError } from './registryConflict.js';
138
+ export { createPromptDeliveryLedger, promptKnownUndelivered, withoutUndeliveredPrompts } from './promptDelivery.js';
137
139
  export * from './resumeRefusalCopy.js';
138
140
  export * from './wireRefusalCopy.js';
139
141
  export * from './inheritEnvWire.js';
@@ -182,6 +184,7 @@ export * from './agentSession/backgroundView.js';
182
184
  export { READ_CREDENTIAL_NOT_ACCEPTED_NOTICE } from './readCredentialRefusal.js';
183
185
  export { readEngineAgentRegistry, isEngineAgentRegistryListingSuperseded } from './agentSession/engineAgentRegistryRead.js';
184
186
  export { stopEngineSessionBackground, engineSessionBackgroundReceiptReadingOf, ENGINE_SESSION_BACKGROUND_RECEIPT_STATES, } from './agentSession/engineSessionBackgroundStop.js';
187
+ export { eraseSessionMemory, sessionMemoryEraseOutcome, SESSION_MEMORY_ERASE_REFUSAL_REASONS, SESSION_MEMORY_ERASE_UNCONFIRMED_WHYS, SESSION_MEMORY_ERASE_RETRIES, } from './sessionMemoryErase.js';
185
188
  export * from './model/catalog.js';
186
189
  export * from './model/catalogLoader.js';
187
190
  export * from './model/providerAuth.js';
@@ -0,0 +1,20 @@
1
+ import type { AgentEvent } from '@sema-agent/sdk';
2
+ import { type SelfHealOutcome } from './adapter/activeRunSelfHeal.js';
3
+ export interface PromptDeliveryRecord {
4
+ readonly u: string;
5
+ readonly s: 'a' | 'd';
6
+ }
7
+ export interface PromptDeliveryLedger {
8
+ attempted(batchUuids: readonly string[]): readonly PromptDeliveryRecord[];
9
+ deliveredByFrame(batchUuids: readonly string[], event: AgentEvent): readonly PromptDeliveryRecord[];
10
+ deliveredBySteerReceipt(batchUuids: readonly string[], outcome: SelfHealOutcome): readonly PromptDeliveryRecord[];
11
+ isAttempted(uuid: string): boolean;
12
+ isDelivered(uuid: string): boolean;
13
+ knownUndelivered(uuid: string): boolean;
14
+ batchOf(uuid: string): readonly string[] | undefined;
15
+ toRecords(): readonly PromptDeliveryRecord[];
16
+ fromRecords(records: readonly PromptDeliveryRecord[]): readonly PromptDeliveryRecord[];
17
+ }
18
+ export declare function createPromptDeliveryLedger(): PromptDeliveryLedger;
19
+ export declare function promptKnownUndelivered(ledger: PromptDeliveryLedger, message: object | null | undefined): boolean;
20
+ export declare function withoutUndeliveredPrompts<T>(ledger: PromptDeliveryLedger, messages: readonly T[]): T[];
@@ -0,0 +1,153 @@
1
+ import { activeRunBusySignal } from './adapter/runStream.js';
2
+ import { selfHealSubmissionDisposition } from './adapter/activeRunSelfHeal.js';
3
+ const uuidOk = (u) => typeof u === 'string' && u.length > 0;
4
+ function cleanBatch(batch) {
5
+ if (!Array.isArray(batch))
6
+ return [];
7
+ const out = [];
8
+ const seen = new Set();
9
+ try {
10
+ for (const u of batch) {
11
+ if (!uuidOk(u) || seen.has(u))
12
+ continue;
13
+ seen.add(u);
14
+ out.push(u);
15
+ }
16
+ }
17
+ catch {
18
+ return [];
19
+ }
20
+ return out;
21
+ }
22
+ function readRecord(rec) {
23
+ if (typeof rec !== 'object' || rec === null || Array.isArray(rec))
24
+ return undefined;
25
+ try {
26
+ const u = rec.u;
27
+ const s = rec.s;
28
+ return uuidOk(u) && (s === 'a' || s === 'd') ? { u, s } : undefined;
29
+ }
30
+ catch {
31
+ return undefined;
32
+ }
33
+ }
34
+ const PRE_RUN_FRAME_TYPES = new Set(['meta', 'approval_request', 'approval_revoke']);
35
+ function frameProvesDelivery(event) {
36
+ if (typeof event !== 'object' || event === null || Array.isArray(event))
37
+ return false;
38
+ try {
39
+ const type = event.type;
40
+ if (typeof type !== 'string' || type.length === 0 || PRE_RUN_FRAME_TYPES.has(type))
41
+ return false;
42
+ if (type === 'done') {
43
+ const result = event.result;
44
+ if (typeof result === 'object' && result !== null && !Array.isArray(result) && result.status === undefined) {
45
+ const r = result;
46
+ if (activeRunBusySignal({ type: 'failed', errorCode: r.errorCode, activeTaskId: r.activeTaskId }) !== null)
47
+ return false;
48
+ }
49
+ }
50
+ return activeRunBusySignal(event) === null;
51
+ }
52
+ catch {
53
+ return false;
54
+ }
55
+ }
56
+ function outcomeProvesDelivery(outcome) {
57
+ if (typeof outcome !== 'object' || outcome === null || Array.isArray(outcome))
58
+ return false;
59
+ try {
60
+ return selfHealSubmissionDisposition(outcome) === 'handed-off';
61
+ }
62
+ catch {
63
+ return false;
64
+ }
65
+ }
66
+ export function createPromptDeliveryLedger() {
67
+ const attemptedSet = new Set();
68
+ const deliveredSet = new Set();
69
+ const batchOfMap = new Map();
70
+ const log = [];
71
+ const add = (u, s, into) => {
72
+ const set = s === 'a' ? attemptedSet : deliveredSet;
73
+ if (set.has(u))
74
+ return;
75
+ set.add(u);
76
+ const rec = Object.freeze({ u, s });
77
+ log.push(rec);
78
+ into.push(rec);
79
+ };
80
+ const markDelivered = (batchUuids) => {
81
+ const added = [];
82
+ for (const u of cleanBatch(batchUuids))
83
+ add(u, 'd', added);
84
+ return Object.freeze(added);
85
+ };
86
+ return Object.freeze({
87
+ attempted(batchUuids) {
88
+ const batch = Object.freeze(cleanBatch(batchUuids));
89
+ const added = [];
90
+ for (const u of batch) {
91
+ batchOfMap.set(u, batch);
92
+ add(u, 'a', added);
93
+ }
94
+ return Object.freeze(added);
95
+ },
96
+ deliveredByFrame(batchUuids, event) {
97
+ return frameProvesDelivery(event) ? markDelivered(batchUuids) : Object.freeze([]);
98
+ },
99
+ deliveredBySteerReceipt(batchUuids, outcome) {
100
+ return outcomeProvesDelivery(outcome) ? markDelivered(batchUuids) : Object.freeze([]);
101
+ },
102
+ isAttempted: (uuid) => uuidOk(uuid) && attemptedSet.has(uuid),
103
+ isDelivered: (uuid) => uuidOk(uuid) && deliveredSet.has(uuid),
104
+ knownUndelivered: (uuid) => uuidOk(uuid) && attemptedSet.has(uuid) && !deliveredSet.has(uuid),
105
+ batchOf: (uuid) => (uuidOk(uuid) ? batchOfMap.get(uuid) : undefined),
106
+ toRecords: () => Object.freeze([...log]),
107
+ fromRecords(records) {
108
+ const added = [];
109
+ if (!Array.isArray(records))
110
+ return Object.freeze(added);
111
+ let list;
112
+ try {
113
+ list = [...records];
114
+ }
115
+ catch {
116
+ return Object.freeze(added);
117
+ }
118
+ for (const raw of list) {
119
+ const rec = readRecord(raw);
120
+ if (rec !== undefined)
121
+ add(rec.u, rec.s, added);
122
+ }
123
+ return Object.freeze(added);
124
+ },
125
+ });
126
+ }
127
+ function uuidOfMessage(m) {
128
+ if (typeof m !== 'object' || m === null)
129
+ return undefined;
130
+ try {
131
+ const u = m.uuid;
132
+ return uuidOk(u) ? u : undefined;
133
+ }
134
+ catch {
135
+ return undefined;
136
+ }
137
+ }
138
+ function assertLedger(ledger, fn) {
139
+ if (typeof ledger !== 'object' || ledger === null || typeof ledger.knownUndelivered !== 'function') {
140
+ throw new TypeError(`${fn}: the first argument must be a ledger from createPromptDeliveryLedger()`);
141
+ }
142
+ }
143
+ export function promptKnownUndelivered(ledger, message) {
144
+ assertLedger(ledger, 'promptKnownUndelivered');
145
+ const u = uuidOfMessage(message);
146
+ return u !== undefined && ledger.knownUndelivered(u);
147
+ }
148
+ export function withoutUndeliveredPrompts(ledger, messages) {
149
+ assertLedger(ledger, 'withoutUndeliveredPrompts');
150
+ if (!Array.isArray(messages))
151
+ return [];
152
+ return messages.filter((m) => !promptKnownUndelivered(ledger, m));
153
+ }
@@ -0,0 +1,3 @@
1
+ export type RegistryConflictClass = 'gone' | 'conflict' | 'none';
2
+ export declare function classifyRegistryConflict(status: number, body: unknown): RegistryConflictClass;
3
+ export declare function isScopeGoneError(err: unknown): boolean;
@@ -0,0 +1,44 @@
1
+ const SCOPE_GONE_CODE = 'scope_gone';
2
+ const nonBlank = (v) => {
3
+ const s = typeof v === 'string' ? v.trim() : '';
4
+ return s === '' ? undefined : s;
5
+ };
6
+ export function classifyRegistryConflict(status, body) {
7
+ if (status !== 409)
8
+ return 'none';
9
+ try {
10
+ const b = (typeof body === 'object' && body !== null ? body : {});
11
+ const error = b.error;
12
+ const objectForm = typeof error === 'object' && error !== null ? error.code : undefined;
13
+ const code = nonBlank(b.errorCode) ?? nonBlank(objectForm) ?? (typeof error === 'string' ? error : undefined);
14
+ return code === SCOPE_GONE_CODE ? 'gone' : 'conflict';
15
+ }
16
+ catch {
17
+ return 'gone';
18
+ }
19
+ }
20
+ const SCOPE_GONE_ERROR_NAMES = Object.freeze(['ScopeGoneError', 'ControlScopeGoneError']);
21
+ export function isScopeGoneError(err) {
22
+ if ((typeof err !== 'object' && typeof err !== 'function') || err === null)
23
+ return false;
24
+ let status;
25
+ try {
26
+ status = err.status;
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ if (status !== 409)
32
+ return false;
33
+ try {
34
+ const o = err;
35
+ const body = o.body;
36
+ if (body !== undefined && body !== null && classifyRegistryConflict(409, body) === 'gone')
37
+ return true;
38
+ const name = o.name;
39
+ return typeof name === 'string' && SCOPE_GONE_ERROR_NAMES.includes(name);
40
+ }
41
+ catch {
42
+ return true;
43
+ }
44
+ }
@@ -50,6 +50,7 @@ export interface TaskRequestInput {
50
50
  memory?: 'off';
51
51
  fileHistory?: 'disabled';
52
52
  inheritEnv?: InheritEnvWord;
53
+ requireExistingSession?: TaskRequest['requireExistingSession'];
53
54
  rewind?: Record<string, unknown>;
54
55
  repo?: string;
55
56
  council?: boolean;