@sema-agent/client-core 0.87.0 → 0.89.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 (56) hide show
  1. package/CHANGELOG.md +177 -0
  2. package/README.md +20 -15
  3. package/dist/adapter/activeRunSelfHeal.js +4 -2
  4. package/dist/adapter/downstream/eventToSdkMessage.js +17 -2
  5. package/dist/adapter/downstream/wiringManifestView.d.ts +11 -0
  6. package/dist/adapter/downstream/wiringManifestView.js +28 -0
  7. package/dist/controlRouter.d.ts +7 -1
  8. package/dist/controlRouter.js +43 -1
  9. package/dist/disableSkillsWire.d.ts +17 -0
  10. package/dist/disableSkillsWire.js +52 -0
  11. package/dist/displayUntrusted.d.ts +4 -0
  12. package/dist/displayUntrusted.js +1742 -591
  13. package/dist/engineErrorCodes.d.ts +2 -0
  14. package/dist/engineErrorCodes.js +1 -0
  15. package/dist/engineNoticeCodes.d.ts +44 -1
  16. package/dist/engineNoticeCodes.js +211 -39
  17. package/dist/engineWireFor.d.ts +1 -0
  18. package/dist/engineWireFor.js +1 -1
  19. package/dist/gateOutcome.d.ts +4 -0
  20. package/dist/gateOutcome.js +5 -0
  21. package/dist/generated/engineFactTables.d.ts +1 -0
  22. package/dist/generated/engineFactTables.js +3 -0
  23. package/dist/generated/engineNoticeTables.d.ts +3 -0
  24. package/dist/generated/engineNoticeTables.js +110 -0
  25. package/dist/hitl/crashConverged.d.ts +1 -1
  26. package/dist/hitl/crashConverged.js +6 -1
  27. package/dist/hitl/planReviewWire.js +30 -10
  28. package/dist/hitl/toolApprovalWire.d.ts +6 -2
  29. package/dist/hitl/toolApprovalWire.js +30 -0
  30. package/dist/hooksWireCaps.js +80 -12
  31. package/dist/index.d.ts +12 -3
  32. package/dist/index.js +8 -3
  33. package/dist/pluginHooksWire.d.ts +46 -4
  34. package/dist/pluginHooksWire.js +277 -24
  35. package/dist/promptDelivery.d.ts +20 -0
  36. package/dist/promptDelivery.js +153 -0
  37. package/dist/providerRejection.d.ts +6 -0
  38. package/dist/providerRejection.js +52 -0
  39. package/dist/registryConflict.d.ts +3 -0
  40. package/dist/registryConflict.js +44 -0
  41. package/dist/request/taskRequest.d.ts +3 -0
  42. package/dist/request/taskRequest.js +64 -8
  43. package/dist/resumeRefusalCopy.d.ts +7 -0
  44. package/dist/resumeRefusalCopy.js +45 -1
  45. package/dist/runTerminal.d.ts +6 -0
  46. package/dist/runTerminal.js +23 -0
  47. package/dist/seam.d.ts +4 -0
  48. package/dist/seatContract.d.ts +9 -1
  49. package/dist/seatContract.js +10 -0
  50. package/dist/sessionMemoryErase.d.ts +76 -0
  51. package/dist/sessionMemoryErase.js +432 -0
  52. package/dist/subagent/engineSubagentResume.js +2 -1
  53. package/dist/subagent/engineSubagentSteer.d.ts +8 -1
  54. package/dist/subagent/engineSubagentSteer.js +26 -3
  55. package/docs/INTEGRATION-CLIENTS.md +767 -14
  56. package/package.json +3 -3
