@sema-agent/client-core 0.108.0 → 0.110.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 +294 -0
- package/README.md +77 -40
- package/dist/adapt/arms.js +41 -13
- package/dist/adapt/textStream.d.ts +2 -0
- package/dist/adapt/textStream.js +37 -16
- package/dist/adapter/activeRunSelfHeal.js +1 -1
- package/dist/adapter/downstream/eventToSdkMessage.js +11 -5
- package/dist/agentSession/engineAgentRegistryRead.js +2 -0
- package/dist/bashExitReading.d.ts +12 -0
- package/dist/bashExitReading.js +44 -0
- package/dist/checkpointRowVerdict.d.ts +1 -1
- package/dist/checkpointRowVerdict.js +4 -2
- package/dist/compensations.js +1 -1
- package/dist/declared/supportFloorDeclared.js +1 -1
- package/dist/displayUntrusted.js +132 -14
- package/dist/engineAgentAbsence.d.ts +2 -0
- package/dist/engineAgentAbsence.js +28 -0
- package/dist/engineErrorCodes.d.ts +7 -0
- package/dist/engineErrorCodes.js +7 -0
- package/dist/engineHealthFacts.d.ts +26 -0
- package/dist/engineHealthFacts.js +63 -4
- package/dist/engineInlineTaskStats.d.ts +4 -3
- package/dist/engineInlineTaskStats.js +18 -2
- package/dist/engineNoticeCodes.d.ts +138 -0
- package/dist/engineNoticeCodes.js +662 -55
- package/dist/engineRepoint.d.ts +140 -0
- package/dist/engineRepoint.js +285 -0
- package/dist/engineRowRef.js +2 -0
- package/dist/engineSessionMessages.d.ts +2 -1
- package/dist/engineSessionMessages.js +5 -0
- package/dist/errorSupplementRoles.js +5 -1
- package/dist/fleet/fleetProjection.js +3 -2
- package/dist/fleetTaskDesc.js +3 -3
- package/dist/generated/engineFactTables.d.ts +3 -0
- package/dist/generated/engineFactTables.js +18 -0
- package/dist/generated/engineNoticeTables.js +3 -0
- package/dist/generated/sdkFactTables.d.ts +1 -0
- package/dist/generated/sdkFactTables.js +4 -0
- package/dist/hitl/approvalAskers.d.ts +6 -0
- package/dist/hitl/approvalAskers.js +30 -0
- package/dist/hitl/approvalCardReasons.js +25 -4
- package/dist/hitl/approvalRequestFrameReading.d.ts +21 -0
- package/dist/hitl/approvalRequestFrameReading.js +90 -0
- package/dist/hitl/approvalResolution.d.ts +8 -1
- package/dist/hitl/approvalResolution.js +4 -0
- package/dist/hitl/approvalsFeed.js +1 -1
- package/dist/hitl/askGateWire.js +0 -2
- package/dist/hitl/askParkRowRouting.js +5 -2
- package/dist/hitl/automaticAnswerVerdict.d.ts +5 -2
- package/dist/hitl/automaticAnswerVerdict.js +18 -4
- package/dist/hitl/decisionConflict.d.ts +2 -1
- package/dist/hitl/decisionConflict.js +10 -4
- package/dist/hitl/decisionRefusal.js +1 -0
- package/dist/hitl/decisionSend.d.ts +3 -0
- package/dist/hitl/decisionSend.js +29 -11
- package/dist/hitl/editedApprovalAcceptance.d.ts +25 -0
- package/dist/hitl/editedApprovalAcceptance.js +26 -0
- package/dist/hitl/frameRouter.js +13 -19
- package/dist/hitl/gateLedger.d.ts +0 -4
- package/dist/hitl/gateLedger.js +0 -19
- package/dist/hitl/hitlBridge.d.ts +5 -0
- package/dist/hitl/hitlBridge.js +13 -9
- package/dist/hitl/hitlHostSurface.d.ts +1 -0
- package/dist/hitl/hitlHostSurface.js +7 -2
- package/dist/hitl/hostWithdrawal.d.ts +15 -0
- package/dist/hitl/hostWithdrawal.js +38 -0
- package/dist/hitl/livePendingAsk.js +17 -16
- package/dist/hitl/localAllowRule.js +2 -1
- package/dist/hitl/noAllowCause.d.ts +1 -1
- package/dist/hitl/noAllowCause.js +4 -2
- package/dist/hitl/parkExpiryJudgements.js +26 -1
- package/dist/hitl/parkOwnership.d.ts +2 -0
- package/dist/hitl/parkOwnership.js +48 -8
- package/dist/hitl/parkResolver.js +32 -16
- package/dist/hitl/parkedUnresumable.d.ts +13 -0
- package/dist/hitl/parkedUnresumable.js +80 -0
- package/dist/hitl/permissionRuleScope.d.ts +4 -0
- package/dist/hitl/permissionRuleScope.js +31 -0
- package/dist/hitl/persistedRulesWire.d.ts +2 -0
- package/dist/hitl/persistedRulesWire.js +13 -12
- package/dist/hitl/planReviewWire.js +14 -4
- package/dist/hitl/sessionAllowRoute.js +5 -4
- package/dist/hitl/sessionPolicyWire.js +6 -4
- package/dist/hitl/standingRows.d.ts +2 -5
- package/dist/hitl/standingRows.js +30 -15
- package/dist/hitl/toolApprovalWire.d.ts +42 -9
- package/dist/hitl/toolApprovalWire.js +267 -44
- package/dist/hooksWireCaps.js +35 -4
- package/dist/hostSettingsSource.js +2 -0
- package/dist/hostTurnSettings.d.ts +10 -4
- package/dist/hostTurnSettings.js +112 -5
- package/dist/i18n/messages.en.d.ts +251 -0
- package/dist/i18n/messages.en.js +254 -2
- package/dist/i18n/messages.zh.js +264 -2
- package/dist/index.d.ts +48 -12
- package/dist/index.js +29 -7
- package/dist/inheritEnvCapability.js +2 -0
- package/dist/inputAuthor.d.ts +16 -0
- package/dist/inputAuthor.js +44 -0
- package/dist/interruptRefusalNote.d.ts +2 -0
- package/dist/interruptRefusalNote.js +17 -0
- package/dist/liveModelCatalog.d.ts +2 -0
- package/dist/liveModelCatalog.js +21 -7
- package/dist/liveQuestionStore.d.ts +4 -1
- package/dist/liveQuestionStore.js +12 -3
- package/dist/localEngineJudge.d.ts +75 -0
- package/dist/localEngineJudge.js +242 -0
- package/dist/localEngineOccupancy.d.ts +88 -0
- package/dist/localEngineOccupancy.js +269 -0
- package/dist/localEngineSeatReading.d.ts +29 -0
- package/dist/localEngineSeatReading.js +294 -0
- package/dist/localEngineSpawn.d.ts +113 -0
- package/dist/localEngineSpawn.js +486 -0
- package/dist/localEngineStep.d.ts +286 -0
- package/dist/localEngineStep.js +1086 -0
- package/dist/localEngineStopLadder.d.ts +29 -0
- package/dist/localEngineStopLadder.js +139 -0
- package/dist/localEngineSwapCard.d.ts +43 -0
- package/dist/localEngineSwapCard.js +205 -0
- package/dist/localEngineTiming.d.ts +8 -0
- package/dist/localEngineTiming.js +8 -0
- package/dist/mcpEngineLeg.d.ts +3 -0
- package/dist/mcpEngineLeg.js +38 -1
- package/dist/mcpPanel.js +2 -0
- package/dist/mcpReconnect.d.ts +9 -1
- package/dist/mcpReconnect.js +132 -20
- package/dist/mcpWireCaps.js +2 -2
- package/dist/memoryEntriesWire.d.ts +1 -1
- package/dist/memoryEntriesWire.js +5 -0
- package/dist/modelCapabilityProbe.js +4 -3
- package/dist/modsFace.d.ts +8 -1
- package/dist/modsFace.js +27 -0
- package/dist/noticePresentation.d.ts +2 -0
- package/dist/noticePresentation.js +79 -1
- package/dist/notifications.d.ts +17 -6
- package/dist/notifications.js +67 -19
- package/dist/ownKey.d.ts +5 -0
- package/dist/ownKey.js +8 -1
- package/dist/peerFrames.d.ts +40 -2
- package/dist/peerFrames.js +158 -3
- package/dist/pluginHooksWire.d.ts +2 -1
- package/dist/pluginHooksWire.js +56 -1
- package/dist/replayBody.js +10 -2
- package/dist/request/taskRequest.d.ts +5 -0
- package/dist/request/taskRequest.js +138 -21
- package/dist/resumeRefusalCopy.d.ts +1 -3
- package/dist/resumeRefusalCopy.js +2 -1
- package/dist/retryStatus.d.ts +2 -2
- package/dist/retryStatus.js +2 -0
- package/dist/ruleReadings.js +1 -1
- package/dist/seam.d.ts +24 -2
- package/dist/seam.js +15 -2
- package/dist/seatContract.d.ts +125 -10
- package/dist/seatContract.js +373 -7
- package/dist/sessionBackground.js +4 -1
- package/dist/sessionBackgroundHandoff.js +2 -1
- package/dist/sessionEngine.d.ts +2 -0
- package/dist/sessionEngine.js +111 -0
- package/dist/sessionMemoryStatus.d.ts +2 -1
- package/dist/sessionSyncFileHistory.d.ts +4 -0
- package/dist/sessionSyncFileHistory.js +28 -0
- package/dist/sseIdleTriage.d.ts +13 -1
- package/dist/sseIdleTriage.js +24 -6
- package/dist/subagent/engineSubagentTail.js +6 -0
- package/dist/subagentContentStore.d.ts +12 -0
- package/dist/subagentContentStore.js +72 -11
- package/dist/terminalStatuses.d.ts +1 -0
- package/dist/terminalStatuses.js +4 -1
- package/dist/toolResult.js +18 -5
- package/dist/turnFailurePresentation.js +9 -2
- package/dist/typePins.d.ts +11 -0
- package/dist/usageAnalytics.d.ts +17 -0
- package/dist/usageAnalytics.js +44 -2
- package/dist/wireFailureShape.d.ts +22 -5
- package/dist/wireFailureShape.js +47 -26
- package/dist/wireRefusalCopy.d.ts +31 -1
- package/dist/wireRefusalCopy.js +167 -2
- package/docs/INTEGRATION-CLIENTS.md +3269 -21
- package/package.json +5 -5
|
@@ -10,6 +10,7 @@ import { denyAttributionOf, flushHeldWithInterruptRewrite, isAskTool } from './f
|
|
|
10
10
|
import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
|
|
11
11
|
import { inheritFrameBinding } from '../frameBinding.js';
|
|
12
12
|
import { approvalCallKey, askGateQuestionId } from './gateIdentity.js';
|
|
13
|
+
import { usableAskQuestions } from './parkOwnership.js';
|
|
13
14
|
import { readDecideReceipt } from '../decideReceipt.js';
|
|
14
15
|
import { decideFailureNote } from '../decideFailureNote.js';
|
|
15
16
|
import { wireFailureTextsOf } from '../wireFailureShape.js';
|
|
@@ -69,12 +70,11 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
|
|
|
69
70
|
return { kind: 'not_sent', lane: 'ask', cause: found.code ?? 'fetch_failed', ...(found.reason !== '' ? { detail: found.reason } : {}), ...(found.rowVerdict !== undefined ? { rowVerdict: found.rowVerdict } : {}) };
|
|
70
71
|
const { pending, gatedCallId } = found;
|
|
71
72
|
const at = { lane: 'ask', ...(gatedCallId !== undefined ? { gatedCallId } : {}) };
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
if (!Array.isArray(questions) || questions.length === 0) {
|
|
73
|
+
const rowQuestions = usableAskQuestions(pending.input?.questions);
|
|
74
|
+
const streamQuestions = usableAskQuestions(gatedCallId ? askArgsByCall.get(gatedCallId)?.questions : undefined);
|
|
75
|
+
const questionsFromModelCall = rowQuestions === null && streamQuestions !== null;
|
|
76
|
+
const questions = rowQuestions ?? streamQuestions;
|
|
77
|
+
if (questions === null) {
|
|
78
78
|
const truncated = pending.input?.truncated === true;
|
|
79
79
|
return {
|
|
80
80
|
kind: 'not_sent',
|
|
@@ -86,11 +86,17 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
|
|
|
86
86
|
};
|
|
87
87
|
}
|
|
88
88
|
const questionId = askGateQuestionId(approvalCallKey(gatedCallId, taskId));
|
|
89
|
+
let withdrawn;
|
|
89
90
|
const answer = await new Promise(resolve => {
|
|
90
91
|
const unregister = registerLocalQuestionResponder(questionId, async (_id, a) => {
|
|
91
92
|
cleanup();
|
|
92
93
|
resolve(a);
|
|
93
94
|
return { ok: true };
|
|
95
|
+
}, (w) => {
|
|
96
|
+
withdrawn = w;
|
|
97
|
+
cleanup();
|
|
98
|
+
publishQuestionFrame({ type: 'question_complete', questionId });
|
|
99
|
+
resolve(null);
|
|
94
100
|
});
|
|
95
101
|
const onAbort = () => {
|
|
96
102
|
cleanup();
|
|
@@ -107,8 +113,12 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
|
|
|
107
113
|
}
|
|
108
114
|
signal?.addEventListener('abort', onAbort, { once: true });
|
|
109
115
|
onPresented?.();
|
|
110
|
-
publishQuestionFrame({ type: 'question', questionId, questions });
|
|
116
|
+
publishQuestionFrame({ type: 'question', questionId, questions, ...(questionsFromModelCall ? { _sema_questionsFromModelCall: true } : {}) });
|
|
111
117
|
});
|
|
118
|
+
if (withdrawn !== undefined) {
|
|
119
|
+
hostLog('debug', `liveHitlAskWire: question-withdrawn-by-host card=${questionId} verdict=${withdrawn.rowVerdict.kind}`);
|
|
120
|
+
return { kind: 'not_sent', ...at, cause: 'withdrawn', toldBy: withdrawn.toldBy, rowVerdict: withdrawn.rowVerdict };
|
|
121
|
+
}
|
|
112
122
|
const bridge = new HitlBridge(deps.client, taskId, deps.onDecideOperation ? { onDecideOperation: deps.onDecideOperation } : undefined);
|
|
113
123
|
if (answer === null) {
|
|
114
124
|
observeCancelByDeny(bridge.decideTool({ decision: 'deny', reason: INTERRUPT_DENY_REASON }, gatedCallId, undefined, pending), taskId);
|
|
@@ -155,7 +165,7 @@ function isFetchStepNoPending(outcome) {
|
|
|
155
165
|
return outcome.kind === 'not_sent' && outcome.cause === 'no_pending' && outcome.gatedCallId === undefined;
|
|
156
166
|
}
|
|
157
167
|
function isHumanDisposition(r) {
|
|
158
|
-
return r.kind === 'decided' || (r.kind === 'unsettled' && r.cause === 'interrupted') || (r.kind === 'not_sent' && r.cause === 'retracted');
|
|
168
|
+
return r.kind === 'decided' || (r.kind === 'unsettled' && r.cause === 'interrupted') || (r.kind === 'not_sent' && (r.cause === 'retracted' || r.cause === 'withdrawn'));
|
|
159
169
|
}
|
|
160
170
|
function failureReasonOf(r) {
|
|
161
171
|
if (r.kind === 'decided')
|
|
@@ -282,7 +292,8 @@ export async function resolvePark(park, ctx) {
|
|
|
282
292
|
if (outcome.kind !== 'decided') {
|
|
283
293
|
hostLog('debug', `liveHitlAskWire: gate not decided (${outcome.kind}/${outcome.cause}: ${failureReasonOf(outcome)}) — fail-soft to suspended terminal`);
|
|
284
294
|
const interrupted = outcome.kind === 'unsettled' && outcome.cause === 'interrupted';
|
|
285
|
-
const
|
|
295
|
+
const toldByHost = outcome.kind === 'not_sent' && outcome.cause === 'withdrawn';
|
|
296
|
+
const failedReason = interrupted || toldByHost
|
|
286
297
|
? undefined
|
|
287
298
|
: outcome.kind === 'not_sent' && outcome.cause === 'retracted'
|
|
288
299
|
? 'the approval card was retracted by the host without a decision (nothing was sent; the run stays parked and remains decidable elsewhere)'
|
|
@@ -312,13 +323,19 @@ export async function resolvePark(park, ctx) {
|
|
|
312
323
|
};
|
|
313
324
|
}
|
|
314
325
|
function failsoftWayOutOf(outcome) {
|
|
326
|
+
if (outcome === undefined)
|
|
327
|
+
return 'card';
|
|
328
|
+
if (typeof outcome !== 'object' || outcome === null)
|
|
329
|
+
return 'speak';
|
|
315
330
|
if (outcome.kind === 'not_sent' && (outcome.cause === 'row_unresumable' || (outcome.cause === 'safety_stop' && outcome.safetyCode === 'binding_missing')))
|
|
316
331
|
return 'reason';
|
|
317
|
-
if (outcome.kind === 'unsettled' && outcome.cause === 'engine_refused' && refusedParkFromError(outcome.refusal) !== null)
|
|
332
|
+
if (outcome.kind === 'unsettled' && outcome.cause === 'engine_refused' && (refusedParkFromError(outcome.refusal) !== null || outcome.refusal?.conflict === 'row_integrity'))
|
|
318
333
|
return 'cancel';
|
|
334
|
+
if (outcome.kind === 'unsettled' && outcome.cause === 'engine_refused')
|
|
335
|
+
return 'speak';
|
|
319
336
|
return 'card';
|
|
320
337
|
}
|
|
321
|
-
function failsoftReasonSentence(park, failedReason, terminal, wayOut
|
|
338
|
+
function failsoftReasonSentence(park, failedReason, terminal, wayOut) {
|
|
322
339
|
if (terminal !== undefined)
|
|
323
340
|
return terminal.errorMessage;
|
|
324
341
|
if (failedReason === undefined)
|
|
@@ -328,10 +345,9 @@ function failsoftReasonSentence(park, failedReason, terminal, wayOut = 'card') {
|
|
|
328
345
|
return renderMessage(msg('approval.park.stillParked', { gate, reason: failedReason }));
|
|
329
346
|
if (wayOut === 'cancel')
|
|
330
347
|
return renderMessage(msg('approval.park.refusedNotResumable', { gate, reason: failedReason }));
|
|
331
|
-
return (
|
|
332
|
-
' — the run is still parked on this approval: decide it on the card when it is shown again, or cancel the run.');
|
|
348
|
+
return renderMessage(msg(wayOut === 'speak' ? 'approval.park.resume.speak' : 'approval.park.resume.card', { gate, reason: failedReason }));
|
|
333
349
|
}
|
|
334
|
-
function failsoftEvents(park, ctx, gateAbortedByUser, failedReason, terminal, noDecisionSurface
|
|
350
|
+
function failsoftEvents(park, ctx, gateAbortedByUser, failedReason, terminal, noDecisionSurface, wayOut) {
|
|
335
351
|
const events = [
|
|
336
352
|
...flushHeldWithInterruptRewrite(ctx.led, {
|
|
337
353
|
terminal: park.pendingDone,
|
|
@@ -360,12 +376,12 @@ export function stalledTerminal(park, ctx, stalledRounds, lastStallReason, decid
|
|
|
360
376
|
`stopped re-attaching. Your last decision was accepted; sema did not observe the run move on after it — check the ` +
|
|
361
377
|
`transcript, decide the call again if the card comes back, or cancel the run.`;
|
|
362
378
|
hostLog('debug', `liveHitlAskWire: ${errorMessage}`);
|
|
363
|
-
return { kind: 'failsoft', events: failsoftEvents(park, ctx, false, undefined, { errorCode: 'hitl_stalled', errorMessage }) };
|
|
379
|
+
return { kind: 'failsoft', events: failsoftEvents(park, ctx, false, undefined, { errorCode: 'hitl_stalled', errorMessage }, false, failsoftWayOutOf(undefined)) };
|
|
364
380
|
}
|
|
365
381
|
const reason = `the run stayed parked across ${stalledRounds} consecutive re-attach rounds without any host progress ` +
|
|
366
382
|
`(last: ${lastStallReason ?? 'the engine kept parking'})`;
|
|
367
383
|
hostLog('debug', `liveHitlAskWire: ${reason} — fail-soft after the round committed`);
|
|
368
|
-
return { kind: 'failsoft', events: failsoftEvents(park, ctx, false, reason) };
|
|
384
|
+
return { kind: 'failsoft', events: failsoftEvents(park, ctx, false, reason, undefined, false, failsoftWayOutOf(undefined)) };
|
|
369
385
|
}
|
|
370
386
|
function notifyParkReattach(ctx, attempt, max, reason) {
|
|
371
387
|
invokeHostCallbackIsolated('liveHitlAskWire: onParkReattach', () => {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type CheckpointRowBar } from '../checkpointRowVerdict.js';
|
|
2
|
+
import type { UiLanguage } from '../env/uiLanguage.js';
|
|
3
|
+
import type { OwnedPendingListView } from './askParkRowRouting.js';
|
|
4
|
+
import { type ParkOwnershipDeps } from './parkOwnership.js';
|
|
5
|
+
export interface ParkedUnresumableRow {
|
|
6
|
+
readonly rowVerdict: CheckpointRowBar;
|
|
7
|
+
readonly rowKey?: string;
|
|
8
|
+
readonly taskId?: string;
|
|
9
|
+
readonly toolName?: string;
|
|
10
|
+
readonly rowDeadline?: 'armed' | 'none';
|
|
11
|
+
}
|
|
12
|
+
export declare function parkedUnresumableRowsOf(listed: OwnedPendingListView | null | undefined, deps?: ParkOwnershipDeps): readonly ParkedUnresumableRow[];
|
|
13
|
+
export declare function parkedUnresumableLine(row: ParkedUnresumableRow, lang?: UiLanguage | (string & {})): string;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { checkpointDeadlineStateOf, checkpointRowBarOf, checkpointRowBarSentence, rowBarWayOutContent, } from '../checkpointRowVerdict.js';
|
|
2
|
+
import { slotReadOf } from '../wireFailureShape.js';
|
|
3
|
+
import { QUEUE_ROW_FAMILY_UNREADABLE_GATE } from './approvalQueueRow.js';
|
|
4
|
+
import { approvalCallKey } from './gateIdentity.js';
|
|
5
|
+
import { checkpointQueueReadingOf } from './hitlBridge.js';
|
|
6
|
+
import { pendingRowIsOwnedByThisSession } from './parkOwnership.js';
|
|
7
|
+
function ownNonEmpty(o, key) {
|
|
8
|
+
const r = slotReadOf(o, key);
|
|
9
|
+
return r.presence === 'present' && typeof r.value === 'string' && r.value !== '' ? r.value : undefined;
|
|
10
|
+
}
|
|
11
|
+
function memberOf(row, rowVerdict) {
|
|
12
|
+
const taskId = ownNonEmpty(row, 'taskId');
|
|
13
|
+
const callId = ownNonEmpty(row, 'toolCallId') ?? ownNonEmpty(row, 'boundCallId');
|
|
14
|
+
const rowKey = callId !== undefined || taskId !== undefined ? approvalCallKey(callId, taskId ?? '') : undefined;
|
|
15
|
+
const toolName = ownNonEmpty(row, 'toolName');
|
|
16
|
+
const deadline = checkpointDeadlineStateOf(row);
|
|
17
|
+
return Object.freeze({
|
|
18
|
+
rowVerdict,
|
|
19
|
+
...(rowKey !== undefined ? { rowKey } : {}),
|
|
20
|
+
...(taskId !== undefined ? { taskId } : {}),
|
|
21
|
+
...(toolName !== undefined ? { toolName } : {}),
|
|
22
|
+
...(deadline !== 'unknown' ? { rowDeadline: deadline } : {}),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export function parkedUnresumableRowsOf(listed, deps) {
|
|
26
|
+
if (typeof listed !== 'object' || listed === null || Array.isArray(listed)) {
|
|
27
|
+
throw new Error('approvals.list answered without a readable body — the rows this engine cannot continue are unknown');
|
|
28
|
+
}
|
|
29
|
+
const pending = listed.pending;
|
|
30
|
+
if (!Array.isArray(pending)) {
|
|
31
|
+
throw new Error('approvals.list answered without a readable `pending` array — the rows this engine cannot continue are unknown');
|
|
32
|
+
}
|
|
33
|
+
const out = [];
|
|
34
|
+
for (const row of pending) {
|
|
35
|
+
const member = parkedMemberOf(row, deps);
|
|
36
|
+
if (member !== undefined)
|
|
37
|
+
out.push(member);
|
|
38
|
+
}
|
|
39
|
+
return Object.freeze(out);
|
|
40
|
+
}
|
|
41
|
+
function parkedMemberOf(row, deps) {
|
|
42
|
+
try {
|
|
43
|
+
if (typeof row !== 'object' || row === null)
|
|
44
|
+
return undefined;
|
|
45
|
+
const reading = checkpointQueueReadingOf(row);
|
|
46
|
+
if (reading === undefined)
|
|
47
|
+
return undefined;
|
|
48
|
+
if (reading.foreign !== null && reading.foreign !== QUEUE_ROW_FAMILY_UNREADABLE_GATE)
|
|
49
|
+
return undefined;
|
|
50
|
+
const bar = checkpointRowBarOf(reading.r);
|
|
51
|
+
if (bar === undefined)
|
|
52
|
+
return undefined;
|
|
53
|
+
if (!pendingRowIsOwnedByThisSession(reading.r, deps))
|
|
54
|
+
return undefined;
|
|
55
|
+
return memberOf(reading.r, bar);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const UNREADABLE_BAR = Object.freeze({ kind: 'unreadable' });
|
|
62
|
+
export function parkedUnresumableLine(row, lang) {
|
|
63
|
+
let bar = UNREADABLE_BAR;
|
|
64
|
+
let state = 'unknown';
|
|
65
|
+
try {
|
|
66
|
+
if (typeof row === 'object' && row !== null) {
|
|
67
|
+
const v = slotReadOf(row, 'rowVerdict');
|
|
68
|
+
if (v.presence === 'present')
|
|
69
|
+
bar = checkpointRowBarOf({ rowVerdict: v.value }) ?? UNREADABLE_BAR;
|
|
70
|
+
const d = slotReadOf(row, 'rowDeadline');
|
|
71
|
+
if (d.presence === 'present' && (d.value === 'armed' || d.value === 'none'))
|
|
72
|
+
state = d.value;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
bar = UNREADABLE_BAR;
|
|
77
|
+
state = 'unknown';
|
|
78
|
+
}
|
|
79
|
+
return `${checkpointRowBarSentence(bar, lang)} · ${rowBarWayOutContent(bar, state, lang)}`;
|
|
80
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { PermissionRuleValue } from '@sema-agent/agent-types';
|
|
2
|
+
export declare function isWholeToolRuleValue(value: PermissionRuleValue): boolean;
|
|
3
|
+
export type AllowRuleScope = 'whole_tool' | 'content' | 'unreadable';
|
|
4
|
+
export declare function allowRuleScopeOf(value: object | null | undefined): AllowRuleScope;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { readOwnDataValue } from '../ownKey.js';
|
|
2
|
+
import { inSlotReadOf } from '../wireFailureShape.js';
|
|
3
|
+
export function isWholeToolRuleValue(value) {
|
|
4
|
+
return value.ruleContent === undefined || value.ruleContent === '';
|
|
5
|
+
}
|
|
6
|
+
export function allowRuleScopeOf(value) {
|
|
7
|
+
try {
|
|
8
|
+
if (typeof value !== 'object' || value === null)
|
|
9
|
+
return 'unreadable';
|
|
10
|
+
const toolName = readOwnDataValue(value, 'toolName');
|
|
11
|
+
if (typeof toolName !== 'string' || toolName === '')
|
|
12
|
+
return 'unreadable';
|
|
13
|
+
let ruleContent;
|
|
14
|
+
const slot = inSlotReadOf(value, 'ruleContent');
|
|
15
|
+
if (slot.state === 'present') {
|
|
16
|
+
const raw = slot.value;
|
|
17
|
+
if (raw !== undefined) {
|
|
18
|
+
if (typeof raw !== 'string' || raw === '')
|
|
19
|
+
return 'unreadable';
|
|
20
|
+
ruleContent = raw;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
else if (slot.state !== 'absent')
|
|
24
|
+
return 'unreadable';
|
|
25
|
+
const snapshot = Object.freeze(Object.assign(Object.create(null), ruleContent !== undefined ? { toolName, ruleContent } : { toolName }));
|
|
26
|
+
return isWholeToolRuleValue(snapshot) ? 'whole_tool' : 'content';
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return 'unreadable';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { displayUntrusted, quoteUntrustedForSentence } from '../displayUntrusted.js';
|
|
2
2
|
import { engineCapTrue } from '../engineCapsCache.js';
|
|
3
3
|
import { CORE_RULE_BEHAVIORS } from '../generated/engineFactTables.js';
|
|
4
|
-
import { slotReadOf, wireFailureShapeOf } from '../wireFailureShape.js';
|
|
4
|
+
import { inValueOf, slotReadOf, wireFailureShapeOf } from '../wireFailureShape.js';
|
|
5
5
|
import { CAPABILITY_OPERATOR_PRINCIPALS_REQUIRED } from '../engineErrorCodes.js';
|
|
6
6
|
import { hostLog } from '../host.js';
|
|
7
7
|
import { msg, renderMessage } from '../i18n/translate.js';
|
|
8
8
|
import { appendOwnElement, guardedRead, readOwnDataValue } from '../ownKey.js';
|
|
9
9
|
import { observedPermissionRulesWrite, permissionRulesWriteAvailable } from '../permissionRulesWriteCapability.js';
|
|
10
|
-
import { isToolApprovalRespondReplayed, readToolApprovalRespondAck } from './toolApprovalWire.js';
|
|
10
|
+
import { isToolApprovalRespondParkedRedeemed, isToolApprovalRespondReplayed, readToolApprovalRespondAck } from './toolApprovalWire.js';
|
|
11
11
|
export function persistedRulesLaneAvailable(baseUrl) {
|
|
12
12
|
return engineCapTrue(baseUrl, 'permissionRules');
|
|
13
13
|
}
|
|
@@ -51,7 +51,7 @@ function ruleKeyOfRow(row) {
|
|
|
51
51
|
const scope = keyObj === undefined ? undefined : ownNonEmpty(keyObj, 'scope');
|
|
52
52
|
return behavior !== undefined && rule !== undefined && scope !== undefined ? Object.freeze({ behavior, rule, scope }) : undefined;
|
|
53
53
|
}
|
|
54
|
-
function
|
|
54
|
+
function unsettledRowOf(row) {
|
|
55
55
|
const key = ruleKeyOfRow(row);
|
|
56
56
|
const reason = ownNonEmpty(row, 'reason');
|
|
57
57
|
const d = slotReadOf(row, 'detail');
|
|
@@ -62,10 +62,6 @@ function indeterminateRowOf(row) {
|
|
|
62
62
|
...(shown !== undefined && shown.trim() !== '' ? { detail: shown } : {}),
|
|
63
63
|
});
|
|
64
64
|
}
|
|
65
|
-
function refusedRowOf(row) {
|
|
66
|
-
const key = ruleKeyOfRow(row);
|
|
67
|
-
return Object.freeze(key !== undefined ? { key } : {});
|
|
68
|
-
}
|
|
69
65
|
const RECEIPT_ROW_ARMS = {
|
|
70
66
|
persisted: (acc) => {
|
|
71
67
|
acc.persisted += 1;
|
|
@@ -74,10 +70,10 @@ const RECEIPT_ROW_ARMS = {
|
|
|
74
70
|
acc.deduped += 1;
|
|
75
71
|
},
|
|
76
72
|
refused: (acc, row) => {
|
|
77
|
-
appendOwnElement(acc.refused,
|
|
73
|
+
appendOwnElement(acc.refused, unsettledRowOf(row));
|
|
78
74
|
},
|
|
79
75
|
indeterminate: (acc, row) => {
|
|
80
|
-
appendOwnElement(acc.indeterminate,
|
|
76
|
+
appendOwnElement(acc.indeterminate, unsettledRowOf(row));
|
|
81
77
|
},
|
|
82
78
|
};
|
|
83
79
|
function receiptCountsOf(rows) {
|
|
@@ -333,9 +329,14 @@ export function revokeTargetFromPersistedRule(rule, opts) {
|
|
|
333
329
|
};
|
|
334
330
|
}
|
|
335
331
|
function readPrincipalOverride(carrier) {
|
|
336
|
-
if (carrier === null || typeof carrier !== 'object'
|
|
332
|
+
if (carrier === null || typeof carrier !== 'object')
|
|
337
333
|
return { ok: true };
|
|
338
|
-
const
|
|
334
|
+
const slot = inValueOf(carrier, 'principal');
|
|
335
|
+
if (slot.state === 'absent')
|
|
336
|
+
return { ok: true };
|
|
337
|
+
if (slot.state === 'unproven')
|
|
338
|
+
return { ok: false };
|
|
339
|
+
const raw = slot.value;
|
|
339
340
|
if (typeof raw !== 'string' || raw.trim().length === 0)
|
|
340
341
|
return { ok: false };
|
|
341
342
|
return { ok: true, principal: raw };
|
|
@@ -636,7 +637,7 @@ export function classifySkippedReason(reason) {
|
|
|
636
637
|
}
|
|
637
638
|
export function readRulePersistOutcome(ack) {
|
|
638
639
|
const parsed = readToolApprovalRespondAck(ack);
|
|
639
|
-
if (parsed === undefined || isToolApprovalRespondReplayed(parsed))
|
|
640
|
+
if (parsed === undefined || isToolApprovalRespondReplayed(parsed) || isToolApprovalRespondParkedRedeemed(parsed))
|
|
640
641
|
return { state: 'unknown' };
|
|
641
642
|
if (parsed.rulePersisted === true)
|
|
642
643
|
return { state: 'persisted' };
|
|
@@ -498,11 +498,15 @@ export function armPlanReviewApproval(result, sessionKey, opts) {
|
|
|
498
498
|
armedPlanReviews.set(taskId, instance);
|
|
499
499
|
armedTaskId = taskId;
|
|
500
500
|
let unregister = () => { };
|
|
501
|
+
const onWithdraw = () => {
|
|
502
|
+
hostLog('debug', `planReviewWire: card-withdrawn-by-host task=${taskId} card=${questionId}`);
|
|
503
|
+
retireArmedPlanReview(taskId, instance);
|
|
504
|
+
};
|
|
501
505
|
const responder = async (_id, answer) => {
|
|
502
506
|
const choice = planReviewAnswerChoiceOf(questionId, answer);
|
|
503
507
|
if (choice === 'inadmissible') {
|
|
504
508
|
if (armedPlanReviews.get(taskId) === instance && !hasLocalQuestionResponder(questionId))
|
|
505
|
-
unregister = registerLocalQuestionResponder(questionId, responder);
|
|
509
|
+
unregister = registerLocalQuestionResponder(questionId, responder, onWithdraw);
|
|
506
510
|
hostLog('debug', `planReviewWire: answer-inadmissible card=${questionId}`);
|
|
507
511
|
return { ok: false, inadmissible: true };
|
|
508
512
|
}
|
|
@@ -523,7 +527,7 @@ export function armPlanReviewApproval(result, sessionKey, opts) {
|
|
|
523
527
|
void decidePlanReviewFor(taskId, decided, modeAfter, cardDelivery, facts);
|
|
524
528
|
return { ok: true };
|
|
525
529
|
};
|
|
526
|
-
unregister = registerLocalQuestionResponder(questionId, responder);
|
|
530
|
+
unregister = registerLocalQuestionResponder(questionId, responder, onWithdraw);
|
|
527
531
|
publishArmedPlanReviewCard(instance);
|
|
528
532
|
hostLog('debug', `planReviewWire: approval card armed for parked plan (task ${taskId})`);
|
|
529
533
|
}
|
|
@@ -1160,11 +1164,17 @@ function mintPlanReviewReopenCard(taskId, opts, sessionKey, receiptMs, taken) {
|
|
|
1160
1164
|
});
|
|
1161
1165
|
const activeKey = `${sessionKey}\u0000${canonicalId}`;
|
|
1162
1166
|
let unregister = () => { };
|
|
1167
|
+
const onWithdraw = () => {
|
|
1168
|
+
hostLog('debug', `planReviewWire: card-withdrawn-by-host task=${taskId} card=${questionId}`);
|
|
1169
|
+
if (activeReopenResponders.get(activeKey)?.questionId === questionId)
|
|
1170
|
+
activeReopenResponders.delete(activeKey);
|
|
1171
|
+
publishQuestionFrameFor(sessionKey, { type: 'question_complete', questionId });
|
|
1172
|
+
};
|
|
1163
1173
|
const responder = async (_id, answer) => {
|
|
1164
1174
|
const choice = planReviewAnswerChoiceOf(questionId, answer);
|
|
1165
1175
|
if (choice === 'inadmissible') {
|
|
1166
1176
|
if (activeReopenResponders.get(activeKey)?.questionId === questionId && !hasLocalQuestionResponder(questionId)) {
|
|
1167
|
-
unregister = registerLocalQuestionResponder(questionId, responder);
|
|
1177
|
+
unregister = registerLocalQuestionResponder(questionId, responder, onWithdraw);
|
|
1168
1178
|
}
|
|
1169
1179
|
hostLog('debug', `planReviewWire: answer-inadmissible card=${questionId}`);
|
|
1170
1180
|
return { ok: false, inadmissible: true };
|
|
@@ -1190,7 +1200,7 @@ function mintPlanReviewReopenCard(taskId, opts, sessionKey, receiptMs, taken) {
|
|
|
1190
1200
|
return { ok: true };
|
|
1191
1201
|
};
|
|
1192
1202
|
const shape = cardShapeOf(facts, false);
|
|
1193
|
-
unregister = registerLocalQuestionResponder(questionId, responder);
|
|
1203
|
+
unregister = registerLocalQuestionResponder(questionId, responder, onWithdraw);
|
|
1194
1204
|
activeReopenResponders.set(activeKey, { questionId, unregister: () => unregister(), shape });
|
|
1195
1205
|
const receipt = waitForGateArmedFor(sessionKey, [questionId], receiptMs);
|
|
1196
1206
|
publishQuestionFrameFor(sessionKey, planReviewQuestionFrame(questionId, REOPEN_QUESTION, shape, facts));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { inSlotReadOf } from '../wireFailureShape.js';
|
|
1
2
|
export function sessionAllowCarried(leg, approvalId) {
|
|
2
3
|
if (leg === 'frame')
|
|
3
4
|
return true;
|
|
@@ -7,10 +8,10 @@ export function sessionAllowCarried(leg, approvalId) {
|
|
|
7
8
|
}
|
|
8
9
|
export function readCardUnreadableEvidence(card) {
|
|
9
10
|
try {
|
|
10
|
-
|
|
11
|
+
const slot = inSlotReadOf(card, 'unreadableEvidence');
|
|
12
|
+
if (slot.state === 'absent')
|
|
11
13
|
return 'absent';
|
|
12
|
-
const
|
|
13
|
-
const list = d !== undefined && Object.hasOwn(d, 'value') ? d.value : undefined;
|
|
14
|
+
const list = slot.state === 'present' ? slot.value : undefined;
|
|
14
15
|
return Array.isArray(list) && list.every((k) => typeof k === 'string') ? list : 'unreadable';
|
|
15
16
|
}
|
|
16
17
|
catch {
|
|
@@ -21,7 +22,7 @@ export function sessionAllowCardReadable(card) {
|
|
|
21
22
|
if (typeof card !== 'object' || card === null)
|
|
22
23
|
return 'card_unreadable';
|
|
23
24
|
try {
|
|
24
|
-
if ('cardPayloadUnreadable'
|
|
25
|
+
if (inSlotReadOf(card, 'cardPayloadUnreadable').state !== 'absent')
|
|
25
26
|
return 'card_unreadable';
|
|
26
27
|
}
|
|
27
28
|
catch {
|
|
@@ -701,10 +701,12 @@ export function sessionPolicyRemovalNotice(outcome, deployment) {
|
|
|
701
701
|
return 'This session’s rule record holds no entry naming a tool the engine refuses that the supplied roster does not mount, so nothing was changed. If runs still fail at startup over a refused tool name, that entry is set somewhere this record does not cover — the settings sent with the run, a parent session’s rule record, or the deployment’s own tool rules — or it is a name the engine refuses that this version does not yet recognise, or the failing run does not mount a tool the supplied roster has under that name.';
|
|
702
702
|
case 'judge_failed':
|
|
703
703
|
return 'Nothing was changed: this client could not check which entries in this session’s rule record name a tool the engine refuses (its copy of the engine’s tool-name check is unavailable or gave no clear answer), so it removed none rather than guess. Removing them needs that check available in this client.';
|
|
704
|
-
case 'loosen_forbidden':
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
704
|
+
case 'loosen_forbidden': {
|
|
705
|
+
const stem = msg('rules.sessionPolicy.remove.loosenForbiddenStem');
|
|
706
|
+
return deploymentHasNoOperator(deployment)
|
|
707
|
+
? renderMessage(msg('rules.sessionPolicy.deployment.noOperator.removeLoosenForbidden', { stem }))
|
|
708
|
+
: renderMessage(msg('rules.sessionPolicy.remove.loosenForbidden', { stem }));
|
|
709
|
+
}
|
|
708
710
|
case 'conflict':
|
|
709
711
|
return 'This session’s rule record was changed by someone else while the refused entries were being removed, twice in a row — nothing was changed. Try again once the other change has finished.';
|
|
710
712
|
case 'read_failed':
|
|
@@ -18,6 +18,7 @@ export interface StandingRowsHostFacts {
|
|
|
18
18
|
}
|
|
19
19
|
export declare function standingRowsOf(card: object | null | undefined, host?: StandingRowsHostFacts | null): StandingRows;
|
|
20
20
|
export declare function seatSessionRowOf(card: object | null | undefined, requestId: string | undefined): boolean;
|
|
21
|
+
export declare function seatAllowNoteCarriedOf(card: object | null | undefined, requestId: string | undefined): boolean;
|
|
21
22
|
export type RememberOffer = 'offerable' | 'refused' | 'unknown';
|
|
22
23
|
export interface RememberOfferable {
|
|
23
24
|
readonly session: RememberOffer;
|
|
@@ -25,13 +26,9 @@ export interface RememberOfferable {
|
|
|
25
26
|
readonly deploymentList: RememberOffer;
|
|
26
27
|
}
|
|
27
28
|
export declare function rememberOfferable(card: object | null | undefined, host?: StandingRowsHostFacts | null): RememberOfferable;
|
|
28
|
-
export interface LocalAllowRuleHostFacts {
|
|
29
|
-
readonly hostLadderEnforces?: boolean;
|
|
30
|
-
}
|
|
31
29
|
export interface LocalAllowRuleEnforcement {
|
|
32
30
|
readonly enforceable: boolean;
|
|
33
|
-
readonly by?: 'host_ladder' | 'engine_grant';
|
|
34
31
|
readonly engine: 'sent' | 'kept' | 'unreadable';
|
|
35
32
|
readonly keptReason?: PermissionsKeptReason;
|
|
36
33
|
}
|
|
37
|
-
export declare function localAllowRuleEnforceable(rule: string, caps: PermissionsAllowGrantReading
|
|
34
|
+
export declare function localAllowRuleEnforceable(rule: string, caps: PermissionsAllowGrantReading): LocalAllowRuleEnforcement;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { DEPLOYMENT_LIST_ASK_CLASSES, readAskClassPairState } from './askClassPair.js';
|
|
2
|
-
import {
|
|
3
|
-
import { readApprovalCardWord, readRuleOffers } from './toolApprovalWire.js';
|
|
2
|
+
import { askLevelVerdict } from './automaticAnswerVerdict.js';
|
|
3
|
+
import { answerCarriage, readApprovalCardWord, readRuleOffers } from './toolApprovalWire.js';
|
|
4
4
|
import { CORE_SKILL_TOOL_NAME } from '../generated/toolNameTables.js';
|
|
5
|
+
import { inSlotReadOf, inValueOf } from '../wireFailureShape.js';
|
|
5
6
|
import { toolPermissionRequestIdDomain } from '../seatContract.js';
|
|
6
7
|
import { permissionsDeclarationReport } from '../permissionsDeclaration.js';
|
|
7
8
|
import { readCardUnreadableEvidence, sessionAllowCardReadable, sessionAllowEligible } from './sessionAllowRoute.js';
|
|
@@ -15,9 +16,10 @@ function ownData(v, key) {
|
|
|
15
16
|
return undefined;
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
|
-
function
|
|
19
|
+
function candidateSlot(v, key) {
|
|
19
20
|
try {
|
|
20
|
-
|
|
21
|
+
const s = inSlotReadOf(v, key);
|
|
22
|
+
return s.state === 'unproven' ? UNREADABLE : s;
|
|
21
23
|
}
|
|
22
24
|
catch {
|
|
23
25
|
return UNREADABLE;
|
|
@@ -25,8 +27,10 @@ function hasKey(v, key) {
|
|
|
25
27
|
}
|
|
26
28
|
function present(v, key) {
|
|
27
29
|
try {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
+
const s = inValueOf(v, key);
|
|
31
|
+
if (s.state === 'unproven')
|
|
32
|
+
return UNREADABLE;
|
|
33
|
+
return s.state === 'present' ? { inCard: true, value: s.value } : { inCard: false, value: undefined };
|
|
30
34
|
}
|
|
31
35
|
catch {
|
|
32
36
|
return UNREADABLE;
|
|
@@ -42,12 +46,12 @@ function persistentRowOf(card, host, evidenceUnreadable) {
|
|
|
42
46
|
if (evidenceUnreadable)
|
|
43
47
|
return { row: 'none', why: 'evidence_unreadable' };
|
|
44
48
|
for (const key of ['ruleOffers', 'ruleOffersReadOnly']) {
|
|
45
|
-
const p =
|
|
49
|
+
const p = candidateSlot(card, key);
|
|
46
50
|
if (p === UNREADABLE)
|
|
47
51
|
return UNREADABLE;
|
|
48
|
-
if (
|
|
52
|
+
if (p.state === 'absent')
|
|
49
53
|
continue;
|
|
50
|
-
const offers = readRuleOffers(
|
|
54
|
+
const offers = readRuleOffers(p.state === 'present' ? p.value : undefined);
|
|
51
55
|
if (offers === undefined || offers.length === 0)
|
|
52
56
|
return { row: 'none', why: 'offers_unreadable' };
|
|
53
57
|
return { row: 'engine_offer', why: 'given' };
|
|
@@ -69,7 +73,7 @@ function persistentRowOf(card, host, evidenceUnreadable) {
|
|
|
69
73
|
return { row: 'none', why: 'tool_name_unreadable' };
|
|
70
74
|
if (tool === CORE_SKILL_TOOL_NAME)
|
|
71
75
|
return { row: 'none', why: 'grant_binding' };
|
|
72
|
-
if (
|
|
76
|
+
if (askLevelVerdict(card, 'standing_rule').kind !== 'may_answer')
|
|
73
77
|
return { row: 'none', why: 'rule_cannot_clear' };
|
|
74
78
|
const face = readApprovalCardWord(card);
|
|
75
79
|
if (face === undefined)
|
|
@@ -149,6 +153,20 @@ export function seatSessionRowOf(card, requestId) {
|
|
|
149
153
|
return false;
|
|
150
154
|
}
|
|
151
155
|
}
|
|
156
|
+
export function seatAllowNoteCarriedOf(card, requestId) {
|
|
157
|
+
if (typeof card !== 'object' || card === null)
|
|
158
|
+
return false;
|
|
159
|
+
try {
|
|
160
|
+
if (Object.hasOwn(card, 'requestId'))
|
|
161
|
+
return false;
|
|
162
|
+
if (toolPermissionRequestIdDomain(requestId) !== 'durable:')
|
|
163
|
+
return false;
|
|
164
|
+
return answerCarriage(card).allowNote === 'model';
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
152
170
|
const SESSION_OFFER = Object.freeze({
|
|
153
171
|
given: 'offerable',
|
|
154
172
|
card_unreadable: 'unknown',
|
|
@@ -178,8 +196,7 @@ export function rememberOfferable(card, host) {
|
|
|
178
196
|
deploymentList: SESSION_OFFER[rows.why.deploymentList],
|
|
179
197
|
});
|
|
180
198
|
}
|
|
181
|
-
export function localAllowRuleEnforceable(rule, caps
|
|
182
|
-
const ladder = typeof host === 'object' && host !== null && ownData(host, 'hostLadderEnforces') === true;
|
|
199
|
+
export function localAllowRuleEnforceable(rule, caps) {
|
|
183
200
|
let engine = 'unreadable';
|
|
184
201
|
let keptReason;
|
|
185
202
|
if (typeof rule === 'string' && rule.trim() !== '') {
|
|
@@ -199,10 +216,8 @@ export function localAllowRuleEnforceable(rule, caps, host) {
|
|
|
199
216
|
engine = 'unreadable';
|
|
200
217
|
}
|
|
201
218
|
}
|
|
202
|
-
const by = ladder ? 'host_ladder' : engine === 'sent' ? 'engine_grant' : undefined;
|
|
203
219
|
return Object.freeze({
|
|
204
|
-
enforceable:
|
|
205
|
-
...(by !== undefined ? { by } : {}),
|
|
220
|
+
enforceable: engine === 'sent',
|
|
206
221
|
engine,
|
|
207
222
|
...(keptReason !== undefined ? { keptReason } : {}),
|
|
208
223
|
});
|