@sema-agent/client-core 0.97.0 → 0.98.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 +429 -0
- package/README.md +74 -36
- package/dist/adapt/arms.d.ts +1 -0
- package/dist/adapt/arms.js +128 -34
- package/dist/adapt/ids.d.ts +12 -1
- package/dist/adapt/ids.js +59 -8
- package/dist/adapt/instanceLedger.d.ts +50 -0
- package/dist/adapt/instanceLedger.js +147 -0
- package/dist/adapt/textStream.d.ts +1 -0
- package/dist/adapt/textStream.js +24 -7
- package/dist/adapt/toolCards.d.ts +27 -5
- package/dist/adapt/toolCards.js +39 -6
- package/dist/adapt/wireShapes.js +6 -2
- package/dist/adapt.d.ts +2 -0
- package/dist/adapt.js +8 -3
- package/dist/adapter/activeRunSelfHeal.d.ts +5 -0
- package/dist/adapter/activeRunSelfHeal.js +51 -28
- package/dist/adapter/downstream/eventToSdkMessage.d.ts +2 -5
- package/dist/adapter/downstream/eventToSdkMessage.js +27 -34
- package/dist/adapter/downstream/terminalToSdkResult.js +11 -4
- package/dist/adapter/downstream/turnUsageToModelUsage.d.ts +5 -2
- package/dist/adapter/downstream/turnUsageToModelUsage.js +2 -0
- package/dist/adapter/runStream.js +29 -13
- package/dist/adapter/types.d.ts +4 -4
- package/dist/agentSession/backgroundView.js +2 -1
- package/dist/agentSession/engineAgentRegistryRead.d.ts +2 -2
- package/dist/agentSession/engineAgentRegistryRead.js +12 -13
- package/dist/agentsWireCaps.d.ts +8 -6
- package/dist/agentsWireCaps.js +177 -162
- package/dist/approvalDecidedByCapability.d.ts +0 -4
- package/dist/approvalDecidedByCapability.js +0 -6
- package/dist/approvalsStreamLiveCapability.d.ts +0 -4
- package/dist/approvalsStreamLiveCapability.js +0 -6
- package/dist/assistantInbox.d.ts +34 -0
- package/dist/assistantInbox.js +118 -0
- package/dist/centerWiringCapability.js +9 -1
- package/dist/clientSlice.d.ts +0 -9
- package/dist/clientSlice.js +0 -3
- package/dist/cloudConfigWireCaps.d.ts +4 -2
- package/dist/cloudConfigWireCaps.js +81 -61
- package/dist/compensations.js +1 -1
- package/dist/connectorStatus.d.ts +21 -0
- package/dist/connectorStatus.js +112 -0
- package/dist/controlRouter.d.ts +14 -1
- package/dist/controlRouter.js +68 -17
- package/dist/coreValuePorts.d.ts +3 -3
- package/dist/decideReceipt.d.ts +2 -2
- package/dist/decideReceipt.js +5 -2
- package/dist/declared/supportFloorDeclared.js +3 -3
- package/dist/deviceExecutorManagementCapability.d.ts +0 -4
- package/dist/deviceExecutorManagementCapability.js +0 -6
- package/dist/deviceRebind.d.ts +49 -1
- package/dist/deviceRebind.js +221 -3
- package/dist/displayUntrusted.d.ts +1 -0
- package/dist/displayUntrusted.js +250 -76
- package/dist/engineAgentAbsence.d.ts +1 -1
- package/dist/engineAgentPanelStore.d.ts +5 -4
- package/dist/engineAgentPanelStore.js +3 -12
- package/dist/engineCapReader.d.ts +0 -4
- package/dist/engineCapReader.js +1 -5
- package/dist/engineCapsCache.d.ts +4 -0
- package/dist/engineCapsCache.js +17 -12
- package/dist/engineClosedSets.d.ts +1 -0
- package/dist/engineClosedSets.js +4 -1
- package/dist/engineErrorCodes.d.ts +9 -2
- package/dist/engineErrorCodes.js +9 -1
- package/dist/engineIdentity.d.ts +1 -0
- package/dist/engineIdentity.js +8 -0
- package/dist/engineRowRef.d.ts +28 -0
- package/dist/engineRowRef.js +111 -0
- package/dist/engineSessionMessages.d.ts +67 -0
- package/dist/engineSessionMessages.js +364 -0
- package/dist/engineToolDirectory.d.ts +18 -0
- package/dist/engineToolDirectory.js +85 -0
- package/dist/engineUrl.d.ts +18 -0
- package/dist/engineUrl.js +52 -0
- package/dist/env/uiLanguage.d.ts +9 -0
- package/dist/env/uiLanguage.js +35 -0
- package/dist/eventLeg.d.ts +92 -0
- package/dist/eventLeg.js +285 -0
- package/dist/executionLaneCapability.d.ts +0 -4
- package/dist/executionLaneCapability.js +3 -16
- package/dist/fileHistoryCaptureCapability.d.ts +0 -4
- package/dist/fileHistoryCaptureCapability.js +5 -7
- package/dist/fleet/fleetLedger.d.ts +0 -3
- package/dist/fleet/fleetLedger.js +21 -36
- package/dist/fleet/fleetProjection.d.ts +2 -2
- package/dist/fleet/fleetProjection.js +10 -8
- package/dist/frameBinding.d.ts +5 -0
- package/dist/frameBinding.js +23 -0
- package/dist/gateFailureCodes.d.ts +2 -0
- package/dist/gateFailureCodes.js +15 -0
- package/dist/gateOutcome.d.ts +3 -1
- package/dist/gateOutcome.js +25 -6
- package/dist/gateVocabulary.d.ts +5 -0
- package/dist/gateVocabulary.js +64 -2
- package/dist/generated/engineAskFaceTables.js +2 -2
- package/dist/generated/engineFactTables.d.ts +6 -3
- package/dist/generated/engineFactTables.js +22 -4
- package/dist/generated/engineNoticeTables.js +9 -0
- package/dist/generated/toolNameTables.d.ts +1 -0
- package/dist/generated/toolNameTables.js +1 -0
- package/dist/hitl/allowRuleShadow.d.ts +6 -1
- package/dist/hitl/allowRuleShadow.js +21 -17
- package/dist/hitl/approvalCardReading.d.ts +15 -0
- package/dist/hitl/approvalCardReading.js +154 -0
- package/dist/hitl/approvalCardReasons.d.ts +13 -0
- package/dist/hitl/approvalCardReasons.js +120 -0
- package/dist/hitl/approvalHolds.d.ts +47 -15
- package/dist/hitl/approvalHolds.js +134 -28
- package/dist/hitl/approvalResolution.d.ts +51 -26
- package/dist/hitl/approvalResolution.js +86 -122
- package/dist/hitl/approvalsFeed.js +6 -18
- package/dist/hitl/askClassPair.d.ts +2 -0
- package/dist/hitl/askClassPair.js +1 -0
- package/dist/hitl/askGateWire.d.ts +20 -2
- package/dist/hitl/askGateWire.js +49 -1
- package/dist/hitl/askParkRowRouting.d.ts +5 -0
- package/dist/hitl/askParkRowRouting.js +32 -1
- package/dist/hitl/askSettlementLine.d.ts +18 -0
- package/dist/hitl/askSettlementLine.js +79 -0
- package/dist/hitl/automaticAnswerVerdict.d.ts +1 -1
- package/dist/hitl/automaticAnswerVerdict.js +21 -5
- package/dist/hitl/crashConverged.d.ts +11 -20
- package/dist/hitl/crashConverged.js +56 -0
- package/dist/hitl/crashConvergedNotice.d.ts +3 -1
- package/dist/hitl/crashConvergedNotice.js +48 -11
- package/dist/hitl/decisionConflict.js +3 -10
- package/dist/hitl/decisionRefusal.d.ts +40 -1
- package/dist/hitl/decisionRefusal.js +199 -42
- package/dist/hitl/frameRouter.d.ts +5 -2
- package/dist/hitl/frameRouter.js +20 -16
- package/dist/hitl/gateLedger.d.ts +2 -0
- package/dist/hitl/gateLedger.js +50 -13
- package/dist/hitl/hitlBridge.d.ts +1 -2
- package/dist/hitl/hitlBridge.js +10 -7
- package/dist/hitl/livePendingAsk.d.ts +3 -3
- package/dist/hitl/livePendingAsk.js +7 -18
- package/dist/hitl/parkResolver.d.ts +2 -24
- package/dist/hitl/parkResolver.js +61 -97
- package/dist/hitl/persistedRulesWire.d.ts +24 -0
- package/dist/hitl/persistedRulesWire.js +122 -2
- package/dist/hitl/planReviewWire.d.ts +26 -13
- package/dist/hitl/planReviewWire.js +112 -78
- package/dist/hitl/refusedToolNameProse.d.ts +0 -1
- package/dist/hitl/refusedToolNameProse.js +0 -3
- package/dist/hitl/replayedApproval.d.ts +4 -3
- package/dist/hitl/replayedApproval.js +7 -4
- package/dist/hitl/sessionPolicyDeliverable.js +2 -4
- package/dist/hitl/sessionPolicyWire.d.ts +32 -20
- package/dist/hitl/sessionPolicyWire.js +162 -52
- package/dist/hitl/standingRows.d.ts +20 -2
- package/dist/hitl/standingRows.js +69 -8
- package/dist/hitl/toolApprovalWire.d.ts +87 -152
- package/dist/hitl/toolApprovalWire.js +591 -380
- package/dist/hookMatcherVerdict.d.ts +6 -1
- package/dist/hookMatcherVerdict.js +14 -7
- package/dist/hooksWireCaps.d.ts +2 -2
- package/dist/hooksWireCaps.js +59 -55
- package/dist/i18n/messages.en.d.ts +121 -0
- package/dist/i18n/messages.en.js +119 -0
- package/dist/i18n/messages.zh.d.ts +3 -0
- package/dist/i18n/messages.zh.js +120 -0
- package/dist/i18n/translate.d.ts +25 -0
- package/dist/i18n/translate.js +127 -0
- package/dist/index.d.ts +53 -18
- package/dist/index.js +38 -10
- package/dist/inheritEnvCapability.d.ts +31 -0
- package/dist/inheritEnvCapability.js +104 -0
- package/dist/liveModelCatalog.d.ts +1 -2
- package/dist/liveModelCatalog.js +2 -6
- package/dist/liveQuestionStore.d.ts +2 -21
- package/dist/mcpLiveness.d.ts +2 -1
- package/dist/mcpProbeWire.d.ts +6 -1
- package/dist/mcpProbeWire.js +1 -1
- package/dist/mcpWireCaps.d.ts +13 -2
- package/dist/mcpWireCaps.js +48 -0
- package/dist/memoryEntriesWire.d.ts +4 -9
- package/dist/model/modelSupplyRules.d.ts +0 -2
- package/dist/model/modelSupplyRules.js +3 -12
- package/dist/model/modelsPolicy.d.ts +0 -5
- package/dist/model/modelsPolicy.js +6 -36
- package/dist/model/subagentModelWord.d.ts +31 -0
- package/dist/model/subagentModelWord.js +261 -0
- package/dist/notifications.d.ts +6 -0
- package/dist/notifications.js +71 -23
- package/dist/outputStyleSeatCapability.d.ts +0 -3
- package/dist/outputStyleSeatCapability.js +0 -3
- package/dist/ownKey.d.ts +15 -0
- package/dist/ownKey.js +60 -3
- package/dist/panelReduce.d.ts +15 -9
- package/dist/panelReduce.js +680 -549
- package/dist/panelStateHolder.d.ts +3 -7
- package/dist/panelStateHolder.js +14 -130
- package/dist/peerLaneCapability.d.ts +0 -4
- package/dist/peerLaneCapability.js +0 -6
- package/dist/permissionRulesWriteCapability.d.ts +0 -4
- package/dist/permissionRulesWriteCapability.js +0 -6
- package/dist/permissionWireCaps.d.ts +2 -2
- package/dist/permissionWireCaps.js +2 -0
- package/dist/permissionsAllowGrantCapability.d.ts +0 -4
- package/dist/permissionsAllowGrantCapability.js +0 -6
- package/dist/permissionsDeclaration.d.ts +0 -11
- package/dist/permissionsDeclaration.js +60 -53
- package/dist/pluginHooksWire.d.ts +1 -1
- package/dist/pluginHooksWire.js +3 -6
- package/dist/preemptWire.d.ts +44 -0
- package/dist/preemptWire.js +83 -0
- package/dist/printToolResultFrame.js +15 -4
- package/dist/replayBody.d.ts +3 -0
- package/dist/replayBody.js +14 -0
- package/dist/request/taskRequest.d.ts +0 -1
- package/dist/request/taskRequest.js +0 -3
- package/dist/resumeRefusalCopy.d.ts +2 -1
- package/dist/resumeRefusalCopy.js +38 -2
- package/dist/retryStatus.d.ts +8 -0
- package/dist/retryStatus.js +106 -1
- package/dist/rewindArchiveCapability.d.ts +0 -4
- package/dist/rewindArchiveCapability.js +2 -10
- package/dist/rewindArchiveReading.d.ts +0 -1
- package/dist/rewindArchiveReading.js +0 -3
- package/dist/runCancelContext.d.ts +2 -1
- package/dist/runCancelContext.js +4 -2
- package/dist/runReattachPolicy.d.ts +31 -0
- package/dist/runReattachPolicy.js +46 -0
- package/dist/runTerminal.d.ts +3 -6
- package/dist/runTerminal.js +8 -9
- package/dist/sandboxWire.d.ts +3 -1
- package/dist/sandboxWire.js +76 -27
- package/dist/scenarioWire.js +1 -1
- package/dist/seam.d.ts +24 -2
- package/dist/seam.js +15 -3
- package/dist/seatContract.d.ts +53 -10
- package/dist/seatContract.js +132 -81
- package/dist/sessionPolicyCapability.d.ts +0 -4
- package/dist/sessionPolicyCapability.js +0 -6
- package/dist/sessionSyncFileHistoryCapability.d.ts +0 -3
- package/dist/sessionSyncFileHistoryCapability.js +0 -3
- package/dist/skillToolListsCapability.d.ts +0 -3
- package/dist/skillToolListsCapability.js +0 -3
- package/dist/sqlEngineCapability.d.ts +0 -4
- package/dist/sqlEngineCapability.js +0 -6
- package/dist/steerPriorityCapability.d.ts +0 -3
- package/dist/steerPriorityCapability.js +0 -3
- package/dist/streamDrainingLegs.d.ts +4 -0
- package/dist/streamDrainingLegs.js +6 -1
- package/dist/subagent/engineCompactWire.d.ts +0 -1
- package/dist/subagent/engineCompactWire.js +7 -5
- package/dist/subagent/engineTaskHandleWire.js +4 -7
- package/dist/subagentContentStore.js +1 -11
- package/dist/submitReopen.d.ts +24 -0
- package/dist/submitReopen.js +75 -0
- package/dist/terminalStatuses.d.ts +3 -0
- package/dist/terminalStatuses.js +10 -2
- package/dist/toolResult.d.ts +10 -1
- package/dist/toolResult.js +34 -6
- package/dist/toolRoster.d.ts +0 -2
- package/dist/toolRoster.js +0 -2
- package/dist/turnFailurePresentation.d.ts +28 -0
- package/dist/turnFailurePresentation.js +205 -0
- package/dist/userInvokedSkillsCapability.d.ts +0 -3
- package/dist/userInvokedSkillsCapability.js +0 -3
- package/dist/webSearchBackendCapability.d.ts +0 -4
- package/dist/webSearchBackendCapability.js +0 -6
- package/dist/wireErrorTriage.js +5 -24
- package/dist/wireFailureShape.d.ts +62 -5
- package/dist/wireFailureShape.js +105 -5
- package/dist/wireRefusalCopy.d.ts +12 -2
- package/dist/wireRefusalCopy.js +50 -52
- package/dist/workflowsJournalCapability.d.ts +0 -4
- package/dist/workflowsJournalCapability.js +0 -6
- package/dist/writeProtectionCapability.d.ts +0 -4
- package/dist/writeProtectionCapability.js +0 -6
- package/docs/INTEGRATION-CLIENTS.md +1305 -46
- package/package.json +5 -4
- package/dist/attachmentSeatCapability.d.ts +0 -12
- package/dist/attachmentSeatCapability.js +0 -23
- package/dist/hitl/approvalOutcomeNote.d.ts +0 -10
- package/dist/hitl/approvalOutcomeNote.js +0 -29
- package/dist/sessionReadRedactionCapability.d.ts +0 -13
- package/dist/sessionReadRedactionCapability.js +0 -35
|
@@ -1,8 +1,9 @@
|
|
|
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';
|
|
4
|
+
import { CORE_ALLOW_ENTRY_INERT_REASONS, CORE_RULE_TOOL_NAME_VERDICTS } from '../generated/engineFactTables.js';
|
|
5
5
|
export type LegacyToolNameVerdict = Exclude<(typeof CORE_RULE_TOOL_NAME_VERDICTS)[number], 'current'>;
|
|
6
|
+
export type SessionRuleInertReason = (typeof CORE_ALLOW_ENTRY_INERT_REASONS)[number];
|
|
6
7
|
export interface SessionPolicyFacade {
|
|
7
8
|
getPolicy(sessionId: string, opts?: {
|
|
8
9
|
signal?: AbortSignal;
|
|
@@ -41,10 +42,16 @@ export type SessionPolicyFailure = {
|
|
|
41
42
|
kind: 'request-rejected';
|
|
42
43
|
message: string;
|
|
43
44
|
} | {
|
|
44
|
-
kind: '
|
|
45
|
+
kind: 'earlier-grammar';
|
|
46
|
+
message: string;
|
|
47
|
+
field?: 'toolAllow';
|
|
48
|
+
entry?: string;
|
|
49
|
+
} | {
|
|
50
|
+
kind: 'inert-entry';
|
|
45
51
|
message: string;
|
|
46
52
|
field?: SessionPolicyRemovedEntry['field'];
|
|
47
|
-
|
|
53
|
+
entry?: string;
|
|
54
|
+
reason?: SessionRuleInertReason;
|
|
48
55
|
verdict?: LegacyToolNameVerdict;
|
|
49
56
|
currentName?: string;
|
|
50
57
|
} | {
|
|
@@ -58,11 +65,12 @@ export type SessionPolicyFailure = {
|
|
|
58
65
|
message: string;
|
|
59
66
|
};
|
|
60
67
|
export declare function classifySessionPolicyFailure(e: unknown): SessionPolicyFailure;
|
|
61
|
-
export type SessionPolicyMalformedWhy = 'envelope' | 'rev' | 'rule_field';
|
|
68
|
+
export type SessionPolicyMalformedWhy = 'envelope' | 'rev' | 'rule_field' | 'grammar_stamp';
|
|
62
69
|
export type SessionPolicyReadOutcome = {
|
|
63
70
|
kind: 'present';
|
|
64
71
|
rev: number;
|
|
65
72
|
rules: SessionPermissionRules;
|
|
73
|
+
grammar?: number;
|
|
66
74
|
} | {
|
|
67
75
|
kind: 'malformed';
|
|
68
76
|
why: SessionPolicyMalformedWhy;
|
|
@@ -82,6 +90,11 @@ export interface SessionPolicyTightenRequest {
|
|
|
82
90
|
}
|
|
83
91
|
export type SessionPolicyTightenUnknownWhy = 'face_unavailable' | 'session_not_found' | 'session_ownerless' | 'unauthorized' | 'request_rejected' | 'store_corrupt' | 'read_unreadable' | 'read_failed' | 'write_refused' | 'write_indeterminate' | 'write_receipt_unreadable' | 'write_receipt_mismatch';
|
|
84
92
|
export declare const SESSION_POLICY_TIGHTEN_UNKNOWN_WHY: readonly SessionPolicyTightenUnknownWhy[];
|
|
93
|
+
export type SessionPolicyEarlierGrammarRefusal = {
|
|
94
|
+
kind: 'refused';
|
|
95
|
+
why: 'earlier_grammar_rows';
|
|
96
|
+
entries: readonly SessionPolicyRemovedEntry[];
|
|
97
|
+
};
|
|
85
98
|
export type SessionPolicyTightenOutcome = {
|
|
86
99
|
kind: 'written';
|
|
87
100
|
rev: number;
|
|
@@ -94,19 +107,23 @@ export type SessionPolicyTightenOutcome = {
|
|
|
94
107
|
} | {
|
|
95
108
|
kind: 'capability_absent';
|
|
96
109
|
why: 'not_reported' | 'absent';
|
|
97
|
-
} | {
|
|
98
|
-
kind: '
|
|
99
|
-
|
|
100
|
-
field?: SessionPolicyRemovedEntry['field'];
|
|
101
|
-
name?: string;
|
|
102
|
-
verdict?: LegacyToolNameVerdict;
|
|
103
|
-
currentName?: string;
|
|
110
|
+
} | SessionPolicyEarlierGrammarRefusal | ({
|
|
111
|
+
kind: 'inert_entry';
|
|
112
|
+
} & SessionPolicyInertEntryRefusal & {
|
|
104
113
|
alreadyInRecord?: boolean;
|
|
105
|
-
} | {
|
|
114
|
+
}) | {
|
|
106
115
|
kind: 'unknown';
|
|
107
116
|
why: SessionPolicyTightenUnknownWhy;
|
|
108
117
|
message?: string;
|
|
109
118
|
};
|
|
119
|
+
export interface SessionPolicyInertEntryRefusal {
|
|
120
|
+
message: string;
|
|
121
|
+
field?: SessionPolicyRemovedEntry['field'];
|
|
122
|
+
entry?: string;
|
|
123
|
+
reason?: SessionRuleInertReason;
|
|
124
|
+
verdict?: LegacyToolNameVerdict;
|
|
125
|
+
currentName?: string;
|
|
126
|
+
}
|
|
110
127
|
export declare function tightenSessionPolicy(facade: SessionPolicyFacade, req: SessionPolicyTightenRequest, opts?: {
|
|
111
128
|
signal?: AbortSignal;
|
|
112
129
|
}): Promise<SessionPolicyTightenOutcome>;
|
|
@@ -153,14 +170,9 @@ export type SessionPolicyRemovalOutcome = {
|
|
|
153
170
|
kind: 'write_unconfirmed';
|
|
154
171
|
why: Extract<SessionPolicyTightenUnknownWhy, 'write_indeterminate' | 'write_receipt_unreadable' | 'write_receipt_mismatch'>;
|
|
155
172
|
message?: string;
|
|
156
|
-
} | {
|
|
157
|
-
kind: '
|
|
158
|
-
|
|
159
|
-
field?: SessionPolicyRemovedEntry['field'];
|
|
160
|
-
name?: string;
|
|
161
|
-
verdict?: LegacyToolNameVerdict;
|
|
162
|
-
currentName?: string;
|
|
163
|
-
};
|
|
173
|
+
} | SessionPolicyEarlierGrammarRefusal | ({
|
|
174
|
+
kind: 'inert_entry';
|
|
175
|
+
} & SessionPolicyInertEntryRefusal);
|
|
164
176
|
export type SessionPolicyRosterRequiredWhy = 'absent' | 'unreadable';
|
|
165
177
|
export declare function removeRefusedSessionRules(facade: SessionPolicyFacade, sessionId: string, roster: ToolRosterView | undefined, opts?: {
|
|
166
178
|
signal?: AbortSignal;
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { wireFailureShapeOf } from '../wireFailureShape.js';
|
|
2
2
|
import { hostLog } from '../host.js';
|
|
3
3
|
import { ruleToolNameVerdictOf } from '../protocolSegment.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { RULES_INERT_ENTRY, RULES_INVALID_SHAPE, isRulesRefusalCode } from '../engineErrorCodes.js';
|
|
5
|
+
import { deniedEntryToolName } from '../engineToolDirectory.js';
|
|
6
6
|
import { rosterMountedNames } from '../rosterMountedNames.js';
|
|
7
|
-
import { CORE_RULE_TOOL_NAME_VERDICTS } from '../generated/engineFactTables.js';
|
|
7
|
+
import { CORE_ALLOW_ENTRY_INERT_REASONS, CORE_RULE_TOOL_NAME_VERDICTS } from '../generated/engineFactTables.js';
|
|
8
|
+
const EARLIER_GRAMMAR_REASON = 'earlier_grammar';
|
|
9
|
+
const _inertReasonPin = true;
|
|
10
|
+
void _inertReasonPin;
|
|
11
|
+
function isSessionRuleInertReason(v) {
|
|
12
|
+
return typeof v === 'string' && CORE_ALLOW_ENTRY_INERT_REASONS.includes(v);
|
|
13
|
+
}
|
|
8
14
|
function isLegacyToolNameVerdict(v) {
|
|
9
15
|
return typeof v === 'string' && v !== 'current' && CORE_RULE_TOOL_NAME_VERDICTS.includes(v);
|
|
10
16
|
}
|
|
@@ -12,25 +18,43 @@ const RULE_FIELD_PRESENCE = {
|
|
|
12
18
|
toolAllow: true,
|
|
13
19
|
toolDeny: true,
|
|
14
20
|
allowDirs: true,
|
|
15
|
-
commandAllow: true,
|
|
16
|
-
commandDeny: true,
|
|
17
21
|
};
|
|
22
|
+
const RECORD_META_KEYS = Object.freeze(['rev', 'gen', 'grammar']);
|
|
23
|
+
const _metaKeysPin = true;
|
|
24
|
+
void _metaKeysPin;
|
|
18
25
|
export const SESSION_POLICY_RULE_FIELDS = Object.freeze(Object.keys(RULE_FIELD_PRESENCE));
|
|
26
|
+
function foreignRecordKeysOf(record) {
|
|
27
|
+
return Object.getOwnPropertyNames(record).filter((key) => !SESSION_POLICY_RULE_FIELDS.includes(key) && !RECORD_META_KEYS.includes(key));
|
|
28
|
+
}
|
|
29
|
+
function recordKeyLabel(key) {
|
|
30
|
+
return /^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(key) ? `"${key}"` : 'unnamed';
|
|
31
|
+
}
|
|
19
32
|
const MAX_RULE_ENTRIES = 10_000;
|
|
20
33
|
export function classifySessionPolicyFailure(e) {
|
|
21
|
-
const { status, errorCode, message, bodyField,
|
|
34
|
+
const { status, errorCode, message, bodyField, bodyVerdict, bodyCurrentName, bodyEntry, bodyReason } = wireFailureShapeOf(e);
|
|
22
35
|
if (errorCode === undefined || errorCode === '')
|
|
23
36
|
return { kind: 'no-verdict', message };
|
|
24
|
-
if (errorCode ===
|
|
37
|
+
if (errorCode === RULES_INERT_ENTRY && bodyReason === EARLIER_GRAMMAR_REASON && status !== undefined && status >= 400 && status < 500) {
|
|
38
|
+
return {
|
|
39
|
+
kind: 'earlier-grammar',
|
|
40
|
+
message,
|
|
41
|
+
...(bodyField === 'toolAllow' ? { field: bodyField } : {}),
|
|
42
|
+
...(bodyEntry !== undefined && bodyEntry !== '' ? { entry: bodyEntry } : {}),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (errorCode === RULES_INERT_ENTRY && status !== undefined && status >= 400 && status < 500) {
|
|
25
46
|
return {
|
|
26
|
-
kind: '
|
|
47
|
+
kind: 'inert-entry',
|
|
27
48
|
message,
|
|
28
49
|
...(bodyField === 'toolDeny' || bodyField === 'toolAllow' ? { field: bodyField } : {}),
|
|
29
|
-
...(
|
|
50
|
+
...(bodyEntry !== undefined && bodyEntry !== '' ? { entry: bodyEntry } : {}),
|
|
51
|
+
...(isSessionRuleInertReason(bodyReason) ? { reason: bodyReason } : {}),
|
|
30
52
|
...(isLegacyToolNameVerdict(bodyVerdict) ? { verdict: bodyVerdict } : {}),
|
|
31
53
|
...(bodyCurrentName !== undefined && bodyCurrentName !== '' ? { currentName: bodyCurrentName } : {}),
|
|
32
54
|
};
|
|
33
55
|
}
|
|
56
|
+
if (errorCode === RULES_INVALID_SHAPE && status !== undefined && status >= 400 && status < 500)
|
|
57
|
+
return { kind: 'request-rejected', message };
|
|
34
58
|
if (isRulesRefusalCode(errorCode) && status !== undefined && status >= 400 && status < 500)
|
|
35
59
|
return { kind: 'error', message };
|
|
36
60
|
if (status === 501 || errorCode === 'capability.session_store_required') {
|
|
@@ -95,6 +119,21 @@ function narrowStoredRecord(body) {
|
|
|
95
119
|
if (typeof rev !== 'number' || !Number.isSafeInteger(rev) || rev < 0) {
|
|
96
120
|
return { ok: false, why: 'rev', detail: 'policy record carries no readable version stamp' };
|
|
97
121
|
}
|
|
122
|
+
let grammarSlot;
|
|
123
|
+
try {
|
|
124
|
+
grammarSlot = Object.getOwnPropertyDescriptor(envelope, 'grammar');
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return { ok: false, why: 'grammar_stamp', detail: 'policy record’s rule-grammar stamp could not be read' };
|
|
128
|
+
}
|
|
129
|
+
let grammar;
|
|
130
|
+
if (grammarSlot !== undefined) {
|
|
131
|
+
const v = Object.hasOwn(grammarSlot, 'value') ? grammarSlot.value : undefined;
|
|
132
|
+
if (typeof v !== 'number' || !Number.isSafeInteger(v) || v < 0) {
|
|
133
|
+
return { ok: false, why: 'grammar_stamp', detail: 'policy record carries a rule-grammar stamp that is not a whole number of 0 or more' };
|
|
134
|
+
}
|
|
135
|
+
grammar = v;
|
|
136
|
+
}
|
|
98
137
|
const rules = {};
|
|
99
138
|
for (const field of SESSION_POLICY_RULE_FIELDS) {
|
|
100
139
|
if (!Object.hasOwn(envelope, field))
|
|
@@ -107,7 +146,19 @@ function narrowStoredRecord(body) {
|
|
|
107
146
|
return { ok: false, why: 'rule_field', detail: read.detail };
|
|
108
147
|
rules[field] = read.list;
|
|
109
148
|
}
|
|
110
|
-
|
|
149
|
+
for (const key of foreignRecordKeysOf(envelope)) {
|
|
150
|
+
const slot = Object.getOwnPropertyDescriptor(envelope, key);
|
|
151
|
+
if (slot === undefined)
|
|
152
|
+
continue;
|
|
153
|
+
if (!Object.hasOwn(slot, 'value') || Array.isArray(slot.value)) {
|
|
154
|
+
return {
|
|
155
|
+
ok: false,
|
|
156
|
+
why: 'rule_field',
|
|
157
|
+
detail: `policy record carries a rule field this client does not read (${recordKeyLabel(key)}) — refusing to treat it as readable, since writing the record back would drop that field`,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return { ok: true, rev, rules, grammar };
|
|
111
162
|
}
|
|
112
163
|
export async function readSessionPolicy(facade, sessionId, opts) {
|
|
113
164
|
let body;
|
|
@@ -126,7 +177,7 @@ export async function readSessionPolicy(facade, sessionId, opts) {
|
|
|
126
177
|
}
|
|
127
178
|
if (!read.ok)
|
|
128
179
|
return { kind: 'malformed', why: read.why, detail: read.detail };
|
|
129
|
-
return { kind: 'present', rev: read.rev, rules: read.rules };
|
|
180
|
+
return { kind: 'present', rev: read.rev, rules: read.rules, ...(read.grammar !== undefined ? { grammar: read.grammar } : {}) };
|
|
130
181
|
}
|
|
131
182
|
export function tightenedSessionRules(prior, add) {
|
|
132
183
|
const p = narrowRulesInput(prior, 'prior');
|
|
@@ -159,6 +210,10 @@ function narrowRulesInput(raw, side) {
|
|
|
159
210
|
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
160
211
|
throw new TypeError(`tightenedSessionRules: "${side}" must be a rules record object`);
|
|
161
212
|
}
|
|
213
|
+
const foreign = foreignRecordKeysOf(raw);
|
|
214
|
+
if (foreign.length > 0) {
|
|
215
|
+
throw new TypeError(`tightenedSessionRules: "${side}" carries a key that is not a rule field (${recordKeyLabel(foreign[0] ?? '')}) — it would not be written, so the record is refused instead of written without it`);
|
|
216
|
+
}
|
|
162
217
|
const out = {};
|
|
163
218
|
for (const field of SESSION_POLICY_RULE_FIELDS) {
|
|
164
219
|
if (!Object.hasOwn(raw, field))
|
|
@@ -173,10 +228,13 @@ function narrowRulesInput(raw, side) {
|
|
|
173
228
|
}
|
|
174
229
|
return out;
|
|
175
230
|
}
|
|
176
|
-
function receiptMismatch(sent, receipt, sentRev, receiptRev) {
|
|
231
|
+
function receiptMismatch(sent, receipt, sentRev, receiptRev, sentGrammar, receiptGrammar) {
|
|
177
232
|
if (receiptRev <= sentRev) {
|
|
178
233
|
return `policy write receipt did not advance the version stamp (sent with ${sentRev}, receipt says ${receiptRev})`;
|
|
179
234
|
}
|
|
235
|
+
if (sentGrammar !== undefined && (receiptGrammar === undefined || receiptGrammar < sentGrammar)) {
|
|
236
|
+
return `policy write receipt does not carry the rule-grammar stamp that was sent (sent ${sentGrammar}, receipt ${receiptGrammar === undefined ? 'carries none' : `says ${receiptGrammar}`}) — the stored entries may now read differently`;
|
|
237
|
+
}
|
|
180
238
|
for (const field of SESSION_POLICY_RULE_FIELDS) {
|
|
181
239
|
const s = Object.hasOwn(sent, field) ? sent[field] : undefined;
|
|
182
240
|
if (s === undefined)
|
|
@@ -227,32 +285,66 @@ function unknownFrom(failure, leg) {
|
|
|
227
285
|
return { kind: 'unknown', why: leg === 'read' ? 'read_failed' : 'write_indeterminate', message };
|
|
228
286
|
case 'loosen-forbidden':
|
|
229
287
|
case 'cas-conflict':
|
|
230
|
-
case '
|
|
288
|
+
case 'earlier-grammar':
|
|
289
|
+
case 'inert-entry':
|
|
231
290
|
case 'error':
|
|
232
291
|
return { kind: 'unknown', why: leg === 'read' ? 'read_failed' : 'write_refused', message };
|
|
233
292
|
}
|
|
234
293
|
}
|
|
235
|
-
function
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
294
|
+
function alreadyInRecordOf(field, entry, prior, add) {
|
|
295
|
+
if (field === undefined || entry === undefined)
|
|
296
|
+
return undefined;
|
|
297
|
+
const inPrior = Object.hasOwn(prior, field) ? prior[field] : undefined;
|
|
298
|
+
const inAdd = Object.hasOwn(add, field) ? add[field] : undefined;
|
|
299
|
+
if (inPrior !== undefined && inPrior.includes(entry))
|
|
300
|
+
return true;
|
|
301
|
+
if (inAdd !== undefined && inAdd.includes(entry))
|
|
302
|
+
return false;
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
305
|
+
function inertEntryRefusalOf(failure) {
|
|
306
|
+
const { field, entry, reason, verdict, currentName } = failure;
|
|
246
307
|
return {
|
|
247
|
-
kind: 'legacy_tool_name',
|
|
248
308
|
message: failure.message,
|
|
249
309
|
...(field !== undefined ? { field } : {}),
|
|
250
|
-
...(
|
|
310
|
+
...(entry !== undefined ? { entry } : {}),
|
|
311
|
+
...(reason !== undefined ? { reason } : {}),
|
|
251
312
|
...(verdict !== undefined ? { verdict } : {}),
|
|
252
313
|
...(currentName !== undefined ? { currentName } : {}),
|
|
253
|
-
...(alreadyInRecord !== undefined ? { alreadyInRecord } : {}),
|
|
254
314
|
};
|
|
255
315
|
}
|
|
316
|
+
function earlierGrammarEntriesOf(prior, outgoing) {
|
|
317
|
+
const priorAllow = Object.hasOwn(prior, 'toolAllow') ? prior.toolAllow : undefined;
|
|
318
|
+
const outAllow = Object.hasOwn(outgoing, 'toolAllow') ? outgoing.toolAllow : undefined;
|
|
319
|
+
if (priorAllow === undefined || outAllow === undefined)
|
|
320
|
+
return [];
|
|
321
|
+
const carried = new Set(outAllow);
|
|
322
|
+
const seen = new Set();
|
|
323
|
+
const out = [];
|
|
324
|
+
for (const rule of priorAllow) {
|
|
325
|
+
if (seen.has(rule) || !carried.has(rule))
|
|
326
|
+
continue;
|
|
327
|
+
const name = deniedEntryToolName(rule);
|
|
328
|
+
if (name === undefined || name === rule)
|
|
329
|
+
continue;
|
|
330
|
+
seen.add(rule);
|
|
331
|
+
out.push({ field: 'toolAllow', rule });
|
|
332
|
+
}
|
|
333
|
+
return out;
|
|
334
|
+
}
|
|
335
|
+
function earlierGrammarOutcome(failure) {
|
|
336
|
+
return {
|
|
337
|
+
kind: 'refused',
|
|
338
|
+
why: 'earlier_grammar_rows',
|
|
339
|
+
entries: Object.hasOwn(failure, 'field') && Object.hasOwn(failure, 'entry') && failure.field !== undefined && failure.entry !== undefined ? [{ field: failure.field, rule: failure.entry }] : [],
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function rulesWithStamp(rules, grammar) {
|
|
343
|
+
return grammar === undefined ? rules : { ...rules, grammar };
|
|
344
|
+
}
|
|
345
|
+
function grammarStampOf(read) {
|
|
346
|
+
return Object.hasOwn(read, 'grammar') ? read.grammar : undefined;
|
|
347
|
+
}
|
|
256
348
|
async function attemptTighten(facade, req, opts) {
|
|
257
349
|
const read = await readSessionPolicy(facade, req.sessionId, opts);
|
|
258
350
|
if (read.kind === 'failed')
|
|
@@ -261,9 +353,15 @@ async function attemptTighten(facade, req, opts) {
|
|
|
261
353
|
return { done: { kind: 'unknown', why: 'read_unreadable', message: read.detail } };
|
|
262
354
|
}
|
|
263
355
|
const rules = tightenedSessionRules(read.rules, req.add);
|
|
356
|
+
const stamp = grammarStampOf(read);
|
|
357
|
+
if (stamp === undefined) {
|
|
358
|
+
const earlier = earlierGrammarEntriesOf(read.rules, rules);
|
|
359
|
+
if (earlier.length > 0)
|
|
360
|
+
return { done: { kind: 'refused', why: 'earlier_grammar_rows', entries: earlier } };
|
|
361
|
+
}
|
|
264
362
|
let receipt;
|
|
265
363
|
try {
|
|
266
|
-
receipt = await facade.putPolicy(req.sessionId, { rules, expectedRev: read.rev }, opts);
|
|
364
|
+
receipt = await facade.putPolicy(req.sessionId, { rules: rulesWithStamp(rules, stamp), expectedRev: read.rev }, opts);
|
|
267
365
|
}
|
|
268
366
|
catch (e) {
|
|
269
367
|
const failure = classifySessionPolicyFailure(e);
|
|
@@ -271,8 +369,12 @@ async function attemptTighten(facade, req, opts) {
|
|
|
271
369
|
return { done: { kind: 'loosen_forbidden', message: failure.message } };
|
|
272
370
|
if (failure.kind === 'cas-conflict')
|
|
273
371
|
return { retryAtRev: read.rev };
|
|
274
|
-
if (failure.kind === '
|
|
275
|
-
return { done:
|
|
372
|
+
if (failure.kind === 'earlier-grammar')
|
|
373
|
+
return { done: earlierGrammarOutcome(failure) };
|
|
374
|
+
if (failure.kind === 'inert-entry') {
|
|
375
|
+
const alreadyInRecord = alreadyInRecordOf(failure.field, failure.entry, read.rules, req.add);
|
|
376
|
+
return { done: { kind: 'inert_entry', ...inertEntryRefusalOf(failure), ...(alreadyInRecord !== undefined ? { alreadyInRecord } : {}) } };
|
|
377
|
+
}
|
|
276
378
|
return { done: unknownFrom(failure, 'write') };
|
|
277
379
|
}
|
|
278
380
|
let narrowed;
|
|
@@ -285,7 +387,7 @@ async function attemptTighten(facade, req, opts) {
|
|
|
285
387
|
if (!narrowed.ok) {
|
|
286
388
|
return { done: { kind: 'unknown', why: 'write_receipt_unreadable', message: narrowed.detail } };
|
|
287
389
|
}
|
|
288
|
-
const mismatch = receiptMismatch(rules, narrowed.rules, read.rev, narrowed.rev);
|
|
390
|
+
const mismatch = receiptMismatch(rules, narrowed.rules, read.rev, narrowed.rev, stamp, narrowed.grammar);
|
|
289
391
|
if (mismatch !== null)
|
|
290
392
|
return { done: { kind: 'unknown', why: 'write_receipt_mismatch', message: mismatch } };
|
|
291
393
|
return { done: { kind: 'written', rev: narrowed.rev } };
|
|
@@ -309,6 +411,8 @@ export async function tightenSessionPolicy(facade, req, opts) {
|
|
|
309
411
|
return second.done;
|
|
310
412
|
return { kind: 'conflict', rev: second.retryAtRev };
|
|
311
413
|
}
|
|
414
|
+
const SESSION_POLICY_INERT_ENTRY_WRITE_NOTE = 'The engine refused to write these session rules, and the stored record was not changed at all: one entry in them reads as matching nothing — written in, it would neither block nor allow any call (the engine’s own error text names the entry and why). Correct that entry or take it out, then write the rules again. If that entry was already in this session’s rule record, it has to be removed from the record before the record can be written again.';
|
|
415
|
+
const SESSION_POLICY_EARLIER_GRAMMAR_NOTE = 'Nothing was written: this session’s rules were saved before the current rule grammar, when an allow entry written in a tool’s own rule form (such as Bash(git:*)) was read as a plain tool name and so allowed nothing. Writing the record back now would read those entries in the current grammar, where they do allow calls — a widening nobody asked for — so the record was left as it is. Rewrite this session’s allow entries in the current form, or take those entries out, before changing its rules here.';
|
|
312
416
|
export function sessionPolicyTightenNotice(outcome) {
|
|
313
417
|
switch (outcome.kind) {
|
|
314
418
|
case 'written':
|
|
@@ -321,8 +425,10 @@ export function sessionPolicyTightenNotice(outcome) {
|
|
|
321
425
|
return outcome.why === 'absent'
|
|
322
426
|
? 'This deployment does not keep per-session tool rules, so there is nothing to tighten here.'
|
|
323
427
|
: 'This engine does not say whether it keeps per-session tool rules — it predates that answer — so the tightening entry stays hidden rather than being written blind.';
|
|
324
|
-
case '
|
|
325
|
-
return
|
|
428
|
+
case 'refused':
|
|
429
|
+
return SESSION_POLICY_EARLIER_GRAMMAR_NOTE;
|
|
430
|
+
case 'inert_entry':
|
|
431
|
+
return SESSION_POLICY_INERT_ENTRY_WRITE_NOTE;
|
|
326
432
|
case 'unknown':
|
|
327
433
|
switch (outcome.why) {
|
|
328
434
|
case 'face_unavailable':
|
|
@@ -348,7 +454,7 @@ export function sessionPolicyTightenNotice(outcome) {
|
|
|
348
454
|
case 'write_receipt_unreadable':
|
|
349
455
|
return 'The tightening was sent but the engine’s answer could not be read — it may already be in effect. Re-read the current rules before writing anything else.';
|
|
350
456
|
case 'write_receipt_mismatch':
|
|
351
|
-
return 'The tightening was sent but the engine’s answer does not account for it (the version did not advance,
|
|
457
|
+
return 'The tightening was sent but the engine’s answer does not account for it (the version did not advance, the entries sent here are not in the returned record, or the record came back without the rule-grammar mark it was sent with, so its entries may now read differently) — it may already be in effect. Re-read the current rules before writing anything else.';
|
|
352
458
|
}
|
|
353
459
|
}
|
|
354
460
|
}
|
|
@@ -371,8 +477,10 @@ function readFailureSentence(kind) {
|
|
|
371
477
|
return 'This session is not readable from here — it may not exist, or it may belong to someone else (the engine answers both the same way, so this client cannot tell which) — so its rule record cannot be shown. Check that you are connected to the engine and the session you meant, or continue in a new session.';
|
|
372
478
|
case 'request-rejected':
|
|
373
479
|
return 'The engine rejected the request to read this session’s rule record as malformed, so it could not be read. Sending the same request again will not change that; check which session was asked for.';
|
|
374
|
-
case '
|
|
375
|
-
return 'The engine answered this read with a refusal
|
|
480
|
+
case 'earlier-grammar':
|
|
481
|
+
return 'The engine answered this read with a refusal that only applies to writing back rules saved under an earlier rule grammar, so this session’s rule record could not be read. Try again; if it keeps happening, whoever runs the engine needs to look at it.';
|
|
482
|
+
case 'inert-entry':
|
|
483
|
+
return 'The engine answered this read with a refusal that only applies to writing a rule entry it reads as matching nothing, so this session’s rule record could not be read. Try again; if it keeps happening, whoever runs the engine needs to look at it.';
|
|
376
484
|
case 'unauthorized':
|
|
377
485
|
return 'This call could not present a verifiable identity, so this session’s rule record could not be read.';
|
|
378
486
|
case 'error':
|
|
@@ -389,6 +497,8 @@ function readMalformedSentence(why) {
|
|
|
389
497
|
return 'The engine’s answer for this session’s rule record carried no usable version, so this version will not show it — a record it cannot place may still hold restrictions that are in effect. Try again.';
|
|
390
498
|
case 'rule_field':
|
|
391
499
|
return 'One of the lists in this session’s rule record came back in a form this version cannot read, so none of the record is shown rather than part of it — a partly shown record could leave out restrictions that are in effect.';
|
|
500
|
+
case 'grammar_stamp':
|
|
501
|
+
return 'The engine’s answer for this session’s rule record carried a rule-grammar mark this version cannot read, so it will not show the record — the same entry can mean different things in different grammars, so what it allows is not known. Try again; if it keeps happening, this version and the engine may be out of step.';
|
|
392
502
|
}
|
|
393
503
|
}
|
|
394
504
|
function ownRead(o, key) {
|
|
@@ -492,9 +602,15 @@ async function attemptRemoval(facade, sessionId, mounted, opts) {
|
|
|
492
602
|
const { next, removed } = judged;
|
|
493
603
|
if (removed.length === 0)
|
|
494
604
|
return { done: { kind: 'nothing_to_remove', rev: read.rev } };
|
|
605
|
+
const stamp = grammarStampOf(read);
|
|
606
|
+
if (stamp === undefined) {
|
|
607
|
+
const earlier = earlierGrammarEntriesOf(read.rules, next);
|
|
608
|
+
if (earlier.length > 0)
|
|
609
|
+
return { done: { kind: 'refused', why: 'earlier_grammar_rows', entries: earlier } };
|
|
610
|
+
}
|
|
495
611
|
let receipt;
|
|
496
612
|
try {
|
|
497
|
-
receipt = await facade.putPolicy(sessionId, { rules: next, expectedRev: read.rev }, opts);
|
|
613
|
+
receipt = await facade.putPolicy(sessionId, { rules: rulesWithStamp(next, stamp), expectedRev: read.rev }, opts);
|
|
498
614
|
}
|
|
499
615
|
catch (e) {
|
|
500
616
|
const failure = classifySessionPolicyFailure(e);
|
|
@@ -502,18 +618,10 @@ async function attemptRemoval(facade, sessionId, mounted, opts) {
|
|
|
502
618
|
return { done: { kind: 'loosen_forbidden', message: failure.message, refused: removed } };
|
|
503
619
|
if (failure.kind === 'cas-conflict')
|
|
504
620
|
return { retryAtRev: read.rev };
|
|
505
|
-
if (failure.kind === '
|
|
506
|
-
return {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
message: failure.message,
|
|
510
|
-
...(failure.field !== undefined ? { field: failure.field } : {}),
|
|
511
|
-
...(failure.name !== undefined ? { name: failure.name } : {}),
|
|
512
|
-
...(failure.verdict !== undefined ? { verdict: failure.verdict } : {}),
|
|
513
|
-
...(failure.currentName !== undefined ? { currentName: failure.currentName } : {}),
|
|
514
|
-
},
|
|
515
|
-
};
|
|
516
|
-
}
|
|
621
|
+
if (failure.kind === 'earlier-grammar')
|
|
622
|
+
return { done: earlierGrammarOutcome(failure) };
|
|
623
|
+
if (failure.kind === 'inert-entry')
|
|
624
|
+
return { done: { kind: 'inert_entry', ...inertEntryRefusalOf(failure) } };
|
|
517
625
|
return { done: removalFailed(failure, 'write') };
|
|
518
626
|
}
|
|
519
627
|
let narrowed;
|
|
@@ -525,7 +633,7 @@ async function attemptRemoval(facade, sessionId, mounted, opts) {
|
|
|
525
633
|
}
|
|
526
634
|
if (!narrowed.ok)
|
|
527
635
|
return { done: { kind: 'write_unconfirmed', why: 'write_receipt_unreadable', message: narrowed.detail } };
|
|
528
|
-
const mismatch = receiptMismatch(next, narrowed.rules, read.rev, narrowed.rev) ??
|
|
636
|
+
const mismatch = receiptMismatch(next, narrowed.rules, read.rev, narrowed.rev, stamp, narrowed.grammar) ??
|
|
529
637
|
(removed.some((e) => {
|
|
530
638
|
const r = Object.hasOwn(narrowed.rules, e.field) ? narrowed.rules[e.field] : undefined;
|
|
531
639
|
return r !== undefined && r.includes(e.rule);
|
|
@@ -609,8 +717,10 @@ export function sessionPolicyRemovalNotice(outcome) {
|
|
|
609
717
|
}
|
|
610
718
|
case 'write_unconfirmed':
|
|
611
719
|
return 'The removal was sent but no clear answer came back from the engine — it may already be in effect. Run the removal again: it re-reads the record, and reports nothing left to remove if it did take effect.';
|
|
612
|
-
case '
|
|
613
|
-
return
|
|
720
|
+
case 'refused':
|
|
721
|
+
return SESSION_POLICY_EARLIER_GRAMMAR_NOTE;
|
|
722
|
+
case 'inert_entry':
|
|
723
|
+
return 'Nothing was changed: the engine refused to write this session’s rule record back, because an entry this removal keeps reads as matching nothing (the engine’s own error text names the entry and why). This removal only takes out names the engine refuses to start a run with, so it leaves that entry for a person to correct or remove; while the record holds it, it cannot be written, so the entries this removal meant to take out stay in it as well.';
|
|
614
724
|
default:
|
|
615
725
|
return generic;
|
|
616
726
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { type PermissionsKeptReason } from '../permissionsDeclaration.js';
|
|
2
|
+
import type { PermissionsAllowGrantReading } from '../permissionsAllowGrantCapability.js';
|
|
3
|
+
export type StandingRowWhy = 'given' | 'card_unreadable' | 'remember_refused' | 'remember_unreadable' | 'route_cannot_carry' | 'classes_unreadable' | 'not_deployment_list_question' | 'evidence_unreadable' | 'offers_unreadable' | 'engine_lane_closed' | 'args_unavailable' | 'grant_binding' | 'tool_name_unreadable' | 'rule_cannot_clear' | 'host_fact_missing' | 'card_face_absent' | 'face_not_generic' | 'rule_not_sent';
|
|
2
4
|
export type StandingPersistentRow = 'engine_offer' | 'local_whole_tool' | 'none';
|
|
3
5
|
export interface StandingRowsWhy {
|
|
4
6
|
readonly session: StandingRowWhy;
|
|
@@ -12,8 +14,24 @@ export interface StandingRows {
|
|
|
12
14
|
readonly why: StandingRowsWhy;
|
|
13
15
|
}
|
|
14
16
|
export interface StandingRowsHostFacts {
|
|
15
|
-
readonly cardFace?: string;
|
|
16
17
|
readonly wholeToolRuleSent?: boolean;
|
|
17
18
|
}
|
|
18
19
|
export declare function standingRowsOf(card: object | null | undefined, host?: StandingRowsHostFacts | null): StandingRows;
|
|
19
20
|
export declare function seatSessionRowOf(card: object | null | undefined, requestId: string | undefined): boolean;
|
|
21
|
+
export type RememberOffer = 'offerable' | 'refused' | 'unknown';
|
|
22
|
+
export interface RememberOfferable {
|
|
23
|
+
readonly session: RememberOffer;
|
|
24
|
+
readonly persistentRule: RememberOffer;
|
|
25
|
+
readonly deploymentList: RememberOffer;
|
|
26
|
+
}
|
|
27
|
+
export declare function rememberOfferable(card: object | null | undefined, host?: StandingRowsHostFacts | null): RememberOfferable;
|
|
28
|
+
export interface LocalAllowRuleHostFacts {
|
|
29
|
+
readonly hostLadderEnforces?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface LocalAllowRuleEnforcement {
|
|
32
|
+
readonly enforceable: boolean;
|
|
33
|
+
readonly by?: 'host_ladder' | 'engine_grant';
|
|
34
|
+
readonly engine: 'sent' | 'kept' | 'unreadable';
|
|
35
|
+
readonly keptReason?: PermissionsKeptReason;
|
|
36
|
+
}
|
|
37
|
+
export declare function localAllowRuleEnforceable(rule: string, caps: PermissionsAllowGrantReading, host?: LocalAllowRuleHostFacts | null): LocalAllowRuleEnforcement;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { readAskClassPairState } from './askClassPair.js';
|
|
1
|
+
import { DEPLOYMENT_LIST_ASK_CLASSES, readAskClassPairState } from './askClassPair.js';
|
|
2
2
|
import { automaticAnswerVerdict } from './automaticAnswerVerdict.js';
|
|
3
|
-
import { readRuleOffers } from './toolApprovalWire.js';
|
|
3
|
+
import { readApprovalCardWord, readRuleOffers } from './toolApprovalWire.js';
|
|
4
|
+
import { CORE_SKILL_TOOL_NAME } from '../generated/toolNameTables.js';
|
|
4
5
|
import { toolPermissionRequestIdDomain } from '../seatContract.js';
|
|
5
|
-
|
|
6
|
+
import { permissionsDeclarationReport } from '../permissionsDeclaration.js';
|
|
6
7
|
const UNREADABLE = Symbol('unreadable');
|
|
7
8
|
function ownData(v, key) {
|
|
8
9
|
try {
|
|
@@ -65,15 +66,17 @@ function persistentRowOf(card, host, evidenceUnreadable) {
|
|
|
65
66
|
const tool = ownData(card, 'toolName');
|
|
66
67
|
if (typeof tool !== 'string' || tool.length === 0)
|
|
67
68
|
return { row: 'none', why: 'tool_name_unreadable' };
|
|
69
|
+
if (tool === CORE_SKILL_TOOL_NAME)
|
|
70
|
+
return { row: 'none', why: 'grant_binding' };
|
|
68
71
|
if (automaticAnswerVerdict(card, 'standing_rule').kind !== 'may_answer')
|
|
69
72
|
return { row: 'none', why: 'rule_cannot_clear' };
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (typeof face !== 'string')
|
|
74
|
-
return { row: 'none', why: 'host_fact_missing' };
|
|
73
|
+
const face = readApprovalCardWord(card);
|
|
74
|
+
if (face === undefined)
|
|
75
|
+
return { row: 'none', why: 'card_face_absent' };
|
|
75
76
|
if (face !== 'generic')
|
|
76
77
|
return { row: 'none', why: 'face_not_generic' };
|
|
78
|
+
if (typeof host !== 'object' || host === null)
|
|
79
|
+
return { row: 'none', why: 'host_fact_missing' };
|
|
77
80
|
const sent = ownData(host, 'wholeToolRuleSent');
|
|
78
81
|
if (typeof sent !== 'boolean')
|
|
79
82
|
return { row: 'none', why: 'host_fact_missing' };
|
|
@@ -146,3 +149,61 @@ export function seatSessionRowOf(card, requestId) {
|
|
|
146
149
|
return false;
|
|
147
150
|
}
|
|
148
151
|
}
|
|
152
|
+
const SESSION_OFFER = Object.freeze({
|
|
153
|
+
given: 'offerable',
|
|
154
|
+
card_unreadable: 'unknown',
|
|
155
|
+
remember_refused: 'refused',
|
|
156
|
+
remember_unreadable: 'unknown',
|
|
157
|
+
route_cannot_carry: 'refused',
|
|
158
|
+
classes_unreadable: 'unknown',
|
|
159
|
+
not_deployment_list_question: 'refused',
|
|
160
|
+
evidence_unreadable: 'unknown',
|
|
161
|
+
offers_unreadable: 'unknown',
|
|
162
|
+
engine_lane_closed: 'refused',
|
|
163
|
+
args_unavailable: 'refused',
|
|
164
|
+
grant_binding: 'refused',
|
|
165
|
+
tool_name_unreadable: 'unknown',
|
|
166
|
+
rule_cannot_clear: 'refused',
|
|
167
|
+
host_fact_missing: 'unknown',
|
|
168
|
+
card_face_absent: 'unknown',
|
|
169
|
+
face_not_generic: 'refused',
|
|
170
|
+
rule_not_sent: 'refused',
|
|
171
|
+
});
|
|
172
|
+
const PERSISTENT_OFFER = Object.freeze({ ...SESSION_OFFER, remember_refused: 'unknown' });
|
|
173
|
+
export function rememberOfferable(card, host) {
|
|
174
|
+
const rows = standingRowsOf(card, host);
|
|
175
|
+
return Object.freeze({
|
|
176
|
+
session: SESSION_OFFER[rows.why.session],
|
|
177
|
+
persistentRule: PERSISTENT_OFFER[rows.why.persistent],
|
|
178
|
+
deploymentList: SESSION_OFFER[rows.why.deploymentList],
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
export function localAllowRuleEnforceable(rule, caps, host) {
|
|
182
|
+
const ladder = typeof host === 'object' && host !== null && ownData(host, 'hostLadderEnforces') === true;
|
|
183
|
+
let engine = 'unreadable';
|
|
184
|
+
let keptReason;
|
|
185
|
+
if (typeof rule === 'string' && rule.trim() !== '') {
|
|
186
|
+
try {
|
|
187
|
+
const report = permissionsDeclarationReport({ deny: [], ask: [], allow: [rule], settingsRead: 'clean' }, caps);
|
|
188
|
+
const kept = report.kept.find((k) => k.bucket === 'allow' && k.rule === rule);
|
|
189
|
+
if (kept !== undefined) {
|
|
190
|
+
engine = 'kept';
|
|
191
|
+
keptReason = kept.reason;
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
const sentAs = report.rewritten.find((r) => r.bucket === 'allow' && r.from === rule)?.to ?? rule;
|
|
195
|
+
engine = report.permissions?.allow?.includes(sentAs) === true ? 'sent' : 'unreadable';
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
engine = 'unreadable';
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const by = ladder ? 'host_ladder' : engine === 'sent' ? 'engine_grant' : undefined;
|
|
203
|
+
return Object.freeze({
|
|
204
|
+
enforceable: by !== undefined,
|
|
205
|
+
...(by !== undefined ? { by } : {}),
|
|
206
|
+
engine,
|
|
207
|
+
...(keptReason !== undefined ? { keptReason } : {}),
|
|
208
|
+
});
|
|
209
|
+
}
|