@sema-agent/client-core 0.91.0 → 0.92.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 +172 -0
- package/README.md +31 -21
- package/dist/adapt/arms.js +4 -12
- package/dist/adapt/panelTasks.d.ts +0 -2
- package/dist/adapt/panelTasks.js +6 -26
- package/dist/adapt.js +2 -2
- package/dist/adapter/activeRunSelfHeal.d.ts +0 -2
- package/dist/adapter/activeRunSelfHeal.js +5 -27
- package/dist/adapter/downstream/terminalToSdkResult.js +16 -7
- package/dist/adapter/runStream.js +3 -1
- package/dist/askSurvivesStandingAnswer.d.ts +4 -1
- package/dist/askSurvivesStandingAnswer.js +13 -0
- package/dist/autoModeUnavailable.js +3 -2
- package/dist/cloudConfigWireCaps.js +5 -4
- package/dist/compensations.js +5 -5
- package/dist/controlRouter.d.ts +23 -23
- package/dist/controlRouter.js +114 -49
- package/dist/coreValuePorts.d.ts +4 -2
- package/dist/coreValuePorts.js +5 -0
- package/dist/decideFailureNote.js +4 -2
- package/dist/decideReceipt.d.ts +9 -0
- package/dist/decideReceipt.js +62 -1
- package/dist/displayUntrusted.js +2 -1
- package/dist/effectiveFacts.d.ts +14 -4
- package/dist/effectiveFacts.js +45 -5
- package/dist/engineAgentAbsence.js +2 -1
- package/dist/engineCapReader.js +16 -10
- package/dist/engineCapsCache.d.ts +2 -0
- package/dist/engineCapsCache.js +13 -2
- package/dist/engineErrorCodes.d.ts +14 -0
- package/dist/engineErrorCodes.js +37 -0
- package/dist/engineNoticeCodes.d.ts +14 -0
- package/dist/engineNoticeCodes.js +71 -0
- package/dist/executionLaneCapability.js +2 -0
- package/dist/gateOutcome.js +2 -17
- package/dist/gateVocabulary.d.ts +2 -4
- package/dist/gateVocabulary.js +3 -11
- package/dist/generated/engineFactTables.d.ts +16 -1
- package/dist/generated/engineFactTables.js +81 -0
- package/dist/generated/engineNoticeTables.js +3 -0
- package/dist/generated/settingsSchemaTables.d.ts +8 -0
- package/dist/generated/settingsSchemaTables.js +40 -0
- package/dist/hitl/allowRuleShadow.d.ts +21 -0
- package/dist/hitl/allowRuleShadow.js +167 -0
- package/dist/hitl/askClassPair.d.ts +14 -0
- package/dist/hitl/askClassPair.js +42 -0
- package/dist/hitl/askGateWire.js +1 -0
- package/dist/hitl/askSurvivesPosture.d.ts +5 -0
- package/dist/hitl/askSurvivesPosture.js +17 -0
- package/dist/hitl/automaticAnswerVerdict.d.ts +21 -0
- package/dist/hitl/automaticAnswerVerdict.js +143 -0
- package/dist/hitl/crashConverged.d.ts +1 -0
- package/dist/hitl/crashConverged.js +1 -1
- package/dist/hitl/frameRouter.d.ts +2 -0
- package/dist/hitl/frameRouter.js +66 -14
- package/dist/hitl/gateLedger.d.ts +6 -0
- package/dist/hitl/gateLedger.js +34 -5
- package/dist/hitl/hitlHostSurface.d.ts +2 -0
- package/dist/hitl/hitlHostSurface.js +7 -0
- package/dist/hitl/livePendingAsk.js +24 -5
- package/dist/hitl/parkResolver.js +3 -1
- package/dist/hitl/persistedRulesWire.d.ts +4 -1
- package/dist/hitl/persistedRulesWire.js +9 -2
- package/dist/hitl/planReviewWire.js +2 -2
- package/dist/hitl/replayedApproval.d.ts +21 -0
- package/dist/hitl/replayedApproval.js +89 -0
- package/dist/hitl/sessionPolicyDeliverable.js +20 -2
- package/dist/hitl/sessionPolicyWire.d.ts +8 -0
- package/dist/hitl/sessionPolicyWire.js +12 -2
- package/dist/hitl/toolApprovalWire.d.ts +6 -1
- package/dist/hitl/toolApprovalWire.js +20 -40
- package/dist/hookMatcherVerdict.js +2 -1
- package/dist/humanTurn.js +3 -4
- package/dist/index.d.ts +15 -5
- package/dist/index.js +12 -3
- package/dist/inheritEnvWire.js +3 -4
- package/dist/mcpLiveness.js +2 -1
- package/dist/memoryVerbsWire.d.ts +1 -1
- package/dist/memoryVerbsWire.js +3 -0
- package/dist/model/catalog.js +2 -1
- package/dist/model/providerPresets.d.ts +3 -1
- package/dist/model/tierVocabulary.d.ts +2 -1
- package/dist/model/tierVocabulary.js +3 -8
- package/dist/peerFrames.js +2 -2
- package/dist/permissionRuleIssue.js +9 -6
- package/dist/permissionsDeclaration.d.ts +7 -1
- package/dist/permissionsDeclaration.js +29 -20
- package/dist/pluginHooksWire.js +2 -11
- package/dist/promptProfileWireCaps.d.ts +2 -1
- package/dist/protocolSegment.d.ts +9 -0
- package/dist/protocolSegment.js +28 -0
- package/dist/request/taskRequest.js +1 -15
- package/dist/ruleCompileVerdict.d.ts +2 -0
- package/dist/ruleCompileVerdict.js +13 -0
- package/dist/ruleRemovalConsequence.d.ts +2 -1
- package/dist/runCancelContext.d.ts +12 -0
- package/dist/runCancelContext.js +83 -1
- package/dist/seam.js +1 -1
- package/dist/seatContract.d.ts +2 -1
- package/dist/sessionMemoryStatus.d.ts +2 -4
- package/dist/sessionSyncFileHistoryCapability.d.ts +18 -0
- package/dist/sessionSyncFileHistoryCapability.js +50 -0
- package/dist/skillToolListsCapability.d.ts +18 -0
- package/dist/skillToolListsCapability.js +50 -0
- package/dist/sqlEngineCapability.js +1 -8
- package/dist/steerPriorityCapability.d.ts +18 -0
- package/dist/steerPriorityCapability.js +80 -0
- package/dist/steerReceipt.d.ts +9 -0
- package/dist/steerReceipt.js +25 -0
- package/dist/subagent/engineSubagentSteer.js +2 -9
- package/dist/supportFloor.js +2 -2
- package/dist/toolResult.d.ts +8 -0
- package/dist/toolResult.js +47 -3
- package/dist/types/engineState.d.ts +2 -12
- package/dist/userInvokedSkillsCapability.d.ts +18 -0
- package/dist/userInvokedSkillsCapability.js +50 -0
- package/dist/wireFailureShape.d.ts +2 -0
- package/dist/wireFailureShape.js +4 -0
- package/dist/workflowsJournalCapability.d.ts +18 -0
- package/dist/workflowsJournalCapability.js +50 -0
- package/dist/writeProtectionCapability.d.ts +2 -4
- package/docs/INTEGRATION-CLIENTS.md +624 -12
- package/package.json +2 -2
package/dist/hitl/gateLedger.js
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
export const SEMA_COLLATERAL_ABORT_KEY = '_sema_collateral_abort';
|
|
2
2
|
export const SEMA_DENIAL_KIND_KEY = '_sema_denial_kind';
|
|
3
|
+
export function durableSeqOf(ev) {
|
|
4
|
+
const rawSeq = ev.id ?? ev.seq;
|
|
5
|
+
if (typeof rawSeq === 'string' && rawSeq.length > 0)
|
|
6
|
+
return rawSeq;
|
|
7
|
+
if (typeof rawSeq === 'number' && Number.isFinite(rawSeq))
|
|
8
|
+
return String(rawSeq);
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
export function hasLedgerPositionKey(ev) {
|
|
12
|
+
const { id, seq } = ev;
|
|
13
|
+
return (id !== undefined && id !== null) || (seq !== undefined && seq !== null);
|
|
14
|
+
}
|
|
15
|
+
export const PREAMBLE_APPROVALS_MAX_ENTRIES = 4096;
|
|
3
16
|
export const START_ARGS_MAX_ENTRIES = 256;
|
|
4
17
|
function normalizeDenyAttribution(a) {
|
|
5
18
|
if (a === undefined)
|
|
@@ -74,13 +87,12 @@ export function createGateLedger() {
|
|
|
74
87
|
let denyStampNextFsEnd = false;
|
|
75
88
|
let denyStampAttribution;
|
|
76
89
|
let seq;
|
|
90
|
+
const preambleApprovalIds = new Set();
|
|
77
91
|
return {
|
|
78
92
|
noteSeq(ev) {
|
|
79
|
-
const
|
|
80
|
-
if (
|
|
81
|
-
seq =
|
|
82
|
-
else if (typeof rawSeq === 'number' && Number.isFinite(rawSeq))
|
|
83
|
-
seq = String(rawSeq);
|
|
93
|
+
const s = durableSeqOf(ev);
|
|
94
|
+
if (s !== undefined)
|
|
95
|
+
seq = s;
|
|
84
96
|
},
|
|
85
97
|
lastSeq() {
|
|
86
98
|
return seq;
|
|
@@ -91,6 +103,23 @@ export function createGateLedger() {
|
|
|
91
103
|
gatedStartArgsByCall.clear();
|
|
92
104
|
startArgsByCall.clear();
|
|
93
105
|
},
|
|
106
|
+
notePreambleApproval(approvalId) {
|
|
107
|
+
if (typeof approvalId !== 'string' || approvalId === '')
|
|
108
|
+
return;
|
|
109
|
+
preambleApprovalIds.delete(approvalId);
|
|
110
|
+
preambleApprovalIds.add(approvalId);
|
|
111
|
+
if (preambleApprovalIds.size > PREAMBLE_APPROVALS_MAX_ENTRIES) {
|
|
112
|
+
const oldest = preambleApprovalIds.values().next().value;
|
|
113
|
+
if (oldest !== undefined)
|
|
114
|
+
preambleApprovalIds.delete(oldest);
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
preambleApprovals() {
|
|
118
|
+
return preambleApprovalIds;
|
|
119
|
+
},
|
|
120
|
+
beginStreamSegment() {
|
|
121
|
+
preambleApprovalIds.clear();
|
|
122
|
+
},
|
|
94
123
|
markStarted(callId) {
|
|
95
124
|
if (startedCalls.has(callId))
|
|
96
125
|
return false;
|
|
@@ -28,6 +28,8 @@ export declare const REMEMBER_NOT_APPLIED_WARN_TEXT = "this session-wide allow w
|
|
|
28
28
|
export declare const EDIT_NOT_FORWARDED_WARN_TEXT = "your edited tool input was NOT forwarded \u2014 the tool is running with the ORIGINAL input you saw before editing";
|
|
29
29
|
export declare function surfaceRememberNotApplied(): void;
|
|
30
30
|
export declare function surfaceEditNotForwarded(): void;
|
|
31
|
+
export declare const REPLAYED_APPROVAL_LIST_UNREADABLE_WARN_TEXT: string;
|
|
32
|
+
export declare function surfaceReplayedApprovalListUnreadable(): void;
|
|
31
33
|
export declare const EDIT_REFUSED_ON_BLIND_ASK_WARN_TEXT: string;
|
|
32
34
|
export declare function surfaceEditRefusedOnBlindAsk(): void;
|
|
33
35
|
export declare const RULE_NOT_SENT_WARN_TEXT = "your \"don't ask again\" choice was NOT sent to this engine \u2014 support for that rule form could not be confirmed. The approval itself went through, but you will be asked again";
|
|
@@ -72,6 +72,13 @@ export function surfaceRememberNotApplied() {
|
|
|
72
72
|
export function surfaceEditNotForwarded() {
|
|
73
73
|
surfaceSelfClearingWarn(EDIT_NOT_FORWARDED_KEY, EDIT_NOT_FORWARDED_WARN_TEXT, EDIT_NOT_FORWARDED_TIMEOUT_MS);
|
|
74
74
|
}
|
|
75
|
+
export const REPLAYED_APPROVAL_LIST_UNREADABLE_WARN_TEXT = "Could not read the pending-approval list, so an approval request read back from this run's event log was not raised as a question, " +
|
|
76
|
+
'and no decision was sent for it.';
|
|
77
|
+
const REPLAYED_APPROVAL_LIST_UNREADABLE_KEY = 'hitl-replayed-approval-list-unreadable';
|
|
78
|
+
const REPLAYED_APPROVAL_LIST_UNREADABLE_TIMEOUT_MS = 10_000;
|
|
79
|
+
export function surfaceReplayedApprovalListUnreadable() {
|
|
80
|
+
surfaceSelfClearingWarn(REPLAYED_APPROVAL_LIST_UNREADABLE_KEY, REPLAYED_APPROVAL_LIST_UNREADABLE_WARN_TEXT, REPLAYED_APPROVAL_LIST_UNREADABLE_TIMEOUT_MS);
|
|
81
|
+
}
|
|
75
82
|
export const EDIT_REFUSED_ON_BLIND_ASK_WARN_TEXT = 'That approval was NOT sent: this request does not expose its tool arguments here, so an edited approval cannot be applied. ' +
|
|
76
83
|
'The request is still waiting — approve or deny it as it is.';
|
|
77
84
|
const EDIT_REFUSED_ON_BLIND_ASK_KEY = 'hitl-edit-refused-blind-ask';
|
|
@@ -5,20 +5,39 @@ function nonEmptyString(v) {
|
|
|
5
5
|
function nonNegativeFinite(v) {
|
|
6
6
|
return typeof v === 'number' && Number.isFinite(v) && v >= 0;
|
|
7
7
|
}
|
|
8
|
+
function snapshotMembers(raw) {
|
|
9
|
+
const length = raw.length;
|
|
10
|
+
if (typeof length !== 'number' || !Number.isSafeInteger(length) || length < 0)
|
|
11
|
+
return undefined;
|
|
12
|
+
const out = [];
|
|
13
|
+
for (let i = 0; i < length; i++) {
|
|
14
|
+
const d = Object.getOwnPropertyDescriptor(raw, i);
|
|
15
|
+
if (d === undefined || !('value' in d))
|
|
16
|
+
return undefined;
|
|
17
|
+
out.push(d.value);
|
|
18
|
+
}
|
|
19
|
+
return raw.length === length ? out : undefined;
|
|
20
|
+
}
|
|
8
21
|
export function readLivePendingRows(body) {
|
|
9
22
|
if (body === null || typeof body !== 'object' || Array.isArray(body))
|
|
10
23
|
return { kind: 'malformed' };
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
24
|
+
const field = Object.getOwnPropertyDescriptor(body, 'livePending');
|
|
25
|
+
if (field === undefined)
|
|
26
|
+
return 'livePending' in body ? { kind: 'malformed' } : { kind: 'not_reported' };
|
|
27
|
+
if (!('value' in field))
|
|
28
|
+
return { kind: 'malformed' };
|
|
29
|
+
const raw = field.value;
|
|
14
30
|
if (raw === undefined)
|
|
15
31
|
return { kind: 'not_reported' };
|
|
16
32
|
if (!Array.isArray(raw))
|
|
17
33
|
return { kind: 'malformed' };
|
|
34
|
+
const members = snapshotMembers(raw);
|
|
35
|
+
if (members === undefined)
|
|
36
|
+
return { kind: 'malformed' };
|
|
18
37
|
const rows = [];
|
|
19
38
|
const seen = new Set();
|
|
20
39
|
let dropped = 0;
|
|
21
|
-
for (const item of
|
|
40
|
+
for (const item of members) {
|
|
22
41
|
if (item === null || typeof item !== 'object' || Array.isArray(item)) {
|
|
23
42
|
dropped++;
|
|
24
43
|
continue;
|
|
@@ -46,7 +65,7 @@ export function readLivePendingRows(body) {
|
|
|
46
65
|
...(isToolApprovalFrame(o.frame) && o.frame.type === 'tool_approval' && o.frame.approvalId === o.approvalId ? { frame: o.frame } : {}),
|
|
47
66
|
});
|
|
48
67
|
}
|
|
49
|
-
if (
|
|
68
|
+
if (members.length > 0 && rows.length === 0)
|
|
50
69
|
return { kind: 'malformed' };
|
|
51
70
|
return { kind: 'present', rows, dropped };
|
|
52
71
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { putOwnKey } from '../ownKey.js';
|
|
2
2
|
import { DecideTransportRetryExhaustedError, HitlBridge, HitlSafetyError, NO_QUESTION_OVERLAY_REASON, findPendingForTask, isNoDecisionSurfaceReason, readDecideCurrentPending, readWireErrorCode, } from './hitlBridge.js';
|
|
3
|
-
import { CHECKPOINT_UNSUPPORTED_VERSION, PARKED_RESUME_STARTUP_FAILED, RESUME_POLICY_FOLD_CODE, isDecideParkMovedCode, refusalSaysStillPending } from '../engineErrorCodes.js';
|
|
3
|
+
import { ASK_GONE, CHECKPOINT_UNSUPPORTED_VERSION, PARKED_RESUME_STARTUP_FAILED, RESUME_POLICY_FOLD_CODE, isDecideParkMovedCode, refusalSaysStillPending } from '../engineErrorCodes.js';
|
|
4
4
|
import { publishQuestionFrame, registerLocalQuestionResponder, hasQuestionOverlay, } from '../liveQuestionStore.js';
|
|
5
5
|
import { hostLog } from '../host.js';
|
|
6
6
|
import { surfaceFsApprovalAndDecide } from './toolApprovalWire.js';
|
|
@@ -58,6 +58,8 @@ function alreadyResolvedFailureVerdict(outcome) {
|
|
|
58
58
|
return false;
|
|
59
59
|
if ('errorCode' in outcome && isDecideParkMovedCode(outcome.errorCode))
|
|
60
60
|
return true;
|
|
61
|
+
if ('errorCode' in outcome && outcome.errorCode === ASK_GONE)
|
|
62
|
+
return true;
|
|
61
63
|
return isAlreadyResolvedGateReason(outcome.reason);
|
|
62
64
|
}
|
|
63
65
|
export function isGateStandingErrorCode(code, retriable) {
|
|
@@ -50,6 +50,9 @@ export type RulesFailure = {
|
|
|
50
50
|
} | {
|
|
51
51
|
kind: 'forbidden';
|
|
52
52
|
message: string;
|
|
53
|
+
} | {
|
|
54
|
+
kind: 'operators-unconfigured';
|
|
55
|
+
message: string;
|
|
53
56
|
} | {
|
|
54
57
|
kind: 'body-shape';
|
|
55
58
|
message: string;
|
|
@@ -100,7 +103,7 @@ export type PersistedRuleWriteBehavior = (typeof PERSISTED_RULE_WRITE_BEHAVIORS)
|
|
|
100
103
|
export type PersistedRuleWriteRequest = RuleWriteRequest;
|
|
101
104
|
export type PersistedRuleWriteWireResult = RuleWriteResult;
|
|
102
105
|
export type PersistedRuleLiveness = RemovalLiveness;
|
|
103
|
-
export declare const PERSISTED_RULE_WRITE_REFUSAL_CAUSES: readonly ["unwritable_behavior", "unreadable_identity", "unusable_principal", "client_too_old", "field_refused", "body_shape", "operator_only", "store_absent", "lane_too_old", "route_absent", "not_committed"];
|
|
106
|
+
export declare const PERSISTED_RULE_WRITE_REFUSAL_CAUSES: readonly ["unwritable_behavior", "unreadable_identity", "unusable_principal", "client_too_old", "field_refused", "body_shape", "operator_only", "operators_unconfigured", "store_absent", "lane_too_old", "route_absent", "not_committed"];
|
|
104
107
|
export type PersistedRuleWriteRefusalCause = (typeof PERSISTED_RULE_WRITE_REFUSAL_CAUSES)[number];
|
|
105
108
|
export declare const PERSISTED_RULE_WRITE_UNKNOWN_REASONS: readonly ["indeterminate", "malformed_result", "identity_mismatch", "unattributed_method_refusal", "unclassified"];
|
|
106
109
|
export type PersistedRuleWriteUnknownReason = (typeof PERSISTED_RULE_WRITE_UNKNOWN_REASONS)[number];
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { engineCapTrue } from '../engineCapsCache.js';
|
|
2
|
+
import { CORE_RULE_BEHAVIORS } from '../generated/engineFactTables.js';
|
|
2
3
|
import { wireFailureShapeOf } from '../wireFailureShape.js';
|
|
4
|
+
import { CAPABILITY_OPERATOR_PRINCIPALS_REQUIRED } from '../engineErrorCodes.js';
|
|
3
5
|
import { hostLog } from '../host.js';
|
|
4
6
|
import { observedPermissionRulesWrite, permissionRulesWriteAvailable } from '../permissionRulesWriteCapability.js';
|
|
5
7
|
import { readToolApprovalRespondAck } from './toolApprovalWire.js';
|
|
@@ -7,7 +9,7 @@ export function persistedRulesLaneAvailable(baseUrl) {
|
|
|
7
9
|
return engineCapTrue(baseUrl, 'permissionRules');
|
|
8
10
|
}
|
|
9
11
|
export function persistedRulesGovernanceAvailable(baseUrl) {
|
|
10
|
-
return engineCapTrue(baseUrl, '
|
|
12
|
+
return engineCapTrue(baseUrl, 'permissionRules');
|
|
11
13
|
}
|
|
12
14
|
export function persistedRulesWriteAvailable(baseUrl) {
|
|
13
15
|
return permissionRulesWriteAvailable(observedPermissionRulesWrite(baseUrl ?? ''));
|
|
@@ -66,6 +68,8 @@ export function classifyRulesFailure(e) {
|
|
|
66
68
|
return { kind: 'too-many-candidates', message };
|
|
67
69
|
if (status === 403 && errorCode === 'auth.operator_only')
|
|
68
70
|
return { kind: 'forbidden', message };
|
|
71
|
+
if (errorCode === CAPABILITY_OPERATOR_PRINCIPALS_REQUIRED)
|
|
72
|
+
return { kind: 'operators-unconfigured', message };
|
|
69
73
|
if (status === 405) {
|
|
70
74
|
return errorCode === 'method_not_allowed'
|
|
71
75
|
? { kind: 'method-unsupported', message }
|
|
@@ -73,7 +77,7 @@ export function classifyRulesFailure(e) {
|
|
|
73
77
|
}
|
|
74
78
|
return { kind: 'error', message };
|
|
75
79
|
}
|
|
76
|
-
export const PERSISTED_RULE_BEHAVIORS =
|
|
80
|
+
export const PERSISTED_RULE_BEHAVIORS = CORE_RULE_BEHAVIORS;
|
|
77
81
|
const BEHAVIOR_SET = new Set(PERSISTED_RULE_BEHAVIORS);
|
|
78
82
|
function rowStr(row, key) {
|
|
79
83
|
if (typeof row !== 'object' || row === null || Array.isArray(row))
|
|
@@ -221,6 +225,7 @@ export const PERSISTED_RULE_WRITE_REFUSAL_CAUSES = Object.freeze([
|
|
|
221
225
|
'field_refused',
|
|
222
226
|
'body_shape',
|
|
223
227
|
'operator_only',
|
|
228
|
+
'operators_unconfigured',
|
|
224
229
|
'store_absent',
|
|
225
230
|
'lane_too_old',
|
|
226
231
|
'route_absent',
|
|
@@ -242,6 +247,8 @@ function writeOutcomeFromFailure(failure) {
|
|
|
242
247
|
return { status: 'refused', cause: 'body_shape', message };
|
|
243
248
|
case 'forbidden':
|
|
244
249
|
return { status: 'refused', cause: 'operator_only', message };
|
|
250
|
+
case 'operators-unconfigured':
|
|
251
|
+
return { status: 'refused', cause: 'operators_unconfigured', message };
|
|
245
252
|
case 'lane-unavailable':
|
|
246
253
|
return { status: 'refused', cause: 'store_absent', message };
|
|
247
254
|
case 'method-unsupported':
|
|
@@ -7,7 +7,7 @@ import { enqueuePlanReviewOutcome } from '../notifications.js';
|
|
|
7
7
|
import { corruptStoredRowContent, corruptStoredRowWhere } from '../wireErrorTriage.js';
|
|
8
8
|
import { decideFailureNote } from '../decideFailureNote.js';
|
|
9
9
|
import { checkpointUnsupportedVersionContent, checkpointUnsupportedVersionFromError } from '../resumeRefusalCopy.js';
|
|
10
|
-
import { REQUEST_FIELD_CONFLICT } from '../engineErrorCodes.js';
|
|
10
|
+
import { REQUEST_FIELD_CONFLICT, isUnavailableFaceCode } from '../engineErrorCodes.js';
|
|
11
11
|
import { denyReasonForWire, isPlanReviewModeAfter } from './hitlBridge.js';
|
|
12
12
|
import { engineCapString } from '../engineCapsCache.js';
|
|
13
13
|
import { planReviewQuestionId, REOPEN_ID_TAIL, taskIdFromPlanReviewQuestionId } from './gateIdentity.js';
|
|
@@ -648,7 +648,7 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
648
648
|
else if (typeof status === 'number') {
|
|
649
649
|
const msg = e instanceof Error ? e.message : String(e);
|
|
650
650
|
hostLog('debug', `planReviewWire: ${decision} → ${status} ${msg.slice(0, 200)}`);
|
|
651
|
-
effect = status >= 400 && status < 500 ? 'not_applied' : 'unconfirmed';
|
|
651
|
+
effect = (status >= 400 && status < 500) || (typeof refusedCode === 'string' && isUnavailableFaceCode(refusedCode)) ? 'not_applied' : 'unconfirmed';
|
|
652
652
|
outcome = `${`The plan_review decision failed: HTTP ${status} ${msg}`.trim()}${decideFailureNote(e)}`;
|
|
653
653
|
}
|
|
654
654
|
else if (noAnswer === 'time-limit' || noAnswer === 'deadline') {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ApprovalsListEnvelope, type CrashConvergedRow } from './crashConverged.js';
|
|
2
|
+
export type ReplayedApprovalDisposition = {
|
|
3
|
+
readonly kind: 'live';
|
|
4
|
+
readonly basis: 'live_leg' | 'preamble' | 'pending_list';
|
|
5
|
+
} | {
|
|
6
|
+
readonly kind: 'decided';
|
|
7
|
+
readonly cause?: CrashConvergedRow['cause'];
|
|
8
|
+
} | {
|
|
9
|
+
readonly kind: 'unknown';
|
|
10
|
+
readonly why: 'unreadable_frame' | 'no_approval_id' | 'not_read' | 'list_unreadable' | 'list_not_reported' | 'list_incomplete';
|
|
11
|
+
};
|
|
12
|
+
export interface ReplayPendingBasis {
|
|
13
|
+
readonly preamble?: ReadonlySet<string>;
|
|
14
|
+
readonly approvals?: {
|
|
15
|
+
readonly read: 'ok';
|
|
16
|
+
readonly body: ApprovalsListEnvelope;
|
|
17
|
+
} | {
|
|
18
|
+
readonly read: 'failed';
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export declare function replayedApprovalDisposition(frame: object, basis: ReplayPendingBasis): ReplayedApprovalDisposition;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { hasLedgerPositionKey } from './gateLedger.js';
|
|
2
|
+
import { readLivePendingRows } from './livePendingAsk.js';
|
|
3
|
+
import { projectCrashConverged } from './crashConverged.js';
|
|
4
|
+
const live = (basis) => Object.freeze({ kind: 'live', basis });
|
|
5
|
+
const unknown = (why) => Object.freeze({ kind: 'unknown', why });
|
|
6
|
+
const decided = (cause) => Object.freeze(cause !== undefined ? { kind: 'decided', cause } : { kind: 'decided' });
|
|
7
|
+
function approvalIdOf(frame) {
|
|
8
|
+
const v = frame.approvalId;
|
|
9
|
+
return typeof v === 'string' && v !== '' ? v : undefined;
|
|
10
|
+
}
|
|
11
|
+
function crashConvergedCauseOf(body, approvalId) {
|
|
12
|
+
const projection = projectCrashConverged(body);
|
|
13
|
+
if (projection === undefined)
|
|
14
|
+
return undefined;
|
|
15
|
+
for (const row of [...projection.resumeSafe, ...projection.needsHuman]) {
|
|
16
|
+
if (row.approvalId === approvalId)
|
|
17
|
+
return row.cause;
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
export function replayedApprovalDisposition(frame, basis) {
|
|
22
|
+
let approvalId;
|
|
23
|
+
try {
|
|
24
|
+
if (typeof frame !== 'object' || frame === null)
|
|
25
|
+
return unknown('unreadable_frame');
|
|
26
|
+
if (!hasLedgerPositionKey(frame))
|
|
27
|
+
return live('live_leg');
|
|
28
|
+
approvalId = approvalIdOf(frame);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return unknown('unreadable_frame');
|
|
32
|
+
}
|
|
33
|
+
if (approvalId === undefined)
|
|
34
|
+
return unknown('no_approval_id');
|
|
35
|
+
let b;
|
|
36
|
+
let inPreamble = false;
|
|
37
|
+
try {
|
|
38
|
+
b = typeof basis === 'object' && basis !== null ? basis : undefined;
|
|
39
|
+
inPreamble = b?.preamble?.has(approvalId) === true;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
inPreamble = false;
|
|
43
|
+
}
|
|
44
|
+
if (inPreamble)
|
|
45
|
+
return live('preamble');
|
|
46
|
+
let approvals;
|
|
47
|
+
try {
|
|
48
|
+
approvals = b?.approvals;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return unknown('not_read');
|
|
52
|
+
}
|
|
53
|
+
if (approvals === undefined || approvals === null)
|
|
54
|
+
return unknown('not_read');
|
|
55
|
+
let body;
|
|
56
|
+
try {
|
|
57
|
+
if (approvals.read !== 'ok')
|
|
58
|
+
return unknown('list_unreadable');
|
|
59
|
+
body = approvals.body;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return unknown('list_unreadable');
|
|
63
|
+
}
|
|
64
|
+
let reading;
|
|
65
|
+
try {
|
|
66
|
+
reading = readLivePendingRows(body);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return unknown('list_unreadable');
|
|
70
|
+
}
|
|
71
|
+
if (reading.kind === 'present' && reading.rows.some((r) => r.approvalId === approvalId))
|
|
72
|
+
return live('pending_list');
|
|
73
|
+
let cause;
|
|
74
|
+
try {
|
|
75
|
+
cause = crashConvergedCauseOf(body, approvalId);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
cause = undefined;
|
|
79
|
+
}
|
|
80
|
+
if (cause !== undefined)
|
|
81
|
+
return decided(cause);
|
|
82
|
+
if (reading.kind === 'not_reported')
|
|
83
|
+
return unknown('list_not_reported');
|
|
84
|
+
if (reading.kind === 'malformed')
|
|
85
|
+
return unknown('list_unreadable');
|
|
86
|
+
if (reading.dropped > 0)
|
|
87
|
+
return unknown('list_incomplete');
|
|
88
|
+
return decided();
|
|
89
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { CONFIG_LEGACY_TOOL_NAME, RULES_LEGACY_TOOL_NAME } from '../engineErrorCodes.js';
|
|
2
2
|
import { rosterMountedNames } from './rosterMountedNames.js';
|
|
3
|
+
import { ruleCompileVerdict } from '../ruleCompileVerdict.js';
|
|
4
|
+
import { protocolSegmentOf } from '../protocolSegment.js';
|
|
3
5
|
import { PREFIXES_IN_PROSE, REFUSED_NAME_CLASSES, REFUSED_NAME_FIX, RULES_LEGACY_TOOL_NAME_WRITE_NOTE } from './refusedToolNameProse.js';
|
|
4
6
|
import { CORE_PROTOCOL_PREFIXES, CORE_RETIRED_TOOL_CURRENT_NAMES } from '../generated/toolNameTables.js';
|
|
5
7
|
const COVERING_NAMESPACE_PREFIXES = CORE_PROTOCOL_PREFIXES;
|
|
@@ -9,7 +11,7 @@ const WITHHELD_NOTICE = {
|
|
|
9
11
|
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.',
|
|
10
12
|
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.',
|
|
11
13
|
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.',
|
|
12
|
-
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,
|
|
14
|
+
not_a_tool_name: 'Not written to this session’s rule record: this entry is not exactly one tool name — it is empty, not text, has spaces around the name, is not a single tool name in rule syntax (for example it contains a backslash or a parenthesis, or is longer than a rule can be), or spells its server or agent peer part in a way the engine never uses when naming tools (for example with a dot or a space), so no tool would ever match it.',
|
|
13
15
|
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}`,
|
|
14
16
|
not_in_roster: 'Not written to this session’s rule record: the tool roster supplied for this check has no tool by exactly this name, as a name or an alias, with letter case counted — it may be misspelled, or name a tool this deployment does not provide — so recorded as it is it would block nothing here.',
|
|
15
17
|
};
|
|
@@ -97,7 +99,23 @@ export function sessionPolicyDeliverable(behavior, rules, opts) {
|
|
|
97
99
|
withheld.push({ rule: '', why: 'not_a_tool_name' });
|
|
98
100
|
continue;
|
|
99
101
|
}
|
|
100
|
-
|
|
102
|
+
let why = withheldWhyOf(r);
|
|
103
|
+
if (why === undefined) {
|
|
104
|
+
const grammar = ruleCompileVerdict(r, 'deny');
|
|
105
|
+
if (grammar === 'unknown')
|
|
106
|
+
return unreadable();
|
|
107
|
+
if (grammar === 'refused')
|
|
108
|
+
why = 'not_a_tool_name';
|
|
109
|
+
}
|
|
110
|
+
if (why === undefined && COVERING_NAMESPACE_PREFIXES.some((p) => r.startsWith(p))) {
|
|
111
|
+
const segment = protocolSegmentOf(r);
|
|
112
|
+
if (segment.kind === 'unknown')
|
|
113
|
+
return unreadable();
|
|
114
|
+
if (segment.kind === 'unminted')
|
|
115
|
+
why = 'not_a_tool_name';
|
|
116
|
+
}
|
|
117
|
+
if (why === undefined && mounted !== undefined && !mounted.has(r))
|
|
118
|
+
why = 'not_in_roster';
|
|
101
119
|
if (why === undefined)
|
|
102
120
|
exact.push(r);
|
|
103
121
|
else {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { SessionPermissionRules, StoredSessionRules } from '@sema-agent/sdk';
|
|
2
2
|
import type { SessionPolicyReading } from '../sessionPolicyCapability.js';
|
|
3
3
|
import type { ToolRosterView } from '../toolRoster.js';
|
|
4
|
+
import { CORE_RULE_TOOL_NAME_VERDICTS } from '../generated/engineFactTables.js';
|
|
5
|
+
export type LegacyToolNameVerdict = Exclude<(typeof CORE_RULE_TOOL_NAME_VERDICTS)[number], 'current'>;
|
|
4
6
|
export interface SessionPolicyFacade {
|
|
5
7
|
getPolicy(sessionId: string, opts?: {
|
|
6
8
|
signal?: AbortSignal;
|
|
@@ -43,6 +45,8 @@ export type SessionPolicyFailure = {
|
|
|
43
45
|
message: string;
|
|
44
46
|
field?: SessionPolicyRemovedEntry['field'];
|
|
45
47
|
name?: string;
|
|
48
|
+
verdict?: LegacyToolNameVerdict;
|
|
49
|
+
currentName?: string;
|
|
46
50
|
} | {
|
|
47
51
|
kind: 'unauthorized';
|
|
48
52
|
message: string;
|
|
@@ -95,6 +99,8 @@ export type SessionPolicyTightenOutcome = {
|
|
|
95
99
|
message: string;
|
|
96
100
|
field?: SessionPolicyRemovedEntry['field'];
|
|
97
101
|
name?: string;
|
|
102
|
+
verdict?: LegacyToolNameVerdict;
|
|
103
|
+
currentName?: string;
|
|
98
104
|
alreadyInRecord?: boolean;
|
|
99
105
|
} | {
|
|
100
106
|
kind: 'unknown';
|
|
@@ -148,6 +154,8 @@ export type SessionPolicyRemovalOutcome = {
|
|
|
148
154
|
message: string;
|
|
149
155
|
field?: SessionPolicyRemovedEntry['field'];
|
|
150
156
|
name?: string;
|
|
157
|
+
verdict?: LegacyToolNameVerdict;
|
|
158
|
+
currentName?: string;
|
|
151
159
|
};
|
|
152
160
|
export type SessionPolicyRosterRequiredWhy = 'absent' | 'unreadable';
|
|
153
161
|
export declare function removeRefusedSessionRules(facade: SessionPolicyFacade, sessionId: string, roster: ToolRosterView | undefined, opts?: {
|
|
@@ -4,6 +4,10 @@ import { isEngineRefusedToolName } from './sessionPolicyDeliverable.js';
|
|
|
4
4
|
import { RULES_LEGACY_TOOL_NAME_WRITE_NOTE } from './refusedToolNameProse.js';
|
|
5
5
|
import { RULES_LEGACY_TOOL_NAME, isRulesRefusalCode } from '../engineErrorCodes.js';
|
|
6
6
|
import { rosterMountedNames } from './rosterMountedNames.js';
|
|
7
|
+
import { CORE_RULE_TOOL_NAME_VERDICTS } from '../generated/engineFactTables.js';
|
|
8
|
+
function isLegacyToolNameVerdict(v) {
|
|
9
|
+
return typeof v === 'string' && v !== 'current' && CORE_RULE_TOOL_NAME_VERDICTS.includes(v);
|
|
10
|
+
}
|
|
7
11
|
const RULE_FIELD_PRESENCE = {
|
|
8
12
|
toolAllow: true,
|
|
9
13
|
toolDeny: true,
|
|
@@ -14,7 +18,7 @@ const RULE_FIELD_PRESENCE = {
|
|
|
14
18
|
export const SESSION_POLICY_RULE_FIELDS = Object.freeze(Object.keys(RULE_FIELD_PRESENCE));
|
|
15
19
|
const MAX_RULE_ENTRIES = 10_000;
|
|
16
20
|
export function classifySessionPolicyFailure(e) {
|
|
17
|
-
const { status, errorCode, message, bodyField, bodyName } = wireFailureShapeOf(e);
|
|
21
|
+
const { status, errorCode, message, bodyField, bodyName, bodyVerdict, bodyCurrentName } = wireFailureShapeOf(e);
|
|
18
22
|
if (errorCode === undefined || errorCode === '')
|
|
19
23
|
return { kind: 'no-verdict', message };
|
|
20
24
|
if (errorCode === RULES_LEGACY_TOOL_NAME) {
|
|
@@ -23,6 +27,8 @@ export function classifySessionPolicyFailure(e) {
|
|
|
23
27
|
message,
|
|
24
28
|
...(bodyField === 'toolDeny' || bodyField === 'toolAllow' ? { field: bodyField } : {}),
|
|
25
29
|
...(bodyName !== undefined && bodyName !== '' ? { name: bodyName } : {}),
|
|
30
|
+
...(isLegacyToolNameVerdict(bodyVerdict) ? { verdict: bodyVerdict } : {}),
|
|
31
|
+
...(bodyCurrentName !== undefined && bodyCurrentName !== '' ? { currentName: bodyCurrentName } : {}),
|
|
26
32
|
};
|
|
27
33
|
}
|
|
28
34
|
if (isRulesRefusalCode(errorCode) && status !== undefined && status >= 400 && status < 500)
|
|
@@ -227,7 +233,7 @@ function unknownFrom(failure, leg) {
|
|
|
227
233
|
}
|
|
228
234
|
}
|
|
229
235
|
function legacyToolNameOutcome(failure, prior, add) {
|
|
230
|
-
const { field, name } = failure;
|
|
236
|
+
const { field, name, verdict, currentName } = failure;
|
|
231
237
|
let alreadyInRecord;
|
|
232
238
|
if (field !== undefined && name !== undefined) {
|
|
233
239
|
const inPrior = Object.hasOwn(prior, field) ? prior[field] : undefined;
|
|
@@ -242,6 +248,8 @@ function legacyToolNameOutcome(failure, prior, add) {
|
|
|
242
248
|
message: failure.message,
|
|
243
249
|
...(field !== undefined ? { field } : {}),
|
|
244
250
|
...(name !== undefined ? { name } : {}),
|
|
251
|
+
...(verdict !== undefined ? { verdict } : {}),
|
|
252
|
+
...(currentName !== undefined ? { currentName } : {}),
|
|
245
253
|
...(alreadyInRecord !== undefined ? { alreadyInRecord } : {}),
|
|
246
254
|
};
|
|
247
255
|
}
|
|
@@ -488,6 +496,8 @@ async function attemptRemoval(facade, sessionId, mounted, opts) {
|
|
|
488
496
|
message: failure.message,
|
|
489
497
|
...(failure.field !== undefined ? { field: failure.field } : {}),
|
|
490
498
|
...(failure.name !== undefined ? { name: failure.name } : {}),
|
|
499
|
+
...(failure.verdict !== undefined ? { verdict: failure.verdict } : {}),
|
|
500
|
+
...(failure.currentName !== undefined ? { currentName: failure.currentName } : {}),
|
|
491
501
|
},
|
|
492
502
|
};
|
|
493
503
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type GateCurrentPending, type HitlFailureStage, type HitlSafetyCode, type HitlClientLike, type DecideOperationObserver } from './hitlBridge.js';
|
|
2
|
+
import { type AllowRuleShadowedWireMember, type ApprovalAllowRuleShadow } from './allowRuleShadow.js';
|
|
2
3
|
import { type RuleStoreUnreadableKind } from '../gateVocabulary.js';
|
|
3
4
|
import { type ApprovalMandateWord } from '../gateVocabulary.js';
|
|
4
5
|
import type { ToolPermissionRequest } from '../seatContract.js';
|
|
@@ -131,8 +132,10 @@ export interface ApprovalCardRequest {
|
|
|
131
132
|
inputHasBidi?: true;
|
|
132
133
|
hasBidiControls?: true;
|
|
133
134
|
persistedRuleShadowed?: string;
|
|
135
|
+
allowRuleShadow?: ApprovalAllowRuleShadow;
|
|
134
136
|
probeCause?: unknown;
|
|
135
137
|
probeReason?: string;
|
|
138
|
+
rememberRefused?: true;
|
|
136
139
|
ruleEvidence?: unknown;
|
|
137
140
|
requiresRealApproval?: true;
|
|
138
141
|
irreversibleParkGate?: true;
|
|
@@ -181,8 +184,10 @@ export interface ToolApprovalFrame {
|
|
|
181
184
|
denialLimitFallback?: DenialLimitFallback;
|
|
182
185
|
ruleStoreUnreadable?: string;
|
|
183
186
|
persistedRuleShadowed?: string;
|
|
187
|
+
allowRuleShadowed?: readonly AllowRuleShadowedWireMember[];
|
|
184
188
|
probeCause?: unknown;
|
|
185
189
|
probeReason?: string;
|
|
190
|
+
rememberRefused?: true;
|
|
186
191
|
ruleEvidence?: unknown;
|
|
187
192
|
requiresRealApproval?: true;
|
|
188
193
|
mandated?: true;
|
|
@@ -207,7 +212,7 @@ export interface ToolApprovalDelegation {
|
|
|
207
212
|
depth: number;
|
|
208
213
|
agentName?: string;
|
|
209
214
|
}
|
|
210
|
-
export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleSuggestions", "ruleOffers", "ruleOffersAbsence", "denialLimitFallback", "ruleStoreUnreadable", "readRootCandidate", "mandated", "mandate", "origin", "askClass", "classes", "inputHasBidi", "parked", "persistedRuleShadowed", "probeCause", "probeReason", "ruleEvidence", "requiresRealApproval", "expiresInMs", "expiresAtMs", "serverNowMs", "delegation", "outcome"];
|
|
215
|
+
export declare const TOOL_APPROVAL_FRAME_KEYS_MIRROR: readonly ["type", "approvalId", "toolCallId", "toolName", "sourceTaskId", "fromSubagent", "sourceAgentName", "message", "args", "argsOmitted", "governanceForced", "ruleSuggestions", "ruleOffers", "ruleOffersAbsence", "denialLimitFallback", "ruleStoreUnreadable", "readRootCandidate", "mandated", "mandate", "origin", "askClass", "classes", "inputHasBidi", "parked", "persistedRuleShadowed", "allowRuleShadowed", "probeCause", "probeReason", "rememberRefused", "ruleEvidence", "requiresRealApproval", "expiresInMs", "expiresAtMs", "serverNowMs", "delegation", "outcome"];
|
|
211
216
|
export interface ApprovalMandatedFacts {
|
|
212
217
|
readonly mandated?: true;
|
|
213
218
|
readonly ruleOffersAbsence?: string;
|