@sema-agent/client-core 0.95.0 → 0.96.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 +180 -0
- package/README.md +49 -25
- package/dist/adapt/arms.js +7 -1
- package/dist/adapt/toolCards.d.ts +1 -0
- package/dist/adapt/toolCards.js +16 -3
- package/dist/adapt/wireShapes.js +2 -16
- package/dist/adapter/activeRunSelfHeal.d.ts +4 -0
- package/dist/adapter/activeRunSelfHeal.js +30 -4
- package/dist/adapter/downstream/eventToSdkMessage.js +11 -3
- package/dist/adapter/downstream/terminalToSdkResult.js +8 -9
- package/dist/adapter/runStream.js +30 -47
- package/dist/agentSession/engineAgentRegistryRead.js +5 -4
- package/dist/agentSession/engineSessionBackgroundStop.js +22 -15
- package/dist/agentsWireCaps.js +13 -2
- package/dist/callerCwdCapability.d.ts +0 -4
- package/dist/callerCwdCapability.js +0 -6
- package/dist/centerWiringCapability.d.ts +0 -4
- package/dist/centerWiringCapability.js +0 -6
- package/dist/classifierVerdictWire.js +4 -16
- package/dist/clientSlice.js +1 -1
- package/dist/cloudConfigWireCaps.d.ts +3 -3
- package/dist/cloudConfigWireCaps.js +65 -45
- package/dist/coreValuePorts.d.ts +11 -7
- package/dist/coreValuePorts.js +5 -8
- package/dist/credentialProvenance.d.ts +28 -0
- package/dist/credentialProvenance.js +76 -0
- package/dist/decideFailureNote.js +3 -1
- package/dist/decideReceipt.js +7 -10
- package/dist/declared/supportFloorDeclared.js +3 -3
- package/dist/displayUntrusted.d.ts +7 -3
- package/dist/displayUntrusted.js +356 -60
- package/dist/engineCapsCache.d.ts +6 -0
- package/dist/engineCapsCache.js +226 -32
- package/dist/engineDirectory.d.ts +79 -0
- package/dist/engineDirectory.js +186 -0
- package/dist/engineErrorCodes.d.ts +14 -1
- package/dist/engineErrorCodes.js +37 -15
- package/dist/engineNoticeCodes.d.ts +17 -0
- package/dist/engineNoticeCodes.js +57 -3
- package/dist/engineWordRenames.d.ts +8 -0
- package/dist/engineWordRenames.js +13 -0
- package/dist/fileHistoryCaptureCapability.d.ts +14 -0
- package/dist/fileHistoryCaptureCapability.js +70 -0
- package/dist/fleet/bgSubagentRow.d.ts +15 -0
- package/dist/fleet/bgSubagentRow.js +67 -0
- package/dist/fleet/fleetLedger.d.ts +1 -0
- package/dist/fleet/fleetLedger.js +103 -89
- package/dist/fleet/fleetProjection.d.ts +1 -0
- package/dist/fleet/fleetProjection.js +10 -3
- package/dist/fleetTaskDesc.js +4 -17
- package/dist/gateOutcome.d.ts +12 -1
- package/dist/gateOutcome.js +58 -1
- package/dist/gateVocabulary.d.ts +7 -2
- package/dist/gateVocabulary.js +5 -1
- package/dist/generated/engineAskFaceTables.js +1 -1
- package/dist/generated/engineFactTables.d.ts +5 -3
- package/dist/generated/engineFactTables.js +17 -4
- package/dist/generated/engineNoticeTables.js +12 -3
- package/dist/generated/settingsSchemaFns.d.ts +3 -0
- package/dist/generated/settingsSchemaFns.js +53 -0
- package/dist/generated/toolNameTables.d.ts +1 -0
- package/dist/generated/toolNameTables.js +1 -0
- package/dist/hitl/approvalEndingCause.d.ts +2 -0
- package/dist/hitl/approvalEndingCause.js +31 -21
- package/dist/hitl/askParkRowRouting.js +1 -1
- package/dist/hitl/automaticAnswerVerdict.d.ts +1 -1
- package/dist/hitl/automaticAnswerVerdict.js +13 -8
- package/dist/hitl/decisionConflict.js +0 -2
- package/dist/hitl/decisionRefusal.d.ts +8 -0
- package/dist/hitl/decisionRefusal.js +48 -14
- package/dist/hitl/frameRouter.d.ts +0 -1
- package/dist/hitl/frameRouter.js +6 -14
- package/dist/hitl/hitlBridge.d.ts +6 -2
- package/dist/hitl/hitlBridge.js +28 -7
- package/dist/hitl/localAllowRule.d.ts +1 -0
- package/dist/hitl/localAllowRule.js +2 -2
- package/dist/hitl/noAllowCause.d.ts +2 -1
- package/dist/hitl/noAllowCause.js +3 -2
- package/dist/hitl/parkResolver.d.ts +1 -0
- package/dist/hitl/parkResolver.js +8 -54
- package/dist/hitl/parkRowBirthWait.d.ts +1 -0
- package/dist/hitl/parkRowBirthWait.js +2 -2
- package/dist/hitl/planReviewWire.d.ts +1 -0
- package/dist/hitl/planReviewWire.js +98 -20
- package/dist/hitl/replayedApproval.d.ts +1 -0
- package/dist/hitl/replayedApproval.js +14 -0
- package/dist/hitl/sessionPolicyDeliverable.js +8 -6
- package/dist/hitl/standingRows.d.ts +18 -0
- package/dist/hitl/standingRows.js +127 -0
- package/dist/hitl/toolApprovalWire.d.ts +15 -3
- package/dist/hitl/toolApprovalWire.js +238 -25
- package/dist/hookMatcherVerdict.d.ts +1 -9
- package/dist/hookMatcherVerdict.js +21 -61
- package/dist/hooksWireCaps.js +2 -2
- package/dist/hostCallbackGuard.d.ts +1 -0
- package/dist/hostCallbackGuard.js +6 -0
- package/dist/index.d.ts +21 -9
- package/dist/index.js +17 -8
- package/dist/mcpProbeCapability.d.ts +0 -4
- package/dist/mcpProbeCapability.js +0 -6
- package/dist/memoryComplianceCapability.d.ts +0 -4
- package/dist/memoryComplianceCapability.js +0 -6
- package/dist/memoryOriginCapability.d.ts +0 -4
- package/dist/memoryOriginCapability.js +0 -6
- package/dist/memorySpecWire.js +2 -2
- package/dist/modelIdentityResolvability.d.ts +2 -2
- package/dist/modelIdentityResolvability.js +12 -3
- package/dist/noticePresentation.d.ts +68 -0
- package/dist/noticePresentation.js +247 -0
- package/dist/outputStyleSeatCapability.d.ts +0 -1
- package/dist/outputStyleSeatCapability.js +0 -3
- package/dist/ownKey.d.ts +2 -0
- package/dist/ownKey.js +18 -0
- package/dist/peerFrames.js +13 -9
- package/dist/permissionModesCapability.d.ts +0 -4
- package/dist/permissionModesCapability.js +0 -6
- package/dist/permissionRuleIssue.js +0 -3
- package/dist/permissionsDeclaration.d.ts +2 -0
- package/dist/permissionsDeclaration.js +56 -41
- package/dist/printPermissionOutcome.d.ts +12 -0
- package/dist/printPermissionOutcome.js +50 -0
- package/dist/printToolResultFrame.d.ts +1 -0
- package/dist/printToolResultFrame.js +5 -9
- package/dist/promptBlockedCopy.d.ts +5 -0
- package/dist/promptBlockedCopy.js +35 -0
- package/dist/protocolSegment.d.ts +9 -6
- package/dist/protocolSegment.js +31 -21
- package/dist/providerRejection.d.ts +4 -0
- package/dist/providerRejection.js +75 -14
- package/dist/replayBody.d.ts +12 -0
- package/dist/replayBody.js +25 -0
- package/dist/resumeRefusalCopy.js +5 -21
- package/dist/retryStatus.d.ts +9 -1
- package/dist/retryStatus.js +9 -2
- package/dist/rewindArchiveCapability.d.ts +3 -13
- package/dist/rewindArchiveCapability.js +7 -58
- package/dist/rewindArchiveReading.d.ts +15 -0
- package/dist/rewindArchiveReading.js +56 -0
- package/dist/ruleCompileVerdict.d.ts +9 -1
- package/dist/ruleCompileVerdict.js +29 -4
- package/dist/runTerminal.d.ts +15 -1
- package/dist/runTerminal.js +56 -8
- package/dist/sandboxWire.js +13 -0
- package/dist/sealedKeyCapability.d.ts +0 -4
- package/dist/sealedKeyCapability.js +0 -6
- package/dist/seam.js +4 -2
- package/dist/seatContract.d.ts +73 -11
- package/dist/seatContract.js +195 -14
- package/dist/sessionReadRedactionCapability.d.ts +0 -4
- package/dist/sessionReadRedactionCapability.js +0 -6
- package/dist/sessionSyncFileHistory.d.ts +4 -0
- package/dist/sessionSyncFileHistory.js +66 -0
- package/dist/sessionSyncFileHistoryCapability.d.ts +0 -1
- package/dist/sessionSyncFileHistoryCapability.js +0 -3
- package/dist/skillToolListsCapability.d.ts +0 -1
- package/dist/skillToolListsCapability.js +0 -3
- package/dist/steerPriorityCapability.d.ts +0 -1
- package/dist/steerPriorityCapability.js +0 -3
- package/dist/storePostureCapability.d.ts +0 -4
- package/dist/storePostureCapability.js +0 -6
- package/dist/subagent/engineSubagentSteer.js +7 -5
- package/dist/subagentContentStore.js +4 -15
- package/dist/supportFloor.d.ts +1 -0
- package/dist/supportFloor.js +13 -9
- package/dist/systemReminderTag.d.ts +1 -0
- package/dist/systemReminderTag.js +8 -0
- package/dist/toolHistoryMismatch.d.ts +0 -3
- package/dist/toolHistoryMismatch.js +3 -7
- package/dist/toolResult.d.ts +1 -1
- package/dist/toolResult.js +2 -1
- package/dist/toolResultContent.d.ts +2 -0
- package/dist/toolResultContent.js +63 -0
- package/dist/usageAnalytics.js +40 -16
- package/dist/userInvokedSkillsCapability.d.ts +0 -1
- package/dist/userInvokedSkillsCapability.js +0 -3
- package/dist/wireErrorTriage.d.ts +2 -0
- package/dist/wireErrorTriage.js +34 -5
- package/dist/wireFailureShape.d.ts +19 -2
- package/dist/wireFailureShape.js +55 -50
- package/docs/INTEGRATION-CLIENTS.md +1262 -17
- package/package.json +5 -5
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { capDisplay, displayUntrusted } from './displayUntrusted.js';
|
|
2
|
+
import { engineNoticeInCatalog, noticeAudienceOf } from './engineNoticeCodes.js';
|
|
3
|
+
import { guardedRead, readOwnDataNonEmptyString, readOwnDataValue } from './ownKey.js';
|
|
4
|
+
const DEFAULT_CUT_MARK = '…';
|
|
5
|
+
const CUT_KEEP_DIVISOR = 2;
|
|
6
|
+
const CLOSERS = '"\')]”’」』)】';
|
|
7
|
+
const ASCII_SENTENCE_END = '.!?;';
|
|
8
|
+
const WIDE_SENTENCE_END = '。!?;';
|
|
9
|
+
const ASCII_CLAUSE_END = ',:';
|
|
10
|
+
const WIDE_CLAUSE_END = ',、:';
|
|
11
|
+
const EM_DASH = '—';
|
|
12
|
+
function isWs(c) {
|
|
13
|
+
return c !== '' && c.trim() === '';
|
|
14
|
+
}
|
|
15
|
+
function readCutOptions(opts) {
|
|
16
|
+
if (typeof opts !== 'object' || opts === null)
|
|
17
|
+
return { boundary: 'sentence', mark: DEFAULT_CUT_MARK };
|
|
18
|
+
const boundary = guardedRead(() => readOwnDataValue(opts, 'boundary'));
|
|
19
|
+
const mark = guardedRead(() => readOwnDataValue(opts, 'mark'));
|
|
20
|
+
return { boundary: boundary === 'word' ? 'word' : 'sentence', mark: typeof mark === 'string' ? mark : DEFAULT_CUT_MARK };
|
|
21
|
+
}
|
|
22
|
+
function sentenceEndAt(src, j) {
|
|
23
|
+
const c = src.charAt(j);
|
|
24
|
+
const ascii = ASCII_SENTENCE_END.includes(c);
|
|
25
|
+
if (!ascii && !WIDE_SENTENCE_END.includes(c))
|
|
26
|
+
return -1;
|
|
27
|
+
let k = j + 1;
|
|
28
|
+
while (k < src.length && CLOSERS.includes(src.charAt(k)))
|
|
29
|
+
k++;
|
|
30
|
+
if (!ascii)
|
|
31
|
+
return k;
|
|
32
|
+
return k === src.length || isWs(src.charAt(k)) ? k : -1;
|
|
33
|
+
}
|
|
34
|
+
function clauseEndAt(src, j) {
|
|
35
|
+
const c = src.charAt(j);
|
|
36
|
+
if (WIDE_CLAUSE_END.includes(c))
|
|
37
|
+
return j + 1;
|
|
38
|
+
if (!ASCII_CLAUSE_END.includes(c))
|
|
39
|
+
return -1;
|
|
40
|
+
const k = j + 1;
|
|
41
|
+
return k === src.length || isWs(src.charAt(k)) ? k : -1;
|
|
42
|
+
}
|
|
43
|
+
function pickCut(src, limit, floor, boundary) {
|
|
44
|
+
let sentence = -1;
|
|
45
|
+
let clause = -1;
|
|
46
|
+
let word = -1;
|
|
47
|
+
for (let j = 0; j <= limit && j < src.length; j++) {
|
|
48
|
+
const c = src.charAt(j);
|
|
49
|
+
if (isWs(c)) {
|
|
50
|
+
word = j;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (boundary === 'word')
|
|
54
|
+
continue;
|
|
55
|
+
const s = sentenceEndAt(src, j);
|
|
56
|
+
if (s !== -1 && s <= limit)
|
|
57
|
+
sentence = Math.max(sentence, s);
|
|
58
|
+
else if (c === EM_DASH && j > 0 && isWs(src.charAt(j - 1)) && (j + 1 === src.length || isWs(src.charAt(j + 1))))
|
|
59
|
+
sentence = Math.max(sentence, j);
|
|
60
|
+
const k = clauseEndAt(src, j);
|
|
61
|
+
if (k !== -1 && k <= limit)
|
|
62
|
+
clause = Math.max(clause, k);
|
|
63
|
+
}
|
|
64
|
+
if (boundary === 'word')
|
|
65
|
+
return word >= floor ? word : -1;
|
|
66
|
+
if (sentence >= floor)
|
|
67
|
+
return sentence;
|
|
68
|
+
if (clause >= floor)
|
|
69
|
+
return clause;
|
|
70
|
+
return word >= floor ? word : -1;
|
|
71
|
+
}
|
|
72
|
+
export function cutForDisplay(text, budget, opts) {
|
|
73
|
+
const src = typeof text === 'string' ? text : (guardedRead(() => String(text ?? '')) ?? '');
|
|
74
|
+
if (typeof budget !== 'number' || !Number.isFinite(budget))
|
|
75
|
+
return { shown: src, truncated: false };
|
|
76
|
+
const limit = Math.max(0, Math.floor(budget));
|
|
77
|
+
if (src.length <= limit)
|
|
78
|
+
return { shown: src, truncated: false };
|
|
79
|
+
const { boundary, mark } = readCutOptions(opts);
|
|
80
|
+
const room = limit - mark.length;
|
|
81
|
+
if (room <= 0)
|
|
82
|
+
return { shown: limit >= mark.length ? mark : '', truncated: true };
|
|
83
|
+
const head = capDisplay(src, room, 'escape');
|
|
84
|
+
const floor = Math.ceil(head.length / CUT_KEEP_DIVISOR);
|
|
85
|
+
const at = pickCut(src, head.length, floor, boundary);
|
|
86
|
+
const kept = (at === -1 ? head : src.slice(0, at)).trimEnd();
|
|
87
|
+
return { shown: `${kept}${mark}`, truncated: true };
|
|
88
|
+
}
|
|
89
|
+
export const NOTICE_SEAT_CLASS = Object.freeze({
|
|
90
|
+
user: Object.freeze({ present: 'persistent+hint', unknown: 'persistent+hint', absent: 'persistent' }),
|
|
91
|
+
operator: Object.freeze({ present: 'transient', unknown: 'transient', absent: 'diagnostic' }),
|
|
92
|
+
unknown: Object.freeze({ present: 'persistent+hint', unknown: 'persistent+hint', absent: 'persistent+hint' }),
|
|
93
|
+
});
|
|
94
|
+
export function noticeSeatClassOf(audience, outlet, deployment) {
|
|
95
|
+
const a = typeof audience === 'string' && Object.hasOwn(NOTICE_SEAT_CLASS, audience) ? audience : 'unknown';
|
|
96
|
+
const row = a === 'operator' && deployment !== 'remote' ? NOTICE_SEAT_CLASS.user : NOTICE_SEAT_CLASS[a];
|
|
97
|
+
return typeof outlet === 'string' && Object.hasOwn(row, outlet) ? row[outlet] : row.unknown;
|
|
98
|
+
}
|
|
99
|
+
const OCCURRENCE_KEY_BY_CODE = Object.freeze({
|
|
100
|
+
'memory.harvest_quarantined': 'reportId',
|
|
101
|
+
'memory.hold_opened': 'reportId',
|
|
102
|
+
'memory.hold_released': 'reportId',
|
|
103
|
+
'memory.hold_disposed': 'reportId',
|
|
104
|
+
'memory.consolidation_recommended': 'reportId',
|
|
105
|
+
'memory.capture_opted_out': 'sessionId',
|
|
106
|
+
'memory.delegation_static_mark_waived': 'runId',
|
|
107
|
+
});
|
|
108
|
+
export function noticePresentationFacts(notice, deployment) {
|
|
109
|
+
const envelope = typeof notice === 'object' && notice !== null ? notice : undefined;
|
|
110
|
+
const code = guardedRead(() => readOwnDataValue(envelope, 'code'));
|
|
111
|
+
const detailRaw = guardedRead(() => readOwnDataValue(envelope, 'detail'));
|
|
112
|
+
const detail = typeof detailRaw === 'object' && detailRaw !== null ? detailRaw : undefined;
|
|
113
|
+
const audience = typeof code === 'string' && engineNoticeInCatalog(code) ? noticeAudienceOf(code) : 'unknown';
|
|
114
|
+
const remedy = guardedRead(() => readOwnDataNonEmptyString(detail, 'remedy'));
|
|
115
|
+
const outlet = remedy !== undefined ? 'present' : 'unknown';
|
|
116
|
+
const keyField = typeof code === 'string' && Object.hasOwn(OCCURRENCE_KEY_BY_CODE, code) ? OCCURRENCE_KEY_BY_CODE[code] : undefined;
|
|
117
|
+
const identity = keyField !== undefined ? guardedRead(() => readOwnDataNonEmptyString(detail, keyField)) : undefined;
|
|
118
|
+
const occurrence = Object.freeze(identity !== undefined ? { kind: 'occurrence', key: identity } : { kind: 'event' });
|
|
119
|
+
return Object.freeze({
|
|
120
|
+
audience,
|
|
121
|
+
outlet,
|
|
122
|
+
...(remedy !== undefined ? { remedy } : {}),
|
|
123
|
+
occurrence,
|
|
124
|
+
seatClass: noticeSeatClassOf(audience, outlet, deployment),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
export const OUTLET_KINDS = Object.freeze(['slash-command', 'cli-command', 'http-endpoint', 'env-or-settings', 'operator-action', 'identity-attach', 'resend']);
|
|
128
|
+
function ctxSlot(ctx, key, closed) {
|
|
129
|
+
if (typeof ctx !== 'object' || ctx === null)
|
|
130
|
+
return undefined;
|
|
131
|
+
const v = guardedRead(() => readOwnDataValue(ctx, key));
|
|
132
|
+
return typeof v === 'string' && closed.includes(v) ? v : undefined;
|
|
133
|
+
}
|
|
134
|
+
function isOutletKind(v) {
|
|
135
|
+
return typeof v === 'string' && OUTLET_KINDS.includes(v);
|
|
136
|
+
}
|
|
137
|
+
export function outletReachable(kind, ctx) {
|
|
138
|
+
if (!isOutletKind(kind))
|
|
139
|
+
return 'unknown';
|
|
140
|
+
const lane = ctxSlot(ctx, 'lane', ['interactive', 'print']);
|
|
141
|
+
switch (kind) {
|
|
142
|
+
case 'resend':
|
|
143
|
+
case 'cli-command':
|
|
144
|
+
return 'reachable';
|
|
145
|
+
case 'slash-command':
|
|
146
|
+
return lane === 'interactive' ? 'reachable' : lane === 'print' ? 'unreachable' : 'unknown';
|
|
147
|
+
case 'http-endpoint': {
|
|
148
|
+
if (lane === 'interactive')
|
|
149
|
+
return 'unreachable';
|
|
150
|
+
const access = ctxSlot(ctx, 'endpointAccess', ['has-address-and-credential', 'no', 'unknown']);
|
|
151
|
+
if (access === 'no')
|
|
152
|
+
return 'unreachable';
|
|
153
|
+
return lane === 'print' && access === 'has-address-and-credential' ? 'reachable' : 'unknown';
|
|
154
|
+
}
|
|
155
|
+
case 'env-or-settings': {
|
|
156
|
+
const scope = ctxSlot(ctx, 'configScope', ['reaches-engine', 'does-not', 'unknown']);
|
|
157
|
+
if (scope === 'reaches-engine')
|
|
158
|
+
return 'reachable';
|
|
159
|
+
if (scope === 'does-not')
|
|
160
|
+
return 'unreachable';
|
|
161
|
+
return ctxSlot(ctx, 'engineManagedByShell', ['shell', 'external-local', 'remote', 'unknown']) === 'remote' ? 'unreachable' : 'unknown';
|
|
162
|
+
}
|
|
163
|
+
case 'operator-action': {
|
|
164
|
+
const op = ctxSlot(ctx, 'operator', ['present', 'absent', 'unknown']);
|
|
165
|
+
return op === 'present' ? 'reachable' : op === 'absent' ? 'unreachable' : 'unknown';
|
|
166
|
+
}
|
|
167
|
+
case 'identity-attach': {
|
|
168
|
+
const id = ctxSlot(ctx, 'identityAttach', ['available', 'unavailable', 'unknown']);
|
|
169
|
+
return id === 'available' ? 'reachable' : id === 'unavailable' ? 'unreachable' : 'unknown';
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const LIST_READ_MAX = 64;
|
|
174
|
+
function listItems(list) {
|
|
175
|
+
if (guardedRead(() => (Array.isArray(list) ? true : undefined)) !== true)
|
|
176
|
+
return [];
|
|
177
|
+
const length = guardedRead(() => readOwnDataValue(list, 'length'));
|
|
178
|
+
if (typeof length !== 'number' || !Number.isInteger(length) || length < 0)
|
|
179
|
+
return [];
|
|
180
|
+
const out = [];
|
|
181
|
+
for (let i = 0; i < Math.min(length, LIST_READ_MAX); i++) {
|
|
182
|
+
const item = guardedRead(() => readOwnDataValue(list, String(i)));
|
|
183
|
+
if (item !== undefined)
|
|
184
|
+
out.push(item);
|
|
185
|
+
}
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
export function planOutletRendering(kinds, ctx) {
|
|
189
|
+
const state = [];
|
|
190
|
+
const confirm = [];
|
|
191
|
+
const omit = [];
|
|
192
|
+
const seen = new Set();
|
|
193
|
+
for (const k of listItems(kinds)) {
|
|
194
|
+
if (!isOutletKind(k) || seen.has(k))
|
|
195
|
+
continue;
|
|
196
|
+
seen.add(k);
|
|
197
|
+
const r = outletReachable(k, ctx);
|
|
198
|
+
(r === 'reachable' ? state : r === 'unknown' ? confirm : omit).push(k);
|
|
199
|
+
}
|
|
200
|
+
return Object.freeze({
|
|
201
|
+
state: Object.freeze(state),
|
|
202
|
+
confirm: Object.freeze(confirm),
|
|
203
|
+
omit: Object.freeze(omit),
|
|
204
|
+
nothingReachable: state.length === 0 && confirm.length === 0 && omit.length > 0,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
export const NOTICE_FULL_TEXT_CAP = 8000;
|
|
208
|
+
const PRINT_BUDGET_USER = 1000;
|
|
209
|
+
const PRINT_BUDGET_OPERATOR = 160;
|
|
210
|
+
const PRINT_PREFIX = '[sema] ';
|
|
211
|
+
const NO_TEXT_SENTENCE = 'Engine notice (no message text)';
|
|
212
|
+
const PRINT_CODE_MAX = 160;
|
|
213
|
+
const PRE_CAP_SLACK = 2;
|
|
214
|
+
const PRINT_SESSION_MAX = 128;
|
|
215
|
+
function lineFace(max) {
|
|
216
|
+
return max === undefined ? { foldLines: false, mark: 'dot' } : { foldLines: false, mark: 'dot', max };
|
|
217
|
+
}
|
|
218
|
+
function printCarrier(carrier) {
|
|
219
|
+
if (typeof carrier !== 'object' || carrier === null)
|
|
220
|
+
return { kind: 'none' };
|
|
221
|
+
const kind = guardedRead(() => readOwnDataValue(carrier, 'kind'));
|
|
222
|
+
if (kind === 'stream-json')
|
|
223
|
+
return { kind };
|
|
224
|
+
if (kind === 'transcript') {
|
|
225
|
+
const sid = guardedRead(() => readOwnDataNonEmptyString(carrier, 'sessionId'));
|
|
226
|
+
if (sid !== undefined) {
|
|
227
|
+
const shown = displayUntrusted(sid, lineFace(PRINT_SESSION_MAX));
|
|
228
|
+
if (shown !== '')
|
|
229
|
+
return { kind, sessionId: shown };
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return { kind: 'none' };
|
|
233
|
+
}
|
|
234
|
+
export function noticePrintLine(input) {
|
|
235
|
+
const textRaw = guardedRead(() => readOwnDataValue(input, 'text'));
|
|
236
|
+
const audience = guardedRead(() => readOwnDataValue(input, 'audience'));
|
|
237
|
+
const codeRaw = guardedRead(() => readOwnDataValue(input, 'code'));
|
|
238
|
+
const carrier = printCarrier(guardedRead(() => readOwnDataValue(input, 'carrier')));
|
|
239
|
+
const washed = displayUntrusted(typeof textRaw === 'string' ? textRaw : '', lineFace(NOTICE_FULL_TEXT_CAP + PRE_CAP_SLACK));
|
|
240
|
+
const bounded = cutForDisplay(washed, NOTICE_FULL_TEXT_CAP);
|
|
241
|
+
const text = bounded.shown.trim() === '' ? NO_TEXT_SENTENCE : bounded.shown;
|
|
242
|
+
const budget = audience === 'operator' ? PRINT_BUDGET_OPERATOR : PRINT_BUDGET_USER;
|
|
243
|
+
const cut = carrier.kind === 'none' ? { shown: text, truncated: bounded.truncated } : cutForDisplay(text, budget);
|
|
244
|
+
const where = cut.truncated && carrier.kind === 'transcript' ? ` (full text: sema -r ${carrier.sessionId})` : '';
|
|
245
|
+
const code = typeof codeRaw === 'string' && codeRaw !== '' ? displayUntrusted(codeRaw, lineFace(PRINT_CODE_MAX)) : '';
|
|
246
|
+
return Object.freeze({ line: `${PRINT_PREFIX}${cut.shown}${where}${code !== '' ? ` [${code}]` : ''}`, truncated: cut.truncated });
|
|
247
|
+
}
|
|
@@ -12,4 +12,3 @@ export declare function noteEngineCapsForOutputStyleSeat(baseUrl: string, caps:
|
|
|
12
12
|
}): void;
|
|
13
13
|
export declare function observedOutputStyleSeat(baseUrl?: string | undefined): OutputStyleSeatReading;
|
|
14
14
|
export declare function outputStyleSeatAdmitted(reading: OutputStyleSeatReading | undefined): boolean;
|
|
15
|
-
export declare function __resetOutputStyleSeatReadingsForTests(): void;
|
package/dist/ownKey.d.ts
CHANGED
|
@@ -3,9 +3,11 @@ export declare function readOwnNonEmptyString(v: object | null | undefined, key:
|
|
|
3
3
|
export declare function readAskOrigin(carrier: object | null | undefined): string | undefined;
|
|
4
4
|
export declare function readOwnDataNonEmptyString(v: object | null | undefined, key: string): string | undefined;
|
|
5
5
|
export declare function readOwnDataValue<T>(v: object | null | undefined, key: string): T | undefined;
|
|
6
|
+
export declare function guardedRead<T>(read: () => T | undefined): T | undefined;
|
|
6
7
|
export declare function ownDataField(v: object, key: string): unknown;
|
|
7
8
|
export declare function ownSendableField(v: object, key: string): unknown;
|
|
8
9
|
export declare function ownDataRefusalKeyOf(e: unknown): string | undefined;
|
|
9
10
|
export declare function readFrameStringKey(frame: unknown, key: string): string | undefined;
|
|
11
|
+
export declare function readFrameTrueMark(frame: object | null | undefined, key: string): boolean;
|
|
10
12
|
export declare function markMcpSectionDroppedNamedRows(rows: object, dropped: ReadonlyMap<string, number>): void;
|
|
11
13
|
export declare function mcpSectionDroppedNamedRowsOf(rows: object | null | undefined, name: string): number;
|
package/dist/ownKey.js
CHANGED
|
@@ -25,6 +25,14 @@ export function readOwnDataValue(v, key) {
|
|
|
25
25
|
const d = Object.getOwnPropertyDescriptor(v, key);
|
|
26
26
|
return d !== undefined && Object.hasOwn(d, 'value') ? d.value : undefined;
|
|
27
27
|
}
|
|
28
|
+
export function guardedRead(read) {
|
|
29
|
+
try {
|
|
30
|
+
return read();
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
28
36
|
export function ownDataField(v, key) {
|
|
29
37
|
return ownField(v, key, false);
|
|
30
38
|
}
|
|
@@ -56,6 +64,16 @@ export function readFrameStringKey(frame, key) {
|
|
|
56
64
|
return undefined;
|
|
57
65
|
}
|
|
58
66
|
}
|
|
67
|
+
export function readFrameTrueMark(frame, key) {
|
|
68
|
+
try {
|
|
69
|
+
if (frame === null || frame === undefined || typeof frame !== 'object' || Array.isArray(frame))
|
|
70
|
+
return false;
|
|
71
|
+
return ownDataField(frame, key) === true;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
59
77
|
const DROPPED_NAMED_ROWS = Symbol.for('@sema-agent/client-core/mcpSectionDroppedNamedRows');
|
|
60
78
|
export function markMcpSectionDroppedNamedRows(rows, dropped) {
|
|
61
79
|
if (dropped.size === 0)
|
package/dist/peerFrames.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { applyDisplayFace,
|
|
1
|
+
import { applyDisplayFace, PEER_NAME_FACE } from './displayUntrusted.js';
|
|
2
2
|
import { CORE_ENGINE_AUTHORITY_ENVELOPE_TAGS, CORE_PERMISSION_MODE_CLASSES } from './generated/engineFactTables.js';
|
|
3
3
|
const WIRE_KEY_FROM = 'from';
|
|
4
4
|
const WIRE_KEY_BODY = 'body';
|
|
@@ -42,14 +42,16 @@ function nonEmptyString(v) {
|
|
|
42
42
|
function finiteNumber(v) {
|
|
43
43
|
return typeof v === 'number' && Number.isFinite(v) ? v : undefined;
|
|
44
44
|
}
|
|
45
|
-
function provenanceAgrees(n, lane,
|
|
45
|
+
function provenanceAgrees(n, lane, carrierFromRaw) {
|
|
46
46
|
const prov = dataProp(n, WIRE_KEY_PROVENANCE);
|
|
47
47
|
if (prov === null || typeof prov !== 'object')
|
|
48
48
|
return false;
|
|
49
49
|
if (dataProp(prov, WIRE_KEY_KIND) !== lane)
|
|
50
50
|
return false;
|
|
51
|
-
const
|
|
52
|
-
if (
|
|
51
|
+
const provFromRaw = dataProp(prov, WIRE_KEY_FROM);
|
|
52
|
+
if (typeof provFromRaw !== 'string' || cleanName(provFromRaw) === undefined)
|
|
53
|
+
return false;
|
|
54
|
+
if (carrierFromRaw !== undefined && provFromRaw !== carrierFromRaw)
|
|
53
55
|
return false;
|
|
54
56
|
const taskId = nonEmptyString(dataProp(n, WIRE_KEY_TASK_ID));
|
|
55
57
|
if (taskId === undefined || nonEmptyString(dataProp(prov, WIRE_KEY_PROV_TASK_ID)) !== taskId)
|
|
@@ -61,9 +63,10 @@ export function classifyPeerNotification(n) {
|
|
|
61
63
|
const seq = finiteNumber(dataProp(n, WIRE_KEY_SEQ));
|
|
62
64
|
const agentMessage = dataProp(n, WIRE_KEY_AGENT_MESSAGE);
|
|
63
65
|
if (agentMessage !== undefined) {
|
|
64
|
-
const
|
|
66
|
+
const fromRaw = dataProp(agentMessage, WIRE_KEY_FROM);
|
|
67
|
+
const from = cleanName(fromRaw);
|
|
65
68
|
const body = dataProp(agentMessage, WIRE_KEY_BODY);
|
|
66
|
-
if (from !== undefined && typeof body === 'string' && provenanceAgrees(n, 'agent_message',
|
|
69
|
+
if (from !== undefined && typeof fromRaw === 'string' && typeof body === 'string' && provenanceAgrees(n, 'agent_message', fromRaw)) {
|
|
67
70
|
const agentType = nonEmptyString(dataProp(dataProp(n, WIRE_KEY_PROVENANCE), WIRE_KEY_AGENT_TYPE));
|
|
68
71
|
return {
|
|
69
72
|
lane: 'agent_message',
|
|
@@ -77,9 +80,10 @@ export function classifyPeerNotification(n) {
|
|
|
77
80
|
}
|
|
78
81
|
const crossSessionMessage = dataProp(n, WIRE_KEY_CROSS_SESSION_MESSAGE);
|
|
79
82
|
if (crossSessionMessage !== undefined) {
|
|
80
|
-
const
|
|
83
|
+
const fromRaw = dataProp(crossSessionMessage, WIRE_KEY_FROM);
|
|
84
|
+
const from = cleanName(fromRaw);
|
|
81
85
|
const body = dataProp(crossSessionMessage, WIRE_KEY_BODY);
|
|
82
|
-
if (from === undefined || typeof body !== 'string' || !provenanceAgrees(n, 'cross_session_message',
|
|
86
|
+
if (from === undefined || typeof fromRaw !== 'string' || typeof body !== 'string' || !provenanceAgrees(n, 'cross_session_message', fromRaw))
|
|
83
87
|
return null;
|
|
84
88
|
const fromSession = nonEmptyString(dataProp(crossSessionMessage, WIRE_KEY_FROM_SESSION));
|
|
85
89
|
const fromName = cleanName(dataProp(crossSessionMessage, WIRE_KEY_FROM_NAME));
|
|
@@ -117,7 +121,7 @@ function neutralizeAuthorityTags(text) {
|
|
|
117
121
|
function cleanName(v) {
|
|
118
122
|
if (typeof v !== 'string')
|
|
119
123
|
return undefined;
|
|
120
|
-
const t = applyDisplayFace(v,
|
|
124
|
+
const t = applyDisplayFace(v, PEER_NAME_FACE);
|
|
121
125
|
return t.length > 0 ? t : undefined;
|
|
122
126
|
}
|
|
123
127
|
function escapeEnvelopeTag(tag, text) {
|
|
@@ -7,9 +7,5 @@ export type PermissionModesReading = {
|
|
|
7
7
|
modes: readonly string[];
|
|
8
8
|
};
|
|
9
9
|
export declare function projectPermissionModesCapability(caps: unknown): PermissionModesReading | undefined;
|
|
10
|
-
export declare function noteEngineCapsForPermissionModes(baseUrl: string, caps: unknown, opts?: {
|
|
11
|
-
generation?: number;
|
|
12
|
-
}): void;
|
|
13
10
|
export declare function observedPermissionModes(baseUrl?: string | undefined): PermissionModesReading;
|
|
14
11
|
export declare function permissionModeAdmitted(reading: PermissionModesReading | undefined, mode: string): boolean;
|
|
15
|
-
export declare function __resetPermissionModesReadingsForTests(): void;
|
|
@@ -40,9 +40,6 @@ const reader = createEngineCapReader({
|
|
|
40
40
|
project: projectPermissionModesCapability,
|
|
41
41
|
makeUnobserved: () => ({ kind: 'unobserved' }),
|
|
42
42
|
});
|
|
43
|
-
export function noteEngineCapsForPermissionModes(baseUrl, caps, opts) {
|
|
44
|
-
reader.note(baseUrl, caps, opts);
|
|
45
|
-
}
|
|
46
43
|
export function observedPermissionModes(baseUrl = engineWireTarget()?.baseUrl) {
|
|
47
44
|
return reader.observed(baseUrl);
|
|
48
45
|
}
|
|
@@ -61,6 +58,3 @@ export function permissionModeAdmitted(reading, mode) {
|
|
|
61
58
|
return false;
|
|
62
59
|
}
|
|
63
60
|
}
|
|
64
|
-
export function __resetPermissionModesReadingsForTests() {
|
|
65
|
-
reader.__resetForTests();
|
|
66
|
-
}
|
|
@@ -34,9 +34,6 @@ const ISSUE_SENTENCES = Object.freeze({
|
|
|
34
34
|
'unsupported.path_base': 'this path rule is not supported as spelled: the base it is relative to is missing or is not a usable absolute path, so it would reach nothing and is refused rather than compiled',
|
|
35
35
|
'unsupported.mcp_paren': 'a parenthesised parameter rule is not supported on an MCP tool name',
|
|
36
36
|
'unsupported.covering_paren': 'a parenthesised parameter rule is not supported on a covering protocol name — the parameter grammar is keyed by an exact tool name, so no mounted tool could ever bear it',
|
|
37
|
-
'invalid.allow_param_rule': 'this rule is invalid: an allow rule cannot carry a parameter pattern — only bare tool rules auto-allow',
|
|
38
|
-
'unsupported.bash_prefix': 'a standing approval for a command belongs in the persisted-rule store, so this allow rule is not supported here (deny and ask rules with the same shape do compile)',
|
|
39
|
-
'unsupported.file_glob': 'a standing approval by path belongs in the consent store, so this allow rule is not supported here (deny and ask path rules do compile)',
|
|
40
37
|
});
|
|
41
38
|
const PARAM_SPLIT_WITH_COLON = 'this rule is invalid: a parameter rule is written name:pattern, and the name before the colon or the pattern after it is empty';
|
|
42
39
|
const PARAM_SPLIT_NO_COLON = 'this rule is invalid: there is no colon inside the parentheses, so this is not a parameter rule (a parameter rule is written name:pattern)';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { compileAllowList, hasCoreValuePort, protocolOf,
|
|
2
|
-
import { clientOwnedServerPrefix,
|
|
1
|
+
import { compileAllowList, hasCoreValuePort, protocolOf, ruleToolGrammarOf } from './coreValuePorts.js';
|
|
2
|
+
import { clientOwnedServerPrefix, mintedPrefixOf, ruleToolNameVerdictOf } from './protocolSegment.js';
|
|
3
3
|
import { ruleCompileVerdict } from './ruleCompileVerdict.js';
|
|
4
4
|
import { allowRulesGate, allowWireForm } from './permissionsAllowGrantCapability.js';
|
|
5
5
|
import { THROWN_SLOT_UNREADABLE, readThrownSlot } from './wireFailureShape.js';
|
|
@@ -74,22 +74,33 @@ function parseRule(rule) {
|
|
|
74
74
|
return { toolName: normalizeLegacyToolName(toolName), ruleContent: unescapeRuleContent(rawContent) };
|
|
75
75
|
}
|
|
76
76
|
function requireRulePorts() {
|
|
77
|
-
const required = ['MCP_NAMESPACE', '
|
|
77
|
+
const required = ['MCP_NAMESPACE', 'judgeRuleToolName', 'protocolOf', 'ruleToolGrammarOf', 'validatePermissionRules'];
|
|
78
78
|
const missing = required.filter((p) => !hasCoreValuePort(p));
|
|
79
79
|
if (missing.length === 0)
|
|
80
80
|
return;
|
|
81
81
|
throw new Error(`permissionsDeclaration: 规则判据端口缺席(${missing.join(' / ')})—— 先用 installCoreValuePorts 装上 core 的同名实现。` +
|
|
82
82
|
'缺席不等于全部合格:不判就发会让整份请求被引擎拒收,不判就删会静默丢掉 deny 规则,所以这里拒绝装配。');
|
|
83
83
|
}
|
|
84
|
-
function
|
|
84
|
+
function nameCheck(name) {
|
|
85
85
|
if (name.includes('('))
|
|
86
|
-
return 'paren_in_tool_name';
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
return { issue: 'paren_in_tool_name' };
|
|
87
|
+
const verdict = ruleToolNameVerdictOf(name);
|
|
88
|
+
if (verdict.kind === 'unknown') {
|
|
89
|
+
throw new Error('permissionsDeclaration: 规则工具名判官(judgeRuleToolName)答不上来(抛 / 回的不是 current · retired · unprefixed_namespace · unmintable_segment 之一)—— ' +
|
|
90
|
+
'装的必须是 core 的同名实现、原样装。判不出不等于够得着:不判就发会让整份请求被引擎拒收,不判就删会静默丢掉 deny 规则,所以这里拒绝装配。');
|
|
91
|
+
}
|
|
92
|
+
if (verdict.kind === 'retired')
|
|
93
|
+
return { issue: 'retired_tool_name' };
|
|
94
|
+
if (verdict.kind === 'unprefixed_namespace')
|
|
95
|
+
return { issue: 'unprefixed_namespaced_name' };
|
|
89
96
|
const ns = protocolOf(name);
|
|
90
|
-
if (ns === undefined)
|
|
91
|
-
return
|
|
92
|
-
|
|
97
|
+
if (ns !== undefined && ns.parse(name) === undefined)
|
|
98
|
+
return { issue: 'incomplete_protocol_name' };
|
|
99
|
+
if (verdict.kind === 'unmintable_segment') {
|
|
100
|
+
const minted = mintedPrefixOf(name);
|
|
101
|
+
return { unminted: minted !== undefined ? { minted } : {} };
|
|
102
|
+
}
|
|
103
|
+
return { ok: true };
|
|
93
104
|
}
|
|
94
105
|
function wholeServerRuleName(name) {
|
|
95
106
|
const read = name.trim();
|
|
@@ -101,11 +112,16 @@ function wholeServerRuleName(name) {
|
|
|
101
112
|
}
|
|
102
113
|
const compileVerdict = ruleCompileVerdict;
|
|
103
114
|
const kept = (reason) => ({ sent: false, reason });
|
|
104
|
-
function
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
115
|
+
function compileRefused(verdict) {
|
|
116
|
+
return {
|
|
117
|
+
sent: false,
|
|
118
|
+
reason: 'compile_refused',
|
|
119
|
+
...(verdict.code !== undefined ? { code: verdict.code } : {}),
|
|
120
|
+
...(verdict.message !== undefined ? { message: verdict.message } : {}),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function sentFate(wire, check, unchecked) {
|
|
124
|
+
return 'unminted' in check ? { sent: true, wire, unchecked, unreachable: check.unminted } : { sent: true, wire, unchecked };
|
|
109
125
|
}
|
|
110
126
|
function judgeList(rules, behavior, allowForm = 'grant') {
|
|
111
127
|
let portsChecked = false;
|
|
@@ -119,42 +135,34 @@ function judgeList(rules, behavior, allowForm = 'grant') {
|
|
|
119
135
|
const parsed = parseRule(rule);
|
|
120
136
|
if (parsed.ruleContent === undefined) {
|
|
121
137
|
const name = wholeServerRuleName(parsed.toolName);
|
|
122
|
-
const
|
|
123
|
-
if (issue
|
|
124
|
-
return kept(issue);
|
|
125
|
-
if (behavior === 'allow')
|
|
126
|
-
|
|
127
|
-
if (segment.kind === 'unminted')
|
|
128
|
-
return kept('unminted_protocol_segment');
|
|
129
|
-
if (segment.kind === 'unknown')
|
|
130
|
-
return kept('judge_failed');
|
|
131
|
-
}
|
|
138
|
+
const check = nameCheck(name);
|
|
139
|
+
if ('issue' in check)
|
|
140
|
+
return kept(check.issue);
|
|
141
|
+
if (behavior === 'allow' && 'unminted' in check)
|
|
142
|
+
return kept('unminted_protocol_segment');
|
|
132
143
|
const verdict = compileVerdict(name, behavior === 'ask' ? 'ask' : 'deny');
|
|
133
|
-
if (verdict === 'refused')
|
|
134
|
-
return
|
|
135
|
-
if (verdict === 'unknown' && behavior === 'allow')
|
|
144
|
+
if (verdict.kind === 'refused')
|
|
145
|
+
return compileRefused(verdict);
|
|
146
|
+
if (verdict.kind === 'unknown' && behavior === 'allow')
|
|
136
147
|
return kept('judge_failed');
|
|
137
|
-
return behavior === 'allow' ? { sent: true, wire: name, unchecked: false } : sentFate(name,
|
|
148
|
+
return behavior === 'allow' ? { sent: true, wire: name, unchecked: false } : sentFate(name, check, verdict.kind === 'unknown');
|
|
138
149
|
}
|
|
139
|
-
const
|
|
140
|
-
if (issue
|
|
141
|
-
return kept(issue);
|
|
150
|
+
const check = nameCheck(parsed.toolName);
|
|
151
|
+
if ('issue' in check)
|
|
152
|
+
return kept(check.issue);
|
|
142
153
|
if (ruleToolGrammarOf(parsed.toolName) === 'path' && parsed.ruleContent.startsWith('~/'))
|
|
143
154
|
return kept('home_relative_path');
|
|
144
155
|
if (behavior === 'allow') {
|
|
145
156
|
if (allowForm !== 'layer')
|
|
146
157
|
return kept('content_form_not_grantable');
|
|
147
|
-
|
|
148
|
-
if (segment.kind === 'unminted')
|
|
158
|
+
if ('unminted' in check)
|
|
149
159
|
return kept('unminted_protocol_segment');
|
|
150
|
-
if (segment.kind === 'unknown')
|
|
151
|
-
return kept('judge_failed');
|
|
152
160
|
return { sent: true, wire: rule, unchecked: false };
|
|
153
161
|
}
|
|
154
162
|
const verdict = compileVerdict(rule, behavior);
|
|
155
|
-
if (verdict === 'refused')
|
|
156
|
-
return
|
|
157
|
-
return sentFate(rule,
|
|
163
|
+
if (verdict.kind === 'refused')
|
|
164
|
+
return compileRefused(verdict);
|
|
165
|
+
return sentFate(rule, check, verdict.kind === 'unknown');
|
|
158
166
|
});
|
|
159
167
|
}
|
|
160
168
|
function outsideSettingsFiles(origin) {
|
|
@@ -370,8 +378,15 @@ function assemble(input, caps) {
|
|
|
370
378
|
for (const [bucket, slots, j] of rows) {
|
|
371
379
|
slots.forEach((s, k) => {
|
|
372
380
|
const f = j.fates[k];
|
|
373
|
-
if (!f.sent)
|
|
374
|
-
keptRows.push({
|
|
381
|
+
if (!f.sent) {
|
|
382
|
+
keptRows.push({
|
|
383
|
+
rule: s.rule,
|
|
384
|
+
bucket,
|
|
385
|
+
reason: f.reason,
|
|
386
|
+
...(f.code !== undefined ? { code: f.code } : {}),
|
|
387
|
+
...(f.message !== undefined ? { message: f.message } : {}),
|
|
388
|
+
});
|
|
389
|
+
}
|
|
375
390
|
});
|
|
376
391
|
for (const wire of j.sent) {
|
|
377
392
|
const winner = j.winnerOf.get(wire);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type GateOutcomeView } from './gateOutcome.js';
|
|
2
|
+
import { type NoticeOutlet, type NoticeSeatClass } from './noticePresentation.js';
|
|
3
|
+
export declare const PRINT_PERMISSION_OUTCOME_FORMS: readonly ["mode_denied", "no_approver", "mode_released"];
|
|
4
|
+
export type PrintPermissionOutcomeForm = (typeof PRINT_PERMISSION_OUTCOME_FORMS)[number];
|
|
5
|
+
export interface PrintPermissionOutcome {
|
|
6
|
+
readonly form: PrintPermissionOutcomeForm;
|
|
7
|
+
readonly sentence: string;
|
|
8
|
+
readonly audience: 'user';
|
|
9
|
+
readonly outlet: NoticeOutlet;
|
|
10
|
+
readonly seatClass: NoticeSeatClass;
|
|
11
|
+
}
|
|
12
|
+
export declare function printPermissionOutcomeOf(gate: GateOutcomeView | undefined): PrintPermissionOutcome | undefined;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { modeReleaseReasonSentence } from './gateOutcome.js';
|
|
2
|
+
import { noticeSeatClassOf } from './noticePresentation.js';
|
|
3
|
+
import { guardedRead, readOwnDataValue } from './ownKey.js';
|
|
4
|
+
export const PRINT_PERMISSION_OUTCOME_FORMS = Object.freeze(['mode_denied', 'no_approver', 'mode_released']);
|
|
5
|
+
const ALLOW_RULE_REMEDY = 'add an allow rule for the tool in settings (or pass --allowedTools)';
|
|
6
|
+
const SENTENCES = Object.freeze({
|
|
7
|
+
dontAsk: {
|
|
8
|
+
sentence: `the dontAsk permission mode denies tools that need approval unless a permission rule allows them — ${ALLOW_RULE_REMEDY}`,
|
|
9
|
+
outlet: 'present',
|
|
10
|
+
},
|
|
11
|
+
bypassPermissions: {
|
|
12
|
+
sentence: 'the bypassPermissions permission mode refused a call because it could not tell whether the write it makes lands on a protected path',
|
|
13
|
+
outlet: 'absent',
|
|
14
|
+
},
|
|
15
|
+
mode: { sentence: "the session's permission mode denied a call that needed approval", outlet: 'absent' },
|
|
16
|
+
no_approver: {
|
|
17
|
+
sentence: `print mode has no approval channel: tools that need approval are denied unless a permission rule allows them — ${ALLOW_RULE_REMEDY}, or pass --permission-mode acceptEdits or auto`,
|
|
18
|
+
outlet: 'present',
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
function outcomeOf(form, sentence, outlet) {
|
|
22
|
+
return Object.freeze({ form, sentence, audience: 'user', outlet, seatClass: noticeSeatClassOf('user', outlet) });
|
|
23
|
+
}
|
|
24
|
+
function slot(from, key) {
|
|
25
|
+
return typeof from === 'object' && from !== null ? guardedRead(() => readOwnDataValue(from, key)) : undefined;
|
|
26
|
+
}
|
|
27
|
+
export function printPermissionOutcomeOf(gate) {
|
|
28
|
+
const disposition = slot(gate, 'disposition');
|
|
29
|
+
const kind = slot(disposition, 'kind');
|
|
30
|
+
if (kind === 'denied') {
|
|
31
|
+
const deniedBy = slot(disposition, 'deniedBy');
|
|
32
|
+
if (deniedBy === 'mode') {
|
|
33
|
+
const mode = slot(disposition, 'mode');
|
|
34
|
+
const row = mode === 'dontAsk' ? SENTENCES.dontAsk : mode === 'bypassPermissions' ? SENTENCES.bypassPermissions : SENTENCES.mode;
|
|
35
|
+
return outcomeOf('mode_denied', row.sentence, row.outlet);
|
|
36
|
+
}
|
|
37
|
+
if (deniedBy === 'ask_resolution') {
|
|
38
|
+
const settled = slot(slot(gate, 'settlement'), 'kind');
|
|
39
|
+
if (settled === 'no_approver' || settled === 'approver_unavailable')
|
|
40
|
+
return outcomeOf('no_approver', SENTENCES.no_approver.sentence, SENTENCES.no_approver.outlet);
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
if (kind === 'allowed') {
|
|
45
|
+
const reason = slot(slot(disposition, 'released'), 'reason');
|
|
46
|
+
const sentence = typeof reason === 'string' ? modeReleaseReasonSentence(reason) : undefined;
|
|
47
|
+
return sentence === undefined ? undefined : outcomeOf('mode_released', sentence, 'absent');
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|