@sema-agent/client-core 0.106.0 → 0.108.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 +199 -0
- package/README.md +35 -26
- package/dist/adapter/activeRunSelfHeal.d.ts +10 -0
- package/dist/adapter/activeRunSelfHeal.js +68 -23
- package/dist/checkpointRowVerdict.d.ts +11 -0
- package/dist/checkpointRowVerdict.js +35 -0
- package/dist/cloudConfigWireCaps.d.ts +10 -0
- package/dist/cloudConfigWireCaps.js +33 -0
- package/dist/coreValuePorts.d.ts +17 -1
- package/dist/coreValuePorts.js +5 -0
- package/dist/decideReceipt.js +2 -1
- package/dist/displayUntrusted.d.ts +5 -3
- package/dist/displayUntrusted.js +50 -41
- package/dist/engineErrorCodes.d.ts +5 -0
- package/dist/engineErrorCodes.js +5 -0
- package/dist/engineNoticeCodes.d.ts +31 -1
- package/dist/engineNoticeCodes.js +182 -9
- package/dist/engineTargetSeat.js +1 -0
- package/dist/errorSupplementRoles.js +4 -1
- package/dist/eventLeg.js +1 -1
- package/dist/executionLaneCapability.js +3 -2
- package/dist/executionLaneWords.d.ts +1 -0
- package/dist/executionLaneWords.js +4 -0
- package/dist/gateOutcome.d.ts +10 -1
- package/dist/gateOutcome.js +48 -1
- package/dist/generated/engineFactTables.d.ts +1 -0
- package/dist/generated/engineFactTables.js +5 -0
- package/dist/generated/engineNoticeTables.js +12 -3
- package/dist/generated/settingsSchemaFns.d.ts +4 -2
- package/dist/generated/settingsSchemaFns.js +9 -3
- package/dist/generated/settingsSchemaMcpRequestEntry.d.ts +1 -1
- package/dist/generated/settingsSchemaMcpRequestEntry.js +9 -18
- package/dist/generated/settingsSchemaReservedMemberName.d.ts +1 -0
- package/dist/generated/settingsSchemaReservedMemberName.js +7 -0
- package/dist/generated/settingsSchemaTables.d.ts +2 -0
- package/dist/generated/settingsSchemaTables.js +10 -0
- package/dist/generated/settingsSchemaWireName.d.ts +1 -0
- package/dist/generated/settingsSchemaWireName.js +8 -0
- package/dist/headlessReconnectWire.js +12 -7
- package/dist/hitl/approvalAskers.d.ts +13 -0
- package/dist/hitl/approvalAskers.js +79 -0
- package/dist/hitl/approvalResolution.d.ts +8 -1
- package/dist/hitl/approvalResolution.js +37 -2
- package/dist/hitl/approvalsFeed.js +5 -13
- package/dist/hitl/askParkRowRouting.js +17 -1
- package/dist/hitl/decisionRefusal.d.ts +1 -0
- package/dist/hitl/decisionRefusal.js +5 -2
- package/dist/hitl/decisionSend.d.ts +2 -0
- package/dist/hitl/decisionSend.js +22 -12
- package/dist/hitl/frameRouter.d.ts +3 -2
- package/dist/hitl/frameRouter.js +28 -6
- package/dist/hitl/gateLedger.d.ts +2 -0
- package/dist/hitl/gateLedger.js +3 -1
- package/dist/hitl/hitlBridge.d.ts +5 -1
- package/dist/hitl/hitlBridge.js +26 -2
- package/dist/hitl/livePendingAsk.js +4 -2
- package/dist/hitl/parkOwnership.js +4 -5
- package/dist/hitl/parkResolver.js +34 -21
- package/dist/hitl/parkRowBirthWait.d.ts +9 -0
- package/dist/hitl/parkRowBirthWait.js +8 -1
- package/dist/hitl/parkRowDelegation.d.ts +6 -0
- package/dist/hitl/parkRowDelegation.js +33 -0
- package/dist/hitl/persistedRulesWire.d.ts +19 -3
- package/dist/hitl/persistedRulesWire.js +118 -20
- package/dist/hitl/planReviewWire.js +16 -18
- package/dist/hitl/policyDenyCause.d.ts +3 -0
- package/dist/hitl/policyDenyCause.js +16 -0
- package/dist/hitl/sessionAllowRoute.d.ts +5 -0
- package/dist/hitl/sessionAllowRoute.js +42 -0
- package/dist/hitl/sessionPolicyDeliverable.js +1 -1
- package/dist/hitl/standingRows.js +10 -10
- package/dist/hitl/toolApprovalWire.d.ts +8 -2
- package/dist/hitl/toolApprovalWire.js +44 -11
- package/dist/hooksReaderGenerations.d.ts +1 -0
- package/dist/hooksReaderGenerations.js +12 -0
- package/dist/hooksWireCaps.js +8 -4
- package/dist/i18n/messages.en.d.ts +43 -0
- package/dist/i18n/messages.en.js +47 -4
- package/dist/i18n/messages.zh.js +53 -4
- package/dist/index.d.ts +7 -1
- package/dist/index.js +5 -1
- package/dist/injectionLaneNotice.js +2 -1
- package/dist/liveModelCatalog.d.ts +4 -2
- package/dist/liveModelCatalog.js +164 -26
- package/dist/model/catalogDefault.d.ts +25 -0
- package/dist/model/catalogDefault.js +44 -0
- package/dist/noticePresentation.js +2 -0
- package/dist/permissionWireCaps.d.ts +1 -0
- package/dist/permissionWireCaps.js +11 -0
- package/dist/pluginHooksWire.d.ts +5 -3
- package/dist/pluginHooksWire.js +116 -7
- package/dist/queuedDelivery.d.ts +1 -0
- package/dist/queuedDelivery.js +12 -0
- package/dist/resultFrameKeys.js +4 -2
- package/dist/resumeRefusalCopy.js +15 -8
- package/dist/runPermissionModeSwitch.d.ts +2 -1
- package/dist/runPermissionModeSwitch.js +16 -6
- package/dist/sdkWireTransit.d.ts +1 -1
- package/dist/seatContract.d.ts +64 -10
- package/dist/seatContract.js +167 -12
- package/dist/sessionEngine.d.ts +2 -1
- package/dist/sessionEngine.js +5 -0
- package/dist/settingsHooksSalvage.d.ts +5 -0
- package/dist/settingsHooksSalvage.js +54 -0
- package/dist/subagentContentStore.js +1 -1
- package/dist/supportFloor.js +2 -0
- package/dist/taskNotificationAdmission.d.ts +19 -0
- package/dist/taskNotificationAdmission.js +84 -0
- package/dist/toolResult.js +2 -1
- package/dist/webSearchWireCaps.js +9 -4
- package/dist/wireRefusalCopy.d.ts +7 -1
- package/dist/wireRefusalCopy.js +7 -1
- package/docs/INTEGRATION-CLIENTS.md +1189 -18
- package/package.json +5 -5
|
@@ -1,11 +1,11 @@
|
|
|
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 { wireFailureShapeOf } from '../wireFailureShape.js';
|
|
4
|
+
import { 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
|
-
import { guardedRead, readOwnDataValue } from '../ownKey.js';
|
|
8
|
+
import { appendOwnElement, guardedRead, readOwnDataValue } from '../ownKey.js';
|
|
9
9
|
import { observedPermissionRulesWrite, permissionRulesWriteAvailable } from '../permissionRulesWriteCapability.js';
|
|
10
10
|
import { isToolApprovalRespondReplayed, readToolApprovalRespondAck } from './toolApprovalWire.js';
|
|
11
11
|
export function persistedRulesLaneAvailable(baseUrl) {
|
|
@@ -17,27 +17,125 @@ export function persistedRulesGovernanceAvailable(baseUrl) {
|
|
|
17
17
|
export function persistedRulesWriteAvailable(baseUrl) {
|
|
18
18
|
return permissionRulesWriteAvailable(observedPermissionRulesWrite(baseUrl ?? ''));
|
|
19
19
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
const NO_INDETERMINATE = Object.freeze([]);
|
|
21
|
+
const INDETERMINATE_DETAIL_SHOWN_MAX = 400;
|
|
22
|
+
function ownArrayItems(v) {
|
|
23
|
+
try {
|
|
24
|
+
if (typeof v !== 'object' || v === null || !Array.isArray(v))
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
const len = slotReadOf(v, 'length');
|
|
31
|
+
if (len.presence !== 'present' || typeof len.value !== 'number' || !Number.isSafeInteger(len.value) || len.value < 0)
|
|
32
|
+
return undefined;
|
|
33
|
+
const out = [];
|
|
34
|
+
for (let i = 0; i < len.value; i++) {
|
|
35
|
+
const item = slotReadOf(v, String(i));
|
|
36
|
+
if (item.presence !== 'present')
|
|
37
|
+
return undefined;
|
|
38
|
+
appendOwnElement(out, item.value);
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
function ownNonEmpty(o, key) {
|
|
43
|
+
const r = slotReadOf(o, key);
|
|
44
|
+
return r.presence === 'present' && typeof r.value === 'string' && r.value !== '' ? r.value : undefined;
|
|
45
|
+
}
|
|
46
|
+
function ruleKeyOfRow(row) {
|
|
47
|
+
const k = slotReadOf(row, 'key');
|
|
48
|
+
const keyObj = k.presence === 'present' && typeof k.value === 'object' && k.value !== null ? k.value : undefined;
|
|
49
|
+
const behavior = keyObj === undefined ? undefined : ownNonEmpty(keyObj, 'behavior');
|
|
50
|
+
const rule = keyObj === undefined ? undefined : ownNonEmpty(keyObj, 'rule');
|
|
51
|
+
const scope = keyObj === undefined ? undefined : ownNonEmpty(keyObj, 'scope');
|
|
52
|
+
return behavior !== undefined && rule !== undefined && scope !== undefined ? Object.freeze({ behavior, rule, scope }) : undefined;
|
|
53
|
+
}
|
|
54
|
+
function indeterminateRowOf(row) {
|
|
55
|
+
const key = ruleKeyOfRow(row);
|
|
56
|
+
const reason = ownNonEmpty(row, 'reason');
|
|
57
|
+
const d = slotReadOf(row, 'detail');
|
|
58
|
+
const shown = d.presence === 'present' && typeof d.value === 'string' ? displayUntrusted(d.value, { max: INDETERMINATE_DETAIL_SHOWN_MAX }) : undefined;
|
|
59
|
+
return Object.freeze({
|
|
60
|
+
...(key !== undefined ? { key } : {}),
|
|
61
|
+
...(reason !== undefined ? { reason } : {}),
|
|
62
|
+
...(shown !== undefined && shown.trim() !== '' ? { detail: shown } : {}),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function refusedRowOf(row) {
|
|
66
|
+
const key = ruleKeyOfRow(row);
|
|
67
|
+
return Object.freeze(key !== undefined ? { key } : {});
|
|
68
|
+
}
|
|
69
|
+
const RECEIPT_ROW_ARMS = {
|
|
70
|
+
persisted: (acc) => {
|
|
71
|
+
acc.persisted += 1;
|
|
72
|
+
},
|
|
73
|
+
'no-op': (acc) => {
|
|
74
|
+
acc.deduped += 1;
|
|
75
|
+
},
|
|
76
|
+
refused: (acc, row) => {
|
|
77
|
+
appendOwnElement(acc.refused, refusedRowOf(row));
|
|
78
|
+
},
|
|
79
|
+
indeterminate: (acc, row) => {
|
|
80
|
+
appendOwnElement(acc.indeterminate, indeterminateRowOf(row));
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
function receiptCountsOf(rows) {
|
|
84
|
+
const acc = { persisted: 0, deduped: 0, indeterminate: [], refused: [] };
|
|
85
|
+
for (const row of rows) {
|
|
86
|
+
if (typeof row !== 'object' || row === null)
|
|
87
|
+
return null;
|
|
88
|
+
const o = slotReadOf(row, 'outcome');
|
|
89
|
+
const word = o.presence === 'present' ? o.value : undefined;
|
|
90
|
+
if (typeof word !== 'string' || !Object.hasOwn(RECEIPT_ROW_ARMS, word))
|
|
91
|
+
return null;
|
|
92
|
+
RECEIPT_ROW_ARMS[word](acc, row);
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
persisted: acc.persisted,
|
|
96
|
+
deduped: acc.deduped,
|
|
97
|
+
indeterminate: acc.indeterminate.length === 0 ? NO_INDETERMINATE : Object.freeze(acc.indeterminate),
|
|
98
|
+
...(acc.refused.length === 0 ? {} : { refused: Object.freeze(acc.refused) }),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function legacyEnvelopeCountsOf(result) {
|
|
102
|
+
if (typeof result !== 'object' || result === null)
|
|
103
|
+
return null;
|
|
104
|
+
const m = slotReadOf(result, 'members');
|
|
105
|
+
const rows = m.presence === 'present' ? ownArrayItems(m.value) : undefined;
|
|
106
|
+
if (rows === undefined)
|
|
107
|
+
return null;
|
|
108
|
+
let persisted = 0;
|
|
109
|
+
let deduped = 0;
|
|
110
|
+
for (const row of rows) {
|
|
111
|
+
if (typeof row !== 'object' || row === null)
|
|
112
|
+
continue;
|
|
113
|
+
const st = slotReadOf(row, 'status');
|
|
114
|
+
const status = st.presence === 'present' ? st.value : undefined;
|
|
115
|
+
if (status === 'persisted')
|
|
116
|
+
persisted += 1;
|
|
117
|
+
else if (status === 'deduped')
|
|
118
|
+
deduped += 1;
|
|
119
|
+
}
|
|
120
|
+
return { persisted, deduped, indeterminate: NO_INDETERMINATE };
|
|
121
|
+
}
|
|
122
|
+
export function readCcImportRedeemCounts(body) {
|
|
123
|
+
try {
|
|
124
|
+
if (typeof body !== 'object' || body === null)
|
|
125
|
+
return null;
|
|
126
|
+
const top = slotReadOf(body, 'members');
|
|
127
|
+
if (top.presence !== 'absent') {
|
|
128
|
+
if (top.presence !== 'present')
|
|
129
|
+
return null;
|
|
130
|
+
const rows = ownArrayItems(top.value);
|
|
131
|
+
return rows === undefined ? null : receiptCountsOf(rows);
|
|
31
132
|
}
|
|
32
|
-
|
|
133
|
+
const legacy = slotReadOf(body, 'result');
|
|
134
|
+
return legacy.presence === 'present' ? legacyEnvelopeCountsOf(legacy.value) : null;
|
|
33
135
|
}
|
|
34
|
-
|
|
35
|
-
return
|
|
36
|
-
persisted: Array.isArray(o.persisted) ? o.persisted.length : 0,
|
|
37
|
-
deduped: Array.isArray(o.deduped) ? o.deduped.length : 0,
|
|
38
|
-
};
|
|
136
|
+
catch {
|
|
137
|
+
return null;
|
|
39
138
|
}
|
|
40
|
-
return null;
|
|
41
139
|
}
|
|
42
140
|
export function classifyRulesFailure(e, context) {
|
|
43
141
|
const { status, errorCode, message, retryAfterSec, committed } = wireFailureShapeOf(e);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { publishQuestionFrame, publishQuestionFrameFor, registerLocalQuestionResponder, unregisterLocalQuestionResponder, hasLocalQuestionResponder, hasQuestionOverlayFor, } from '../liveQuestionStore.js';
|
|
2
2
|
import { hostLog } from '../host.js';
|
|
3
|
+
import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
|
|
3
4
|
import { abortableSleep, settleWithinDeadline } from '../abortableSleep.js';
|
|
4
5
|
import { clientHasVerbs, engineWireFor, noAnswerKindOf, sendOnce } from '../engineWireFor.js';
|
|
5
6
|
import { engineWireTarget } from '../engineWireTarget.js';
|
|
@@ -677,6 +678,8 @@ function planReviewSettledVerdict(r) {
|
|
|
677
678
|
return status !== 'completed' && !gateMoved ? 'still_parked' : 'unconfirmed';
|
|
678
679
|
if (status === 'completed')
|
|
679
680
|
return 'took_effect';
|
|
681
|
+
if (status === 'running' && r.decision === 'reject')
|
|
682
|
+
return 'took_effect';
|
|
680
683
|
const newGateHasCard = () => {
|
|
681
684
|
const leg = durableDecideLegOf({ gateKind: r.gateKindAfter ?? null });
|
|
682
685
|
return leg === 'approval' || leg === 'plan_review';
|
|
@@ -874,7 +877,7 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
874
877
|
const bodyStatus = bodyWordOf(body, 'status');
|
|
875
878
|
const effective = postStatus ?? bodyStatus;
|
|
876
879
|
statusSeen = effective;
|
|
877
|
-
effect = planReviewSettledVerdict({ postStatus, bodyStatus, reparked: bodySaysReparked(body), gateBefore, gateAfter, gateKindAfter });
|
|
880
|
+
effect = planReviewSettledVerdict({ decision, postStatus, bodyStatus, reparked: bodySaysReparked(body), gateBefore, gateAfter, gateKindAfter });
|
|
878
881
|
const code = bodyWordOf(body, 'errorCode');
|
|
879
882
|
if (effect === 'still_parked' && postStatus === 'needs_review' && !bodySaysReparked(body)) {
|
|
880
883
|
outcome =
|
|
@@ -892,7 +895,7 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
892
895
|
}
|
|
893
896
|
else if (effect === 'took_effect') {
|
|
894
897
|
outcome =
|
|
895
|
-
`The plan was ${decision === 'approve' ? 'approved and the parked task resumed to completion' : 'rejected (plan discarded)'} — final status: ${effective} ` +
|
|
898
|
+
`The plan was ${decision === 'approve' ? 'approved and the parked task resumed to completion' : 'rejected (plan discarded)'} — ${effective === 'completed' ? 'final status' : 'status'}: ${effective} ` +
|
|
896
899
|
`(${postStatus !== undefined ? 'post-decide re-checked: task left the review gate' : "from the decision's own reply; the post-decide re-check was unavailable"}).`;
|
|
897
900
|
}
|
|
898
901
|
else if (effective === 'needs_review' && (gateBefore === undefined || gateAfter === undefined || postStatus !== 'needs_review')) {
|
|
@@ -1026,22 +1029,17 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
1026
1029
|
hostLog('debug', `planReviewWire: outcome enqueue threw: ${String(e)}`);
|
|
1027
1030
|
}
|
|
1028
1031
|
if (onOutcome !== undefined) {
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
});
|
|
1041
|
-
}
|
|
1042
|
-
catch (e) {
|
|
1043
|
-
hostLog('debug', `planReviewWire: onOutcome callback threw for task ${taskId} (ignored): ${String(e)}`);
|
|
1044
|
-
}
|
|
1032
|
+
invokeHostCallbackIsolated(`planReviewWire: onOutcome callback (task ${taskId})`, () => onOutcome({
|
|
1033
|
+
taskId,
|
|
1034
|
+
dispatchNo,
|
|
1035
|
+
decision: semanticDecision,
|
|
1036
|
+
effect,
|
|
1037
|
+
...(effect === 'still_parked' && stillParkedCause !== undefined ? { cause: stillParkedCause } : {}),
|
|
1038
|
+
detail: outcome,
|
|
1039
|
+
tail,
|
|
1040
|
+
prompt,
|
|
1041
|
+
...(statusSeen !== undefined ? { status: statusSeen } : {}),
|
|
1042
|
+
}));
|
|
1045
1043
|
}
|
|
1046
1044
|
}
|
|
1047
1045
|
function reopenIdSuffix() {
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare const POLICY_DENY_CAUSES: readonly ["no_approval_ui", "dont_ask", "deny_rule", "engine_floor"];
|
|
2
|
+
export type PolicyDenyCause = (typeof POLICY_DENY_CAUSES)[number];
|
|
3
|
+
export declare function readPolicyDenyCause(decision: object | null | undefined): PolicyDenyCause | undefined;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const POLICY_DENY_CAUSES = Object.freeze(['no_approval_ui', 'dont_ask', 'deny_rule', 'engine_floor']);
|
|
2
|
+
export function readPolicyDenyCause(decision) {
|
|
3
|
+
if (decision === null || typeof decision !== 'object')
|
|
4
|
+
return undefined;
|
|
5
|
+
let d;
|
|
6
|
+
try {
|
|
7
|
+
d = Object.getOwnPropertyDescriptor(decision, 'policyCause');
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
if (d === undefined || !Object.hasOwn(d, 'value'))
|
|
13
|
+
return undefined;
|
|
14
|
+
const v = d.value;
|
|
15
|
+
return typeof v === 'string' && POLICY_DENY_CAUSES.includes(v) ? v : undefined;
|
|
16
|
+
}
|
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
export type SessionAllowLeg = 'frame' | 'v1' | 'generic';
|
|
2
2
|
export declare function sessionAllowCarried(leg: SessionAllowLeg, approvalId: unknown): boolean;
|
|
3
|
+
export type SessionAllowWhy = 'given' | 'route_cannot_carry' | 'evidence_unreadable' | 'card_unreadable';
|
|
4
|
+
export type CardUnreadableEvidence = readonly string[] | 'absent' | 'unreadable';
|
|
5
|
+
export declare function readCardUnreadableEvidence(card: object): CardUnreadableEvidence;
|
|
6
|
+
export declare function sessionAllowCardReadable(card: object | null | undefined): Exclude<SessionAllowWhy, 'route_cannot_carry'>;
|
|
7
|
+
export declare function sessionAllowEligible(card: object | null | undefined): SessionAllowWhy;
|
|
@@ -5,3 +5,45 @@ export function sessionAllowCarried(leg, approvalId) {
|
|
|
5
5
|
return typeof approvalId === 'string' && approvalId !== '';
|
|
6
6
|
return false;
|
|
7
7
|
}
|
|
8
|
+
export function readCardUnreadableEvidence(card) {
|
|
9
|
+
try {
|
|
10
|
+
if (!('unreadableEvidence' in card))
|
|
11
|
+
return 'absent';
|
|
12
|
+
const d = Object.getOwnPropertyDescriptor(card, 'unreadableEvidence');
|
|
13
|
+
const list = d !== undefined && Object.hasOwn(d, 'value') ? d.value : undefined;
|
|
14
|
+
return Array.isArray(list) && list.every((k) => typeof k === 'string') ? list : 'unreadable';
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return 'unreadable';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function sessionAllowCardReadable(card) {
|
|
21
|
+
if (typeof card !== 'object' || card === null)
|
|
22
|
+
return 'card_unreadable';
|
|
23
|
+
try {
|
|
24
|
+
if ('cardPayloadUnreadable' in card)
|
|
25
|
+
return 'card_unreadable';
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return 'card_unreadable';
|
|
29
|
+
}
|
|
30
|
+
const evidence = readCardUnreadableEvidence(card);
|
|
31
|
+
if (evidence === 'unreadable')
|
|
32
|
+
return 'card_unreadable';
|
|
33
|
+
return evidence !== 'absent' && evidence.length > 0 ? 'evidence_unreadable' : 'given';
|
|
34
|
+
}
|
|
35
|
+
export function sessionAllowEligible(card) {
|
|
36
|
+
if (typeof card !== 'object' || card === null)
|
|
37
|
+
return 'card_unreadable';
|
|
38
|
+
let carried;
|
|
39
|
+
try {
|
|
40
|
+
const d = Object.getOwnPropertyDescriptor(card, 'allowSessionCarried');
|
|
41
|
+
carried = d !== undefined && Object.hasOwn(d, 'value') ? d.value : undefined;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return 'card_unreadable';
|
|
45
|
+
}
|
|
46
|
+
if (carried !== true)
|
|
47
|
+
return 'route_cannot_carry';
|
|
48
|
+
return sessionAllowCardReadable(card);
|
|
49
|
+
}
|
|
@@ -12,7 +12,7 @@ const WITHHELD_NOTICE = {
|
|
|
12
12
|
qualified_deny: 'Not written to this session’s rule record: that record holds whole tool names and command names only, and this rule has a part in parentheses it cannot carry — recording just the name could block more than this rule does.',
|
|
13
13
|
peer_wide: 'Not written to this session’s rule record: this rule names a server or agent peer without naming one of its tools, and an engine that matches that record by exact tool name would block none of that peer’s tools.',
|
|
14
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.',
|
|
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
|
|
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 form this record does not accept (for example with a dot or a space) · This record does not take entries of that form; record the exact name of a single tool instead.',
|
|
16
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}`,
|
|
17
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.',
|
|
18
18
|
namespace_covering_unconfirmed: msg('session.policy.withheld.namespaceCoveringUnconfirmed'),
|
|
@@ -4,6 +4,7 @@ import { readApprovalCardWord, readRuleOffers } from './toolApprovalWire.js';
|
|
|
4
4
|
import { CORE_SKILL_TOOL_NAME } from '../generated/toolNameTables.js';
|
|
5
5
|
import { toolPermissionRequestIdDomain } from '../seatContract.js';
|
|
6
6
|
import { permissionsDeclarationReport } from '../permissionsDeclaration.js';
|
|
7
|
+
import { readCardUnreadableEvidence, sessionAllowCardReadable, sessionAllowEligible } from './sessionAllowRoute.js';
|
|
7
8
|
const UNREADABLE = Symbol('unreadable');
|
|
8
9
|
function ownData(v, key) {
|
|
9
10
|
try {
|
|
@@ -90,18 +91,17 @@ function enginePremiseOf(card) {
|
|
|
90
91
|
return 'card_unreadable';
|
|
91
92
|
if (refused.inCard)
|
|
92
93
|
return refused.value === true ? 'remember_refused' : 'remember_unreadable';
|
|
93
|
-
const
|
|
94
|
-
if (
|
|
94
|
+
const list = readCardUnreadableEvidence(card);
|
|
95
|
+
if (list === 'unreadable')
|
|
95
96
|
return 'card_unreadable';
|
|
96
|
-
if (
|
|
97
|
+
if (list === 'absent')
|
|
97
98
|
return { evidenceUnreadable: false };
|
|
98
|
-
const list = ownData(card, 'unreadableEvidence');
|
|
99
|
-
if (!Array.isArray(list) || !list.every((k) => typeof k === 'string'))
|
|
100
|
-
return 'card_unreadable';
|
|
101
99
|
if (list.includes('rememberRefused'))
|
|
102
100
|
return 'remember_unreadable';
|
|
103
101
|
return { evidenceUnreadable: list.length > 0 };
|
|
104
102
|
}
|
|
103
|
+
const _sessionWhyPin = true;
|
|
104
|
+
void _sessionWhyPin;
|
|
105
105
|
export function standingRowsOf(card, host) {
|
|
106
106
|
if (typeof card !== 'object' || card === null)
|
|
107
107
|
return allClosed('card_unreadable');
|
|
@@ -110,11 +110,11 @@ export function standingRowsOf(card, host) {
|
|
|
110
110
|
if (typeof premise === 'string')
|
|
111
111
|
return allClosed(premise);
|
|
112
112
|
const evidenceUnreadable = premise.evidenceUnreadable;
|
|
113
|
-
const
|
|
114
|
-
const
|
|
113
|
+
const sessionWhy = sessionAllowEligible(card);
|
|
114
|
+
const sessionRow = sessionWhy === 'given';
|
|
115
115
|
let deploymentList;
|
|
116
116
|
if (!sessionRow) {
|
|
117
|
-
deploymentList =
|
|
117
|
+
deploymentList = sessionWhy;
|
|
118
118
|
}
|
|
119
119
|
else {
|
|
120
120
|
const pair = readAskClassPairState(card);
|
|
@@ -143,7 +143,7 @@ export function seatSessionRowOf(card, requestId) {
|
|
|
143
143
|
const domain = toolPermissionRequestIdDomain(requestId);
|
|
144
144
|
if (domain !== 'live:' && domain !== 'durable:')
|
|
145
145
|
return false;
|
|
146
|
-
return typeof enginePremiseOf(card) !== 'string';
|
|
146
|
+
return typeof enginePremiseOf(card) !== 'string' && sessionAllowCardReadable(card) === 'given';
|
|
147
147
|
}
|
|
148
148
|
catch {
|
|
149
149
|
return false;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { type HitlClientLike, type DecideOperationObserver } from './hitlBridge.js';
|
|
2
|
+
import { type ApprovalAskReason } from './approvalAskers.js';
|
|
3
|
+
export type { ApprovalAskReason, ApprovalAskReasonByHook, ApprovalAskReasonByPolicy } from './approvalAskers.js';
|
|
2
4
|
import { readApprovalCardWord, type ApprovalCardWord } from './approvalCardReading.js';
|
|
3
5
|
import { type ApprovalAllowRuleShadow } from './allowRuleShadow.js';
|
|
4
6
|
import { CORE_PERSISTED_RULE_MATCHES } from '../generated/engineFactTables.js';
|
|
@@ -9,6 +11,7 @@ import { type ApprovalResolution } from './approvalResolution.js';
|
|
|
9
11
|
import { type ApprovalMandateWord } from '../gateVocabulary.js';
|
|
10
12
|
import type { ToolPermissionRequest } from '../seatContract.js';
|
|
11
13
|
import { type ApprovalDecidedByReading } from '../approvalDecidedByCapability.js';
|
|
14
|
+
import { type PolicyDenyCause } from './policyDenyCause.js';
|
|
12
15
|
import { RULE_OFFER_MATCHES, RULE_OFFER_BATCH_MEMBER_KINDS, RULE_OFFER_UNCOVERED_REASONS, RULE_OFFERS_ABSENCE_REASONS } from '@sema-agent/sdk';
|
|
13
16
|
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
17
|
export declare function toolNameIsFsWrite(name: string): boolean;
|
|
@@ -71,6 +74,7 @@ export interface ApprovalCardDenyDecision {
|
|
|
71
74
|
kind: 'deny';
|
|
72
75
|
reason?: string;
|
|
73
76
|
settledBy?: ApprovalDenySettledBy;
|
|
77
|
+
policyCause?: PolicyDenyCause;
|
|
74
78
|
}
|
|
75
79
|
export declare const APPROVAL_DENY_SETTLED_BY_WORDS: readonly ["human", "policy"];
|
|
76
80
|
export type ApprovalDenySettledBy = (typeof APPROVAL_DENY_SETTLED_BY_WORDS)[number];
|
|
@@ -125,6 +129,8 @@ export interface ApprovalCardRequest {
|
|
|
125
129
|
approvalCard?: ApprovalCardWord;
|
|
126
130
|
askClass?: string;
|
|
127
131
|
classes?: readonly string[];
|
|
132
|
+
agentId?: string;
|
|
133
|
+
askReasons?: readonly ApprovalAskReason[];
|
|
128
134
|
boundGrants?: BoundSkillGrantsWire;
|
|
129
135
|
grantBinding?: ApprovalGrantBinding;
|
|
130
136
|
unreadableEvidence?: readonly string[];
|
|
@@ -163,7 +169,7 @@ export declare function approvalCardPortFor(sessionKey: string): ApprovalCardPor
|
|
|
163
169
|
export declare function __resetSettledApprovalsForTests(): void;
|
|
164
170
|
export declare function _resetApprovalCardPortForTest(): void;
|
|
165
171
|
export declare function surfaceApprovalCard(req: ApprovalCardRequest): Promise<ApprovalCardDecision>;
|
|
166
|
-
declare const REMEMBER_REFUSAL_CODES: readonly ["remember_not_in_proof", "capability.store_required", "decide.workflow_remember_unsupported", "decide.parked_remember_unsupported"];
|
|
172
|
+
declare const REMEMBER_REFUSAL_CODES: readonly ["remember_not_in_proof", "capability.store_required", "decide.workflow_remember_unsupported", "decide.parked_remember_unsupported", "remember_requires_binding"];
|
|
167
173
|
export type RememberRefusalCode = (typeof REMEMBER_REFUSAL_CODES)[number];
|
|
168
174
|
export declare function rememberRefusalOf(error: unknown): RememberRefusalCode | undefined;
|
|
169
175
|
export declare function surfaceFsApprovalAndDecide(deps: FsApprovalWireDeps, taskId: string, argsByCall: Map<string, unknown>, signal?: AbortSignal, parkGatedCallId?: string, parkToolName?: string): Promise<ApprovalResolution>;
|
|
@@ -276,7 +282,7 @@ export declare function approvalSupersedesDetail(card: Pick<ApprovalCardRequest,
|
|
|
276
282
|
export declare function readDenialLimitFallback(v: unknown): DenialLimitFallback | undefined;
|
|
277
283
|
export declare function readRuleOffers(v: unknown): RuleOfferReading[] | undefined;
|
|
278
284
|
export declare function readRuleOfferSupply(offers: unknown): RuleOfferReading[] | undefined;
|
|
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'>;
|
|
285
|
+
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' | 'agentId' | 'askReasons' | 'grantBinding' | 'unreadableEvidence' | 'message' | 'approvalCard'>;
|
|
280
286
|
export type ApprovalCardFacts = ApprovalCardV1Facts & Pick<ApprovalCardRequest, 'argsRewritten' | 'ruleOffers' | 'probeReason' | 'mandated' | 'mandate' | 'readRootCandidate' | 'origin' | 'boundGrants' | 'classifierNoRuling' | 'allowSessionCarried' | 'cardPayloadUnreadable'>;
|
|
281
287
|
export interface ApprovalCardFrameFactsOptions {
|
|
282
288
|
readonly streamArgs?: object;
|
|
@@ -7,6 +7,7 @@ import { trimEditedRuleText } from './editedRuleTextPrecheck.js';
|
|
|
7
7
|
import { appendOwnElement, guardedRead, readAskOrigin, readOwnDataNonEmptyString, readOwnDataValue, readOwnNonEmptyString } from '../ownKey.js';
|
|
8
8
|
import { ownReadingSlot } from '../engineWireFor.js';
|
|
9
9
|
import { DEPLOYMENT_LIST_ASK_CLASSES, readAskClassPair } from './askClassPair.js';
|
|
10
|
+
import { approvalAgentIdOf, approvalAskReasonsOf } from './approvalAskers.js';
|
|
10
11
|
import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
|
|
11
12
|
import { approvalWindowKeysOf, readApprovalCardWord, readRequiresRealApproval } from './approvalCardReading.js';
|
|
12
13
|
import { readAllowRuleShadowFromWire, retiredAllowRuleShadowKeyPresent, RETIRED_SHADOW_EVIDENCE_LABEL } from './allowRuleShadow.js';
|
|
@@ -15,7 +16,7 @@ import { readEngineActiveBgTasks } from '../fleet/fleetLedger.js';
|
|
|
15
16
|
import { CORE_SKILL_TOOL_NAME } from '../generated/toolNameTables.js';
|
|
16
17
|
import { CORE_ALLOW_ENTRY_INERT_REASONS, CORE_PERSISTED_RULE_MATCHES } from '../generated/engineFactTables.js';
|
|
17
18
|
import { msg, renderMessage } from '../i18n/translate.js';
|
|
18
|
-
import { sessionAllowCarried } from './sessionAllowRoute.js';
|
|
19
|
+
import { sessionAllowCarried, sessionAllowEligible } from './sessionAllowRoute.js';
|
|
19
20
|
import { INTERRUPT_DENY_REASON, observeCancelByDeny, surfaceApprovalParking, surfaceRememberNotApplied, surfaceEditNotForwarded, surfaceEditRefusedOnBlindAsk, surfaceRuleArmNotSent, surfaceRuleArmRejected } from './hitlHostSurface.js';
|
|
20
21
|
import { approvalCallKey, liveFrameCallKey } from './gateIdentity.js';
|
|
21
22
|
import { displayUntrusted, quoteUntrustedForSentence } from '../displayUntrusted.js';
|
|
@@ -28,6 +29,7 @@ import { parkRowDeadlineState } from './parkExpiryJudgements.js';
|
|
|
28
29
|
import { isApprovalMandateWord } from '../gateVocabulary.js';
|
|
29
30
|
import { approvalDecidedByAdmitted } from '../approvalDecidedByCapability.js';
|
|
30
31
|
import { decideFailureNote } from '../decideFailureNote.js';
|
|
32
|
+
import { readPolicyDenyCause } from './policyDenyCause.js';
|
|
31
33
|
import { slotPresenceOf, wireFailureTextsOf } from '../wireFailureShape.js';
|
|
32
34
|
import { APPROVAL_LIVE_FACE_KEYS, approvalLiveFaceOf, hasOwnStrictTrue } from './approvalLiveFace.js';
|
|
33
35
|
import { subagentSourceSessionOf } from './subagentSource.js';
|
|
@@ -157,11 +159,13 @@ function cardPortMissReason() {
|
|
|
157
159
|
return APPROVAL_CARD_PORT_KEY_MISMATCH_REASON;
|
|
158
160
|
return NO_APPROVAL_CARD_PORT_REASON;
|
|
159
161
|
}
|
|
162
|
+
const LEGACY_REMEMBER_REFUSAL_CODE = 'remember_requires_binding';
|
|
160
163
|
const REMEMBER_REFUSAL_CODES = Object.freeze([
|
|
161
164
|
'remember_not_in_proof',
|
|
162
165
|
'capability.store_required',
|
|
163
166
|
DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED,
|
|
164
167
|
'decide.parked_remember_unsupported',
|
|
168
|
+
LEGACY_REMEMBER_REFUSAL_CODE,
|
|
165
169
|
]);
|
|
166
170
|
export function rememberRefusalOf(error) {
|
|
167
171
|
return rememberRefusalIn(readWireErrorCodeReading(error));
|
|
@@ -449,7 +453,7 @@ export async function surfaceFsApprovalAndDecide(deps, taskId, argsByCall, signa
|
|
|
449
453
|
closeDurableRead(readMark);
|
|
450
454
|
}
|
|
451
455
|
if (!found.ok)
|
|
452
|
-
return { kind: 'not_sent', lane: 'decide', cause: found.code ?? 'fetch_failed', ...(found.reason !== '' ? { detail: found.reason } : {}) };
|
|
456
|
+
return { kind: 'not_sent', lane: 'decide', cause: found.code ?? 'fetch_failed', ...(found.reason !== '' ? { detail: found.reason } : {}), ...(found.rowVerdict !== undefined ? { rowVerdict: found.rowVerdict } : {}) };
|
|
453
457
|
if (held !== undefined)
|
|
454
458
|
return joinDurableDecision(held, deps, signal);
|
|
455
459
|
const rowIdentity = parkRowHolderIdentity(found.pending, found.gatedCallId);
|
|
@@ -500,7 +504,7 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
|
|
|
500
504
|
const durableRuleOffersAbsence = pending.ruleOffersAbsence;
|
|
501
505
|
hold.presented = true;
|
|
502
506
|
deps.onPresented?.();
|
|
503
|
-
const
|
|
507
|
+
const cardRequest = withUnreadableEvidence(pending, 'row', {
|
|
504
508
|
toolName,
|
|
505
509
|
args,
|
|
506
510
|
callKey,
|
|
@@ -517,6 +521,7 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
|
|
|
517
521
|
...approvalLiveFaceOf(pending).facts,
|
|
518
522
|
...((w) => (w !== undefined ? { approvalCard: w } : {}))(readApprovalCardWord(pending)),
|
|
519
523
|
...(readAskClassPair(pending) ?? {}),
|
|
524
|
+
...((r) => (r !== undefined ? { askReasons: r } : {}))(approvalAskReasonsOf(pending)),
|
|
520
525
|
...(pending.gateKind === 'irreversible_ask' ? { irreversibleParkGate: true } : {}),
|
|
521
526
|
...(pending.hasBidiControls === true ? { hasBidiControls: true } : {}),
|
|
522
527
|
...(ruleOffersReadOnly !== undefined ? { ruleOffersReadOnly } : {}),
|
|
@@ -533,7 +538,8 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
|
|
|
533
538
|
allowSessionCarried: true,
|
|
534
539
|
grantBinding: readApprovalGrantBinding(pending),
|
|
535
540
|
parkRow: parkRowOf(taskId, pending, gatedCallId),
|
|
536
|
-
})
|
|
541
|
+
});
|
|
542
|
+
const card = await surfaceCallTrackedCard(gatedCallId, undefined, ledgerSlot, cardRequest);
|
|
537
543
|
hold.phase = 'respond';
|
|
538
544
|
if (rowArgsGap !== undefined && card.kind === 'allow' && card.updatedInput !== undefined) {
|
|
539
545
|
hostLog('error', `liveToolApprovalWire: approval card for ${gatedCallId ?? `task ${taskId}`} returned an edited approval, but the tool input is ${rowArgsGap === 'truncated' ? 'a size marker on the pending row' : 'missing from both the stream and the pending row'} — nothing sent (the park stays pending)`);
|
|
@@ -560,10 +566,15 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
|
|
|
560
566
|
let rememberStop;
|
|
561
567
|
try {
|
|
562
568
|
let rememberRefused = false;
|
|
563
|
-
const
|
|
569
|
+
const sessionAllowWhy = sessionAllowEligible(cardRequest);
|
|
570
|
+
const sessionDowngraded = card.allowSession === true && sessionAllowWhy !== 'given';
|
|
571
|
+
if (sessionDowngraded)
|
|
572
|
+
hostLog('debug', `liveToolApprovalWire: ${gatedCallId ?? `task ${taskId}`} session-wide allow taken as allow-once — the card is not eligible (${sessionAllowWhy})`);
|
|
573
|
+
const answered = sessionDowngraded ? { ...card, allowSession: false } : card;
|
|
574
|
+
const approveBody = answerPayloadFor(answered, 'decide');
|
|
564
575
|
const { remember: _remember, ...plainApproveBody } = approveBody;
|
|
565
576
|
void _remember;
|
|
566
|
-
emitAnswerCommitted(callKey, 'decide',
|
|
577
|
+
emitAnswerCommitted(callKey, 'decide', answered, approveBody);
|
|
567
578
|
if (approveBody.remember === 'session') {
|
|
568
579
|
let sessionReceipt;
|
|
569
580
|
let sessionDecided = false;
|
|
@@ -591,7 +602,7 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
|
|
|
591
602
|
}
|
|
592
603
|
const raw = await bridge.decideTool(plainApproveBody, gatedCallId, decideOpts, pending);
|
|
593
604
|
const receipt = readDecideReceipt(raw);
|
|
594
|
-
if (rememberRefused)
|
|
605
|
+
if (rememberRefused || sessionDowngraded)
|
|
595
606
|
surfaceRememberNotApplied();
|
|
596
607
|
return { kind: 'decided', ...at, decision: 'allow', ...(receipt !== undefined ? { receipt } : {}) };
|
|
597
608
|
}
|
|
@@ -603,6 +614,7 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
|
|
|
603
614
|
}
|
|
604
615
|
case 'deny': {
|
|
605
616
|
const denySettledByForWire = readApprovalDenySettledBy(card);
|
|
617
|
+
const denyPolicyCauseForWire = readPolicyDenyCause(card);
|
|
606
618
|
const attributionKeys = decisionAttributionKeyOf(decidedByReading, denySettledByForWire);
|
|
607
619
|
const denyReasonSnapshot = ((r) => (typeof r === 'string' ? r : undefined))(card.reason);
|
|
608
620
|
let lastSendCarriedAttribution = false;
|
|
@@ -636,7 +648,7 @@ async function presentFsApprovalAndDecide(deps, taskId, argsByCall, signal, foun
|
|
|
636
648
|
const attributionDropped = ladder.dropped;
|
|
637
649
|
const attributionRenamed = ladder.renamed;
|
|
638
650
|
const receipt = readDecideReceipt(raw);
|
|
639
|
-
const deny = denyFactsOf(denySettledByForWire, denyReasonSnapshot, attributionDropped, attributionRenamed);
|
|
651
|
+
const deny = denyFactsOf(denySettledByForWire, denyReasonSnapshot, attributionDropped, attributionRenamed, denyPolicyCauseForWire);
|
|
640
652
|
return {
|
|
641
653
|
kind: 'decided',
|
|
642
654
|
...at,
|
|
@@ -1008,14 +1020,16 @@ function readDecidedByReadingOf(holder) {
|
|
|
1008
1020
|
function decidedByReadingSnapshotOf(holder) {
|
|
1009
1021
|
return approvalDecidedByAdmitted(readDecidedByReadingOf(holder)) ? Object.freeze({ kind: 'present', value: true }) : undefined;
|
|
1010
1022
|
}
|
|
1011
|
-
function denyFactsOf(settledBy, reasonSnapshot, dropped, renamed) {
|
|
1023
|
+
function denyFactsOf(settledBy, reasonSnapshot, dropped, renamed, policyCauseSnapshot) {
|
|
1012
1024
|
const reason = reasonSnapshot !== undefined && reasonSnapshot.trim() !== '' ? reasonSnapshot : undefined;
|
|
1013
1025
|
const attributionKey = dropped ? 'dropped' : renamed ? 'renamed' : undefined;
|
|
1026
|
+
const policyCause = settledBy === 'policy' ? policyCauseSnapshot : undefined;
|
|
1014
1027
|
if (settledBy === undefined && reason === undefined && attributionKey === undefined)
|
|
1015
1028
|
return undefined;
|
|
1016
1029
|
return {
|
|
1017
1030
|
...(settledBy !== undefined ? { settledBy } : {}),
|
|
1018
1031
|
...(reason !== undefined ? { reason } : {}),
|
|
1032
|
+
...(policyCause !== undefined ? { policyCause } : {}),
|
|
1019
1033
|
...(attributionKey !== undefined ? { attributionKey } : {}),
|
|
1020
1034
|
};
|
|
1021
1035
|
}
|
|
@@ -1816,6 +1830,16 @@ function cardFactsOf(carrier, leg, opts, liveFacts, approvalId) {
|
|
|
1816
1830
|
if (w !== undefined)
|
|
1817
1831
|
out.approvalCard = w;
|
|
1818
1832
|
});
|
|
1833
|
+
v1Bit(() => {
|
|
1834
|
+
const id = approvalAgentIdOf(carrier);
|
|
1835
|
+
if (id !== undefined)
|
|
1836
|
+
out.agentId = id;
|
|
1837
|
+
});
|
|
1838
|
+
v1Bit(() => {
|
|
1839
|
+
const reasons = approvalAskReasonsOf(carrier);
|
|
1840
|
+
if (reasons !== undefined)
|
|
1841
|
+
out.askReasons = reasons;
|
|
1842
|
+
});
|
|
1819
1843
|
}
|
|
1820
1844
|
v1Bit(() => {
|
|
1821
1845
|
const pair = readAskClassPair(carrier);
|
|
@@ -2038,15 +2062,22 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
|
|
|
2038
2062
|
enterLiveRespondPhase(hold);
|
|
2039
2063
|
const noteCapable = lane?.approvalDecisionNoteCapable === true;
|
|
2040
2064
|
const denyReasonRead = card.kind === 'deny' ? ((r) => (typeof r === 'string' ? r : undefined))(card.reason) : undefined;
|
|
2065
|
+
const sessionAllowWhy = sessionAllowEligible(facts);
|
|
2066
|
+
const sessionDowngraded = card.kind === 'allow' && card.allowSession === true && sessionAllowWhy !== 'given';
|
|
2067
|
+
if (sessionDowngraded)
|
|
2068
|
+
hostLog('debug', `liveToolApprovalWire: ${approvalId} session-wide allow taken as allow-once — the card is not eligible (${sessionAllowWhy})`);
|
|
2041
2069
|
const answerSnapshot = card.kind === 'deny'
|
|
2042
2070
|
? ((by) => ({ kind: 'deny', ...(denyReasonRead !== undefined ? { reason: denyReasonRead } : {}), ...(by !== undefined ? { settledBy: by } : {}) }))(readApprovalDenySettledBy(card))
|
|
2043
|
-
: card
|
|
2071
|
+
: sessionDowngraded && card.kind === 'allow'
|
|
2072
|
+
? { ...card, allowSession: false }
|
|
2073
|
+
: card;
|
|
2044
2074
|
const basePayload = answerPayloadFor(answerSnapshot, 'respond', { approvalDecisionNoteCapable: noteCapable });
|
|
2045
2075
|
const decision = basePayload?.decision ?? 'deny';
|
|
2046
2076
|
if (card.kind === 'allow' && typeof card.note === 'string' && card.note.trim() !== '' && answerCarriage({ callKey: liveFrameCallKey(approvalId) }).allowNote === 'none') {
|
|
2047
2077
|
hostLog('debug', `liveToolApprovalWire: ${approvalId} card carried a note on Yes (len=${card.note.length}) — this engine has no field that brings it to the model, so it was not sent`);
|
|
2048
2078
|
}
|
|
2049
2079
|
const denySettledByForWire = card.kind === 'deny' ? readApprovalDenySettledBy(card) : undefined;
|
|
2080
|
+
const denyPolicyCauseForWire = card.kind === 'deny' ? readPolicyDenyCause(card) : undefined;
|
|
2050
2081
|
const attributionKeys = decision === 'deny' ? decisionAttributionKeyOf(decidedByReading, denySettledByForWire) : {};
|
|
2051
2082
|
const sentAttributionKey = carriesAttributionKey(attributionKeys);
|
|
2052
2083
|
let lastSendCarriedAttribution = sentAttributionKey;
|
|
@@ -2273,6 +2304,8 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
|
|
|
2273
2304
|
logAfterDecided(`liveToolApprovalWire: ${approvalId} allow_session ack rememberApplied=false — grant not stored, surfacing honest notice`);
|
|
2274
2305
|
surfaceRememberNotApplied();
|
|
2275
2306
|
}
|
|
2307
|
+
if (replayed === undefined && sessionDowngraded)
|
|
2308
|
+
surfaceRememberNotApplied();
|
|
2276
2309
|
if (respondOpts.updatedInput !== undefined && applied?.updatedInputForwarded === false) {
|
|
2277
2310
|
logAfterDecided(`liveToolApprovalWire: ${approvalId} edited args were NOT forwarded (ack.updatedInputForwarded=false) — the tool runs on the ORIGINAL input`);
|
|
2278
2311
|
surfaceEditNotForwarded();
|
|
@@ -2286,7 +2319,7 @@ async function presentToolApprovalFrameAndRespond(frame, carriers, approvalId, r
|
|
|
2286
2319
|
'this answer was not delivered, reporting the earlier decision');
|
|
2287
2320
|
return { kind: 'decided', lane: 'respond', decision: replayed.decision, ack: replayed };
|
|
2288
2321
|
}
|
|
2289
|
-
const deny = decision === 'deny' ? denyFactsOf(denySettledByForWire, denyReasonSnapshot, attributionDropped, attributionRenamed) : undefined;
|
|
2322
|
+
const deny = decision === 'deny' ? denyFactsOf(denySettledByForWire, denyReasonSnapshot, attributionDropped, attributionRenamed, denyPolicyCauseForWire) : undefined;
|
|
2290
2323
|
return { kind: 'decided', lane: 'respond', decision, ...(ack !== undefined ? { ack } : {}), ...(deny !== undefined ? { deny } : {}) };
|
|
2291
2324
|
}
|
|
2292
2325
|
catch (e) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function hooksReaderNotReadKeys(serverVersion: string | undefined): readonly string[];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { engineVersionTier } from './supportFloor.js';
|
|
2
|
+
const HOOKS_READER_GENERATIONS = Object.freeze([
|
|
3
|
+
Object.freeze({ from: '7.139.0', below: '7.147.0', notRead: Object.freeze(['onFailure']) }),
|
|
4
|
+
]);
|
|
5
|
+
const HOOKS_READER_FAIL_CLOSED = Object.freeze([...new Set(HOOKS_READER_GENERATIONS.flatMap((g) => g.notRead))]);
|
|
6
|
+
export function hooksReaderNotReadKeys(serverVersion) {
|
|
7
|
+
for (const g of HOOKS_READER_GENERATIONS) {
|
|
8
|
+
if (engineVersionTier(serverVersion, g.from) === 'engine' && engineVersionTier(serverVersion, g.below) === 'legacy')
|
|
9
|
+
return g.notRead;
|
|
10
|
+
}
|
|
11
|
+
return HOOKS_READER_FAIL_CLOSED;
|
|
12
|
+
}
|