@sema-agent/client-core 0.103.0 → 0.104.1

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 (45) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/README.md +18 -14
  3. package/dist/adapter/downstream/eventToSdkMessage.js +2 -0
  4. package/dist/adapter/downstream/terminalToSdkResult.d.ts +0 -1
  5. package/dist/adapter/downstream/terminalToSdkResult.js +3 -1
  6. package/dist/agentSession/engineAgentRegistryRead.js +3 -0
  7. package/dist/coreValuePorts.d.ts +3 -1
  8. package/dist/coreValuePorts.js +5 -0
  9. package/dist/deviceRebind.d.ts +17 -4
  10. package/dist/deviceRebind.js +43 -14
  11. package/dist/engineAgentAbsence.d.ts +1 -0
  12. package/dist/executionPlacementWords.d.ts +3 -0
  13. package/dist/executionPlacementWords.js +2 -0
  14. package/dist/generated/engineFactTables.d.ts +3 -2
  15. package/dist/generated/engineFactTables.js +17 -1
  16. package/dist/generated/engineNoticeTables.js +12 -0
  17. package/dist/hitl/automaticAnswerVerdict.js +47 -11
  18. package/dist/hitl/livePendingAsk.d.ts +2 -1
  19. package/dist/hitl/livePendingAsk.js +7 -1
  20. package/dist/hitl/sessionPolicyDeliverable.js +31 -5
  21. package/dist/hitl/toolApprovalWire.d.ts +5 -4
  22. package/dist/hitl/toolApprovalWire.js +215 -47
  23. package/dist/host.d.ts +0 -3
  24. package/dist/hostTurnSettings.js +3 -1
  25. package/dist/humanTurn.js +2 -2
  26. package/dist/index.d.ts +5 -2
  27. package/dist/index.js +1 -0
  28. package/dist/jsonFaithful.js +5 -3
  29. package/dist/mcpLiveness.d.ts +1 -1
  30. package/dist/mcpWireCaps.js +1 -1
  31. package/dist/printModelStatus.d.ts +1 -0
  32. package/dist/printModelStatus.js +6 -2
  33. package/dist/retryStatus.d.ts +8 -1
  34. package/dist/retryStatus.js +13 -4
  35. package/dist/ruleCompileVerdict.d.ts +1 -0
  36. package/dist/ruleCompileVerdict.js +3 -1
  37. package/dist/seatContract.d.ts +7 -7
  38. package/dist/seatContract.js +39 -10
  39. package/dist/sessionRowTag.d.ts +24 -0
  40. package/dist/sessionRowTag.js +77 -0
  41. package/dist/steering.d.ts +1 -0
  42. package/dist/steering.js +34 -26
  43. package/dist/systemReminderTag.js +14 -4
  44. package/docs/INTEGRATION-CLIENTS.md +346 -11
  45. package/package.json +5 -5
@@ -2,6 +2,7 @@ import { rosterMountedNames } from '../rosterMountedNames.js';
2
2
  import { msg, renderMessage } from '../i18n/translate.js';
3
3
  import { readOwnDataValue } from '../ownKey.js';
4
4
  import { denyEntryReading, ruleCompileVerdict } from '../ruleCompileVerdict.js';
5
+ import { hasCoreValuePort, ruleNameCovers } from '../coreValuePorts.js';
5
6
  import { ruleToolNameVerdictOf } from '../protocolSegment.js';
6
7
  import { PREFIXES_IN_PROSE, REFUSED_NAME_FIX } from './refusedToolNameProse.js';
7
8
  import { CORE_PROTOCOL_PREFIXES } from '../generated/toolNameTables.js';
@@ -13,7 +14,7 @@ const WITHHELD_NOTICE = {
13
14
  wildcard: 'Not written to this session’s rule record: this rule uses a wildcard (*), and an engine that matches that record by exact tool name would block none of the tools it covers.',
14
15
  not_a_tool_name: 'Not written to this session’s rule record: this entry is not exactly one tool name — it is empty, not text, has spaces around the name, is not a single tool name in rule syntax (for example it contains a backslash or a parenthesis, or is longer than a rule can be), or spells its server or agent peer part in a way the engine never uses when naming tools (for example with a dot or a space), so no tool would ever match it.',
15
16
  legacy_tool_name: `Not written to this session’s rule record: unless a tool by exactly this name is available, the engine refuses it — it is a retired tool name, or it contains "__" but does not start with ${PREFIXES_IN_PROSE} — and with it in that record every later run of this session would fail at startup until that entry is removed from the record. ${REFUSED_NAME_FIX}`,
16
- not_in_roster: 'Not written to this session’s rule record: the tool roster supplied for this check has no tool by exactly this name, as a name or an alias, with letter case counted — it may be misspelled, or name a tool this deployment does not provide — so recorded as it is it would block nothing here.',
17
+ not_in_roster: 'Not written to this session’s rule record: the tool roster supplied for this check has no tool this rule would block — no tool by exactly this name, as a name or an alias, with letter case counted, and, where the engine also reads the name as naming every tool of a server (a server whose own name contains "__"), no tool of that server either — it may be misspelled, or name a tool this deployment does not provide — so recorded as it is it would block nothing here.',
17
18
  namespace_covering_unconfirmed: msg('session.policy.withheld.namespaceCoveringUnconfirmed'),
18
19
  };
19
20
  export const SESSION_POLICY_WITHHELD_WHY = Object.freeze(Object.keys(WITHHELD_NOTICE));
@@ -151,11 +152,14 @@ export function sessionPolicyDeliverable(behavior, rules, opts) {
151
152
  why = 'not_a_tool_name';
152
153
  }
153
154
  let inertReason;