@@ -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",
@@ -72,6 +73,7 @@ export const CORE_ENGINE_NOTICE_CODES = Object.freeze([
72
73
  "task.halt_unconsumed",
73
74
  "task.late_approval",
74
75
  "task.interrupt_unconsumed",
76
+ "task.tool_turn_incomplete",
75
77
  "approval.read_root_granted",
76
78
  "approval.read_root_grant_rejected",
77
79
  "memory.capture_opted_out",
@@ -79,6 +81,10 @@ export const CORE_ENGINE_NOTICE_CODES = Object.freeze([
79
81
  "skills.listing_truncated",
80
82
  "agents.listing_truncated",
81
83
  "tool_result.offload_put_failed",
84
+ "instructions.source_changed",
85
+ "session.records_skipped",
86
+ "memory.layer_locked_legacy",
87
+ "server.model_unreachable",
82
88
  ]);
83
89
  export const CORE_NOTICE_AUDIENCE = Object.freeze({
84
90
  "config.autocompact_window_clamped": "operator",
@@ -147,6 +153,7 @@ export const CORE_NOTICE_AUDIENCE = Object.freeze({
147
153
  "route.base_url_changed_key_unchanged": "operator",
148
154
  "route.response_model_mismatch": "operator",
149
155
  "route.tool_call_id_folded": "operator",
156
+ "context.tool_turn_repaired": "operator",
150
157
  "task.user_steer_undrained": "user",
151
158
  "task.user_followup_undrained": "user",
152
159
  "steering.parked_input_blocked": "user",
@@ -154,6 +161,7 @@ export const CORE_NOTICE_AUDIENCE = Object.freeze({
154
161
  "task.halt_unconsumed": "user",
155
162
  "task.late_approval": "user",
156
163
  "task.interrupt_unconsumed": "user",
164
+ "task.tool_turn_incomplete": "user",
157
165
  "approval.read_root_granted": "user",
158
166
  "approval.read_root_grant_rejected": "user",
159
167
  "memory.capture_opted_out": "user",
@@ -161,6 +169,10 @@ export const CORE_NOTICE_AUDIENCE = Object.freeze({
161
169
  "skills.listing_truncated": "operator",
162
170
  "agents.listing_truncated": "operator",
163
171
  "tool_result.offload_put_failed": "operator",
172
+ "instructions.source_changed": "user",
173
+ "session.records_skipped": "user",
174
+ "memory.layer_locked_legacy": "operator",
175
+ "server.model_unreachable": "user",
164
176
  });
165
177
  export const CORE_MCP_INJECTION_DROP_REASONS = Object.freeze([
166
178
  "malformed_entry",
@@ -168,3 +180,101 @@ export const CORE_MCP_INJECTION_DROP_REASONS = Object.freeze([
168
180
  "gate_closed",
169
181
  "over_cap",
170
182
  ]);
183
+ export const CORE_NOTICE_MINTER = Object.freeze({
184
+ "config.autocompact_window_clamped": "engine",
185
+ "config.env_timeout_discarded": "engine",
186
+ "config.materialize_env_discarded": "engine",
187
+ "config.models_swapped": "engine",
188
+ "config.context_settings_swapped": "engine",
189
+ "config.read_face_swapped": "engine",
190
+ "config.read_face_deployment_clamped": "engine",
191
+ "config.write_face_swapped": "engine",
192
+ "config.write_face_deployment_clamped": "engine",
193
+ "config.tool_model_gate_removed": "engine",
194
+ "config.tool_model_gate_unknown_class": "engine",
195
+ "config.tool_model_gate_env_invalid": "engine",
196
+ "config.artifact_host_invalid": "engine",
197
+ "config.task_root_not_canonical": "engine",
198
+ "config.write_protection_target_view_absent": "engine",
199
+ "config.write_protection_unresolved": "engine",
200
+ "config.execution_env_capability_invalid": "engine",
201
+ "config.tool_card_undeclared": "engine",
202
+ "config.tool_face_undeclared": "engine",
203
+ "config.tool_face_invalid": "engine",
204
+ "config.forward_sink_failed": "engine",
205
+ "config.durable_gate_unavailable": "engine",
206
+ "config.peer_admission_out_of_range": "engine",
207
+ "config.peer_lane_unmounted": "engine",
208
+ "config.secret_env_scrubbed": "engine",
209
+ "config.permission_mode_unavailable": "engine",
210
+ "permission.ask_released_by_mode": "engine",
211
+ "permission.read_deny_unapplied": "engine",
212
+ "permission.rule_store_unavailable": "engine",
213
+ "resume.legacy_mode_seed_migrated": "engine",
214
+ "peer.inbound_disposition": "engine",
215
+ "peer.held_settled": "engine",
216
+ "peer.idle_subscription": "engine",
217
+ "classifier.denial_limit": "engine",
218
+ "classifier.fallback": "engine",
219
+ "classifier.role_defaulted": "engine",
220
+ "classifier.contract_broken": "engine",
221
+ "classifier.unavailable": "engine",
222
+ "checkpoint.execution_outcome_unrecorded": "engine",
223
+ "delegation.transcript_integrity": "engine",
224
+ "delegation.ask_unresolvable": "engine",
225
+ "mcp.injection_dropped": "engine",
226
+ "mcp.revocation_probe_failed": "engine",
227
+ "mcp.server_redialed": "engine",
228
+ "workflow.governance_key_stripped": "engine",
229
+ "workflow.agent_option_ignored": "engine",
230
+ "memory.session_polluted": "engine",
231
+ "memory.harvest_quarantined": "engine",
232
+ "memory.delegation_static_mark_waived": "engine",
233
+ "memory.content_class_declared": "engine",
234
+ "memory.hold_opened": "engine",
235
+ "memory.hold_released": "engine",
236
+ "memory.hold_disposed": "engine",
237
+ "memory.consolidation_recommended": "engine",
238
+ "memory.consolidation_committed": "engine",
239
+ "memory.consolidation_conflict": "engine",
240
+ "memory.consolidation_incomplete": "engine",
241
+ "memory.consolidation_refused": "engine",
242
+ "memory.consolidation_withheld": "engine",
243
+ "memory.restore_withheld": "engine",
244
+ "memory.project_scope_absent": "engine",
245
+ "route.fallback_to_primary": "engine",
246
+ "route.classifier_unroutable": "engine",
247
+ "route.base_url_changed_key_unchanged": "engine",
248
+ "route.response_model_mismatch": "engine",
249
+ "route.tool_call_id_folded": "engine",
250
+ "context.tool_turn_repaired": "engine",
251
+ "task.user_steer_undrained": "engine",
252
+ "task.user_followup_undrained": "engine",
253
+ "steering.parked_input_blocked": "engine",
254
+ "task.turn_interrupted": "engine",
255
+ "task.halt_unconsumed": "engine",
256
+ "task.late_approval": "engine",
257
+ "task.interrupt_unconsumed": "engine",
258
+ "task.tool_turn_incomplete": "engine",
259
+ "approval.read_root_granted": "engine",
260
+ "approval.read_root_grant_rejected": "engine",
261
+ "memory.capture_opted_out": "engine",
262
+ "memory.capture_optout_unpersisted": "engine",
263
+ "skills.listing_truncated": "engine",
264
+ "agents.listing_truncated": "engine",
265
+ "tool_result.offload_put_failed": "engine",
266
+ "instructions.source_changed": "server",
267
+ "session.records_skipped": "server",
268
+ "memory.layer_locked_legacy": "server",
269
+ "server.model_unreachable": "server",
270
+ });
271
+ export const CORE_USER_STEER_UNDRAINED_CAUSES = Object.freeze([
272
+ "leg_end",
273
+ "crashed",
274
+ ]);
275
+ export const CORE_TOOL_TURN_INCOMPLETE_CAUSES = Object.freeze([
276
+ "truncated",
277
+ "unnamed",
278
+ "bad_frame",
279
+ "missing_result_synthesized",
280
+ ]);
@@ -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
  }
@@ -144,6 +144,8 @@ export interface ApprovalCardRequest {
144
144
  mandate?: ApprovalMandateWord;
145
145
  denialLimitFallback?: DenialLimitFallback;
146
146
  origin?: string;
147
+ askClass?: string;
148
+ classes?: readonly string[];
147
149
  ruleStoreUnreadable?: RuleStoreUnreadableKind;
148
150
  }
149
151
  export type ApprovalCardPort = (req: ApprovalCardRequest) => Promise<ApprovalCardDecision>;
@@ -183,6 +185,8 @@ export interface ToolApprovalFrame {
183
185
  mandated?: true;
184
186
  mandate?: string;
185
187
  origin?: string;
188
+ askClass?: string;
189
+ classes?: readonly string[];
186
190
  inputHasBidi?: true;
187
191
  parked?: true;
188
192
  expiresInMs?: number;
@@ -200,7 +204,7 @@ export interface ToolApprovalDelegation {
200
204
  depth: number;
201
205
  agentName?: string;
202
206
  }
203
- export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleSuggestions", "ruleOffers", "ruleOffersAbsence", "denialLimitFallback", "ruleStoreUnreadable", "readRootCandidate", "mandated", "mandate", "origin", "inputHasBidi", "parked", "persistedRuleShadowed", "probeCause", "ruleEvidence", "requiresRealApproval", "expiresInMs", "expiresAtMs", "serverNowMs", "delegation", "outcome"];
207
+ export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleSuggestions", "ruleOffers", "ruleOffersAbsence", "denialLimitFallback", "ruleStoreUnreadable", "readRootCandidate", "mandated", "mandate", "origin", "askClass", "classes", "inputHasBidi", "parked", "persistedRuleShadowed", "probeCause", "ruleEvidence", "requiresRealApproval", "expiresInMs", "expiresAtMs", "serverNowMs", "delegation", "outcome"];
204
208
  export interface ApprovalMandatedFacts {
205
209
  readonly mandated?: true;
206
210
  readonly ruleOffersAbsence?: string;
@@ -210,7 +214,7 @@ export declare function approvalIsMandated(v: ApprovalMandatedFacts | undefined
210
214
  export declare function readApprovalMandate(v: {
211
215
  readonly mandate?: string;
212
216
  } | undefined | null): ApprovalMandateWord | undefined;
213
- export declare function toolPermissionRequestAskBits(card: object | null | undefined): Pick<ToolPermissionRequest, 'mandated' | 'mandate' | 'origin' | 'ruleOffersAbsence'>;
217
+ export declare function toolPermissionRequestAskBits(card: object | null | undefined): Pick<ToolPermissionRequest, 'mandated' | 'mandate' | 'origin' | 'ruleOffersAbsence' | 'askClass' | 'classes'>;
214
218
  export declare function isFromSubagent(frame: ToolApprovalFrame): boolean;
215
219
  export type ToolApprovalRespondDecision = 'allow' | 'allow_session' | 'deny';
216
220
  export interface RespondToolApprovalOpts {
@@ -210,6 +210,7 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
210
210
  ...(hasOwnStrictTrue(pending, 'mandated') ? { mandated: true } : {}),
211
211
  ...((w) => (w !== undefined ? { mandate: w } : {}))(readApprovalMandate(pending)),
212
212
  ...((o) => (o !== undefined ? { origin: o } : {}))(readOwnNonEmptyString(pending, 'origin')),
213
+ ...(readAskClassPair(pending) ?? {}),
213
214
  ...(pending.gateKind === 'irreversible_ask' ? { irreversibleParkGate: true } : {}),
214
215
  ...(pending.hasBidiControls === true ? { hasBidiControls: true } : {}),
215
216
  ...(ruleOffersReadOnly !== undefined ? { ruleOffersReadOnly } : {}),
@@ -386,6 +387,8 @@ export const TOOL_APPROVAL_FRAME_KEYS_MIRROR = [
386
387
  'mandated',
387
388
  'mandate',
388
389
  'origin',
390
+ 'askClass',
391
+ 'classes',
389
392
  'inputHasBidi',
390
393
  'parked',
391
394
  'persistedRuleShadowed',
@@ -425,13 +428,39 @@ export function toolPermissionRequestAskBits(card) {
425
428
  const mandate = readApprovalMandate(card);
426
429
  const origin = readOwnNonEmptyString(card, 'origin');
427
430
  const ruleOffersAbsence = readOwnNonEmptyString(card, 'ruleOffersAbsence');
431
+ const askClassPair = readAskClassPair(card);
428
432
  return {
429
433
  ...(mandated ? { mandated: true } : {}),
430
434
  ...(mandate !== undefined ? { mandate } : {}),
431
435
  ...(origin !== undefined ? { origin } : {}),
432
436
  ...(ruleOffersAbsence !== undefined ? { ruleOffersAbsence } : {}),
437
+ ...(askClassPair ?? {}),
433
438
  };
434
439
  }
440
+ function readAskClassPair(v) {
441
+ if (typeof v !== 'object' || v === null)
442
+ return undefined;
443
+ const askClass = readOwnNonEmptyString(v, 'askClass');
444
+ if (askClass === undefined || !Object.hasOwn(v, 'classes'))
445
+ return undefined;
446
+ const raw = v.classes;
447
+ if (!Array.isArray(raw) || raw.length === 0)
448
+ return undefined;
449
+ const classes = [];
450
+ const seen = new Set();
451
+ for (let i = 0; i < raw.length; i++) {
452
+ if (!Object.hasOwn(raw, i))
453
+ return undefined;
454
+ const c = raw[i];
455
+ if (typeof c !== 'string' || c === '' || seen.has(c))
456
+ return undefined;
457
+ seen.add(c);
458
+ classes.push(c);
459
+ }
460
+ if (classes[0] !== askClass)
461
+ return undefined;
462
+ return { askClass, classes: Object.freeze(classes) };
463
+ }
435
464
  function hasOwnStrictTrue(v, key) {
436
465
  return Object.hasOwn(v, key) && v[key] === true;
437
466
  }
@@ -870,6 +899,7 @@ export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamA
870
899
  ...(denialLimitFallback !== undefined ? { denialLimitFallback } : {}),
871
900
  ...((k) => (k !== undefined ? { ruleStoreUnreadable: k } : {}))(readRuleStoreUnreadable(frame.ruleStoreUnreadable)),
872
901
  ...((o) => (o !== undefined ? { origin: o } : {}))(readOwnNonEmptyString(frame, 'origin')),
902
+ ...(readAskClassPair(frame) ?? {}),
873
903
  });
874
904
  if (argsNotRealInput && card.kind === 'allow' && card.updatedInput !== undefined) {
875
905
  hostLog('error', `liveToolApprovalWire: ${approvalId} card returned an edited approval on a card whose arguments are not the tool's real input (${lane?.argsUnavailable === true ? 'args-unavailable ask' : 'arguments omitted or absent on the frame'}) — nothing sent (the ask stays pending)`);
@@ -1,8 +1,8 @@
1
- import { engineCapNestedTrue, engineCapState } from './engineCapsCache.js';
1
+ import { engineCapNestedTrue, engineCapState, engineCapValue } from './engineCapsCache.js';
2
2
  import { displayUntrusted } from './displayUntrusted.js';
3
3
  import { collapseLabel } from './fleetTaskDesc.js';
4
4
  import { hostLog, hostSettings } from './host.js';
5
- import { decidePluginHooks, dedupeSettingsHookGroups, filterSettingsHookGroups, occupancyOf, settingsHookDropText, } from './pluginHooksWire.js';
5
+ import { createSettingsHookJudge, decidePluginHooks, dedupeSettingsHookGroups, filterSettingsHookGroups, occupancyOf, settingsHookDropText, } from './pluginHooksWire.js';
6
6
  import { MAX_HOOK_NOTICE_TEXT_CHARS } from './notifications.js';
7
7
  import { goalStopHookMatcher, __resetGoalStopHookForTests } from './goalStopHook.js';
8
8
  export { GOAL_STOP_HOOK_WIRE_ENV, CC_STOP_SEMANTICS_MIN_SERVER, ccStopSemanticsFromVersion, engineCcStopSemantics, isGoalStopHookWireArmed, setWireSessionStopHook, getWireSessionStopHook, getWireSessionStopHookCcSemantics, buildGoalStopHookPrompt, } from './goalStopHook.js';
@@ -121,7 +121,7 @@ function readPluginHooks(settings, warnIfAbsent) {
121
121
  }
122
122
  return { state: 'absent' };
123
123
  }
124
- const NO_PLAN_ENGINE = { ownedByThisShell: null, pluginHooksCapable: null };
124
+ const NO_PLAN_ENGINE = { ownedByThisShell: null, pluginHooksCapable: null, hooksCapable: null };
125
125
  export function hooksForWire(opts = {}) {
126
126
  const given = opts?.plan;
127
127
  if (isHooksWirePlan(given))
@@ -131,7 +131,7 @@ export function hooksForWire(opts = {}) {
131
131
  }
132
132
  if (isHooksWirePlan(opts))
133
133
  return opts.wire;
134
- return buildHooksWirePlan(NO_PLAN_ENGINE, new Set(), false).wire;
134
+ return buildHooksWirePlan(NO_PLAN_ENGINE, () => false, false, true).wire;
135
135
  }
136
136
  function isHooksWirePlan(v) {
137
137
  try {
@@ -139,14 +139,17 @@ function isHooksWirePlan(v) {
139
139
  return false;
140
140
  const o = v;
141
141
  for (const k of PLAN_KEYS)
142
- if (!Object.hasOwn(o, k))
142
+ if (!Object.hasOwn(o, k) && k !== 'settingsVerdicts' && k !== 'settingsExcluded')
143
143
  return false;
144
144
  const reader = o.pluginReader;
145
145
  const engineFacts = o.engine;
146
146
  const wire = o.wire;
147
+ const arrayOrAbsent = (k) => !Object.hasOwn(o, k) || Array.isArray(o[k]);
147
148
  return (Array.isArray(o.verdicts) &&
148
149
  Array.isArray(o.excluded) &&
149
150
  Array.isArray(o.settingsDropped) &&
151
+ arrayOrAbsent('settingsVerdicts') &&
152
+ arrayOrAbsent('settingsExcluded') &&
150
153
  (reader === 'present' || reader === 'absent' || reader === 'failed') &&
151
154
  typeof engineFacts === 'object' &&
152
155
  engineFacts !== null &&
@@ -157,7 +160,7 @@ function isHooksWirePlan(v) {
157
160
  return false;
158
161
  }
159
162
  }
160
- const PLAN_KEYS = ['engine', 'pluginReader', 'verdicts', 'excluded', 'settingsDropped', 'wire'];
163
+ const PLAN_KEYS = ['engine', 'pluginReader', 'verdicts', 'excluded', 'settingsDropped', 'settingsVerdicts', 'settingsExcluded', 'wire'];
161
164
  const _planKeysPin = [true, true];
162
165
  void _planKeysPin;
163
166
  export function hooksWirePlan(opts = {}) {
@@ -168,6 +171,7 @@ export function hooksWirePlan(opts = {}) {
168
171
  pluginHooksCapable: engineCapState(o.baseUrl, 'taskSettings') === 'unprobed'
169
172
  ? null
170
173
  : engineCapNestedTrue(o.baseUrl, 'taskSettings', 'pluginHooks'),
174
+ hooksCapable: taskHooksCapability(o.baseUrl),
171
175
  };
172
176
  const shellEvents = new Set();
173
177
  if (Array.isArray(o.shellHookEvents)) {
@@ -175,12 +179,72 @@ export function hooksWirePlan(opts = {}) {
175
179
  if (typeof e === 'string')
176
180
  shellEvents.add(e);
177
181
  }
178
- return buildHooksWirePlan(engine, shellEvents, true);
182
+ const typesByEvent = shellHookTypesOf(o);
183
+ const shellRuns = (event, type) => {
184
+ if (!shellEvents.has(event))
185
+ return false;
186
+ const types = typesByEvent?.get(event);
187
+ return types === undefined || (typeof type === 'string' && types.has(type));
188
+ };
189
+ const settingsShellRuns = typesByEvent !== undefined ? shellRuns : (event, type) => type !== 'prompt' && type !== 'agent' && shellRuns(event, type);
190
+ return buildHooksWirePlan(engine, shellRuns, true, promptAuthoredByUserOf(o), settingsShellRuns);
191
+ }
192
+ function taskHooksCapability(baseUrl) {
193
+ const v = engineCapValue(baseUrl, 'taskSettings');
194
+ if (v.kind !== 'value')
195
+ return null;
196
+ if (v.value === false)
197
+ return false;
198
+ if (typeof v.value !== 'object' || v.value === null || Array.isArray(v.value))
199
+ return null;
200
+ const hooks = v.value.hooks;
201
+ return hooks === true ? true : hooks === false ? false : null;
202
+ }
203
+ function shellHookTypesOf(o) {
204
+ let raw;
205
+ try {
206
+ raw = o.shellHookTypesByEvent;
207
+ if (raw === undefined)
208
+ return undefined;
209
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
210
+ throw new TypeError('not a plain object');
211
+ const out = new Map();
212
+ for (const key of Object.keys(raw)) {
213
+ const types = raw[key];
214
+ if (!Array.isArray(types) || !types.every((t) => typeof t === 'string'))
215
+ throw new TypeError(`value for one event is not an array of strings`);
216
+ out.set(key, new Set(types));
217
+ }
218
+ return out;
219
+ }
220
+ catch (err) {
221
+ hostLog('debug', `hooksWirePlan: shellHookTypesByEvent is malformed — treated as absent (command and http hooks count as running on every reported event; prompt and agent hooks from settings stay with the engine): ${String(err)}`);
222
+ return undefined;
223
+ }
224
+ }
225
+ function promptAuthoredByUserOf(o) {
226
+ let v;
227
+ try {
228
+ v = o.promptAuthoredByUser;
229
+ }
230
+ catch (err) {
231
+ hostLog('debug', `hooksWirePlan: promptAuthoredByUser threw while being read — treated as written by the user: ${String(err)}`);
232
+ return true;
233
+ }
234
+ if (v === false)
235
+ return false;
236
+ if (v !== undefined && v !== true) {
237
+ hostLog('debug', 'hooksWirePlan: promptAuthoredByUser is not a boolean — treated as written by the user');
238
+ }
239
+ return true;
179
240
  }
180
- function buildHooksWirePlan(engine, shellEvents, withPlugins) {
241
+ function buildHooksWirePlan(engine, shellRuns, withPlugins, promptAuthoredByUser, settingsShellRuns = shellRuns) {
181
242
  const settingsDropped = [];
243
+ const settingsVerdicts = [];
244
+ const settingsExcluded = [];
245
+ const judge = withPlugins ? createSettingsHookJudge(engine, settingsShellRuns, promptAuthoredByUser) : undefined;
182
246
  let settingsStripped = 0;
183
- const planOf = (pluginReader, wire, verdicts = [], excluded = []) => ({ engine, pluginReader, verdicts, excluded, settingsDropped, wire });
247
+ const planOf = (pluginReader, wire, verdicts = [], excluded = []) => ({ engine, pluginReader, verdicts, excluded, settingsDropped, settingsVerdicts, settingsExcluded, wire });
184
248
  const closedPlan = (settings, reason) => {
185
249
  if (!withPlugins)
186
250
  return planOf('absent', undefined);
@@ -193,7 +257,8 @@ function buildHooksWirePlan(engine, shellEvents, withPlugins) {
193
257
  exclusion: reason,
194
258
  managedOnly: false,
195
259
  engine,
196
- shellHookEvents: shellEvents,
260
+ shellRuns,
261
+ promptAuthoredByUser,
197
262
  occupied: new Map(),
198
263
  }));
199
264
  if (d === undefined)
@@ -265,8 +330,10 @@ function buildHooksWirePlan(engine, shellEvents, withPlugins) {
265
330
  for (const [event, groups] of Object.entries(hooks)) {
266
331
  if (!Array.isArray(groups) || groups.length === 0)
267
332
  continue;
268
- const kept = filterSettingsHookGroups(source, event, groups, engine.pluginHooksCapable === true, shellEvents.has(event), !withPlugins);
333
+ const kept = filterSettingsHookGroups(source, event, groups, engine.pluginHooksCapable === true, (type) => shellRuns(event, type), !withPlugins, judge);
269
334
  settingsDropped.push(...kept.drops);
335
+ settingsVerdicts.push(...kept.verdicts);
336
+ settingsExcluded.push(...kept.excluded);
270
337
  settingsStripped += kept.stripped;
271
338
  if (kept.groups.length === 0)
272
339
  continue;
@@ -334,7 +401,8 @@ function buildHooksWirePlan(engine, shellEvents, withPlugins) {
334
401
  exclusion: startup ? 'startup_mode' : undefined,
335
402
  managedOnly: managedOnlyPlugins,
336
403
  engine,
337
- shellHookEvents: shellEvents,
404
+ shellRuns,
405
+ promptAuthoredByUser,
338
406
  occupied,
339
407
  });
340
408
  });
package/dist/index.d.ts CHANGED
@@ -40,6 +40,7 @@ export * from './deviceExecutorManagementCapability.js';
40
40
  export * from './mcpReconnect.js';
41
41
  export * from './leaderConflict.js';
42
42
  export * from './runTerminal.js';
43
+ export { providerRejectionResend, type ProviderRejectionResend } from './providerRejection.js';
43
44
  export * from './runCancelContext.js';
44
45
  export * from './argvFlagValue.js';
45
46
  export * from './readFacePosture.js';
@@ -66,7 +67,7 @@ export type { DisplayUntrustedOptions, DisplayMark, DisplayMarkSpan, DisplayUntr
66
67
  export type * from './types/engineState.js';
67
68
  export * from './hostEnv.js';
68
69
  export * from './unrefTimer.js';
69
- export * from './controlRouter.js';
70
+ export { ControlRouter, ControlSafetyError, errCodes, type ControlClientLike, type ControlRouterOptions, type ControlSafetyCode, type ErrCodes, type QueuedCommand, type QueuePriority, type RunsResourceLike, } from './controlRouter.js';
70
71
  export * from './sseIdleTriage.js';
71
72
  export * from './engineWireSdk.js';
72
73
  export * from './classifierVerdictWire.js';
@@ -111,6 +112,8 @@ export * from './adapter/downstream/terminalToSdkResult.js';
111
112
  export * from './adapter/runStream.js';
112
113
  export * from './adapter/activeRunSelfHeal.js';
113
114
  export * from './request/taskRequest.js';
115
+ export { disableSkillsDeclaration } from './disableSkillsWire.js';
116
+ export type { DisableSkillsDeclaration } from './disableSkillsWire.js';
114
117
  export * from './clientSlice.js';
115
118
  export * from './host.js';
116
119
  export { DEFAULT_SESSION_KEY } from './sessionSlot.js';
@@ -146,6 +149,10 @@ export * from './wireErrorTriage.js';
146
149
  export * from './corruptSession.js';
147
150
  export * from './corruptSessionCopy.js';
148
151
  export { classifyToolHistoryMismatch, resolveToolHistoryRewindTarget } from './toolHistoryMismatch.js';
152
+ export { classifyRegistryConflict, isScopeGoneError } from './registryConflict.js';
153
+ export type { RegistryConflictClass } from './registryConflict.js';
154
+ export { createPromptDeliveryLedger, promptKnownUndelivered, withoutUndeliveredPrompts } from './promptDelivery.js';
155
+ export type { PromptDeliveryLedger, PromptDeliveryRecord } from './promptDelivery.js';
149
156
  export * from './resumeRefusalCopy.js';
150
157
  export * from './wireRefusalCopy.js';
151
158
  export * from './inheritEnvWire.js';
@@ -155,7 +162,7 @@ export * from './detachWire.js';
155
162
  export * from './workflowMonitor.js';
156
163
  export * from './workflowClient.js';
157
164
  export * from './hooksWireCaps.js';
158
- export { ENGINE_FIRED_HOOK_EVENTS, PLUGIN_HOOK_DISPOSITIONS, PLUGIN_HOOK_REASONS, PLUGIN_HOOK_EXCLUSION_REASONS, SETTINGS_HOOK_DROP_REASONS, isEngineFiredHookEvent, pluginHookVerdictOf, pluginHookReasonText, settingsHookDropText, pluginHookExecutorLabel, hooksNotRunNotice, type EngineFiredHookEvent, type PluginHookDisposition, type PluginHookReason, type PluginHookExclusionReason, type SettingsHookDropReason, type HookSettingsSource, type PluginHookKey, type PluginHookVerdict, type PluginHookExclusion, type SettingsHookDrop, type HooksWireEngineFacts, type PluginHookReaderState, type HooksWirePlan, type HooksWirePlanOptions, } from './pluginHooksWire.js';
165
+ export { ENGINE_FIRED_HOOK_EVENTS, PLUGIN_HOOK_DISPOSITIONS, PLUGIN_HOOK_REASONS, PLUGIN_HOOK_EXCLUSION_REASONS, SETTINGS_HOOK_DROP_REASONS, isEngineFiredHookEvent, pluginHookVerdictOf, pluginHookReasonText, settingsHookDropText, pluginHookExecutorLabel, hooksNotRunNotice, settingsHooksKeptLocally, type EngineFiredHookEvent, type PluginHookDisposition, type PluginHookReason, type PluginHookExclusionReason, type SettingsHookDropReason, type HookSettingsSource, type PluginHookKey, type PluginHookVerdict, type PluginHookExclusion, type SettingsHookDrop, type SettingsHookVerdict, type SettingsHookExclusion, type HooksWireEngineFacts, type PluginHookReaderState, type HooksWirePlan, type HooksWirePlanOptions, } from './pluginHooksWire.js';
159
166
  export * from './liveInitToolFace.js';
160
167
  export * from './printInitToolFace.js';
161
168
  export * from './model/providerPresets.js';
@@ -193,11 +200,13 @@ export * from './request/printNotification.js';
193
200
  export * from './seatContract.js';
194
201
  export * from './agentSession/contract.js';
195
202
  export * from './agentSession/backgroundView.js';
196
- export { READ_CREDENTIAL_NOT_ACCEPTED_NOTICE } from './readCredentialRefusal.js';
203
+ export { READ_CREDENTIAL_NOT_ACCEPTED_NOTICE, isReadCredentialRefusal } from './readCredentialRefusal.js';
197
204
  export { readEngineAgentRegistry, isEngineAgentRegistryListingSuperseded } from './agentSession/engineAgentRegistryRead.js';
198
205
  export type { EngineAgentRegistryClient } from './agentSession/engineAgentRegistryRead.js';
199
206
  export { stopEngineSessionBackground, engineSessionBackgroundReceiptReadingOf, ENGINE_SESSION_BACKGROUND_RECEIPT_STATES, } from './agentSession/engineSessionBackgroundStop.js';
200
207
  export type { EngineSessionBackgroundStopClient, EngineSessionBackgroundStopWire, EngineSessionBackgroundStopOptions, EngineSessionBackgroundReceipt, EngineSessionBackgroundStopUnavailableWhy, EngineSessionBackgroundStopRefusedWhy, EngineSessionBackgroundStopResult, EngineSessionBackgroundReceiptState, EngineSessionBackgroundReceiptReading, } from './agentSession/engineSessionBackgroundStop.js';
208
+ export { eraseSessionMemory, sessionMemoryEraseOutcome, SESSION_MEMORY_ERASE_REFUSAL_REASONS, SESSION_MEMORY_ERASE_UNCONFIRMED_WHYS, SESSION_MEMORY_ERASE_RETRIES, } from './sessionMemoryErase.js';
209
+ export type { SessionMemoryEraseWire, SessionMemoryEraseCall, SessionMemoryEraseOptions, SessionMemoryEraseSent, SessionMemoryEraseNotSentWhy, SessionMemoryEraseJson, SessionMemoryEraseAnswer, SessionMemoryEraseRefusalReason, SessionMemoryEraseUnconfirmedWhy, SessionMemoryEraseRetry, SessionMemoryEraseOutcome, } from './sessionMemoryErase.js';
201
210
  export * from './model/catalog.js';
202
211
  export * from './model/catalogLoader.js';
203
212
  export * from './model/providerAuth.js';