@sema-agent/client-core 0.89.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 +297 -0
- package/README.md +35 -24
- 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.d.ts +2 -0
- package/dist/adapter/runStream.js +180 -38
- 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 +22 -2
- package/dist/attachmentsWireCaps.js +95 -22
- package/dist/compensations.js +10 -14
- package/dist/controlRouter.d.ts +9 -2
- package/dist/controlRouter.js +66 -28
- package/dist/coreValuePorts.d.ts +1 -0
- 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/engineCapReader.js +12 -1
- 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/engineNoticeCodes.d.ts +2 -9
- package/dist/engineNoticeCodes.js +19 -27
- package/dist/fileHistoryLegNotice.d.ts +20 -0
- package/dist/fileHistoryLegNotice.js +122 -0
- package/dist/gateOutcome.d.ts +1 -1
- package/dist/gateOutcome.js +1 -0
- package/dist/generated/engineFactTables.d.ts +13 -0
- package/dist/generated/engineFactTables.js +50 -0
- package/dist/generated/engineNoticeTables.js +0 -3
- 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/hookToolAliases.d.ts +3 -0
- package/dist/hookToolAliases.js +10 -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 +30 -5
- package/dist/index.js +19 -4
- 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 +18 -0
- package/dist/permissionsAllowGrantCapability.js +84 -0
- package/dist/permissionsDeclaration.d.ts +48 -0
- package/dist/permissionsDeclaration.js +307 -0
- package/dist/promptDelivery.d.ts +16 -5
- package/dist/promptDelivery.js +172 -32
- package/dist/providerRejection.d.ts +3 -1
- package/dist/providerRejection.js +27 -1
- package/dist/readDenyTiers.d.ts +10 -0
- package/dist/readDenyTiers.js +29 -0
- package/dist/request/taskRequest.js +27 -1
- 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/sdkRegistryTransit.d.ts +1 -0
- package/dist/sdkRegistryTransit.js +1 -0
- package/dist/seam.d.ts +2 -0
- package/dist/supportFloor.d.ts +10 -0
- package/dist/supportFloor.js +42 -0
- package/dist/taskRequestWords.d.ts +7 -0
- package/dist/taskRequestWords.js +24 -0
- 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 +1369 -18
- package/package.json +3 -3
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { hasCoreValuePort, protocolOf, retiredToolNames, ruleToolGrammarOf, validatePermissionRules } from './coreValuePorts.js';
|
|
2
|
+
import { allowRulesGate } from './permissionsAllowGrantCapability.js';
|
|
3
|
+
const RULE_TRUST_ORDER = [
|
|
4
|
+
'policySettings',
|
|
5
|
+
'cliArg',
|
|
6
|
+
'flagSettings',
|
|
7
|
+
'session',
|
|
8
|
+
'userSettings',
|
|
9
|
+
'localSettings',
|
|
10
|
+
'projectSettings',
|
|
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
|
+
]);
|
|
26
|
+
function normalizeLegacyToolName(name) {
|
|
27
|
+
switch (name) {
|
|
28
|
+
case 'Task':
|
|
29
|
+
return 'Agent';
|
|
30
|
+
case 'KillShell':
|
|
31
|
+
return 'TaskStop';
|
|
32
|
+
case 'AgentOutputTool':
|
|
33
|
+
case 'BashOutputTool':
|
|
34
|
+
return 'TaskOutput';
|
|
35
|
+
default:
|
|
36
|
+
return name;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function unescapedIndex(str, char, fromEnd) {
|
|
40
|
+
for (let k = 0; k < str.length; k++) {
|
|
41
|
+
const i = fromEnd ? str.length - 1 - k : k;
|
|
42
|
+
if (str[i] !== char)
|
|
43
|
+
continue;
|
|
44
|
+
let backslashes = 0;
|
|
45
|
+
for (let j = i - 1; j >= 0 && str[j] === '\\'; j--)
|
|
46
|
+
backslashes++;
|
|
47
|
+
if (backslashes % 2 === 0)
|
|
48
|
+
return i;
|
|
49
|
+
}
|
|
50
|
+
return -1;
|
|
51
|
+
}
|
|
52
|
+
function unescapeRuleContent(content) {
|
|
53
|
+
return content.replace(/\\\(/g, '(').replace(/\\\)/g, ')').replace(/\\\\/g, '\\');
|
|
54
|
+
}
|
|
55
|
+
function parseRule(rule) {
|
|
56
|
+
const open = unescapedIndex(rule, '(', false);
|
|
57
|
+
if (open === -1)
|
|
58
|
+
return { toolName: normalizeLegacyToolName(rule) };
|
|
59
|
+
const close = unescapedIndex(rule, ')', true);
|
|
60
|
+
if (close === -1 || close <= open || close !== rule.length - 1)
|
|
61
|
+
return { toolName: normalizeLegacyToolName(rule) };
|
|
62
|
+
const toolName = rule.substring(0, open);
|
|
63
|
+
const rawContent = rule.substring(open + 1, close);
|
|
64
|
+
if (!toolName)
|
|
65
|
+
return { toolName: normalizeLegacyToolName(rule) };
|
|
66
|
+
if (rawContent === '' || rawContent === '*')
|
|
67
|
+
return { toolName: normalizeLegacyToolName(toolName) };
|
|
68
|
+
return { toolName: normalizeLegacyToolName(toolName), ruleContent: unescapeRuleContent(rawContent) };
|
|
69
|
+
}
|
|
70
|
+
function requireRulePorts() {
|
|
71
|
+
const required = ['MCP_NAMESPACE', 'RETIRED_TOOL_NAMES', 'protocolOf', 'ruleToolGrammarOf', 'validatePermissionRules'];
|
|
72
|
+
const missing = required.filter((p) => !hasCoreValuePort(p));
|
|
73
|
+
if (missing.length === 0)
|
|
74
|
+
return;
|
|
75
|
+
throw new Error(`permissionsDeclaration: 规则判据端口缺席(${missing.join(' / ')})—— 先用 installCoreValuePorts 装上 core 的同名实现。` +
|
|
76
|
+
'缺席不等于全部合格:不判就发会让整份请求被引擎拒收,不判就删会静默丢掉 deny 规则,所以这里拒绝装配。');
|
|
77
|
+
}
|
|
78
|
+
function wireIssue(name) {
|
|
79
|
+
if (name.includes('('))
|
|
80
|
+
return 'paren_in_tool_name';
|
|
81
|
+
if (retiredToolNames().has(name))
|
|
82
|
+
return 'retired_tool_name';
|
|
83
|
+
const ns = protocolOf(name);
|
|
84
|
+
if (ns === undefined)
|
|
85
|
+
return name.includes('__') ? 'unprefixed_namespaced_name' : undefined;
|
|
86
|
+
return ns.parse(name) !== undefined ? undefined : 'incomplete_protocol_name';
|
|
87
|
+
}
|
|
88
|
+
function wholeServerRuleName(name) {
|
|
89
|
+
const read = name.trim();
|
|
90
|
+
const ns = protocolOf(read);
|
|
91
|
+
if (ns === undefined || !read.startsWith(ns.prefix))
|
|
92
|
+
return name;
|
|
93
|
+
const peer = read.slice(ns.prefix.length);
|
|
94
|
+
return peer !== '' && !peer.includes('__') && !peer.endsWith('_') ? `${read}__*` : name;
|
|
95
|
+
}
|
|
96
|
+
const RULE_ROOT = '/';
|
|
97
|
+
function compileVerdict(rule, behavior) {
|
|
98
|
+
try {
|
|
99
|
+
const issues = validatePermissionRules([{ rule, behavior }], { pathBases: { root: RULE_ROOT } });
|
|
100
|
+
if (!Array.isArray(issues))
|
|
101
|
+
return 'unknown';
|
|
102
|
+
return issues.length === 0 ? 'compiles' : 'refused';
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return 'unknown';
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const kept = (reason) => ({ sent: false, reason });
|
|
109
|
+
function judgeList(rules, behavior) {
|
|
110
|
+
let portsChecked = false;
|
|
111
|
+
return rules.map((rule) => {
|
|
112
|
+
if (rule === '')
|
|
113
|
+
return kept('empty_rule');
|
|
114
|
+
if (!portsChecked) {
|
|
115
|
+
requireRulePorts();
|
|
116
|
+
portsChecked = true;
|
|
117
|
+
}
|
|
118
|
+
const parsed = parseRule(rule);
|
|
119
|
+
if (parsed.ruleContent === undefined) {
|
|
120
|
+
const name = wholeServerRuleName(parsed.toolName);
|
|
121
|
+
const issue = wireIssue(name);
|
|
122
|
+
if (issue !== undefined)
|
|
123
|
+
return kept(issue);
|
|
124
|
+
const verdict = compileVerdict(name, behavior);
|
|
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' };
|
|
130
|
+
}
|
|
131
|
+
const issue = wireIssue(parsed.toolName);
|
|
132
|
+
if (issue !== undefined)
|
|
133
|
+
return kept(issue);
|
|
134
|
+
if (ruleToolGrammarOf(parsed.toolName) === 'path' && parsed.ruleContent.startsWith('~/'))
|
|
135
|
+
return kept('home_relative_path');
|
|
136
|
+
const verdict = compileVerdict(rule, behavior);
|
|
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
|
+
});
|
|
143
|
+
}
|
|
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);
|
|
152
|
+
const groups = RULE_TRUST_ORDER.map(() => []);
|
|
153
|
+
const rankOf = (origin) => RULE_TRUST_ORDER.indexOf(origin);
|
|
154
|
+
if (originOf !== undefined) {
|
|
155
|
+
for (const [r, origin] of originOf) {
|
|
156
|
+
const rank = rankOf(origin);
|
|
157
|
+
if (rank >= 0 && typeof r === 'string' && firstSlot.has(r))
|
|
158
|
+
groups[rank].push(r);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
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])));
|
|
179
|
+
};
|
|
180
|
+
RULE_TRUST_ORDER.forEach((origin, rank) => {
|
|
181
|
+
if (origin === 'cliArg' && cli !== undefined)
|
|
182
|
+
cli.slots.forEach((s, k) => (cliFates[k] = place(s, cliFates[k])));
|
|
183
|
+
appendGroup(groups[rank], origin);
|
|
184
|
+
});
|
|
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 };
|
|
193
|
+
}
|
|
194
|
+
const WHO = 'permissionsDeclaration';
|
|
195
|
+
function ownField(obj, key, where) {
|
|
196
|
+
if (Object.hasOwn(obj, key))
|
|
197
|
+
return obj[key];
|
|
198
|
+
if (key in obj)
|
|
199
|
+
throw new TypeError(`${WHO}: ${where}${key} 只在原型链上,不是自有字段 —— 拒绝读取(请交普通对象)。`);
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
function ruleList(v, key) {
|
|
203
|
+
if (v === undefined || v === null)
|
|
204
|
+
return undefined;
|
|
205
|
+
if (!Array.isArray(v))
|
|
206
|
+
throw new TypeError(`${WHO}: ${key} 必须是规则串数组或缺席 —— 收到 typeof=${typeof v}。`);
|
|
207
|
+
const out = [];
|
|
208
|
+
const n = v.length;
|
|
209
|
+
for (let i = 0; i < n; i++) {
|
|
210
|
+
if (!Object.hasOwn(v, i)) {
|
|
211
|
+
if (i in v)
|
|
212
|
+
throw new TypeError(`${WHO}: ${key}[${i}] 是空洞,而原型链上在这个下标有值 —— 拒绝读取(请交不带空洞的普通数组)。`);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const r = v[i];
|
|
216
|
+
if (typeof r === 'string')
|
|
217
|
+
out.push(r);
|
|
218
|
+
}
|
|
219
|
+
return out;
|
|
220
|
+
}
|
|
221
|
+
function originMap(v, key) {
|
|
222
|
+
if (v === undefined || v === null)
|
|
223
|
+
return undefined;
|
|
224
|
+
const ok = typeof v === 'object' &&
|
|
225
|
+
typeof v.get === 'function' &&
|
|
226
|
+
typeof v[Symbol.iterator] === 'function';
|
|
227
|
+
if (!ok)
|
|
228
|
+
throw new TypeError(`${WHO}: origins.${key} 必须是 Map(规则原串 → 来源)或缺席 —— 收到 typeof=${typeof v}。`);
|
|
229
|
+
const snap = new Map();
|
|
230
|
+
for (const entry of v) {
|
|
231
|
+
const [r, origin] = entry;
|
|
232
|
+
if (!snap.has(r))
|
|
233
|
+
snap.set(r, origin);
|
|
234
|
+
}
|
|
235
|
+
return snap;
|
|
236
|
+
}
|
|
237
|
+
const slotsOf = (list, name) => (list ?? []).map((rule, at) => ({ rule, list: name, at }));
|
|
238
|
+
function assemble(input, caps) {
|
|
239
|
+
if (input === null || typeof input !== 'object' || Array.isArray(input)) {
|
|
240
|
+
throw new TypeError(`${WHO}: 入参必须是对象 —— 收到 ${input === null ? 'null' : Array.isArray(input) ? 'array' : `typeof=${typeof input}`}。`);
|
|
241
|
+
}
|
|
242
|
+
const deny = ruleList(ownField(input, 'deny', ''), 'deny');
|
|
243
|
+
const ask = ruleList(ownField(input, 'ask', ''), 'ask');
|
|
244
|
+
const allow = ruleList(ownField(input, 'allow', ''), 'allow');
|
|
245
|
+
const cliDeny = ruleList(ownField(input, 'cliDeny', ''), 'cliDeny');
|
|
246
|
+
const originsRaw = ownField(input, 'origins', '');
|
|
247
|
+
let origins = {};
|
|
248
|
+
if (originsRaw !== undefined && originsRaw !== null) {
|
|
249
|
+
if (typeof originsRaw !== 'object' || Array.isArray(originsRaw)) {
|
|
250
|
+
throw new TypeError(`${WHO}: origins 必须是 { deny?, ask?, allow? } 对象或缺席 —— 收到 typeof=${typeof originsRaw}。`);
|
|
251
|
+
}
|
|
252
|
+
const od = originMap(ownField(originsRaw, 'deny', 'origins.'), 'deny');
|
|
253
|
+
const oa = originMap(ownField(originsRaw, 'ask', 'origins.'), 'ask');
|
|
254
|
+
const ol = originMap(ownField(originsRaw, 'allow', 'origins.'), 'allow');
|
|
255
|
+
origins = { ...(od ? { deny: od } : {}), ...(oa ? { ask: oa } : {}), ...(ol ? { allow: ol } : {}) };
|
|
256
|
+
}
|
|
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 };
|
|
307
|
+
}
|
package/dist/promptDelivery.d.ts
CHANGED
|
@@ -1,20 +1,31 @@
|
|
|
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[];
|
|
25
|
+
knownUndeliveredMessage(message: object | null | undefined): boolean;
|
|
26
|
+
withoutUndelivered<T>(messages: readonly T[]): T[];
|
|
17
27
|
}
|
|
28
|
+
export declare function frameProvesDelivery(event: object | null | undefined): boolean;
|
|
18
29
|
export declare function createPromptDeliveryLedger(): PromptDeliveryLedger;
|
|
19
|
-
export declare function promptKnownUndelivered(ledger: PromptDeliveryLedger, message: object | null | undefined): boolean;
|
|
20
|
-
export declare function withoutUndeliveredPrompts<T>(ledger: PromptDeliveryLedger, messages: readonly T[]): T[];
|
|
30
|
+
export declare function promptKnownUndelivered(ledger: Pick<PromptDeliveryLedger, 'knownUndelivered'>, message: object | null | undefined): boolean;
|
|
31
|
+
export declare function withoutUndeliveredPrompts<T>(ledger: Pick<PromptDeliveryLedger, 'knownUndelivered'>, messages: readonly T[]): T[];
|
package/dist/promptDelivery.js
CHANGED
|
@@ -1,8 +1,43 @@
|
|
|
1
1
|
import { activeRunBusySignal } from './adapter/runStream.js';
|
|
2
2
|
import { selfHealSubmissionDisposition } from './adapter/activeRunSelfHeal.js';
|
|
3
3
|
const uuidOk = (u) => typeof u === 'string' && u.length > 0;
|
|
4
|
+
function isArraySafe(v) {
|
|
5
|
+
try {
|
|
6
|
+
return Array.isArray(v);
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
const generationOk = (g) => typeof g === 'number' && Number.isSafeInteger(g) && g >= 0;
|
|
13
|
+
function generationOpt(opts) {
|
|
14
|
+
if (typeof opts !== 'object' || opts === null)
|
|
15
|
+
return undefined;
|
|
16
|
+
try {
|
|
17
|
+
const g = opts.generation;
|
|
18
|
+
return generationOk(g) ? g : undefined;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const levelOf = (rec) => !Object.hasOwn(rec, 'by') ? 1 : Object.hasOwn(rec, 'share') && rec.share === 'partial' ? 2 : 3;
|
|
25
|
+
function deliveredByWord(v) {
|
|
26
|
+
const words = { frame: true, steer_receipt: true };
|
|
27
|
+
return typeof v === 'string' && Object.hasOwn(words, v) ? v : undefined;
|
|
28
|
+
}
|
|
29
|
+
function deliveredShareWord(v) {
|
|
30
|
+
const words = { full: true, partial: true };
|
|
31
|
+
return typeof v === 'string' && Object.hasOwn(words, v) ? v : undefined;
|
|
32
|
+
}
|
|
33
|
+
function ownExtension(rec, key) {
|
|
34
|
+
const desc = Object.getOwnPropertyDescriptor(rec, key);
|
|
35
|
+
if (desc === undefined)
|
|
36
|
+
return key in rec ? { kind: 'unreadable' } : { kind: 'absent' };
|
|
37
|
+
return 'value' in desc ? { kind: 'value', value: desc.value } : { kind: 'unreadable' };
|
|
38
|
+
}
|
|
4
39
|
function cleanBatch(batch) {
|
|
5
|
-
if (!
|
|
40
|
+
if (!isArraySafe(batch))
|
|
6
41
|
return [];
|
|
7
42
|
const out = [];
|
|
8
43
|
const seen = new Set();
|
|
@@ -19,23 +54,81 @@ function cleanBatch(batch) {
|
|
|
19
54
|
}
|
|
20
55
|
return out;
|
|
21
56
|
}
|
|
22
|
-
function readRecord(
|
|
23
|
-
if (typeof
|
|
57
|
+
function readRecord(raw) {
|
|
58
|
+
if (typeof raw !== 'object' || raw === null)
|
|
24
59
|
return undefined;
|
|
60
|
+
let u;
|
|
61
|
+
let s;
|
|
25
62
|
try {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
63
|
+
if (Array.isArray(raw))
|
|
64
|
+
return undefined;
|
|
65
|
+
u = raw.u;
|
|
66
|
+
s = raw.s;
|
|
29
67
|
}
|
|
30
68
|
catch {
|
|
31
69
|
return undefined;
|
|
32
70
|
}
|
|
71
|
+
if (!uuidOk(u) || (s !== 'a' && s !== 'd'))
|
|
72
|
+
return undefined;
|
|
73
|
+
const legacy = { u, s };
|
|
74
|
+
try {
|
|
75
|
+
let rec = legacy;
|
|
76
|
+
if (s === 'd') {
|
|
77
|
+
const byExt = ownExtension(raw, 'by');
|
|
78
|
+
const shareExt = ownExtension(raw, 'share');
|
|
79
|
+
const by = byExt.kind === 'value' ? deliveredByWord(byExt.value) : undefined;
|
|
80
|
+
const share = shareExt.kind === 'value' ? deliveredShareWord(shareExt.value) : undefined;
|
|
81
|
+
if (by !== undefined && (shareExt.kind === 'absent' || share !== undefined))
|
|
82
|
+
rec = share === undefined ? { u, s, by } : { u, s, by, share };
|
|
83
|
+
}
|
|
84
|
+
const dropped = Object.keys(raw).filter((k) => !Object.hasOwn(rec, k)).length;
|
|
85
|
+
return { rec, dropped };
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return { rec: legacy, dropped: 0 };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function readTierStrict(holder, key) {
|
|
92
|
+
try {
|
|
93
|
+
if (!Object.hasOwn(holder, key))
|
|
94
|
+
return [];
|
|
95
|
+
const raw = holder[key];
|
|
96
|
+
if (!Array.isArray(raw))
|
|
97
|
+
return null;
|
|
98
|
+
const out = [];
|
|
99
|
+
const seen = new Set();
|
|
100
|
+
for (const u of raw) {
|
|
101
|
+
if (!uuidOk(u) || seen.has(u))
|
|
102
|
+
continue;
|
|
103
|
+
seen.add(u);
|
|
104
|
+
out.push(u);
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function batchTiers(batch) {
|
|
113
|
+
const none = { full: [], partial: [] };
|
|
114
|
+
if (isArraySafe(batch))
|
|
115
|
+
return { full: cleanBatch(batch), partial: [] };
|
|
116
|
+
if (typeof batch !== 'object' || batch === null)
|
|
117
|
+
return none;
|
|
118
|
+
const full = readTierStrict(batch, 'full');
|
|
119
|
+
const partial = readTierStrict(batch, 'partial');
|
|
120
|
+
if (full === null || partial === null)
|
|
121
|
+
return none;
|
|
122
|
+
const inFull = new Set(full);
|
|
123
|
+
return { full, partial: partial.filter((u) => !inFull.has(u)) };
|
|
33
124
|
}
|
|
34
125
|
const PRE_RUN_FRAME_TYPES = new Set(['meta', 'approval_request', 'approval_revoke']);
|
|
35
|
-
function frameProvesDelivery(event) {
|
|
36
|
-
if (typeof event !== 'object' || event === null
|
|
126
|
+
export function frameProvesDelivery(event) {
|
|
127
|
+
if (typeof event !== 'object' || event === null)
|
|
37
128
|
return false;
|
|
38
129
|
try {
|
|
130
|
+
if (Array.isArray(event))
|
|
131
|
+
return false;
|
|
39
132
|
const type = event.type;
|
|
40
133
|
if (typeof type !== 'string' || type.length === 0 || PRE_RUN_FRAME_TYPES.has(type))
|
|
41
134
|
return false;
|
|
@@ -54,9 +147,11 @@ function frameProvesDelivery(event) {
|
|
|
54
147
|
}
|
|
55
148
|
}
|
|
56
149
|
function outcomeProvesDelivery(outcome) {
|
|
57
|
-
if (typeof outcome !== 'object' || outcome === null
|
|
150
|
+
if (typeof outcome !== 'object' || outcome === null)
|
|
58
151
|
return false;
|
|
59
152
|
try {
|
|
153
|
+
if (Array.isArray(outcome))
|
|
154
|
+
return false;
|
|
60
155
|
return selfHealSubmissionDisposition(outcome) === 'handed-off';
|
|
61
156
|
}
|
|
62
157
|
catch {
|
|
@@ -65,48 +160,88 @@ function outcomeProvesDelivery(outcome) {
|
|
|
65
160
|
}
|
|
66
161
|
export function createPromptDeliveryLedger() {
|
|
67
162
|
const attemptedSet = new Set();
|
|
68
|
-
const
|
|
163
|
+
const deliveredLevel = new Map();
|
|
69
164
|
const batchOfMap = new Map();
|
|
165
|
+
const generationOf = new Map();
|
|
70
166
|
const log = [];
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
167
|
+
let droppedKeys = 0;
|
|
168
|
+
const keep = (rec, into) => {
|
|
169
|
+
const frozen = Object.freeze(rec);
|
|
170
|
+
log.push(frozen);
|
|
171
|
+
into.push(frozen);
|
|
172
|
+
};
|
|
173
|
+
const add = (rec, into) => {
|
|
174
|
+
if (rec.s === 'a') {
|
|
175
|
+
if (attemptedSet.has(rec.u))
|
|
176
|
+
return;
|
|
177
|
+
attemptedSet.add(rec.u);
|
|
178
|
+
keep(rec, into);
|
|
74
179
|
return;
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
180
|
+
}
|
|
181
|
+
const level = levelOf(rec);
|
|
182
|
+
if ((deliveredLevel.get(rec.u) ?? 0) >= level)
|
|
183
|
+
return;
|
|
184
|
+
deliveredLevel.set(rec.u, level);
|
|
185
|
+
keep(rec, into);
|
|
79
186
|
};
|
|
80
|
-
const markDelivered = (
|
|
187
|
+
const markDelivered = (batch, by) => {
|
|
81
188
|
const added = [];
|
|
82
|
-
|
|
83
|
-
|
|
189
|
+
const { full, partial } = batchTiers(batch);
|
|
190
|
+
for (const u of full)
|
|
191
|
+
add({ u, s: 'd', by }, added);
|
|
192
|
+
for (const u of partial)
|
|
193
|
+
add({ u, s: 'd', by, share: 'partial' }, added);
|
|
84
194
|
return Object.freeze(added);
|
|
85
195
|
};
|
|
86
|
-
|
|
87
|
-
|
|
196
|
+
const levelNow = (uuid) => (uuidOk(uuid) ? (deliveredLevel.get(uuid) ?? 0) : 0);
|
|
197
|
+
const knownUndeliveredUuid = (uuid) => uuidOk(uuid) && attemptedSet.has(uuid) && !deliveredLevel.has(uuid);
|
|
198
|
+
const ledger = Object.freeze({
|
|
199
|
+
attempted(batchUuids, opts) {
|
|
200
|
+
const generation = generationOpt(opts);
|
|
88
201
|
const batch = Object.freeze(cleanBatch(batchUuids));
|
|
89
202
|
const added = [];
|
|
90
203
|
for (const u of batch) {
|
|
91
204
|
batchOfMap.set(u, batch);
|
|
92
|
-
|
|
205
|
+
if (generation !== undefined) {
|
|
206
|
+
generationOf.delete(u);
|
|
207
|
+
generationOf.set(u, generation);
|
|
208
|
+
}
|
|
209
|
+
add({ u, s: 'a' }, added);
|
|
93
210
|
}
|
|
94
211
|
return Object.freeze(added);
|
|
95
212
|
},
|
|
96
213
|
deliveredByFrame(batchUuids, event) {
|
|
97
|
-
return frameProvesDelivery(event) ? markDelivered(batchUuids) : Object.freeze([]);
|
|
214
|
+
return frameProvesDelivery(event) ? markDelivered(batchUuids, 'frame') : Object.freeze([]);
|
|
98
215
|
},
|
|
99
216
|
deliveredBySteerReceipt(batchUuids, outcome) {
|
|
100
|
-
return outcomeProvesDelivery(outcome) ? markDelivered(batchUuids) : Object.freeze([]);
|
|
217
|
+
return outcomeProvesDelivery(outcome) ? markDelivered(batchUuids, 'steer_receipt') : Object.freeze([]);
|
|
101
218
|
},
|
|
102
219
|
isAttempted: (uuid) => uuidOk(uuid) && attemptedSet.has(uuid),
|
|
103
|
-
isDelivered: (uuid) =>
|
|
104
|
-
knownUndelivered: (uuid) =>
|
|
220
|
+
isDelivered: (uuid) => levelNow(uuid) >= 2,
|
|
221
|
+
knownUndelivered: (uuid) => knownUndeliveredUuid(uuid),
|
|
222
|
+
deliveryShareOf: (uuid) => {
|
|
223
|
+
const level = levelNow(uuid);
|
|
224
|
+
return level === 3 ? 'full' : level === 2 ? 'partial' : undefined;
|
|
225
|
+
},
|
|
226
|
+
undeliveredInGeneration(generation) {
|
|
227
|
+
const out = [];
|
|
228
|
+
if (!generationOk(generation))
|
|
229
|
+
return Object.freeze(out);
|
|
230
|
+
for (const [u, g] of generationOf) {
|
|
231
|
+
if (g !== generation)
|
|
232
|
+
continue;
|
|
233
|
+
generationOf.delete(u);
|
|
234
|
+
if (knownUndeliveredUuid(u))
|
|
235
|
+
out.push(u);
|
|
236
|
+
}
|
|
237
|
+
return Object.freeze(out);
|
|
238
|
+
},
|
|
239
|
+
droppedKeyCount: () => droppedKeys,
|
|
105
240
|
batchOf: (uuid) => (uuidOk(uuid) ? batchOfMap.get(uuid) : undefined),
|
|
106
241
|
toRecords: () => Object.freeze([...log]),
|
|
107
242
|
fromRecords(records) {
|
|
108
243
|
const added = [];
|
|
109
|
-
if (!
|
|
244
|
+
if (!isArraySafe(records))
|
|
110
245
|
return Object.freeze(added);
|
|
111
246
|
let list;
|
|
112
247
|
try {
|
|
@@ -116,13 +251,18 @@ export function createPromptDeliveryLedger() {
|
|
|
116
251
|
return Object.freeze(added);
|
|
117
252
|
}
|
|
118
253
|
for (const raw of list) {
|
|
119
|
-
const
|
|
120
|
-
if (
|
|
121
|
-
|
|
254
|
+
const read = readRecord(raw);
|
|
255
|
+
if (read === undefined)
|
|
256
|
+
continue;
|
|
257
|
+
droppedKeys += read.dropped;
|
|
258
|
+
add(read.rec, added);
|
|
122
259
|
}
|
|
123
260
|
return Object.freeze(added);
|
|
124
261
|
},
|
|
262
|
+
knownUndeliveredMessage: (message) => promptKnownUndelivered(ledger, message),
|
|
263
|
+
withoutUndelivered: (messages) => withoutUndeliveredPrompts(ledger, messages),
|
|
125
264
|
});
|
|
265
|
+
return ledger;
|
|
126
266
|
}
|
|
127
267
|
function uuidOfMessage(m) {
|
|
128
268
|
if (typeof m !== 'object' || m === null)
|
|
@@ -147,7 +287,7 @@ export function promptKnownUndelivered(ledger, message) {
|
|
|
147
287
|
}
|
|
148
288
|
export function withoutUndeliveredPrompts(ledger, messages) {
|
|
149
289
|
assertLedger(ledger, 'withoutUndeliveredPrompts');
|
|
150
|
-
if (!
|
|
290
|
+
if (!isArraySafe(messages))
|
|
151
291
|
return [];
|
|
152
292
|
return messages.filter((m) => !promptKnownUndelivered(ledger, m));
|
|
153
293
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type RunTerminalFailureDetail } from './runTerminal.js';
|
|
2
|
+
import { type ToolHistoryErrorCarrier } from './toolHistoryMismatch.js';
|
|
2
3
|
export interface ProviderRejectionResend {
|
|
3
4
|
resend: 'edit_and_resend' | 'not_by_editing';
|
|
4
5
|
text: string;
|
|
5
6
|
}
|
|
6
|
-
export declare function
|
|
7
|
+
export declare function readProviderRejectionResendKey(v: object | null | undefined): ProviderRejectionResend | undefined;
|
|
8
|
+
export declare function providerRejectionResend(detail: RunTerminalFailureDetail | null | undefined, carrier?: ToolHistoryErrorCarrier | null): ProviderRejectionResend | undefined;
|