@sema-agent/client-core 0.90.0 → 0.91.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 +193 -0
- package/README.md +24 -18
- package/dist/adapt/arms.js +9 -0
- package/dist/adapter/downstream/eventToSdkMessage.js +4 -2
- package/dist/adapter/downstream/wiringManifestView.d.ts +6 -0
- package/dist/adapter/downstream/wiringManifestView.js +16 -0
- package/dist/adapter/runStream.js +37 -20
- package/dist/askSurvivesStandingAnswer.d.ts +10 -0
- package/dist/askSurvivesStandingAnswer.js +40 -0
- package/dist/attachmentSeatCapability.d.ts +11 -0
- package/dist/attachmentSeatCapability.js +27 -0
- package/dist/attachmentsWireCaps.d.ts +9 -0
- package/dist/attachmentsWireCaps.js +74 -21
- package/dist/compensations.js +10 -14
- package/dist/controlRouter.d.ts +9 -2
- package/dist/controlRouter.js +66 -28
- package/dist/decideFailureNote.js +4 -2
- package/dist/disableSkillsWire.d.ts +1 -0
- package/dist/disableSkillsWire.js +8 -1
- package/dist/displayUntrusted.js +153 -4
- package/dist/engineCapsCache.d.ts +3 -1
- package/dist/engineCapsCache.js +56 -6
- package/dist/engineClosedSets.d.ts +2 -0
- package/dist/engineClosedSets.js +3 -0
- package/dist/engineErrorCodes.d.ts +3 -0
- package/dist/engineErrorCodes.js +10 -0
- package/dist/generated/engineFactTables.d.ts +12 -0
- package/dist/generated/engineFactTables.js +47 -0
- package/dist/hitl/askGateWire.d.ts +1 -1
- package/dist/hitl/askGateWire.js +1 -1
- package/dist/hitl/crashConverged.d.ts +7 -0
- package/dist/hitl/crashConverged.js +57 -0
- package/dist/hitl/crashConvergedNotice.d.ts +4 -0
- package/dist/hitl/crashConvergedNotice.js +57 -0
- package/dist/hitl/hitlBridge.d.ts +7 -4
- package/dist/hitl/parkResolver.d.ts +7 -2
- package/dist/hitl/parkResolver.js +19 -7
- package/dist/hitl/planReviewWire.d.ts +1 -1
- package/dist/hitl/planReviewWire.js +18 -4
- package/dist/hitl/sessionPolicyWire.d.ts +5 -0
- package/dist/hitl/sessionPolicyWire.js +63 -0
- package/dist/hitl/suspendedReopen.js +2 -2
- package/dist/hitl/toolApprovalWire.d.ts +4 -1
- package/dist/hitl/toolApprovalWire.js +20 -0
- package/dist/hookMatcherVerdict.d.ts +29 -0
- package/dist/hookMatcherVerdict.js +265 -0
- package/dist/humanTurn.d.ts +91 -0
- package/dist/humanTurn.js +253 -0
- package/dist/humanTurnRead.d.ts +105 -0
- package/dist/humanTurnRead.js +331 -0
- package/dist/index.d.ts +25 -8
- package/dist/index.js +16 -6
- package/dist/inheritEnvWire.js +1 -1
- package/dist/liveModelCatalog.d.ts +2 -0
- package/dist/liveModelCatalog.js +51 -3
- package/dist/permissionRuleIssue.d.ts +1 -1
- package/dist/permissionRuleIssue.js +22 -3
- package/dist/permissionsAllowGrantCapability.d.ts +2 -1
- package/dist/permissionsAllowGrantCapability.js +11 -2
- package/dist/permissionsDeclaration.d.ts +23 -0
- package/dist/permissionsDeclaration.js +133 -53
- package/dist/promptDelivery.d.ts +11 -3
- package/dist/promptDelivery.js +164 -29
- package/dist/providerRejection.d.ts +1 -0
- package/dist/providerRejection.js +20 -0
- package/dist/readDenyTiers.d.ts +10 -0
- package/dist/readDenyTiers.js +29 -0
- package/dist/request/taskRequest.js +2 -2
- package/dist/resumeRefusalCopy.d.ts +11 -0
- package/dist/resumeRefusalCopy.js +61 -4
- package/dist/rewindArchiveCapability.js +4 -4
- package/dist/rewindWireCaps.d.ts +2 -1
- package/dist/rewindWireCaps.js +19 -2
- package/dist/seam.d.ts +2 -0
- package/dist/taskRequestWords.d.ts +7 -1
- package/dist/taskRequestWords.js +18 -8
- package/dist/toolHistoryBadTurn.d.ts +1 -0
- package/dist/toolHistoryBadTurn.js +89 -0
- package/dist/toolHistoryMismatch.d.ts +2 -1
- package/dist/toolHistoryMismatch.js +19 -6
- package/dist/userAttachmentEnvelope.d.ts +61 -0
- package/dist/userAttachmentEnvelope.js +204 -0
- package/dist/wireFailureShape.d.ts +1 -0
- package/dist/wireFailureShape.js +4 -0
- package/dist/wireRefusalCopy.d.ts +6 -0
- package/dist/wireRefusalCopy.js +26 -3
- package/docs/INTEGRATION-CLIENTS.md +875 -14
- package/package.json +2 -2
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export declare const PERMISSION_RULE_ISSUE_CODES: readonly string[];
|
|
2
2
|
export declare const RETIRED_PERMISSION_RULE_ISSUE_CODES: readonly string[];
|
|
3
|
-
export declare function permissionRuleIssueDetail(code: unknown, message: unknown): string;
|
|
3
|
+
export declare function permissionRuleIssueDetail(code: unknown, message: unknown, rule?: string): string;
|
|
@@ -21,7 +21,7 @@ const ISSUE_MESSAGE_MAX = 200;
|
|
|
21
21
|
const ISSUE_SENTENCES = Object.freeze({
|
|
22
22
|
'invalid.empty_tool': 'this rule is invalid: it names no tool',
|
|
23
23
|
'invalid.paren': 'this rule is invalid: its parentheses do not parse (escape a literal one as \\( or \\))',
|
|
24
|
-
'invalid.param_split': 'this rule is invalid:
|
|
24
|
+
'invalid.param_split': 'this rule is invalid: its parentheses do not hold a parameter rule written name:pattern',
|
|
25
25
|
'invalid.allow_param_rule': 'this rule is invalid: an allow rule cannot carry a parameter pattern — only bare tool rules auto-allow',
|
|
26
26
|
'invalid.primary_field': "this rule is invalid: it patterns the tool's primary field, which matches the whole trimmed string rather than a prefix",
|
|
27
27
|
'invalid.cap_exceeded': 'this rule set is invalid: it is over one of the deployment caps (rule count, rule length, pattern length, or star count)',
|
|
@@ -35,8 +35,27 @@ const ISSUE_SENTENCES = Object.freeze({
|
|
|
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
37
|
});
|
|
38
|
-
|
|
39
|
-
|
|
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';
|
|
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)';
|
|
40
|
+
function colonInsideParens(rule) {
|
|
41
|
+
if (typeof rule !== 'string')
|
|
42
|
+
return undefined;
|
|
43
|
+
for (let i = 0; i < rule.length; i += 1) {
|
|
44
|
+
const ch = rule[i];
|
|
45
|
+
if (ch === '\\') {
|
|
46
|
+
i += 1;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (ch === '(')
|
|
50
|
+
return rule.indexOf(':', i + 1) !== -1;
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
export function permissionRuleIssueDetail(code, message, rule) {
|
|
55
|
+
const colon = code === 'invalid.param_split' ? colonInsideParens(rule) : undefined;
|
|
56
|
+
const known = colon !== undefined
|
|
57
|
+
? colon ? PARAM_SPLIT_WITH_COLON : PARAM_SPLIT_NO_COLON
|
|
58
|
+
: typeof code === 'string' && Object.hasOwn(ISSUE_SENTENCES, code) ? ISSUE_SENTENCES[code] : undefined;
|
|
40
59
|
const text = typeof message === 'string' && message.length > 0
|
|
41
60
|
? capForDisplay(message, ISSUE_MESSAGE_MAX)
|
|
42
61
|
: '';
|
|
@@ -12,6 +12,7 @@ export declare function noteEngineCapsForPermissionsAllowGrant(baseUrl: string,
|
|
|
12
12
|
}): void;
|
|
13
13
|
export declare function observedPermissionsAllowGrant(baseUrl?: string | undefined): PermissionsAllowGrantReading;
|
|
14
14
|
export declare function permissionsAllowGrantAvailable(reading: PermissionsAllowGrantReading): boolean;
|
|
15
|
-
export declare function
|
|
15
|
+
export declare function allowRulesGate(reading: PermissionsAllowGrantReading, settingsRead: 'clean' | 'partial' | 'unreadable' | undefined): 'open' | 'capability_off' | 'settings_read_not_clean';
|
|
16
|
+
export declare function permissionsAllowGrantDoctorDetail(reading: PermissionsAllowGrantReading, settingsRead?: 'clean' | 'partial' | 'unreadable'): string;
|
|
16
17
|
export declare function forgetPermissionsAllowGrantReading(baseUrl: string | undefined): void;
|
|
17
18
|
export declare function __resetPermissionsAllowGrantReadingsForTests(): void;
|
|
@@ -44,6 +44,11 @@ export function permissionsAllowGrantAvailable(reading) {
|
|
|
44
44
|
return false;
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
+
export function allowRulesGate(reading, settingsRead) {
|
|
48
|
+
if (!permissionsAllowGrantAvailable(reading))
|
|
49
|
+
return 'capability_off';
|
|
50
|
+
return settingsRead === 'clean' ? 'open' : 'settings_read_not_clean';
|
|
51
|
+
}
|
|
47
52
|
function ownReadingField(reading, key) {
|
|
48
53
|
try {
|
|
49
54
|
if (reading === null || typeof reading !== 'object' || Array.isArray(reading))
|
|
@@ -54,10 +59,14 @@ function ownReadingField(reading, key) {
|
|
|
54
59
|
return undefined;
|
|
55
60
|
}
|
|
56
61
|
}
|
|
57
|
-
export function permissionsAllowGrantDoctorDetail(reading) {
|
|
58
|
-
|
|
62
|
+
export function permissionsAllowGrantDoctorDetail(reading, settingsRead) {
|
|
63
|
+
const gate = allowRulesGate(reading, settingsRead);
|
|
64
|
+
if (gate === 'open') {
|
|
59
65
|
return 'allow rules sent to the engine — it reads them as rules, not as an exclusive list: a matching call counts as answered for, and the engine still raises its own prompts where it requires them';
|
|
60
66
|
}
|
|
67
|
+
if (gate === 'settings_read_not_clean') {
|
|
68
|
+
return 'allow rules from settings files kept on this side — this engine reads allow rules as rules, but the settings read that produced them was not reported clean (a settings file was unreadable or had a problem, or no read health was given), so they are not sent; command-line and session allow rules are still sent, and matching approvals are still answered here';
|
|
69
|
+
}
|
|
61
70
|
const kind = ownReadingField(reading, 'kind');
|
|
62
71
|
if (kind === 'present' && ownReadingField(reading, 'grants') === false) {
|
|
63
72
|
return 'allow rules kept on this side — this engine says it does not read allow rules as rules of that kind, so they are not sent and matching approvals are still answered here';
|
|
@@ -12,6 +12,7 @@ export interface PermissionsDeclarationInput {
|
|
|
12
12
|
readonly allow: readonly string[];
|
|
13
13
|
readonly origins?: WireRuleOrigins;
|
|
14
14
|
readonly cliDeny?: readonly string[];
|
|
15
|
+
readonly settingsRead: 'clean' | 'partial' | 'unreadable';
|
|
15
16
|
}
|
|
16
17
|
export interface PermissionsDeclarationBuckets {
|
|
17
18
|
deny?: string[];
|
|
@@ -21,5 +22,27 @@ export interface PermissionsDeclarationBuckets {
|
|
|
21
22
|
export interface PermissionsDeclaration {
|
|
22
23
|
permissions?: PermissionsDeclarationBuckets;
|
|
23
24
|
}
|
|
25
|
+
export declare const PERMISSIONS_KEPT_REASONS: readonly ["allow_capability_off", "settings_read_not_clean", "empty_rule", "paren_in_tool_name", "retired_tool_name", "unprefixed_namespaced_name", "incomplete_protocol_name", "home_relative_path", "compile_refused", "content_form_not_grantable", "judge_failed", "duplicate"];
|
|
26
|
+
export type PermissionsKeptReason = (typeof PERMISSIONS_KEPT_REASONS)[number];
|
|
27
|
+
export interface PermissionsKeptRule {
|
|
28
|
+
rule: string;
|
|
29
|
+
bucket: 'deny' | 'ask' | 'allow';
|
|
30
|
+
reason: PermissionsKeptReason;
|
|
31
|
+
}
|
|
32
|
+
export interface PermissionsRewrittenRule {
|
|
33
|
+
from: string;
|
|
34
|
+
to: string;
|
|
35
|
+
bucket: 'deny' | 'ask' | 'allow';
|
|
36
|
+
}
|
|
37
|
+
export interface PermissionsUncheckedRule {
|
|
38
|
+
rule: string;
|
|
39
|
+
bucket: 'deny' | 'ask';
|
|
40
|
+
}
|
|
41
|
+
export interface PermissionsDeclarationReport extends PermissionsDeclaration {
|
|
42
|
+
kept: PermissionsKeptRule[];
|
|
43
|
+
rewritten: PermissionsRewrittenRule[];
|
|
44
|
+
unchecked: PermissionsUncheckedRule[];
|
|
45
|
+
}
|
|
24
46
|
export declare function permissionsDeclaration(input: PermissionsDeclarationInput, caps: PermissionsAllowGrantReading): PermissionsDeclaration;
|
|
47
|
+
export declare function permissionsDeclarationReport(input: PermissionsDeclarationInput, caps: PermissionsAllowGrantReading): PermissionsDeclarationReport;
|
|
25
48
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { hasCoreValuePort, protocolOf, retiredToolNames, ruleToolGrammarOf, validatePermissionRules } from './coreValuePorts.js';
|
|
2
|
-
import {
|
|
2
|
+
import { allowRulesGate } from './permissionsAllowGrantCapability.js';
|
|
3
3
|
const RULE_TRUST_ORDER = [
|
|
4
4
|
'policySettings',
|
|
5
5
|
'cliArg',
|
|
@@ -9,6 +9,20 @@ const RULE_TRUST_ORDER = [
|
|
|
9
9
|
'localSettings',
|
|
10
10
|
'projectSettings',
|
|
11
11
|
];
|
|
12
|
+
export const PERMISSIONS_KEPT_REASONS = Object.freeze([
|
|
13
|
+
'allow_capability_off',
|
|
14
|
+
'settings_read_not_clean',
|
|
15
|
+
'empty_rule',
|
|
16
|
+
'paren_in_tool_name',
|
|
17
|
+
'retired_tool_name',
|
|
18
|
+
'unprefixed_namespaced_name',
|
|
19
|
+
'incomplete_protocol_name',
|
|
20
|
+
'home_relative_path',
|
|
21
|
+
'compile_refused',
|
|
22
|
+
'content_form_not_grantable',
|
|
23
|
+
'judge_failed',
|
|
24
|
+
'duplicate',
|
|
25
|
+
]);
|
|
12
26
|
function normalizeLegacyToolName(name) {
|
|
13
27
|
switch (name) {
|
|
14
28
|
case 'Task':
|
|
@@ -61,15 +75,15 @@ function requireRulePorts() {
|
|
|
61
75
|
throw new Error(`permissionsDeclaration: 规则判据端口缺席(${missing.join(' / ')})—— 先用 installCoreValuePorts 装上 core 的同名实现。` +
|
|
62
76
|
'缺席不等于全部合格:不判就发会让整份请求被引擎拒收,不判就删会静默丢掉 deny 规则,所以这里拒绝装配。');
|
|
63
77
|
}
|
|
64
|
-
function
|
|
78
|
+
function wireIssue(name) {
|
|
65
79
|
if (name.includes('('))
|
|
66
|
-
return
|
|
80
|
+
return 'paren_in_tool_name';
|
|
67
81
|
if (retiredToolNames().has(name))
|
|
68
|
-
return
|
|
82
|
+
return 'retired_tool_name';
|
|
69
83
|
const ns = protocolOf(name);
|
|
70
84
|
if (ns === undefined)
|
|
71
|
-
return
|
|
72
|
-
return ns.parse(name) !== undefined;
|
|
85
|
+
return name.includes('__') ? 'unprefixed_namespaced_name' : undefined;
|
|
86
|
+
return ns.parse(name) !== undefined ? undefined : 'incomplete_protocol_name';
|
|
73
87
|
}
|
|
74
88
|
function wholeServerRuleName(name) {
|
|
75
89
|
const read = name.trim();
|
|
@@ -91,12 +105,12 @@ function compileVerdict(rule, behavior) {
|
|
|
91
105
|
return 'unknown';
|
|
92
106
|
}
|
|
93
107
|
}
|
|
94
|
-
|
|
95
|
-
|
|
108
|
+
const kept = (reason) => ({ sent: false, reason });
|
|
109
|
+
function judgeList(rules, behavior) {
|
|
96
110
|
let portsChecked = false;
|
|
97
|
-
|
|
98
|
-
if (
|
|
99
|
-
|
|
111
|
+
return rules.map((rule) => {
|
|
112
|
+
if (rule === '')
|
|
113
|
+
return kept('empty_rule');
|
|
100
114
|
if (!portsChecked) {
|
|
101
115
|
requireRulePorts();
|
|
102
116
|
portsChecked = true;
|
|
@@ -104,50 +118,78 @@ function wireSubset(rules, behavior) {
|
|
|
104
118
|
const parsed = parseRule(rule);
|
|
105
119
|
if (parsed.ruleContent === undefined) {
|
|
106
120
|
const name = wholeServerRuleName(parsed.toolName);
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
continue;
|
|
121
|
+
const issue = wireIssue(name);
|
|
122
|
+
if (issue !== undefined)
|
|
123
|
+
return kept(issue);
|
|
111
124
|
const verdict = compileVerdict(name, behavior);
|
|
112
|
-
if (verdict === 'refused'
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
125
|
+
if (verdict === 'refused')
|
|
126
|
+
return kept('compile_refused');
|
|
127
|
+
if (verdict === 'unknown' && behavior === 'allow')
|
|
128
|
+
return kept('judge_failed');
|
|
129
|
+
return { sent: true, wire: name, unchecked: verdict === 'unknown' };
|
|
116
130
|
}
|
|
117
|
-
|
|
118
|
-
|
|
131
|
+
const issue = wireIssue(parsed.toolName);
|
|
132
|
+
if (issue !== undefined)
|
|
133
|
+
return kept(issue);
|
|
119
134
|
if (ruleToolGrammarOf(parsed.toolName) === 'path' && parsed.ruleContent.startsWith('~/'))
|
|
120
|
-
|
|
135
|
+
return kept('home_relative_path');
|
|
121
136
|
const verdict = compileVerdict(rule, behavior);
|
|
122
|
-
if (verdict === '
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
137
|
+
if (verdict === 'refused')
|
|
138
|
+
return kept(behavior === 'allow' ? 'content_form_not_grantable' : 'compile_refused');
|
|
139
|
+
if (verdict === 'unknown' && behavior === 'allow')
|
|
140
|
+
return kept('judge_failed');
|
|
141
|
+
return { sent: true, wire: rule, unchecked: verdict === 'unknown' };
|
|
142
|
+
});
|
|
126
143
|
}
|
|
127
|
-
function
|
|
128
|
-
|
|
144
|
+
function outsideSettingsFiles(origin) {
|
|
145
|
+
return origin === 'cliArg' || origin === 'session';
|
|
146
|
+
}
|
|
147
|
+
function judgeBucket(raw, behavior, originOf, cli, settingsGate) {
|
|
148
|
+
const firstSlot = new Map();
|
|
149
|
+
for (const s of raw)
|
|
150
|
+
if (!firstSlot.has(s.rule))
|
|
151
|
+
firstSlot.set(s.rule, s);
|
|
129
152
|
const groups = RULE_TRUST_ORDER.map(() => []);
|
|
130
153
|
const rankOf = (origin) => RULE_TRUST_ORDER.indexOf(origin);
|
|
131
154
|
if (originOf !== undefined) {
|
|
132
155
|
for (const [r, origin] of originOf) {
|
|
133
156
|
const rank = rankOf(origin);
|
|
134
|
-
if (rank >= 0 && typeof r === 'string' &&
|
|
157
|
+
if (rank >= 0 && typeof r === 'string' && firstSlot.has(r))
|
|
135
158
|
groups[rank].push(r);
|
|
136
159
|
}
|
|
137
160
|
}
|
|
138
|
-
const unknownOrigin = [...
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
161
|
+
const unknownOrigin = [...firstSlot.keys()].filter((r) => originOf === undefined || rankOf(originOf.get(r)) < 0);
|
|
162
|
+
const fateOfRule = new Map();
|
|
163
|
+
const sent = [];
|
|
164
|
+
const winnerOf = new Map();
|
|
165
|
+
const cliFates = cli === undefined ? [] : [...cli.fates];
|
|
166
|
+
const place = (slot, fate) => {
|
|
167
|
+
if (!fate.sent)
|
|
168
|
+
return fate;
|
|
169
|
+
if (winnerOf.has(fate.wire))
|
|
170
|
+
return kept('duplicate');
|
|
171
|
+
winnerOf.set(fate.wire, slot);
|
|
172
|
+
sent.push(fate.wire);
|
|
173
|
+
return fate;
|
|
174
|
+
};
|
|
175
|
+
const appendGroup = (group, origin) => {
|
|
176
|
+
const held = settingsGate !== undefined && !outsideSettingsFiles(origin);
|
|
177
|
+
const fates = held ? group.map(() => kept(settingsGate)) : judgeList(group, behavior);
|
|
178
|
+
group.forEach((r, k) => fateOfRule.set(r, place(firstSlot.get(r), fates[k])));
|
|
143
179
|
};
|
|
144
180
|
RULE_TRUST_ORDER.forEach((origin, rank) => {
|
|
145
|
-
if (origin === 'cliArg')
|
|
146
|
-
|
|
147
|
-
|
|
181
|
+
if (origin === 'cliArg' && cli !== undefined)
|
|
182
|
+
cli.slots.forEach((s, k) => (cliFates[k] = place(s, cliFates[k])));
|
|
183
|
+
appendGroup(groups[rank], origin);
|
|
148
184
|
});
|
|
149
|
-
|
|
150
|
-
|
|
185
|
+
appendGroup(unknownOrigin, undefined);
|
|
186
|
+
const fates = raw.map((s) => {
|
|
187
|
+
const f = fateOfRule.get(s.rule);
|
|
188
|
+
if (firstSlot.get(s.rule) === s)
|
|
189
|
+
return f;
|
|
190
|
+
return f.sent || f.reason === 'duplicate' ? kept('duplicate') : f;
|
|
191
|
+
});
|
|
192
|
+
return { sent, fates: [...fates, ...cliFates], winnerOf };
|
|
151
193
|
}
|
|
152
194
|
const WHO = 'permissionsDeclaration';
|
|
153
195
|
function ownField(obj, key, where) {
|
|
@@ -192,7 +234,8 @@ function originMap(v, key) {
|
|
|
192
234
|
}
|
|
193
235
|
return snap;
|
|
194
236
|
}
|
|
195
|
-
|
|
237
|
+
const slotsOf = (list, name) => (list ?? []).map((rule, at) => ({ rule, list: name, at }));
|
|
238
|
+
function assemble(input, caps) {
|
|
196
239
|
if (input === null || typeof input !== 'object' || Array.isArray(input)) {
|
|
197
240
|
throw new TypeError(`${WHO}: 入参必须是对象 —— 收到 ${input === null ? 'null' : Array.isArray(input) ? 'array' : `typeof=${typeof input}`}。`);
|
|
198
241
|
}
|
|
@@ -211,17 +254,54 @@ export function permissionsDeclaration(input, caps) {
|
|
|
211
254
|
const ol = originMap(ownField(originsRaw, 'allow', 'origins.'), 'allow');
|
|
212
255
|
origins = { ...(od ? { deny: od } : {}), ...(oa ? { ask: oa } : {}), ...(ol ? { allow: ol } : {}) };
|
|
213
256
|
}
|
|
214
|
-
const
|
|
215
|
-
const
|
|
216
|
-
const
|
|
217
|
-
const
|
|
218
|
-
const
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
257
|
+
const gate = allowRulesGate(caps, ownField(input, 'settingsRead', ''));
|
|
258
|
+
const cliSlots = slotsOf(cliDeny, 'cliDeny');
|
|
259
|
+
const cli = cliDeny === undefined ? undefined : { slots: cliSlots, fates: judgeList(cliDeny, 'deny') };
|
|
260
|
+
const denySlots = slotsOf(deny, 'deny');
|
|
261
|
+
const askSlots = slotsOf(ask, 'ask');
|
|
262
|
+
const allowSlots = slotsOf(allow, 'allow');
|
|
263
|
+
const denyJ = judgeBucket(denySlots, 'deny', origins.deny, cli);
|
|
264
|
+
const askJ = judgeBucket(askSlots, 'ask', origins.ask, undefined);
|
|
265
|
+
const allowJ = gate === 'capability_off'
|
|
266
|
+
? { sent: [], fates: allowSlots.map(() => kept('allow_capability_off')), winnerOf: new Map() }
|
|
267
|
+
: judgeBucket(allowSlots, 'allow', origins.allow, undefined, gate === 'settings_read_not_clean' ? 'settings_read_not_clean' : undefined);
|
|
268
|
+
const buckets = {};
|
|
269
|
+
if (denyJ.sent.length > 0)
|
|
270
|
+
buckets.deny = [...denyJ.sent];
|
|
271
|
+
if (askJ.sent.length > 0)
|
|
272
|
+
buckets.ask = [...askJ.sent];
|
|
273
|
+
if (allowJ.sent.length > 0)
|
|
274
|
+
buckets.allow = [...allowJ.sent];
|
|
275
|
+
const keptRows = [];
|
|
276
|
+
const rewritten = [];
|
|
277
|
+
const unchecked = [];
|
|
278
|
+
const rows = [
|
|
279
|
+
['deny', [...denySlots, ...cliSlots], denyJ],
|
|
280
|
+
['ask', askSlots, askJ],
|
|
281
|
+
['allow', allowSlots, allowJ],
|
|
282
|
+
];
|
|
283
|
+
for (const [bucket, slots, j] of rows) {
|
|
284
|
+
slots.forEach((s, k) => {
|
|
285
|
+
const f = j.fates[k];
|
|
286
|
+
if (!f.sent)
|
|
287
|
+
keptRows.push({ rule: s.rule, bucket, reason: f.reason });
|
|
288
|
+
});
|
|
289
|
+
for (const wire of j.sent) {
|
|
290
|
+
const winner = j.winnerOf.get(wire);
|
|
291
|
+
if (winner.rule !== wire)
|
|
292
|
+
rewritten.push({ from: winner.rule, to: wire, bucket });
|
|
293
|
+
const f = (winner.list === 'cliDeny' ? j.fates[denySlots.length + winner.at] : j.fates[winner.at]);
|
|
294
|
+
if (f.sent && f.unchecked && bucket !== 'allow')
|
|
295
|
+
unchecked.push({ rule: wire, bucket });
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return { buckets, report: { kept: keptRows, rewritten, unchecked } };
|
|
299
|
+
}
|
|
300
|
+
export function permissionsDeclaration(input, caps) {
|
|
301
|
+
const { buckets } = assemble(input, caps);
|
|
302
|
+
return Object.keys(buckets).length > 0 ? { permissions: buckets } : {};
|
|
303
|
+
}
|
|
304
|
+
export function permissionsDeclarationReport(input, caps) {
|
|
305
|
+
const { buckets, report } = assemble(input, caps);
|
|
306
|
+
return { ...(Object.keys(buckets).length > 0 ? { permissions: buckets } : {}), ...report };
|
|
227
307
|
}
|
package/dist/promptDelivery.d.ts
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
import type { AgentEvent } from '@sema-agent/sdk';
|
|
2
2
|
import { type SelfHealOutcome } from './adapter/activeRunSelfHeal.js';
|
|
3
|
+
import type { HumanTurnDelivered } from './humanTurn.js';
|
|
3
4
|
export interface PromptDeliveryRecord {
|
|
4
5
|
readonly u: string;
|
|
5
6
|
readonly s: 'a' | 'd';
|
|
7
|
+
readonly by?: 'frame' | 'steer_receipt';
|
|
8
|
+
readonly share?: 'full' | 'partial';
|
|
6
9
|
}
|
|
7
10
|
export interface PromptDeliveryLedger {
|
|
8
|
-
attempted(batchUuids: readonly string[]
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
attempted(batchUuids: readonly string[], opts?: {
|
|
12
|
+
readonly generation?: number;
|
|
13
|
+
}): readonly PromptDeliveryRecord[];
|
|
14
|
+
deliveredByFrame(batchUuids: readonly string[] | HumanTurnDelivered, event: AgentEvent): readonly PromptDeliveryRecord[];
|
|
15
|
+
deliveredBySteerReceipt(batchUuids: readonly string[] | HumanTurnDelivered, outcome: SelfHealOutcome): readonly PromptDeliveryRecord[];
|
|
11
16
|
isAttempted(uuid: string): boolean;
|
|
12
17
|
isDelivered(uuid: string): boolean;
|
|
13
18
|
knownUndelivered(uuid: string): boolean;
|
|
19
|
+
deliveryShareOf(uuid: string): 'full' | 'partial' | undefined;
|
|
20
|
+
undeliveredInGeneration(generation: number): readonly string[];
|
|
21
|
+
droppedKeyCount(): number;
|
|
14
22
|
batchOf(uuid: string): readonly string[] | undefined;
|
|
15
23
|
toRecords(): readonly PromptDeliveryRecord[];
|
|
16
24
|
fromRecords(records: readonly PromptDeliveryRecord[]): readonly PromptDeliveryRecord[];
|