@sema-agent/client-core 0.93.0 → 0.94.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.
- package/CHANGELOG.md +261 -0
- package/README.md +31 -18
- package/dist/adapt/arms.js +8 -5
- package/dist/adapter/downstream/terminalToSdkResult.js +2 -3
- package/dist/agentSession/engineSessionBackgroundStop.js +2 -1
- package/dist/agentsWireCaps.d.ts +15 -0
- package/dist/agentsWireCaps.js +111 -1
- package/dist/approvalReachWire.d.ts +18 -0
- package/dist/approvalReachWire.js +58 -0
- package/dist/approvalsStreamLiveCapability.js +16 -11
- package/dist/callerCwdCapability.d.ts +16 -0
- package/dist/callerCwdCapability.js +47 -0
- package/dist/controlRouter.js +3 -10
- package/dist/coreValuePorts.d.ts +64 -1
- package/dist/coreValuePorts.js +30 -0
- package/dist/detachWire.d.ts +1 -4
- package/dist/detachWire.js +1 -1
- package/dist/deviceExecutorManagementCapability.js +39 -34
- package/dist/deviceFlowPolling.d.ts +5 -0
- package/dist/deviceFlowPolling.js +28 -0
- package/dist/displayUntrusted.js +652 -21
- package/dist/engineErrorCodes.d.ts +5 -1
- package/dist/engineErrorCodes.js +9 -12
- package/dist/engineNoticeCodes.d.ts +22 -2
- package/dist/engineNoticeCodes.js +73 -6
- package/dist/engineRefusedToolName.d.ts +1 -0
- package/dist/engineRefusedToolName.js +8 -0
- package/dist/executionLaneCapability.js +14 -9
- package/dist/fileHistoryCaptureCapability.js +14 -9
- package/dist/fileHistoryLegNotice.d.ts +11 -0
- package/dist/fileHistoryLegNotice.js +50 -13
- package/dist/fleet/fleetLedger.js +5 -0
- package/dist/gateOutcome.d.ts +2 -1
- package/dist/gateOutcome.js +3 -0
- package/dist/gateVocabulary.js +1 -1
- package/dist/generated/engineAskFaceTables.d.ts +3 -0
- package/dist/generated/engineAskFaceTables.js +12 -0
- package/dist/generated/engineFactTables.d.ts +4 -2
- package/dist/generated/engineFactTables.js +13 -0
- package/dist/generated/engineNoticeTables.d.ts +5 -0
- package/dist/generated/engineNoticeTables.js +7 -0
- package/dist/generated/sdkFactTables.d.ts +3 -0
- package/dist/generated/sdkFactTables.js +17 -0
- package/dist/generated/settingsSchemaTables.d.ts +3 -0
- package/dist/generated/settingsSchemaTables.js +3 -0
- package/dist/headlessPermissionModeWire.d.ts +2 -2
- package/dist/headlessPermissionModeWire.js +11 -21
- package/dist/hitl/approvalHolds.d.ts +36 -0
- package/dist/hitl/approvalHolds.js +89 -0
- package/dist/hitl/approvalOutcomeNote.js +2 -1
- package/dist/hitl/approvalResolution.d.ts +3 -2
- package/dist/hitl/approvalResolution.js +21 -0
- package/dist/hitl/approvalsFeed.js +5 -0
- package/dist/hitl/askGateWire.js +3 -0
- package/dist/hitl/automaticAnswerVerdict.d.ts +8 -2
- package/dist/hitl/automaticAnswerVerdict.js +69 -21
- package/dist/hitl/decisionRefusal.d.ts +19 -0
- package/dist/hitl/decisionRefusal.js +63 -0
- package/dist/hitl/frameRouter.d.ts +10 -0
- package/dist/hitl/frameRouter.js +59 -3
- package/dist/hitl/gateLedger.d.ts +3 -0
- package/dist/hitl/gateLedger.js +17 -0
- package/dist/hitl/hitlBridge.d.ts +2 -2
- package/dist/hitl/hitlBridge.js +8 -10
- package/dist/hitl/hitlHostSurface.d.ts +6 -0
- package/dist/hitl/hitlHostSurface.js +25 -4
- package/dist/hitl/localAllowRule.js +2 -1
- package/dist/hitl/noAllowCause.d.ts +9 -0
- package/dist/hitl/noAllowCause.js +26 -0
- package/dist/hitl/parkResolver.js +6 -3
- package/dist/hitl/parkRowBirthWait.js +2 -1
- package/dist/hitl/persistedRulesWire.d.ts +6 -1
- package/dist/hitl/persistedRulesWire.js +12 -4
- package/dist/hitl/planReviewWire.d.ts +0 -1
- package/dist/hitl/planReviewWire.js +30 -9
- package/dist/hitl/sessionPolicyDeliverable.d.ts +1 -1
- package/dist/hitl/sessionPolicyDeliverable.js +3 -8
- package/dist/hitl/sessionPolicyWire.js +1 -1
- package/dist/hitl/toolApprovalWire.d.ts +37 -2
- package/dist/hitl/toolApprovalWire.js +392 -13
- package/dist/humanTurnRead.d.ts +15 -1
- package/dist/humanTurnRead.js +28 -2
- package/dist/index.d.ts +18 -6
- package/dist/index.js +14 -5
- package/dist/limitsWire.d.ts +0 -1
- package/dist/limitsWire.js +2 -10
- package/dist/liveModelCatalog.d.ts +14 -0
- package/dist/liveModelCatalog.js +61 -0
- package/dist/mcpProbeCapability.js +14 -9
- package/dist/mcpReconnect.js +13 -8
- package/dist/memoryComplianceCapability.js +15 -10
- package/dist/memoryOriginCapability.js +15 -10
- package/dist/modelIdentityResolvability.d.ts +4 -2
- package/dist/modelIdentityResolvability.js +31 -4
- package/dist/outputStyleSeatCapability.d.ts +16 -0
- package/dist/outputStyleSeatCapability.js +47 -0
- package/dist/ownKey.d.ts +1 -0
- package/dist/ownKey.js +10 -0
- package/dist/peerLaneCapability.js +16 -11
- package/dist/permissionRuleIssue.js +1 -1
- package/dist/permissionRulesWriteCapability.js +16 -11
- package/dist/permissionsAllowGrantCapability.d.ts +2 -0
- package/dist/permissionsAllowGrantCapability.js +38 -16
- package/dist/permissionsDeclaration.d.ts +10 -1
- package/dist/permissionsDeclaration.js +97 -15
- package/dist/protocolSegment.d.ts +1 -0
- package/dist/protocolSegment.js +13 -1
- package/dist/request/taskRequest.d.ts +11 -3
- package/dist/request/taskRequest.js +13 -1
- package/dist/resultFrameKeys.d.ts +4 -0
- package/dist/resultFrameKeys.js +13 -0
- package/dist/resumeRefusalCopy.js +6 -16
- package/dist/retryStatus.js +6 -4
- package/dist/rewindArchiveCapability.js +13 -8
- package/dist/{hitl/rosterMountedNames.d.ts → rosterMountedNames.d.ts} +1 -1
- package/dist/runCancelContext.js +2 -1
- package/dist/sealedKeyCapability.js +19 -14
- package/dist/seatContract.d.ts +33 -5
- package/dist/seatContract.js +143 -2
- package/dist/sessionPolicyCapability.js +15 -10
- package/dist/sessionReadRedactionCapability.d.ts +16 -0
- package/dist/sessionReadRedactionCapability.js +47 -0
- package/dist/sessionSyncFileHistoryCapability.js +14 -9
- package/dist/skillToolListsCapability.js +14 -9
- package/dist/sqlEngineCapability.js +18 -13
- package/dist/steerPriorityCapability.js +21 -16
- package/dist/subagent/agentStateDetail.d.ts +1 -0
- package/dist/subagent/agentStateDetail.js +41 -0
- package/dist/subagent/engineCompactWire.d.ts +15 -0
- package/dist/subagent/engineCompactWire.js +75 -13
- package/dist/supportFloor.js +1 -1
- package/dist/taskRequestWords.d.ts +2 -4
- package/dist/taskRequestWords.js +3 -2
- package/dist/types/engineState.d.ts +1 -1
- package/dist/userAttachmentEnvelope.d.ts +22 -1
- package/dist/userAttachmentEnvelope.js +90 -2
- package/dist/userInvokedSkillsCapability.js +14 -9
- package/dist/webSearchBackendCapability.js +21 -16
- package/dist/wireErrorTriage.d.ts +11 -0
- package/dist/wireErrorTriage.js +35 -0
- package/dist/wireFailureShape.d.ts +6 -0
- package/dist/wireFailureShape.js +30 -3
- package/dist/wireRefusalCopy.d.ts +6 -1
- package/dist/wireRefusalCopy.js +22 -8
- package/dist/wiringInstructionsAttribution.d.ts +10 -0
- package/dist/wiringInstructionsAttribution.js +67 -0
- package/dist/workflowClient.js +22 -1
- package/dist/workflowsJournalCapability.js +14 -9
- package/dist/writeProtectionCapability.js +28 -23
- package/docs/INTEGRATION-CLIENTS.md +817 -45
- package/package.json +5 -5
- package/dist/askSurvivesStandingAnswer.d.ts +0 -13
- package/dist/askSurvivesStandingAnswer.js +0 -53
- package/dist/hitl/askSurvivesPosture.d.ts +0 -20
- package/dist/hitl/askSurvivesPosture.js +0 -48
- /package/dist/{hitl/rosterMountedNames.js → rosterMountedNames.js} +0 -0
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { APPROVAL_CARD_PORT_KEY_MISMATCH_REASON, DEFAULT_DENY_REASON, DecideTransportRetryExhaustedError, HitlBridge, NO_APPROVAL_CARD_PORT_REASON, HitlSafetyError, denyReasonForWire, findPendingForTask, readDecideCurrentPending, readWireErrorCode, } from './hitlBridge.js';
|
|
2
2
|
import { askParkRowArm } from './askParkRowRouting.js';
|
|
3
3
|
import { hostLog } from '../host.js';
|
|
4
|
-
import { readOwnDataValue, readOwnNonEmptyString } from '../ownKey.js';
|
|
4
|
+
import { readOwnDataNonEmptyString, readOwnDataValue, readOwnNonEmptyString } from '../ownKey.js';
|
|
5
5
|
import { readAskClassPair } from './askClassPair.js';
|
|
6
6
|
import { legacyShadowedRuleTextOf, readAllowRuleShadowFromWire } from './allowRuleShadow.js';
|
|
7
7
|
import { createSessionSlot, DEFAULT_SESSION_KEY } from '../sessionSlot.js';
|
|
8
8
|
import { readEngineActiveBgTasks } from '../fleet/fleetLedger.js';
|
|
9
|
-
import { observeCancelByDeny, surfaceRememberNotApplied, surfaceEditNotForwarded, surfaceEditRefusedOnBlindAsk, surfaceRuleArmNotSent, surfaceRuleArmRejected } from './hitlHostSurface.js';
|
|
9
|
+
import { INTERRUPT_DENY_REASON, observeCancelByDeny, surfaceApprovalParking, surfaceRememberNotApplied, surfaceEditNotForwarded, surfaceEditRefusedOnBlindAsk, surfaceRuleArmNotSent, surfaceRuleArmRejected } from './hitlHostSurface.js';
|
|
10
10
|
import { approvalCallKey, liveFrameCallKey } from './gateIdentity.js';
|
|
11
|
+
import { displayUntrusted } from '../displayUntrusted.js';
|
|
12
|
+
import { approvalDecisionHolds, holdLiveCard, joinLiveCard, liveCardHoldOf } from './approvalHolds.js';
|
|
13
|
+
import { approvalResolutionOf } from './approvalResolution.js';
|
|
11
14
|
import { isApprovalMandateWord } from '../gateVocabulary.js';
|
|
12
15
|
import { denyAttributionRefusalFromError } from '../wireRefusalCopy.js';
|
|
13
16
|
import { approvalDecidedByAdmitted } from '../approvalDecidedByCapability.js';
|
|
14
17
|
import { decideFailureNote } from '../decideFailureNote.js';
|
|
18
|
+
import { wireFailureTextsOf } from '../wireFailureShape.js';
|
|
15
19
|
import { refusalSaysStillPending } from '../engineErrorCodes.js';
|
|
16
20
|
import { readDecideReceipt } from '../decideReceipt.js';
|
|
17
21
|
import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS, ASK_ORIGINS, PERSISTED_RULE_MANDATES, } from '@sema-agent/sdk';
|
|
@@ -110,6 +114,7 @@ export function approvalCardPortFor(sessionKey) {
|
|
|
110
114
|
export function _resetApprovalCardPortForTest() {
|
|
111
115
|
cardPortByKey.clear();
|
|
112
116
|
cardPortMissesByKey.clear();
|
|
117
|
+
callCardLedger.clear();
|
|
113
118
|
}
|
|
114
119
|
export function surfaceApprovalCard(req) {
|
|
115
120
|
const cardPort = approvalCardPortFor(DEFAULT_SESSION_KEY);
|
|
@@ -170,11 +175,122 @@ function isParkRowInputSizeMarker(v) {
|
|
|
170
175
|
const o = v;
|
|
171
176
|
return o.truncated === true && typeof o.bytes === 'number' && Number.isFinite(o.bytes) && o.bytes >= 0;
|
|
172
177
|
}
|
|
178
|
+
function durableHoldKey(row, callKey) {
|
|
179
|
+
const createdAt = typeof row.createdAt === 'number' && Number.isFinite(row.createdAt) ? row.createdAt : null;
|
|
180
|
+
return JSON.stringify([typeof row.sessionId === 'string' ? row.sessionId : null, typeof row.taskId === 'string' ? row.taskId : null, callKey, createdAt]);
|
|
181
|
+
}
|
|
182
|
+
function openDurableRead() {
|
|
183
|
+
const mark = ++approvalDecisionHolds.clock;
|
|
184
|
+
approvalDecisionHolds.openReads.add(mark);
|
|
185
|
+
return mark;
|
|
186
|
+
}
|
|
187
|
+
function closeDurableRead(mark) {
|
|
188
|
+
const h = approvalDecisionHolds;
|
|
189
|
+
h.openReads.delete(mark);
|
|
190
|
+
let oldest = Infinity;
|
|
191
|
+
for (const m of h.openReads)
|
|
192
|
+
if (m < oldest)
|
|
193
|
+
oldest = m;
|
|
194
|
+
for (const [key, s] of h.settled)
|
|
195
|
+
if (s.at <= oldest)
|
|
196
|
+
h.settled.delete(key);
|
|
197
|
+
}
|
|
198
|
+
function heldDurableDecision(holdKey, readMark) {
|
|
199
|
+
const live = approvalDecisionHolds.inFlight.get(holdKey);
|
|
200
|
+
if (live !== undefined)
|
|
201
|
+
return live.lane === 'durable' ? live : undefined;
|
|
202
|
+
const recent = approvalDecisionHolds.settled.get(holdKey);
|
|
203
|
+
return recent !== undefined && recent.at > readMark ? recent.hold : undefined;
|
|
204
|
+
}
|
|
205
|
+
function holdDurableDecision(holdKey, callerSignal, decide) {
|
|
206
|
+
const h = approvalDecisionHolds;
|
|
207
|
+
const interruption = new AbortController();
|
|
208
|
+
let settled = false;
|
|
209
|
+
const interrupt = () => {
|
|
210
|
+
if (!settled && !interruption.signal.aborted)
|
|
211
|
+
interruption.abort();
|
|
212
|
+
};
|
|
213
|
+
if (callerSignal?.aborted === true)
|
|
214
|
+
interrupt();
|
|
215
|
+
else
|
|
216
|
+
callerSignal?.addEventListener('abort', interrupt, { once: true });
|
|
217
|
+
let resolveOutcome = () => { };
|
|
218
|
+
let rejectOutcome = () => { };
|
|
219
|
+
const outcome = new Promise((resolve, reject) => {
|
|
220
|
+
resolveOutcome = resolve;
|
|
221
|
+
rejectOutcome = reject;
|
|
222
|
+
});
|
|
223
|
+
const hold = { lane: 'durable', outcome, presented: false, interrupt, cardSignal: interruption.signal };
|
|
224
|
+
h.inFlight.set(holdKey, hold);
|
|
225
|
+
const release = () => {
|
|
226
|
+
if (h.inFlight.get(holdKey) === hold)
|
|
227
|
+
h.inFlight.delete(holdKey);
|
|
228
|
+
if (h.openReads.size > 0)
|
|
229
|
+
h.settled.set(holdKey, { at: ++h.clock, hold });
|
|
230
|
+
};
|
|
231
|
+
const settle = (give) => {
|
|
232
|
+
settled = true;
|
|
233
|
+
callerSignal?.removeEventListener('abort', interrupt);
|
|
234
|
+
const trailing = hold.trailing;
|
|
235
|
+
if (trailing === undefined) {
|
|
236
|
+
release();
|
|
237
|
+
give();
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
give();
|
|
241
|
+
trailing.then(release, release);
|
|
242
|
+
};
|
|
243
|
+
decide(hold).then((o) => settle(() => resolveOutcome(o)), (e) => settle(() => rejectOutcome(e)));
|
|
244
|
+
return outcome;
|
|
245
|
+
}
|
|
246
|
+
async function joinDurableDecision(hold, deps, signal) {
|
|
247
|
+
if (signal?.aborted === true)
|
|
248
|
+
hold.interrupt();
|
|
249
|
+
else
|
|
250
|
+
signal?.addEventListener('abort', hold.interrupt, { once: true });
|
|
251
|
+
let o;
|
|
252
|
+
try {
|
|
253
|
+
o = await hold.outcome;
|
|
254
|
+
}
|
|
255
|
+
finally {
|
|
256
|
+
signal?.removeEventListener('abort', hold.interrupt);
|
|
257
|
+
}
|
|
258
|
+
if (hold.presented) {
|
|
259
|
+
try {
|
|
260
|
+
deps.onPresented?.();
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return { ...o, joinedInFlight: true };
|
|
266
|
+
}
|
|
173
267
|
export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signal, parkGatedCallId) {
|
|
174
268
|
const decidedByReading = decidedByReadingSnapshotOf(deps);
|
|
175
|
-
const
|
|
269
|
+
const readMark = openDurableRead();
|
|
270
|
+
let found;
|
|
271
|
+
let held;
|
|
272
|
+
try {
|
|
273
|
+
found = await findPendingForTask(deps.client, taskId, (toolName) => toolName !== undefined && (toolNameIsFsWrite(toolName) || toolNameIsShellExec(toolName)), signal ? { signal } : undefined, parkGatedCallId, (row) => askParkRowArm(row) === 'tool-gate');
|
|
274
|
+
if (found.ok)
|
|
275
|
+
held = heldDurableDecision(durableHoldKey(found.pending, approvalCallKey(found.gatedCallId, taskId)), readMark);
|
|
276
|
+
}
|
|
277
|
+
finally {
|
|
278
|
+
closeDurableRead(readMark);
|
|
279
|
+
}
|
|
176
280
|
if (!found.ok)
|
|
177
281
|
return { kind: 'failed', stage: 'fetch', reason: found.reason, ...(found.code !== undefined ? { code: found.code } : {}) };
|
|
282
|
+
if (held !== undefined)
|
|
283
|
+
return joinDurableDecision(held, deps, signal);
|
|
284
|
+
const callKey = approvalCallKey(found.gatedCallId, taskId);
|
|
285
|
+
const fetched = found;
|
|
286
|
+
return holdDurableDecision(durableHoldKey(fetched.pending, callKey), signal, async (hold) => {
|
|
287
|
+
const slot = {};
|
|
288
|
+
const out = await presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, fetched, decidedByReading, callKey, hold, slot);
|
|
289
|
+
noteCallOutcome(slot, out);
|
|
290
|
+
return out;
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, found, decidedByReading, callKey, hold, ledgerSlot) {
|
|
178
294
|
const { pending, gatedCallId } = found;
|
|
179
295
|
const toolName = typeof pending.toolName === 'string' ? pending.toolName : '';
|
|
180
296
|
const streamArgs = gatedCallId !== undefined ? argsByCall.get(gatedCallId) : undefined;
|
|
@@ -191,18 +307,18 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
|
|
|
191
307
|
return { kind: 'failed', stage: 'input', gatedCallId, reason: 'gate has no tool input payload' };
|
|
192
308
|
}
|
|
193
309
|
const bridge = new HitlBridge(deps.client, taskId, deps.onDecideOperation ? { onDecideOperation: deps.onDecideOperation } : undefined);
|
|
194
|
-
const callKey = approvalCallKey(gatedCallId, taskId);
|
|
195
310
|
const ruleOffersReadOnly = readRuleOffers(pending.ruleOffers);
|
|
196
311
|
const durableProbeCause = pending.riskDescriptor?.probeCause;
|
|
197
312
|
const durableProbeReason = readProbeReason(pending.riskDescriptor);
|
|
198
313
|
const durableShadow = readAllowRuleShadowFromWire(pending, 'row');
|
|
199
314
|
const durableRuleOffersAbsence = pending.ruleOffersAbsence;
|
|
315
|
+
hold.presented = true;
|
|
200
316
|
deps.onPresented?.();
|
|
201
|
-
const card = await
|
|
317
|
+
const card = await surfaceCallTrackedCard(gatedCallId, undefined, ledgerSlot, {
|
|
202
318
|
toolName,
|
|
203
319
|
args,
|
|
204
320
|
callKey,
|
|
205
|
-
|
|
321
|
+
signal: hold.cardSignal,
|
|
206
322
|
...(rowArgsGap !== undefined
|
|
207
323
|
? {
|
|
208
324
|
argsUnavailable: true,
|
|
@@ -245,7 +361,11 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
|
|
|
245
361
|
hostLog('debug', `liveHitlAskWire: approval card retracted without a decision for ${gatedCallId ?? '(no call)'} — nothing sent`);
|
|
246
362
|
return { kind: 'retracted', gatedCallId };
|
|
247
363
|
case 'aborted':
|
|
248
|
-
|
|
364
|
+
{
|
|
365
|
+
const cancelDeny = bridge.decideTool({ decision: 'deny', reason: INTERRUPT_DENY_REASON }, gatedCallId, undefined, pending);
|
|
366
|
+
hold.trailing = cancelDeny;
|
|
367
|
+
observeCancelByDeny(cancelDeny, taskId);
|
|
368
|
+
}
|
|
249
369
|
return { kind: 'aborted', gatedCallId };
|
|
250
370
|
case 'allow':
|
|
251
371
|
try {
|
|
@@ -283,11 +403,13 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
|
|
|
283
403
|
const wireCode = readWireErrorCode(e);
|
|
284
404
|
const wireRetriable = typeof e === 'object' && e !== null && refusalSaysStillPending(e);
|
|
285
405
|
const safetyCode = e instanceof HitlSafetyError ? e.code : undefined;
|
|
406
|
+
const failureTexts = wireFailureTextsOf(e);
|
|
407
|
+
hostLog('debug', `liveToolApprovalWire: decide(approve) failed: ${failureTexts.log}`);
|
|
286
408
|
return {
|
|
287
409
|
kind: 'failed',
|
|
288
410
|
stage: 'decide',
|
|
289
411
|
gatedCallId,
|
|
290
|
-
reason: `decide(approve) failed: ${
|
|
412
|
+
reason: `decide(approve) failed: ${failureTexts.reason}${decideFailureNote(e, 'approve')}`,
|
|
291
413
|
...(e instanceof DecideTransportRetryExhaustedError ? { retryExhausted: true } : {}),
|
|
292
414
|
...(currentPending !== undefined ? { currentPending } : {}),
|
|
293
415
|
...(wireCode !== undefined ? { errorCode: wireCode } : {}),
|
|
@@ -368,11 +490,13 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
|
|
|
368
490
|
const wireRetriable = typeof e === 'object' && e !== null && refusalSaysStillPending(e);
|
|
369
491
|
const safetyCode = e instanceof HitlSafetyError ? e.code : undefined;
|
|
370
492
|
const attributionRefusal = denyAttributionRefusalFromError(e, lastSendCarriedAttribution);
|
|
493
|
+
const failureTexts = wireFailureTextsOf(e);
|
|
494
|
+
hostLog('debug', `liveToolApprovalWire: decide(deny) failed: ${failureTexts.log}`);
|
|
371
495
|
return {
|
|
372
496
|
kind: 'failed',
|
|
373
497
|
stage: 'decide',
|
|
374
498
|
gatedCallId,
|
|
375
|
-
reason: `decide(deny) failed: ${
|
|
499
|
+
reason: `decide(deny) failed: ${failureTexts.reason}${decideFailureNote(e, 'deny')}`,
|
|
376
500
|
...(e instanceof DecideTransportRetryExhaustedError ? { retryExhausted: true } : {}),
|
|
377
501
|
...(currentPending !== undefined ? { currentPending } : {}),
|
|
378
502
|
...(wireCode !== undefined ? { errorCode: wireCode } : {}),
|
|
@@ -546,6 +670,36 @@ export function readToolApprovalRespondAck(v) {
|
|
|
546
670
|
}
|
|
547
671
|
return out;
|
|
548
672
|
}
|
|
673
|
+
export function readToolApprovalRespondParking(v) {
|
|
674
|
+
if (typeof v !== 'object' || v === null)
|
|
675
|
+
return undefined;
|
|
676
|
+
const read = (fn) => {
|
|
677
|
+
try {
|
|
678
|
+
return fn();
|
|
679
|
+
}
|
|
680
|
+
catch {
|
|
681
|
+
return undefined;
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
if (read(() => readOwnDataNonEmptyString(v, 'reason')) !== 'parking_in_progress')
|
|
685
|
+
return undefined;
|
|
686
|
+
const approvalId = read(() => readOwnDataNonEmptyString(v, 'approvalId'));
|
|
687
|
+
if (approvalId === undefined)
|
|
688
|
+
return { kind: 'claimed_unreadable' };
|
|
689
|
+
const hint = read(() => readOwnDataValue(v, 'retryAfterMs'));
|
|
690
|
+
const retryAfterMs = typeof hint === 'number' && Number.isFinite(hint) && hint >= 0 ? hint : undefined;
|
|
691
|
+
const decidePath = read(() => readOwnDataNonEmptyString(v, 'decidePath'));
|
|
692
|
+
const message = read(() => readOwnDataNonEmptyString(v, 'message'));
|
|
693
|
+
return {
|
|
694
|
+
kind: 'parking',
|
|
695
|
+
view: {
|
|
696
|
+
approvalId,
|
|
697
|
+
...(retryAfterMs !== undefined ? { retryAfterMs } : {}),
|
|
698
|
+
...(decidePath !== undefined ? { decidePath } : {}),
|
|
699
|
+
...(message !== undefined ? { message } : {}),
|
|
700
|
+
},
|
|
701
|
+
};
|
|
702
|
+
}
|
|
549
703
|
function isNonEmptyStringArray(v) {
|
|
550
704
|
return Array.isArray(v) && v.length > 0 && v.every(x => typeof x === 'string' && x !== '');
|
|
551
705
|
}
|
|
@@ -699,13 +853,27 @@ function readProbeReason(v) {
|
|
|
699
853
|
try {
|
|
700
854
|
if (typeof v !== 'object' || v === null || !Object.hasOwn(v, 'probeReason'))
|
|
701
855
|
return undefined;
|
|
702
|
-
|
|
703
|
-
return typeof raw === 'string' && raw.trim() !== '' ? raw : undefined;
|
|
856
|
+
return usableProbeReason(v.probeReason);
|
|
704
857
|
}
|
|
705
858
|
catch {
|
|
706
859
|
return undefined;
|
|
707
860
|
}
|
|
708
861
|
}
|
|
862
|
+
function usableProbeReason(raw) {
|
|
863
|
+
return typeof raw === 'string' && raw.trim() !== '' ? raw : undefined;
|
|
864
|
+
}
|
|
865
|
+
const PROBE_CAUSE_CODE_WORD = /^[a-z][a-z0-9_]{0,31}(?:\.[a-z][a-z0-9_]{0,31}){1,3}$/;
|
|
866
|
+
export function askProbeReasonDetail(card) {
|
|
867
|
+
const reason = usableProbeReason(ownDataValue(card, 'probeReason'));
|
|
868
|
+
if (reason !== undefined)
|
|
869
|
+
return displayUntrusted(reason);
|
|
870
|
+
const cause = ownDataValue(card, 'probeCause');
|
|
871
|
+
if (!isWireRecordCarrier(cause))
|
|
872
|
+
return undefined;
|
|
873
|
+
const code = ownDataValue(cause, 'code');
|
|
874
|
+
const head = 'An automatic check could not clear this call, so it is asking you; the engine did not say the specific reason';
|
|
875
|
+
return typeof code === 'string' && PROBE_CAUSE_CODE_WORD.test(code) ? `${head} (check code: ${code}).` : `${head}.`;
|
|
876
|
+
}
|
|
709
877
|
function readRememberRefused(v) {
|
|
710
878
|
try {
|
|
711
879
|
return typeof v === 'object' && v !== null && Object.hasOwn(v, 'rememberRefused') && v.rememberRefused === true;
|
|
@@ -714,6 +882,167 @@ function readRememberRefused(v) {
|
|
|
714
882
|
return false;
|
|
715
883
|
}
|
|
716
884
|
}
|
|
885
|
+
function ownDataValue(v, key) {
|
|
886
|
+
if (typeof v !== 'object' || v === null)
|
|
887
|
+
return undefined;
|
|
888
|
+
try {
|
|
889
|
+
const d = Object.getOwnPropertyDescriptor(v, key);
|
|
890
|
+
return d !== undefined && 'value' in d ? d.value : undefined;
|
|
891
|
+
}
|
|
892
|
+
catch {
|
|
893
|
+
return undefined;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
function ownStringList(v) {
|
|
897
|
+
if (!Array.isArray(v))
|
|
898
|
+
return undefined;
|
|
899
|
+
try {
|
|
900
|
+
const length = v.length;
|
|
901
|
+
if (typeof length !== 'number' || !Number.isSafeInteger(length) || length < 0)
|
|
902
|
+
return undefined;
|
|
903
|
+
const out = [];
|
|
904
|
+
for (let i = 0; i < length; i++) {
|
|
905
|
+
const d = Object.getOwnPropertyDescriptor(v, i);
|
|
906
|
+
if (d === undefined || !('value' in d) || typeof d.value !== 'string' || d.value === '')
|
|
907
|
+
return undefined;
|
|
908
|
+
out.push(d.value);
|
|
909
|
+
}
|
|
910
|
+
return v.length === length ? Object.freeze(out) : undefined;
|
|
911
|
+
}
|
|
912
|
+
catch {
|
|
913
|
+
return undefined;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
export function readApprovalBoundGrants(v) {
|
|
917
|
+
const g = ownDataValue(v, 'boundGrants');
|
|
918
|
+
if (typeof g !== 'object' || g === null || Array.isArray(g))
|
|
919
|
+
return undefined;
|
|
920
|
+
const skill = ownDataValue(g, 'skill');
|
|
921
|
+
if (!(skill === null || (typeof skill === 'string' && skill !== '')))
|
|
922
|
+
return undefined;
|
|
923
|
+
const allowedTools = ownStringList(ownDataValue(g, 'allowedTools'));
|
|
924
|
+
const disallowedTools = ownStringList(ownDataValue(g, 'disallowedTools'));
|
|
925
|
+
if (allowedTools === undefined || disallowedTools === undefined)
|
|
926
|
+
return undefined;
|
|
927
|
+
return Object.freeze({ skill, allowedTools, disallowedTools });
|
|
928
|
+
}
|
|
929
|
+
const CAUSE_WORD = /^[a-z][a-z0-9_]{0,63}$/;
|
|
930
|
+
export function readApprovalClassifierNoRuling(v) {
|
|
931
|
+
const w = ownDataValue(v, 'classifierNoRuling');
|
|
932
|
+
return typeof w === 'string' && CAUSE_WORD.test(w) ? w : undefined;
|
|
933
|
+
}
|
|
934
|
+
const CLASSIFIER_NO_RULING_REASONS = Object.freeze({
|
|
935
|
+
error: 'could not run for this call (it failed)',
|
|
936
|
+
timeout: 'could not run for this call (it timed out)',
|
|
937
|
+
parse_error: 'answered for this call but gave no verdict it could use',
|
|
938
|
+
});
|
|
939
|
+
const _noRulingPin = [true, true];
|
|
940
|
+
void _noRulingPin;
|
|
941
|
+
export function classifierNoRulingDetail(word) {
|
|
942
|
+
if (typeof word === 'string' && Object.hasOwn(CLASSIFIER_NO_RULING_REASONS, word)) {
|
|
943
|
+
return `The auto-mode classifier ${CLASSIFIER_NO_RULING_REASONS[word]}, so it is asking you instead.`;
|
|
944
|
+
}
|
|
945
|
+
if (typeof word === 'string' && CAUSE_WORD.test(word))
|
|
946
|
+
return `The auto-mode classifier did not rule on this call (reason: ${word}), so it is asking you instead.`;
|
|
947
|
+
return 'The auto-mode classifier did not rule on this call, so it is asking you instead.';
|
|
948
|
+
}
|
|
949
|
+
const entries = (n) => (n === 1 ? '1 tool entry' : `${String(n)} tool entries`);
|
|
950
|
+
export function approvalBoundGrantsDetail(grants) {
|
|
951
|
+
const g = readApprovalBoundGrants({ boundGrants: grants });
|
|
952
|
+
if (g === undefined)
|
|
953
|
+
return undefined;
|
|
954
|
+
if (g.skill === null)
|
|
955
|
+
return 'This call names no loaded skill, so approving it grants nothing beyond this call.';
|
|
956
|
+
const a = g.allowedTools.length;
|
|
957
|
+
const d = g.disallowedTools.length;
|
|
958
|
+
if (a === 0 && d === 0)
|
|
959
|
+
return 'Approving this activates the skill shown on the card; it lists no tools to run without asking and none to refuse.';
|
|
960
|
+
const allow = `${entries(a)} it lists ${a === 1 ? 'runs' : 'run'} without asking once it is active`;
|
|
961
|
+
const deny = `${entries(d)} ${d === 1 ? 'is' : 'are'} refused for the rest of this run`;
|
|
962
|
+
return `Approving this activates the skill shown on the card: ${allow}, and ${deny}.`;
|
|
963
|
+
}
|
|
964
|
+
const CALL_LEDGER_MAX_CALLS = 256;
|
|
965
|
+
const CALL_LEDGER_MAX_ASKS_PER_CALL = 8;
|
|
966
|
+
const callCardLedger = new Map();
|
|
967
|
+
function askSignatureOf(req) {
|
|
968
|
+
return JSON.stringify([req.classes !== undefined ? [...req.classes] : null, req.origin ?? null, req.mandate ?? null]);
|
|
969
|
+
}
|
|
970
|
+
function priorCardView(a) {
|
|
971
|
+
return Object.freeze({
|
|
972
|
+
...(a.approvalId !== undefined ? { approvalId: a.approvalId } : {}),
|
|
973
|
+
...(a.origin !== undefined ? { origin: a.origin } : {}),
|
|
974
|
+
...(a.mandate !== undefined ? { mandate: a.mandate } : {}),
|
|
975
|
+
...(a.outcome !== undefined ? { outcome: a.outcome } : {}),
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
function noteCardOnCall(callId, approvalId, req) {
|
|
979
|
+
const sig = askSignatureOf(req);
|
|
980
|
+
let asks = callCardLedger.get(callId);
|
|
981
|
+
if (asks === undefined) {
|
|
982
|
+
asks = [];
|
|
983
|
+
callCardLedger.set(callId, asks);
|
|
984
|
+
while (callCardLedger.size > CALL_LEDGER_MAX_CALLS) {
|
|
985
|
+
const oldest = callCardLedger.keys().next().value;
|
|
986
|
+
if (oldest === undefined)
|
|
987
|
+
break;
|
|
988
|
+
callCardLedger.delete(oldest);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
let at = asks.findIndex((a) => a.sig === sig);
|
|
992
|
+
if (at < 0) {
|
|
993
|
+
asks.push({ sig, ...(approvalId !== undefined ? { approvalId } : {}), ...(req.origin !== undefined ? { origin: req.origin } : {}), ...(req.mandate !== undefined ? { mandate: req.mandate } : {}) });
|
|
994
|
+
while (asks.length > CALL_LEDGER_MAX_ASKS_PER_CALL)
|
|
995
|
+
asks.shift();
|
|
996
|
+
at = asks.length - 1;
|
|
997
|
+
}
|
|
998
|
+
else if (asks[at].approvalId === undefined && approvalId !== undefined) {
|
|
999
|
+
asks[at].approvalId = approvalId;
|
|
1000
|
+
}
|
|
1001
|
+
const entry = asks[at];
|
|
1002
|
+
const prior = asks.slice(0, at);
|
|
1003
|
+
return { entry, ...(prior.length > 0 ? { prior: Object.freeze(prior.map(priorCardView)) } : {}) };
|
|
1004
|
+
}
|
|
1005
|
+
async function surfaceCallTrackedCard(callId, approvalId, slot, req) {
|
|
1006
|
+
if (callId === undefined || callId === '')
|
|
1007
|
+
return surfaceApprovalCard(req);
|
|
1008
|
+
const { entry, prior } = noteCardOnCall(callId, approvalId, req);
|
|
1009
|
+
slot.entry = entry;
|
|
1010
|
+
return surfaceApprovalCard(prior !== undefined ? { ...req, priorCardsForCall: prior } : req);
|
|
1011
|
+
}
|
|
1012
|
+
function noteCallOutcome(slot, outcome) {
|
|
1013
|
+
const entry = slot.entry;
|
|
1014
|
+
if (entry === undefined)
|
|
1015
|
+
return;
|
|
1016
|
+
const r = approvalResolutionOf(outcome);
|
|
1017
|
+
if (r.kind === 'decided')
|
|
1018
|
+
entry.outcome = r.decision === 'deny' ? 'denied' : 'allowed';
|
|
1019
|
+
}
|
|
1020
|
+
export function approvalPriorCardsDetail(card) {
|
|
1021
|
+
const prior = ownDataValue(card, 'priorCardsForCall');
|
|
1022
|
+
let isList = false;
|
|
1023
|
+
try {
|
|
1024
|
+
isList = Array.isArray(prior);
|
|
1025
|
+
}
|
|
1026
|
+
catch {
|
|
1027
|
+
return undefined;
|
|
1028
|
+
}
|
|
1029
|
+
if (!isList)
|
|
1030
|
+
return undefined;
|
|
1031
|
+
const length = ownDataValue(prior, 'length');
|
|
1032
|
+
if (typeof length !== 'number' || !Number.isSafeInteger(length) || length <= 0 || length > CALL_LEDGER_MAX_ASKS_PER_CALL)
|
|
1033
|
+
return undefined;
|
|
1034
|
+
const items = [];
|
|
1035
|
+
for (let i = 0; i < length; i++)
|
|
1036
|
+
items.push(ownDataValue(prior, String(i)));
|
|
1037
|
+
const outcome = ownDataValue(items[length - 1], 'outcome');
|
|
1038
|
+
const earlier = outcome === 'allowed' ? 'you already approved an earlier one' : outcome === 'denied' ? 'an earlier one was denied' : 'an earlier one was shown first';
|
|
1039
|
+
const fromRule = items.some((p) => ownDataValue(p, 'origin') === 'ask_rule');
|
|
1040
|
+
const thisFromRule = ownDataValue(card, 'origin') === 'ask_rule';
|
|
1041
|
+
if (fromRule && !thisFromRule) {
|
|
1042
|
+
return `This is a second approval for the same call: ${earlier}, which your ask rule asked for. This one is a separate check, not from your ask rule, and needs its own answer.`;
|
|
1043
|
+
}
|
|
1044
|
+
return `This is a second approval for the same call: ${earlier}. This one is a separate check and needs its own answer.`;
|
|
1045
|
+
}
|
|
717
1046
|
function isNonNegativeFinite(v) {
|
|
718
1047
|
return typeof v === 'number' && Number.isFinite(v) && v >= 0;
|
|
719
1048
|
}
|
|
@@ -881,6 +1210,18 @@ function subagentBadgeFor(frame) {
|
|
|
881
1210
|
const MAX_RESPOND_NOTE_CHARS = 2048;
|
|
882
1211
|
export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamArgs, signal, lane) {
|
|
883
1212
|
const approvalId = frame.approvalId;
|
|
1213
|
+
const key = liveFrameCallKey(approvalId);
|
|
1214
|
+
const held = liveCardHoldOf(key);
|
|
1215
|
+
if (held !== undefined)
|
|
1216
|
+
return joinLiveCard(held, signal);
|
|
1217
|
+
return holdLiveCard(key, signal, async (hold) => {
|
|
1218
|
+
const slot = {};
|
|
1219
|
+
const out = await presentToolApprovalFrameAndRespond(frame, approvalId, respond, streamArgs, signal, lane, hold, slot);
|
|
1220
|
+
noteCallOutcome(slot, out);
|
|
1221
|
+
return out;
|
|
1222
|
+
});
|
|
1223
|
+
}
|
|
1224
|
+
async function presentToolApprovalFrameAndRespond(frame, approvalId, respond, streamArgs, signal, lane, hold, ledgerSlot) {
|
|
884
1225
|
const frameToolCallId = typeof frame.toolCallId === 'string' && frame.toolCallId !== '' ? frame.toolCallId : undefined;
|
|
885
1226
|
const decidedByReading = decidedByReadingSnapshotOf(lane);
|
|
886
1227
|
const toolName = typeof frame.toolName === 'string' ? frame.toolName : 'Write';
|
|
@@ -910,14 +1251,14 @@ export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamA
|
|
|
910
1251
|
const ruleOffers = readRuleOffers(frame.ruleOffers);
|
|
911
1252
|
const denialLimitFallback = readDenialLimitFallback(frame.denialLimitFallback);
|
|
912
1253
|
const frameShadow = readAllowRuleShadowFromWire(frame, 'frame');
|
|
913
|
-
const card = await
|
|
1254
|
+
const card = await surfaceCallTrackedCard(frameToolCallId, approvalId, ledgerSlot, {
|
|
914
1255
|
toolName,
|
|
915
1256
|
args: args,
|
|
916
1257
|
callKey: liveFrameCallKey(approvalId),
|
|
917
1258
|
...(frameToolCallId !== undefined
|
|
918
1259
|
? { toolCallId: frameToolCallId }
|
|
919
1260
|
: {}),
|
|
920
|
-
|
|
1261
|
+
signal: hold.cardSignal,
|
|
921
1262
|
...(isFromSubagent(frame) ? { workerBadge: subagentBadgeFor(frame) } : {}),
|
|
922
1263
|
...(wireNote !== undefined ? { wireNote } : {}),
|
|
923
1264
|
...(argsNotRealInput ? { argsUnavailable: true } : {}),
|
|
@@ -953,7 +1294,16 @@ export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamA
|
|
|
953
1294
|
...(denialLimitFallback !== undefined ? { denialLimitFallback } : {}),
|
|
954
1295
|
...((o) => (o !== undefined ? { origin: o } : {}))(readOwnNonEmptyString(frame, 'origin')),
|
|
955
1296
|
...(readAskClassPair(frame) ?? {}),
|
|
1297
|
+
...((g) => (g !== undefined ? { boundGrants: g } : {}))(readApprovalBoundGrants(frame)),
|
|
1298
|
+
...((w) => (w !== undefined ? { classifierNoRuling: w } : {}))(readApprovalClassifierNoRuling(frame)),
|
|
956
1299
|
});
|
|
1300
|
+
if (hold.superseded !== undefined) {
|
|
1301
|
+
hostLog('debug', `liveToolApprovalWire: ${approvalId} was settled by the engine while its card was up${hold.superseded.parked ? ' (parked)' : ''} — card withdrawn; ` +
|
|
1302
|
+
`the card came back ${card.kind}; nothing sent`);
|
|
1303
|
+
if (hold.superseded.parked && (card.kind === 'allow' || card.kind === 'deny'))
|
|
1304
|
+
surfaceApprovalParking();
|
|
1305
|
+
return { decision: 'unresolved', retracted: true };
|
|
1306
|
+
}
|
|
957
1307
|
if (argsNotRealInput && card.kind === 'allow' && card.updatedInput !== undefined) {
|
|
958
1308
|
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)`);
|
|
959
1309
|
surfaceEditRefusedOnBlindAsk();
|
|
@@ -963,6 +1313,22 @@ export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamA
|
|
|
963
1313
|
hostLog('debug', `liveToolApprovalWire: ${approvalId} card retracted without a decision${card.reason ? ` (${card.reason})` : ''} — nothing sent`);
|
|
964
1314
|
return { decision: 'unresolved', retracted: true };
|
|
965
1315
|
}
|
|
1316
|
+
if (card.kind === 'aborted') {
|
|
1317
|
+
hold.phase = 'respond';
|
|
1318
|
+
const interruptOpts = lane?.approvalDecisionNoteCapable === true ? { note: INTERRUPT_DENY_REASON } : {};
|
|
1319
|
+
let interruptDeny;
|
|
1320
|
+
try {
|
|
1321
|
+
interruptDeny = Promise.resolve(respond(approvalId, 'deny', interruptOpts));
|
|
1322
|
+
}
|
|
1323
|
+
catch (e) {
|
|
1324
|
+
interruptDeny = Promise.reject(e);
|
|
1325
|
+
}
|
|
1326
|
+
hold.trailing = interruptDeny.then(() => undefined, () => undefined);
|
|
1327
|
+
observeCancelByDeny(interruptDeny, approvalId);
|
|
1328
|
+
hostLog('debug', `liveToolApprovalWire: ${approvalId} card aborted — one tool-level deny issued to settle the pending ask`);
|
|
1329
|
+
return { decision: 'unresolved', aborted: true };
|
|
1330
|
+
}
|
|
1331
|
+
hold.phase = 'respond';
|
|
966
1332
|
const decision = card.kind === 'allow' ? (card.allowSession ? 'allow_session' : 'allow') : 'deny';
|
|
967
1333
|
const denySettledByForWire = card.kind === 'deny' ? readApprovalDenySettledBy(card) : undefined;
|
|
968
1334
|
const attributionKeys = decision === 'deny' ? decisionAttributionKeyOf(decidedByReading, denySettledByForWire) : {};
|
|
@@ -1101,6 +1467,19 @@ export async function surfaceToolApprovalFrameAndRespond(frame, respond, streamA
|
|
|
1101
1467
|
throw first;
|
|
1102
1468
|
}
|
|
1103
1469
|
}
|
|
1470
|
+
const parkingBody = readToolApprovalRespondParking(raw);
|
|
1471
|
+
if (parkingBody !== undefined) {
|
|
1472
|
+
const parking = parkingBody.kind === 'parking' && parkingBody.view.approvalId === approvalId ? parkingBody.view : undefined;
|
|
1473
|
+
if (parking !== undefined) {
|
|
1474
|
+
hostLog('debug', `liveToolApprovalWire: respond(${decision}) for ${approvalId} answered 202 parking_in_progress${parking.retryAfterMs !== undefined ? ` (retryAfterMs=${parking.retryAfterMs})` : ''} — ` +
|
|
1475
|
+
`the answer did NOT take effect${parking.decidePath !== undefined ? ' (the engine pointed at a decide path for the parked card)' : ''}; not re-sent, not reported as decided; the user is told to answer again on the parked card`);
|
|
1476
|
+
surfaceApprovalParking(parking.retryAfterMs);
|
|
1477
|
+
return { decision: 'unresolved', parking };
|
|
1478
|
+
}
|
|
1479
|
+
hostLog('error', `liveToolApprovalWire: 202 parking body for ${approvalId} is ${parkingBody.kind === 'parking' ? `for another approval (${parkingBody.view.approvalId})` : 'unreadable (approvalId is not usable)'} — ` +
|
|
1480
|
+
'reporting the answer as NOT settled (never as decided, never as parked); not re-sent');
|
|
1481
|
+
return { decision: 'unresolved' };
|
|
1482
|
+
}
|
|
1104
1483
|
const parsed = readToolApprovalRespondAck(raw);
|
|
1105
1484
|
let ack = parsed;
|
|
1106
1485
|
if (parsed !== undefined && (parsed.approvalId !== approvalId || parsed.decision !== decision)) {
|
package/dist/humanTurnRead.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ImageInput } from './imagesWireCaps.js';
|
|
2
|
-
import type { UserAttachmentDescriptor, UserAttachmentNotIncludedReason, UserAttachmentSource } from './userAttachmentEnvelope.js';
|
|
2
|
+
import type { UserAttachmentDescriptor, UserAttachmentMcpResourcePart, UserAttachmentNotIncludedReason, UserAttachmentSource } from './userAttachmentEnvelope.js';
|
|
3
3
|
import type { HumanTurnAttachment, HumanTurnEncoding, HumanTurnInput, HumanTurnMissing } from './humanTurn.js';
|
|
4
4
|
import type { ImageVision } from './liveModelCatalog.js';
|
|
5
5
|
export interface CellView {
|
|
@@ -55,6 +55,20 @@ export type DescriptorView = {
|
|
|
55
55
|
path: string;
|
|
56
56
|
sizeBytes: number | null;
|
|
57
57
|
reason: UserAttachmentNotIncludedReason;
|
|
58
|
+
} | {
|
|
59
|
+
kind: 'not-included';
|
|
60
|
+
target: 'mcp-resource';
|
|
61
|
+
server: string;
|
|
62
|
+
uri: string;
|
|
63
|
+
name: string;
|
|
64
|
+
reason: UserAttachmentNotIncludedReason;
|
|
65
|
+
} | {
|
|
66
|
+
kind: 'mcp-resource';
|
|
67
|
+
server: string;
|
|
68
|
+
uri: string;
|
|
69
|
+
name: string;
|
|
70
|
+
parts: UserAttachmentMcpResourcePart[];
|
|
71
|
+
truncated: boolean;
|
|
58
72
|
};
|
|
59
73
|
export declare function readDescriptor(x: UserAttachmentDescriptor | HumanTurnAttachment['descriptor'], imageDelivery: 'required' | 'ignored'): DescriptorView | undefined;
|
|
60
74
|
type Reason = Exclude<Extract<HumanTurnEncoding, {
|
package/dist/humanTurnRead.js
CHANGED
|
@@ -40,6 +40,23 @@ const strArray = (v) => ownArray(v).map(str);
|
|
|
40
40
|
const sizeOf = (v) => (v === null ? null : typeof v === 'number' ? v : bad());
|
|
41
41
|
const posInt = (v) => (typeof v === 'number' && Number.isSafeInteger(v) && v >= 1 ? v : bad());
|
|
42
42
|
const natInt = (v) => (typeof v === 'number' && Number.isSafeInteger(v) && v >= 0 ? v : bad());
|
|
43
|
+
const NOT_INCLUDED_REASON_LIST = ['too-large', 'unreadable', 'read-denied', 'approval-required', 'server-not-connected', 'resource-not-listed', 'read-failed'];
|
|
44
|
+
const notIncludedReasonsExhaustive = true;
|
|
45
|
+
void notIncludedReasonsExhaustive;
|
|
46
|
+
const NOT_INCLUDED_REASONS = new Set(NOT_INCLUDED_REASON_LIST);
|
|
47
|
+
function readMcpPart(x) {
|
|
48
|
+
if (!isPlainObject(x))
|
|
49
|
+
bad();
|
|
50
|
+
const o = x;
|
|
51
|
+
const kind = ownData(o, 'kind');
|
|
52
|
+
if (kind === 'text')
|
|
53
|
+
return { kind, text: str(ownData(o, 'text')) };
|
|
54
|
+
if (kind === 'binary') {
|
|
55
|
+
const mt = ownData(o, 'mimeType');
|
|
56
|
+
return { kind, mimeType: mt === null ? null : str(mt) };
|
|
57
|
+
}
|
|
58
|
+
return bad();
|
|
59
|
+
}
|
|
43
60
|
function sourceOf(v) {
|
|
44
61
|
return v === 'at-mention' || v === 'compact-restore' ? v : bad();
|
|
45
62
|
}
|
|
@@ -120,9 +137,12 @@ function readDescriptorAny(x, imageDelivery) {
|
|
|
120
137
|
case 'not-included': {
|
|
121
138
|
const target = ownData(o, 'target');
|
|
122
139
|
const reason = ownData(o, 'reason');
|
|
123
|
-
if (
|
|
140
|
+
if (!NOT_INCLUDED_REASONS.has(reason))
|
|
124
141
|
bad();
|
|
125
|
-
if (
|
|
142
|
+
if (target === 'mcp-resource') {
|
|
143
|
+
return { kind, target, server: str(ownData(o, 'server')), uri: str(ownData(o, 'uri')), name: name(), reason: reason };
|
|
144
|
+
}
|
|
145
|
+
if (target !== 'file' && target !== 'directory' && target !== 'path')
|
|
126
146
|
bad();
|
|
127
147
|
return {
|
|
128
148
|
kind,
|
|
@@ -133,6 +153,12 @@ function readDescriptorAny(x, imageDelivery) {
|
|
|
133
153
|
reason: reason,
|
|
134
154
|
};
|
|
135
155
|
}
|
|
156
|
+
case 'mcp-resource': {
|
|
157
|
+
const truncated = ownData(o, 'truncated');
|
|
158
|
+
if (typeof truncated !== 'boolean')
|
|
159
|
+
bad();
|
|
160
|
+
return { kind, server: str(ownData(o, 'server')), uri: str(ownData(o, 'uri')), name: name(), parts: ownArray(ownData(o, 'parts')).map(readMcpPart), truncated: truncated };
|
|
161
|
+
}
|
|
136
162
|
default:
|
|
137
163
|
return undefined;
|
|
138
164
|
}
|