155
+ let coveringRow = false;
154
156
  if (engineAsked) {
155
157
  const reading = entryReadingOf(r);
156
158
  if (reading === 'unknown')
157
159
  return unreadable();
158
- if (reading !== 'row') {
160
+ if (reading === 'covering')
161
+ coveringRow = true;
162
+ else if (reading !== 'row') {
159
163
  inertReason = reading.reason;
160
164
  if (why === undefined)
161
165
  why = reading.why;
@@ -168,8 +172,13 @@ export function sessionPolicyDeliverable(behavior, rules, opts) {
168
172
  if (grammar === 'refused')
169
173
  why = 'not_a_tool_name';
170
174
  }
171
- if (why === undefined && mounted !== undefined && !mounted.has(r) && covering === undefined)
172
- why = 'not_in_roster';
175
+ if (why === undefined && mounted !== undefined && !mounted.has(r) && covering === undefined) {
176
+ const reach = coveringRow ? coveredRosterName(r, mounted) : 'none';
177
+ if (reach === 'unknown')
178
+ return unreadable();
179
+ if (reach === 'none')
180
+ why = 'not_in_roster';
181
+ }
173
182
  if (why === undefined)
174
183
  exact.push(r);
175
184
  else {
@@ -189,12 +198,29 @@ export function sessionPolicyDeliverable(behavior, rules, opts) {
189
198
  function entryReadingOf(rule) {
190
199
  const reading = denyEntryReading(rule);
191
200
  if (reading.kind === 'row')
192
- return 'row';
201
+ return reading.covering === true ? 'covering' : 'row';
193
202
  if (reading.kind === 'unknown')
194
203
  return 'unknown';
195
204
  const reason = reading.reason;
196
205
  return reason === 'grammar' || reason === 'content_unsupported' ? { why: 'not_a_tool_name', reason } : { why: 'wildcard', reason };
197
206
  }
207
+ function coveredRosterName(rule, mounted) {
208
+ if (!hasCoreValuePort('ruleNameCovers'))
209
+ return 'unknown';
210
+ try {
211
+ for (const name of mounted) {
212
+ const v = ruleNameCovers(rule, name);
213
+ if (v === true)
214
+ return 'covers';
215
+ if (v !== false)
216
+ return 'unknown';
217
+ }
218
+ return 'none';
219
+ }
220
+ catch {
221
+ return 'unknown';
222
+ }
223
+ }
198
224
  export function sessionPolicyWithheldNotice(why, lang) {
199
225
  if (typeof why === 'string' && Object.hasOwn(WITHHELD_NOTICE, why)) {
200
226
  const sentence = WITHHELD_NOTICE[why];
@@ -10,7 +10,7 @@ import { type ApprovalMandateWord } from '../gateVocabulary.js';
10
10
  import type { ToolPermissionRequest } from '../seatContract.js';
11
11
  import { type ApprovalDecidedByReading } from '../approvalDecidedByCapability.js';
12
12
  import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS } from '@sema-agent/sdk';
13
- import type { ApprovalDecidedBy, ApprovalCardDelegation, ToolApprovalDecision as SdkToolApprovalDecision, ToolApprovalRespondAck, ToolApprovalRespondReplayed, PersistedRuleAnchor, RuleOfferMatch, RuleOfferBatchMember, RuleOfferUncoveredDetail, RuleOfferUncoveredReason, AskOrigin, RuleOffersAbsence, DenialLimitKind, DenialLimitFallback, ToolApprovalFrame, RuleOffer as SdkRuleOffer } from '@sema-agent/sdk';
13
+ import type { ApprovalDecidedBy, ApprovalCardDelegation, ToolApprovalDecision as SdkToolApprovalDecision, ToolApprovalRespondAck, ToolApprovalRespondReplayed, PersistedRuleAnchor, RuleOfferMatch, RuleOfferBatchMember, RuleOfferUncoveredDetail, RuleOfferUncoveredReason, AskOrigin, RuleOffersAbsence, DenialLimitKind, DenialLimitFallback, ToolApprovalFrame, ApprovalRequestFrame, RuleOffer as SdkRuleOffer } from '@sema-agent/sdk';
14
14
  export declare function toolNameIsFsWrite(name: string): boolean;
15
15
  export declare function toolNameIsShellExec(name: string): boolean;
16
16
  export interface ToolApprovalGateLike {
@@ -97,6 +97,7 @@ export interface ApprovalCardRequest {
97
97
  };
98
98
  wireNote?: string;
99
99
  argsUnavailable?: true;
100
+ argsRewritten?: true;
100
101
  governanceForced?: true;
101
102
  delegation?: ToolApprovalDelegation;
102
103
  ruleOffers?: RuleOfferReading[];
@@ -178,7 +179,7 @@ export interface ReadRootCandidateView {
178
179
  covers?: 'exact';
179
180
  }
180
181
  export type ToolApprovalDelegation = ApprovalCardDelegation;
181
- export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleOffers", "ruleOffersAbsence", "denialLimitFallback", "readRootCandidate", "mandated", "mandate", "origin", "approvalCard", "askClass", "classes", "inputHasBidi", "parked", "persistedRuleShadowed", "allowRuleShadowed", "probeCause", "probeReason", "rememberRefused", "boundGrantDigest", "boundGrantAbsence", "boundGrants", "classifierNoRuling", "ruleEvidence", "requiresRealApproval", "expiresInMs", "expiresAtMs", "serverNowMs", "delegation", "outcome", "cause"];
182
+ export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleOffers", "ruleOffersAbsence", "denialLimitFallback", "readRootCandidate", "mandated", "mandate", "origin", "approvalCard", "askClass", "classes", "inputHasBidi", "parked", "persistedRuleShadowed", "allowRuleShadowed", "probeCause", "probeReason", "rememberRefused", "boundGrantDigest", "boundGrantAbsence", "boundGrants", "classifierNoRuling", "ruleEvidence", "requiresRealApproval", "expiresInMs", "expiresAtMs", "serverNowMs", "delegation", "outcome", "cause", "requestedBy"];
182
183
  export interface ApprovalMandatedFacts {
183
184
  readonly mandated?: true;
184
185
  readonly ruleOffersAbsence?: string;
@@ -276,7 +277,7 @@ export declare function readDenialLimitFallback(v: unknown): DenialLimitFallback
276
277
  export declare function readRuleOffers(v: unknown): RuleOfferReading[] | undefined;
277
278
  export declare function readRuleOfferSupply(offers: unknown): RuleOfferReading[] | undefined;
278
279
  export type ApprovalCardV1Facts = Pick<ApprovalCardRequest, 'toolName' | 'args'> & Pick<ApprovalCardRequest, 'toolCallId' | 'workerBadge' | 'wireNote' | 'argsUnavailable' | 'governanceForced' | 'delegation' | 'ruleOffersReadOnly' | 'inputHasBidi' | 'allowRuleShadow' | 'probeCause' | 'rememberRefused' | 'ruleEvidence' | 'requiresRealApproval' | 'ruleOffersAbsence' | 'denialLimitFallback' | 'askClass' | 'classes' | 'grantBinding' | 'unreadableEvidence' | 'message' | 'approvalCard'>;
279
- export type ApprovalCardFacts = ApprovalCardV1Facts & Pick<ApprovalCardRequest, 'ruleOffers' | 'probeReason' | 'mandated' | 'mandate' | 'readRootCandidate' | 'origin' | 'boundGrants' | 'classifierNoRuling' | 'allowSessionCarried' | 'cardPayloadUnreadable'>;
280
+ export type ApprovalCardFacts = ApprovalCardV1Facts & Pick<ApprovalCardRequest, 'argsRewritten' | 'ruleOffers' | 'probeReason' | 'mandated' | 'mandate' | 'readRootCandidate' | 'origin' | 'boundGrants' | 'classifierNoRuling' | 'allowSessionCarried' | 'cardPayloadUnreadable'>;
280
281
  export interface ApprovalCardFrameFactsOptions {
281
282
  readonly streamArgs?: object;
282
283
  readonly argsUnavailable?: true;
@@ -292,7 +293,7 @@ export interface ToolApprovalFrameLaneOpts {
292
293
  windowIsCurrent?: boolean;
293
294
  argsUnavailable?: true;
294
295
  }
295
- export declare function surfaceToolApprovalFrameAndRespond(frame: ToolApprovalFrame, respond: RespondToolApprovalFn, streamArgs: unknown | undefined, signal?: AbortSignal, lane?: ToolApprovalFrameLaneOpts, run?: {
296
+ export declare function surfaceToolApprovalFrameAndRespond(frame: ToolApprovalFrame | ApprovalRequestFrame, respond: RespondToolApprovalFn, streamArgs: unknown | undefined, signal?: AbortSignal, lane?: ToolApprovalFrameLaneOpts, run?: {
296
297
  readonly taskId?: string;
297
298
  readonly sessionId?: string;
298
299
  }): Promise<ApprovalResolution>;
@@ -28,9 +28,10 @@ import { isApprovalMandateWord } from '../gateVocabulary.js';
28
28
  import { approvalDecidedByAdmitted } from '../approvalDecidedByCapability.js';
29
29
  import { decideFailureNote } from '../decideFailureNote.js';
30
30
  import { slotPresenceOf, wireFailureTextsOf } from '../wireFailureShape.js';
31
+ import { isPlainRecord } from '../jsonFaithful.js';
31
32
  import { DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED } from '../engineErrorCodes.js';
32
33
  import { readDecideReceipt } from '../decideReceipt.js';
33
- import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS, ASK_ORIGINS, } from '@sema-agent/sdk';
34
+ import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS, ASK_ORIGINS, isApprovalRequestFrameV1, } from '@sema-agent/sdk';
34
35
  export function toolNameIsFsWrite(name) {
35
36
  const n = name.replace(/[\s_-]+/g, '').toLowerCase();
36
37
  return (n === 'write' || n === 'filewrite' || n === 'edit' || n === 'fileedit' || n === 'multiedit' || n === 'notebookedit');
@@ -193,6 +194,11 @@ const PARK_ROW_ARGS_ABSENT_NOTE = "this request's tool arguments are not availab
193
194
  const PARK_ROW_ARGS_TRUNCATED_NOTE = "this request's tool arguments were too large to keep on the pending approval record and were replaced by a size marker, " +
194
195
  'so you are deciding without seeing them; deny it if you are not sure what it will do. ' +
195
196
  'Edits are not accepted on this card: there is no original input to edit';
197
+ const ARGS_REWRITTEN_NOTE = "this request's tool arguments were changed after the model made the call (for example by a hook) — " +
198
+ 'the arguments shown here are the changed ones recorded for this approval, and they differ from the call shown in the conversation';
199
+ const ARGS_FROM_MODEL_CALL_NOTE = 'this approval request did not carry usable tool arguments (for example because they were too large to include), ' +
200
+ "so the arguments shown are the model's original call from the conversation — " +
201
+ 'if they were changed after that call (for example by a hook), what runs differs from what is shown';
196
202
  const PARK_EDIT_REFUSED_REASON = "the card returned an edited approval, but this request's tool arguments are not available on this surface " +
197
203
  '(missing, or replaced by a size marker on the pending approval record), so the edit cannot be applied — nothing was sent; ' +
198
204
  'approve or deny it as it is';
@@ -205,6 +211,139 @@ function isParkRowInputSizeMarker(v) {
205
211
  const o = v;
206
212
  return o.truncated === true && typeof o.bytes === 'number' && Number.isFinite(o.bytes) && o.bytes >= 0;
207
213
  }
214
+ function recordedInputShapeOf(v) {
215
+ if (v === undefined || v === null)
216
+ return 'absent';
217
+ try {
218
+ if (typeof v !== 'object' || !isPlainRecord(v))
219
+ return 'other';
220
+ return isParkRowInputSizeMarker(v) ? 'size_marker' : 'input';
221
+ }
222
+ catch {
223
+ return 'other';
224
+ }
225
+ }
226
+ function argsProvablyDiffer(recorded, streamArgs) {
227
+ try {
228
+ if (typeof streamArgs !== 'object' || streamArgs === null || Array.isArray(streamArgs))
229
+ return false;
230
+ return jsonValuesDiffer(recorded, streamArgs, 0, { nodes: 0 }) === true;
231
+ }
232
+ catch {
233
+ return false;
234
+ }
235
+ }
236
+ function cardInputOf(recorded, stream) {
237
+ const shape = recordedInputShapeOf(recorded);
238
+ const streamPresent = stream !== undefined && stream !== null;
239
+ if (shape === 'input')
240
+ return { from: 'recorded', args: recorded, rewritten: streamPresent && argsProvablyDiffer(recorded, stream) };
241
+ if (streamPresent)
242
+ return { from: 'stream', args: stream, shape };
243
+ return { from: 'none', shape, recorded };
244
+ }
245
+ const CARD_ARGS_COMPARE_MAX_NODES = 20_000;
246
+ const CARD_ARGS_COMPARE_MAX_DEPTH = 256;
247
+ const NOT_COMPARABLE = Symbol('not comparable');
248
+ function ownDataForCompare(o, k) {
249
+ const d = Object.getOwnPropertyDescriptor(o, k);
250
+ return d !== undefined && Object.hasOwn(d, 'value') ? d.value : NOT_COMPARABLE;
251
+ }
252
+ const TO_JSON_CHAIN_MAX = 32;
253
+ function hasToJsonHook(o) {
254
+ let at = o;
255
+ for (let depth = 0; at !== null; depth++) {
256
+ if (depth >= TO_JSON_CHAIN_MAX)
257
+ return true;
258
+ const d = Object.getOwnPropertyDescriptor(at, 'toJSON');
259
+ if (d !== undefined)
260
+ return !Object.hasOwn(d, 'value') || typeof d.value === 'function';
261
+ at = Object.getPrototypeOf(at);
262
+ }
263
+ return false;
264
+ }
265
+ function jsonValueKindOf(v) {
266
+ if (v === null)
267
+ return 'null';
268
+ switch (typeof v) {
269
+ case 'boolean':
270
+ return 'boolean';
271
+ case 'string':
272
+ return 'string';
273
+ case 'number':
274
+ return Number.isFinite(v) ? 'number' : undefined;
275
+ case 'object':
276
+ if (Array.isArray(v))
277
+ return hasToJsonHook(v) ? undefined : 'array';
278
+ return isPlainRecord(v) && !hasToJsonHook(v) ? 'object' : undefined;
279
+ default:
280
+ return undefined;
281
+ }
282
+ }
283
+ function jsonValuesDiffer(a, b, depth, budget) {
284
+ if (++budget.nodes > CARD_ARGS_COMPARE_MAX_NODES || depth > CARD_ARGS_COMPARE_MAX_DEPTH)
285
+ return undefined;
286
+ const ka = jsonValueKindOf(a);
287
+ const kb = jsonValueKindOf(b);
288
+ if (ka === undefined || kb === undefined)
289
+ return undefined;
290
+ if (ka !== kb)
291
+ return true;
292
+ if (ka === 'array') {
293
+ const xa = a;
294
+ const xb = b;
295
+ const la = ownDataForCompare(xa, 'length');
296
+ const lb = ownDataForCompare(xb, 'length');
297
+ if (typeof la !== 'number' || typeof lb !== 'number')
298
+ return undefined;
299
+ if (la !== lb)
300
+ return true;
301
+ for (let i = 0; i < la; i++) {
302
+ const va = ownDataForCompare(xa, String(i));
303
+ const vb = ownDataForCompare(xb, String(i));
304
+ if (va === NOT_COMPARABLE || vb === NOT_COMPARABLE)
305
+ return undefined;
306
+ const d = jsonValuesDiffer(va, vb, depth + 1, budget);
307
+ if (d !== false)
308
+ return d;
309
+ }
310
+ return false;
311
+ }
312
+ if (ka === 'object') {
313
+ const ra = a;
314
+ const rb = b;
315
+ const keysA = Object.keys(ra);
316
+ const keysB = Object.keys(rb);
317
+ budget.nodes += keysA.length + keysB.length;
318
+ if (budget.nodes > CARD_ARGS_COMPARE_MAX_NODES)
319
+ return undefined;
320
+ const inA = new Set(keysA);
321
+ const inB = new Set(keysB);
322
+ for (const k of keysA) {
323
+ const va = ownDataForCompare(ra, k);
324
+ if (va === NOT_COMPARABLE)
325
+ return undefined;
326
+ if (!inB.has(k))
327
+ return jsonValueKindOf(va) === undefined ? undefined : true;
328
+ const vb = ownDataForCompare(rb, k);
329
+ if (vb === NOT_COMPARABLE)
330
+ return undefined;
331
+ const d = jsonValuesDiffer(va, vb, depth + 1, budget);
332
+ if (d !== false)
333
+ return d;
334
+ }
335
+ for (const k of keysB) {
336
+ if (inA.has(k))
337
+ continue;
338
+ const vb = ownDataForCompare(rb, k);
339
+ if (vb === NOT_COMPARABLE)
340
+ return undefined;
341
+ return jsonValueKindOf(vb) === undefined ? undefined : true;
342
+ }
343
+ return false;
344
+ }
345
+ return a === b ? false : true;
346
+ }
208
347
  function durableHoldKey(row, callKey) {
209
348
  const createdAt = typeof row.createdAt === 'number' && Number.isFinite(row.createdAt) ? row.createdAt : null;
210
349
  return JSON.stringify([typeof row.sessionId === 'string' ? row.sessionId : null, typeof row.taskId === 'string' ? row.taskId : null, callKey, createdAt]);
@@ -331,7 +470,7 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
331
470
  });
332
471
  }
333
472
  function parkRowHolderIdentity(row, gatedCallId) {
334
- const askSig = carrierAskSig(row);
473
+ const askSig = carrierAskSig(row, row);
335
474
  return {
336
475
  ...(typeof row.taskId === 'string' && row.taskId !== '' ? { taskId: row.taskId } : {}),
337
476
  ...(gatedCallId !== undefined && gatedCallId !== '' ? { toolCallId: gatedCallId } : {}),
@@ -344,15 +483,10 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
344
483
  const at = { lane: 'decide', ...(gatedCallId !== undefined ? { gatedCallId } : {}) };
345
484
  const toolName = typeof pending.toolName === 'string' ? pending.toolName : '';
346
485
  const streamArgs = gatedCallId !== undefined ? argsByCall.get(gatedCallId) : undefined;
347
- const rowInput = pending.input;
348
- const rowArgsGap = streamArgs !== undefined && streamArgs !== null
349
- ? undefined
350
- : rowInput === undefined || rowInput === null
351
- ? 'absent'
352
- : isParkRowInputSizeMarker(rowInput)
353
- ? 'truncated'
354
- : undefined;
355
- const args = streamArgs !== undefined && streamArgs !== null ? streamArgs : rowArgsGap !== undefined ? {} : rowInput;
486
+ const reading = cardInputOf(pending.input, streamArgs);
487
+ const rowArgsGap = reading.from !== 'none' ? undefined : reading.shape === 'absent' ? 'absent' : reading.shape === 'size_marker' ? 'truncated' : undefined;
488
+ const args = reading.from !== 'none' ? reading.args : rowArgsGap !== undefined ? {} : reading.recorded;
489
+ const argsRewritten = reading.from === 'recorded' && reading.rewritten;
356
490
  if (typeof args !== 'object' || args === null) {
357
491
  return { kind: 'not_sent', ...at, cause: 'no_input', detail: 'gate has no tool input payload' };
358
492
  }
@@ -375,6 +509,7 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
375
509
  wireNote: rowArgsGap === 'truncated' ? PARK_ROW_ARGS_TRUNCATED_NOTE : PARK_ROW_ARGS_ABSENT_NOTE,
376
510
  }
377
511
  : {}),
512
+ ...(argsRewritten ? { argsRewritten: true, wireNote: ARGS_REWRITTEN_NOTE } : {}),
378
513
  ...(pending.governanceForced === true ? { governanceForced: true } : {}),
379
514
  ...(readRequiresRealApproval(pending) ? { requiresRealApproval: true } : {}),
380
515
  ...(hasOwnStrictTrue(pending, 'mandated') ? { mandated: true } : {}),
@@ -583,6 +718,7 @@ export const TOOL_APPROVAL_FRAME_KEYS_MIRROR = [
583
718
  'delegation',
584
719
  'outcome',
585
720
  'cause',
721
+ 'requestedBy',
586
722
  ];
587
723
  const _mirrorPin = [true];
588
724
  void _mirrorPin;
@@ -1074,21 +1210,23 @@ function serverMayReadCallLaw(carrier) {
1074
1210
  const UNREADABLE_EVIDENCE_KEYS = Object.freeze({
1075
1211
  frame: Object.freeze([
1076
1212
  ['rememberRefused', 'rememberRefused'], ['governanceForced', 'governanceForced'], ['requiresRealApproval', 'requiresRealApproval'],
1077
- ['mandated', 'mandated'], ['mandate', 'mandate'], ['origin', 'origin'], ['ruleOffersAbsence', 'ruleOffersAbsence'], ['ruleOffers', 'ruleOffers'],
1213
+ ['ruleOffersAbsence', 'ruleOffersAbsence'], ['ruleOffers', 'ruleOffers'],
1078
1214
  ['allowRuleShadowed', 'allowRuleShadow'], ['askClass', 'askClass'], ['classes', 'classes'],
1079
1215
  ]),
1216
+ live: Object.freeze([['mandated', 'mandated'], ['mandate', 'mandate'], ['origin', 'origin']]),
1080
1217
  row: Object.freeze([
1081
1218
  ['rememberRefused', 'rememberRefused'], ['governanceForced', 'governanceForced'], ['requiresRealApproval', 'requiresRealApproval'],
1082
1219
  ['mandated', 'mandated'], ['mandate', 'mandate'], ['origin', 'origin'], ['ruleOffersAbsence', 'ruleOffersAbsence'], ['ruleOffers', 'ruleOffersReadOnly'],
1083
1220
  ['askClass', 'askClass'], ['classes', 'classes'],
1084
1221
  ]),
1085
1222
  v1: Object.freeze([
1086
- ['rememberRefused', 'rememberRefused'], ['governanceForced', 'governanceForced'], ['ruleOffersAbsence', 'ruleOffersAbsence'], ['ruleOffers', 'ruleOffersReadOnly'],
1223
+ ['rememberRefused', 'rememberRefused'], ['governanceForced', 'governanceForced'], ['requiresRealApproval', 'requiresRealApproval'],
1224
+ ['ruleOffersAbsence', 'ruleOffersAbsence'], ['ruleOffers', 'ruleOffersReadOnly'],
1087
1225
  ['allowRuleShadowed', 'allowRuleShadow'], ['askClass', 'askClass'], ['classes', 'classes'],
1088
1226
  ]),
1089
1227
  generic: Object.freeze([['ruleOffersAbsence', 'ruleOffersAbsence'], ['askClass', 'askClass'], ['classes', 'classes']]),
1090
1228
  });
1091
- function v1RiskUnreadable(card) {
1229
+ function riskSubtreeUnreadable(card) {
1092
1230
  if (slotPresenceOf(card, 'risk') === 'absent')
1093
1231
  return false;
1094
1232
  try {
@@ -1099,12 +1237,18 @@ function v1RiskUnreadable(card) {
1099
1237
  return true;
1100
1238
  }
1101
1239
  }
1102
- function withUnreadableEvidence(carrier, leg, req, reported = []) {
1240
+ function withUnreadableEvidence(carrier, leg, req, reported = [], live) {
1103
1241
  const keys = new Set(reported);
1104
1242
  for (const [from, to] of UNREADABLE_EVIDENCE_KEYS[leg]) {
1105
1243
  if (slotPresenceOf(carrier, from) !== 'absent' && !Object.hasOwn(req, to))
1106
1244
  keys.add(from);
1107
1245
  }
1246
+ if (live !== undefined) {
1247
+ for (const [from, to] of UNREADABLE_EVIDENCE_KEYS.live) {
1248
+ if (slotPresenceOf(live, from) !== 'absent' && !Object.hasOwn(req, to))
1249
+ keys.add(from);
1250
+ }
1251
+ }
1108
1252
  if (leg === 'row') {
1109
1253
  const rd = ownDataValue(carrier, 'riskDescriptor');
1110
1254
  if (typeof rd === 'object' && rd !== null && slotPresenceOf(rd, 'allowRuleShadowed') !== 'absent' && !Object.hasOwn(req, 'allowRuleShadow'))
@@ -1115,7 +1259,7 @@ function withUnreadableEvidence(carrier, leg, req, reported = []) {
1115
1259
  if (retiredAllowRuleShadowKeyPresent(carrier, shadowLeg))
1116
1260
  keys.add(RETIRED_SHADOW_EVIDENCE_LABEL[shadowLeg]);
1117
1261
  }
1118
- if (leg === 'v1' && v1RiskUnreadable(carrier))
1262
+ if ((leg === 'v1' || leg === 'frame') && riskSubtreeUnreadable(carrier))
1119
1263
  keys.add('risk');
1120
1264
  return keys.size === 0 ? req : { ...req, unreadableEvidence: Object.freeze([...keys].sort()) };
1121
1265
  }
@@ -1174,9 +1318,9 @@ function askSignatureOf(req) {
1174
1318
  function askSigOf(classes, origin, mandate) {
1175
1319
  return JSON.stringify([classes !== undefined ? [...classes] : null, origin ?? null, mandate ?? null]);
1176
1320
  }
1177
- function carrierAskSig(v) {
1321
+ function carrierAskSig(v, live) {
1178
1322
  try {
1179
- return askSigOf(readAskClassPair(v)?.classes, readAskOrigin(v), readApprovalMandate(v));
1323
+ return askSigOf(readAskClassPair(v)?.classes, live === undefined ? undefined : readAskOrigin(live), live === undefined ? undefined : readApprovalMandate(live));
1180
1324
  }
1181
1325
  catch {
1182
1326
  return undefined;
@@ -1487,9 +1631,10 @@ function v1Bit(stamp) {
1487
1631
  const SUSPENDED_ASK_ARGS_NOTE = "this request's tool arguments are not available on this surface — it was raised while no client was connected, " +
1488
1632
  'so you are deciding without seeing them; deny it if you are not sure what it will do. ' +
1489
1633
  'Edits are not accepted on this card: there is no original input to edit';
1490
- function cardFactsOf(carrier, leg, opts) {
1634
+ function cardFactsOf(carrier, leg, opts, liveFacts) {
1491
1635
  const c = carrier;
1492
1636
  const full = leg !== 'generic';
1637
+ const live = leg === 'frame' ? carrier : leg === 'v1' ? liveFacts : undefined;
1493
1638
  const asFrame = carrier;
1494
1639
  const fromSubagent = full
1495
1640
  ? (() => {
@@ -1505,14 +1650,23 @@ function cardFactsOf(carrier, leg, opts) {
1505
1650
  try {
1506
1651
  let stream;
1507
1652
  let blind = false;
1508
- if (leg === 'frame' && typeof opts === 'object' && opts !== null) {
1653
+ if (typeof opts === 'object' && opts !== null) {
1509
1654
  stream = opts.streamArgs;
1510
1655
  blind = opts.argsUnavailable === true;
1511
1656
  }
1512
- const raw = stream !== undefined && stream !== null ? stream : c.args !== undefined && c.args !== null ? c.args : undefined;
1513
- if (typeof raw === 'object' && raw !== null)
1514
- return blind ? { args: raw, argsUnavailable: true } : { args: raw };
1515
- const fallback = argsFallbackOf(c.message, c.argsOmitted);
1657
+ const reading = cardInputOf(c.args, stream);
1658
+ if (reading.from === 'recorded') {
1659
+ if (blind)
1660
+ return { args: reading.args, argsUnavailable: true };
1661
+ return reading.rewritten ? { args: reading.args, argsRewritten: true, wireNote: ARGS_REWRITTEN_NOTE } : { args: reading.args };
1662
+ }
1663
+ if (reading.from === 'stream' && typeof reading.args === 'object' && reading.args !== null) {
1664
+ if (blind)
1665
+ return { args: reading.args, argsUnavailable: true };
1666
+ return { args: reading.args, wireNote: ARGS_FROM_MODEL_CALL_NOTE };
1667
+ }
1668
+ const omitted = c.argsOmitted === true || reading.shape === 'size_marker';
1669
+ const fallback = argsFallbackOf(c.message, omitted);
1516
1670
  const note = blind ? SUSPENDED_ASK_ARGS_NOTE : fallback.omittedNote;
1517
1671
  return { args: fallback.args, argsUnavailable: true, ...(note !== undefined ? { wireNote: note } : {}) };
1518
1672
  }
@@ -1538,6 +1692,8 @@ function cardFactsOf(carrier, leg, opts) {
1538
1692
  out.wireNote = argsPart.wireNote;
1539
1693
  if (argsPart.argsUnavailable === true)
1540
1694
  out.argsUnavailable = true;
1695
+ if (argsPart.argsRewritten === true)
1696
+ out.argsRewritten = true;
1541
1697
  if (full) {
1542
1698
  v1Bit(() => {
1543
1699
  if (c.governanceForced === true)
@@ -1567,9 +1723,9 @@ function cardFactsOf(carrier, leg, opts) {
1567
1723
  if (isWireRecordCarrier(pc))
1568
1724
  out.probeCause = pc;
1569
1725
  });
1570
- if (leg === 'frame') {
1726
+ if (live !== undefined) {
1571
1727
  v1Bit(() => {
1572
- const r = readProbeReason(carrier);
1728
+ const r = readProbeReason(live);
1573
1729
  if (r !== undefined)
1574
1730
  out.probeReason = r;
1575
1731
  });
@@ -1590,18 +1746,18 @@ function cardFactsOf(carrier, leg, opts) {
1590
1746
  catch {
1591
1747
  realApprovalUnreadable = true;
1592
1748
  }
1593
- if (leg === 'frame') {
1749
+ if (live !== undefined) {
1594
1750
  v1Bit(() => {
1595
- if (hasOwnStrictTrue(carrier, 'mandated'))
1751
+ if (hasOwnStrictTrue(live, 'mandated'))
1596
1752
  out.mandated = true;
1597
1753
  });
1598
1754
  v1Bit(() => {
1599
- const w = readApprovalMandate(carrier);
1755
+ const w = readApprovalMandate(live);
1600
1756
  if (w !== undefined)
1601
1757
  out.mandate = w;
1602
1758
  });
1603
1759
  v1Bit(() => {
1604
- const rc = readReadRootCandidate(c.readRootCandidate);
1760
+ const rc = readReadRootCandidate(live.readRootCandidate);
1605
1761
  if (rc !== undefined)
1606
1762
  out.readRootCandidate = rc;
1607
1763
  });
@@ -1621,9 +1777,9 @@ function cardFactsOf(carrier, leg, opts) {
1621
1777
  if (dlf !== undefined)
1622
1778
  out.denialLimitFallback = dlf;
1623
1779
  });
1624
- if (leg === 'frame') {
1780
+ if (live !== undefined) {
1625
1781
  v1Bit(() => {
1626
- const o = readAskOrigin(carrier);
1782
+ const o = readAskOrigin(live);
1627
1783
  if (o !== undefined)
1628
1784
  out.origin = o;
1629
1785
  });
@@ -1642,14 +1798,14 @@ function cardFactsOf(carrier, leg, opts) {
1642
1798
  out.classes = pair.classes;
1643
1799
  }
1644
1800
  });
1645
- if (leg === 'frame') {
1801
+ if (live !== undefined) {
1646
1802
  v1Bit(() => {
1647
- const g = readApprovalBoundGrants(carrier);
1803
+ const g = readApprovalBoundGrants(live);
1648
1804
  if (g !== undefined)
1649
1805
  out.boundGrants = g;
1650
1806
  });
1651
1807
  v1Bit(() => {
1652
- const w = readApprovalClassifierNoRuling(carrier);
1808
+ const w = readApprovalClassifierNoRuling(live);
1653
1809
  if (w !== undefined)
1654
1810
  out.classifierNoRuling = w;
1655
1811
  });
@@ -1664,7 +1820,7 @@ function cardFactsOf(carrier, leg, opts) {
1664
1820
  if (leg === 'frame')
1665
1821
  out.allowSessionCarried = true;
1666
1822
  out.grantBinding = readApprovalGrantBinding(carrier);
1667
- return withUnreadableEvidence(carrier, leg, out, realApprovalUnreadable ? ['requiresRealApproval'] : []);
1823
+ return withUnreadableEvidence(carrier, leg, out, realApprovalUnreadable ? ['requiresRealApproval'] : [], live);
1668
1824
  }
1669
1825
  export function approvalCardFactsOfV1(card) {
1670
1826
  if (typeof card !== 'object' || card === null)
@@ -1685,9 +1841,19 @@ const MAX_RESPOND_NOTE_CHARS = 2048;
1685
1841
  export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamArgs, signal, lane, run) {
1686
1842
  const receivedAtMs = Date.now();
1687
1843
  const approvalId = frame.approvalId;
1688
- const entry = { frame, approvalId, identity: liveCardIdentityOf(approvalId, frame, run), respond, streamArgs, signal, lane, receivedAtMs };
1844
+ const carriers = openingFrameCarriersOf(frame);
1845
+ const entry = { frame, carriers, approvalId, identity: liveCardIdentityOf(approvalId, frame, carriers, run), respond, streamArgs, signal, lane, receivedAtMs };
1689
1846
  return enterLiveCard(entry);
1690
1847
  }
1848
+ function openingFrameCarriersOf(frame) {
1849
+ if (frame.type !== 'approval_request')
1850
+ return { body: frame, live: frame, leg: 'frame' };
1851
+ const card = frame.card;
1852
+ const pinned = Object.create(frame, { card: { value: card, enumerable: true } });
1853
+ if (isApprovalRequestFrameV1(pinned))
1854
+ return { body: pinned.card, live: frame, leg: 'v1' };
1855
+ return { body: typeof card === 'object' && card !== null ? card : {}, live: undefined, leg: 'generic' };
1856
+ }
1691
1857
  function enterLiveCard(e) {
1692
1858
  const { approvalId, signal } = e;
1693
1859
  const held = liveCardHoldOf(liveFrameCallKey(approvalId));
@@ -1701,7 +1867,7 @@ function enterLiveCard(e) {
1701
1867
  return joinOtherLaneHolder(e, other);
1702
1868
  return holdLiveCard(e.identity, signal, async (hold) => {
1703
1869
  const slot = {};
1704
- const out = await presentToolApprovalFrameAndRespond(e.frame, approvalId, e.respond, e.streamArgs, signal, e.lane, hold, slot, e.receivedAtMs);
1870
+ const out = await presentToolApprovalFrameAndRespond(e.frame, e.carriers, approvalId, e.respond, e.streamArgs, signal, e.lane, hold, slot, e.receivedAtMs);
1705
1871
  noteCallOutcome(slot, out);
1706
1872
  return out;
1707
1873
  });
@@ -1716,15 +1882,16 @@ async function joinOtherLaneHolder(e, other) {
1716
1882
  return { kind: 'not_sent', lane: 'respond', cause: 'retracted', joinedInFlight: true };
1717
1883
  return enterLiveCard(e);
1718
1884
  }
1719
- function liveCardIdentityOf(approvalId, frame, run) {
1720
- const toolCallId = typeof frame.toolCallId === 'string' && frame.toolCallId !== '' ? frame.toolCallId : undefined;
1721
- const fromSubagent = isFromSubagent(frame);
1885
+ function liveCardIdentityOf(approvalId, frame, carriers, run) {
1886
+ const body = carriers.body;
1887
+ const toolCallId = typeof body.toolCallId === 'string' && body.toolCallId !== '' ? body.toolCallId : undefined;
1888
+ const fromSubagent = isFromSubagent(carriers.body);
1722
1889
  const bound = fromSubagent ? undefined : frameBindingOf(frame);
1723
1890
  const source = bound ?? run;
1724
- const ownRun = fromSubagent ? frame.sourceTaskId : source?.taskId;
1891
+ const ownRun = fromSubagent ? body.sourceTaskId : source?.taskId;
1725
1892
  const taskId = typeof ownRun === 'string' && ownRun !== '' ? ownRun : undefined;
1726
1893
  const sessionId = !fromSubagent && typeof source?.sessionId === 'string' && source.sessionId !== '' ? source.sessionId : undefined;
1727
- const askSig = carrierAskSig(frame);
1894
+ const askSig = carrierAskSig(carriers.body, carriers.live);
1728
1895
  return {
1729
1896
  approvalId,
1730
1897
  ...(toolCallId !== undefined ? { toolCallId } : {}),
@@ -1779,10 +1946,11 @@ function readApprovalCardClaimSignal(v) {
1779
1946
  export function claimApprovalCard(identity, options) {
1780
1947
  return claimApprovalCardFor(readApprovalHolderIdentity(identity), readApprovalCardClaimSignal(options));
1781
1948
  }
1782
- async function presentToolApprovalFrameAndRespond(frame, approvalId, respond, streamArgs, signal, lane, hold, ledgerSlot, receivedAtMs) {
1783
- const frameToolCallId = typeof frame.toolCallId === 'string' && frame.toolCallId !== '' ? frame.toolCallId : undefined;
1949
+ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, respond, streamArgs, signal, lane, hold, ledgerSlot, receivedAtMs) {
1950
+ const bodyToolCallId = carriers.body.toolCallId;
1951
+ const frameToolCallId = typeof bodyToolCallId === 'string' && bodyToolCallId !== '' ? bodyToolCallId : undefined;
1784
1952
  const decidedByReading = decidedByReadingSnapshotOf(lane);
1785
- const facts = cardFactsOf(frame, 'frame', { streamArgs, ...(lane?.argsUnavailable === true ? { argsUnavailable: true } : {}) });
1953
+ const facts = cardFactsOf(carriers.body, carriers.leg, { streamArgs, ...(lane?.argsUnavailable === true ? { argsUnavailable: true } : {}) }, carriers.live);
1786
1954
  const argsNotRealInput = facts.argsUnavailable === true;
1787
1955
  const ruleOffers = facts.ruleOffers;
1788
1956
  if (argsNotRealInput && lane?.argsUnavailable !== true && facts.wireNote !== undefined) {
@@ -1792,7 +1960,7 @@ async function presentToolApprovalFrameAndRespond(frame, approvalId, respond, st
1792
1960
  ...facts,
1793
1961
  callKey: liveFrameCallKey(approvalId),
1794
1962
  signal: hold.cardSignal,
1795
- ...approvalWindowKeysOf(frame, { windowIsCurrent: lane?.windowIsCurrent === true, receivedAtMs }),
1963
+ ...approvalWindowKeysOf(frame, { windowIsCurrent: lane?.windowIsCurrent === true, receivedAtMs, ...(carriers.leg === 'generic' ? { unknownShape: true } : {}) }),
1796
1964
  });
1797
1965
  if (hold.superseded !== undefined) {
1798
1966
  hostLog('debug', `liveToolApprovalWire: ${approvalId} was settled by the engine while its card was up${hold.superseded.parked ? ' (parked)' : ''} — card withdrawn; ` +
package/dist/host.d.ts CHANGED
@@ -3,9 +3,6 @@ import type { WireHooksConfig } from './finalVerifyWire.js';
3
3
  import { type NotificationQueuePort } from './notifications.js';
4
4
  export type SettingsDocument = Record<string, unknown>;
5
5
  export type ManagedPolicyState = 'absent' | 'in-effect' | 'in-effect-pieces' | 'unreadable-covered' | 'unreadable-uncovered' | 'remote-unknown';
6
- export interface ManagedPolicyReading {
7
- readonly state: ManagedPolicyState;
8
- }
9
6
  export type HooksStartupMode = 'safe' | 'bare';
10
7
  export type PluginHookOptionValue = string | number | boolean;
11
8
  export interface PluginHookSensitiveOption {
@@ -454,8 +454,10 @@ function permissionsArm(src, side, adoption) {
454
454
  appendOwnElement(buckets[bucket], rule);
455
455
  origins[bucket].set(rule, origin);
456
456
  }
457
- else if (rank(origin) < rank(prev))
457
+ else if (rank(origin) < rank(prev)) {
458
+ origins[bucket].delete(rule);
458
459
  origins[bucket].set(rule, origin);
460
+ }
459
461
  };
460
462
  let projectionError;
461
463
  const portsProjection = newRulePortsProjection();
package/dist/humanTurn.js CHANGED
@@ -1,4 +1,4 @@
1
- import { CORE_MAX_PENDING_STEER_CHARS, CORE_MCP_IMAGE_MAX_BASE64, CORE_USER_ATTACHMENT_LABEL_MAX } from './generated/engineFactTables.js';
1
+ import { CORE_IMAGE_ADMISSION_HARD_IMAGE_BASE64_CHARS, CORE_MAX_PENDING_STEER_CHARS, CORE_USER_ATTACHMENT_LABEL_MAX } from './generated/engineFactTables.js';
2
2
  import { mintEnvelopeOf, mintReferenceOf, seatTextOf } from './userAttachmentEnvelope.js';
3
3
  import { readEncodingShares, readHumanTurnInput } from './humanTurnRead.js';
4
4
  import { putOwnKey } from './ownKey.js';
@@ -224,7 +224,7 @@ function encodeObjective(v) {
224
224
  let count = 0;
225
225
  let total = 0;
226
226
  const seat = (len) => {
227
- if (len > CORE_MCP_IMAGE_MAX_BASE64)
227
+ if (len > CORE_IMAGE_ADMISSION_HARD_IMAGE_BASE64_CHARS)
228
228
  return 'image_too_large';
229
229
  if (count + 1 > IMAGE_SEAT_MAX_COUNT)
230
230
  return 'seat_count';