@sema-agent/client-core 0.89.0 → 0.90.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +18 -13
  3. package/dist/adapter/runStream.d.ts +2 -0
  4. package/dist/adapter/runStream.js +157 -32
  5. package/dist/attachmentsWireCaps.d.ts +13 -2
  6. package/dist/attachmentsWireCaps.js +25 -5
  7. package/dist/coreValuePorts.d.ts +1 -0
  8. package/dist/engineCapReader.js +12 -1
  9. package/dist/engineNoticeCodes.d.ts +2 -9
  10. package/dist/engineNoticeCodes.js +19 -27
  11. package/dist/fileHistoryLegNotice.d.ts +20 -0
  12. package/dist/fileHistoryLegNotice.js +122 -0
  13. package/dist/gateOutcome.d.ts +1 -1
  14. package/dist/gateOutcome.js +1 -0
  15. package/dist/generated/engineFactTables.d.ts +1 -0
  16. package/dist/generated/engineFactTables.js +3 -0
  17. package/dist/generated/engineNoticeTables.js +0 -3
  18. package/dist/hookToolAliases.d.ts +3 -0
  19. package/dist/hookToolAliases.js +10 -0
  20. package/dist/index.d.ts +8 -0
  21. package/dist/index.js +5 -0
  22. package/dist/permissionsAllowGrantCapability.d.ts +17 -0
  23. package/dist/permissionsAllowGrantCapability.js +75 -0
  24. package/dist/permissionsDeclaration.d.ts +25 -0
  25. package/dist/permissionsDeclaration.js +227 -0
  26. package/dist/promptDelivery.d.ts +5 -2
  27. package/dist/promptDelivery.js +8 -3
  28. package/dist/providerRejection.d.ts +2 -1
  29. package/dist/providerRejection.js +7 -1
  30. package/dist/request/taskRequest.js +27 -1
  31. package/dist/sdkRegistryTransit.d.ts +1 -0
  32. package/dist/sdkRegistryTransit.js +1 -0
  33. package/dist/supportFloor.d.ts +10 -0
  34. package/dist/supportFloor.js +42 -0
  35. package/dist/taskRequestWords.d.ts +1 -0
  36. package/dist/taskRequestWords.js +14 -0
  37. package/docs/INTEGRATION-CLIENTS.md +503 -13
  38. package/package.json +2 -2
@@ -28,7 +28,6 @@ export const CORE_ENGINE_NOTICE_CODES = Object.freeze([
28
28
  "permission.ask_released_by_mode",
29
29
  "permission.read_deny_unapplied",
30
30
  "permission.rule_store_unavailable",
31
- "resume.legacy_mode_seed_migrated",
32
31
  "peer.inbound_disposition",
33
32
  "peer.held_settled",
34
33
  "peer.idle_subscription",
@@ -116,7 +115,6 @@ export const CORE_NOTICE_AUDIENCE = Object.freeze({
116
115
  "permission.ask_released_by_mode": "user",
117
116
  "permission.read_deny_unapplied": "user",
118
117
  "permission.rule_store_unavailable": "user",
119
- "resume.legacy_mode_seed_migrated": "operator",
120
118
  "peer.inbound_disposition": "user",
121
119
  "peer.held_settled": "user",
122
120
  "peer.idle_subscription": "user",
@@ -210,7 +208,6 @@ export const CORE_NOTICE_MINTER = Object.freeze({
210
208
  "permission.ask_released_by_mode": "engine",
211
209
  "permission.read_deny_unapplied": "engine",
212
210
  "permission.rule_store_unavailable": "engine",
213
- "resume.legacy_mode_seed_migrated": "engine",
214
211
  "peer.inbound_disposition": "engine",
215
212
  "peer.held_settled": "engine",
216
213
  "peer.idle_subscription": "engine",
@@ -0,0 +1,3 @@
1
+ export declare const HOOK_TOOL_ALIASES: Readonly<Record<string, string>>;
2
+ export declare function hookToolAliasesOf(): Readonly<Record<string, string>>;
3
+ export declare function canonicalHookToolName(name: string): string;
@@ -0,0 +1,10 @@
1
+ import { CORE_HOOK_TOOL_ALIASES } from './generated/engineFactTables.js';
2
+ export const HOOK_TOOL_ALIASES = CORE_HOOK_TOOL_ALIASES;
3
+ export function hookToolAliasesOf() {
4
+ return HOOK_TOOL_ALIASES;
5
+ }
6
+ export function canonicalHookToolName(name) {
7
+ if (typeof name !== 'string')
8
+ return name;
9
+ return Object.hasOwn(HOOK_TOOL_ALIASES, name) ? (HOOK_TOOL_ALIASES[name] ?? name) : name;
10
+ }
package/dist/index.d.ts CHANGED
@@ -31,6 +31,7 @@ export * from './executionLaneCapability.js';
31
31
  export * from './approvalsStreamLiveCapability.js';
32
32
  export * from './peerLaneCapability.js';
33
33
  export * from './permissionRulesWriteCapability.js';
34
+ export * from './permissionsAllowGrantCapability.js';
34
35
  export * from './sessionPolicyCapability.js';
35
36
  export * from './memoryComplianceCapability.js';
36
37
  export * from './memoryOriginCapability.js';
@@ -90,6 +91,8 @@ export * from './oneShotWireCaps.js';
90
91
  export * from './rewindWireCaps.js';
91
92
  export * from './rewindArchiveCapability.js';
92
93
  export * from './fileHistoryCaptureCapability.js';
94
+ export { FILE_HISTORY_LEG_VERDICTS, fileHistoryLegNoticeVerdict, fileHistoryLegNoticeSentence, fileHistoryDeclarationOfBody, createFileHistoryDeclarationLedger, } from './fileHistoryLegNotice.js';
95
+ export type { FileHistoryDeclarationFact, FileHistoryLegNoticeVerdict, FileHistoryLegNoticeInput, FileHistoryDeclarationLedger } from './fileHistoryLegNotice.js';
93
96
  export * from './sealedKeyCapability.js';
94
97
  export * from './storePostureCapability.js';
95
98
  export * from './centerWiringCapability.js';
@@ -114,6 +117,8 @@ export * from './adapter/activeRunSelfHeal.js';
114
117
  export * from './request/taskRequest.js';
115
118
  export { disableSkillsDeclaration } from './disableSkillsWire.js';
116
119
  export type { DisableSkillsDeclaration } from './disableSkillsWire.js';
120
+ export { permissionsDeclaration } from './permissionsDeclaration.js';
121
+ export type { PermissionsDeclaration, PermissionsDeclarationBuckets, PermissionsDeclarationInput, WireRuleOrigin, WireRuleOrigins } from './permissionsDeclaration.js';
117
122
  export * from './clientSlice.js';
118
123
  export * from './host.js';
119
124
  export { DEFAULT_SESSION_KEY } from './sessionSlot.js';
@@ -156,6 +161,8 @@ export type { PromptDeliveryLedger, PromptDeliveryRecord } from './promptDeliver
156
161
  export * from './resumeRefusalCopy.js';
157
162
  export * from './wireRefusalCopy.js';
158
163
  export * from './inheritEnvWire.js';
164
+ export { SUPPORT_FLOOR, supportFloorOf, engineVersionTier } from './supportFloor.js';
165
+ export type { SupportFloor, SupportFloorComponent, EngineVersionTier } from './supportFloor.js';
159
166
  export * from './decideReceipt.js';
160
167
  export * from './sessionMap.js';
161
168
  export * from './detachWire.js';
@@ -163,6 +170,7 @@ export * from './workflowMonitor.js';
163
170
  export * from './workflowClient.js';
164
171
  export * from './hooksWireCaps.js';
165
172
  export { ENGINE_FIRED_HOOK_EVENTS, PLUGIN_HOOK_DISPOSITIONS, PLUGIN_HOOK_REASONS, PLUGIN_HOOK_EXCLUSION_REASONS, SETTINGS_HOOK_DROP_REASONS, isEngineFiredHookEvent, pluginHookVerdictOf, pluginHookReasonText, settingsHookDropText, pluginHookExecutorLabel, hooksNotRunNotice, settingsHooksKeptLocally, type EngineFiredHookEvent, type PluginHookDisposition, type PluginHookReason, type PluginHookExclusionReason, type SettingsHookDropReason, type HookSettingsSource, type PluginHookKey, type PluginHookVerdict, type PluginHookExclusion, type SettingsHookDrop, type SettingsHookVerdict, type SettingsHookExclusion, type HooksWireEngineFacts, type PluginHookReaderState, type HooksWirePlan, type HooksWirePlanOptions, } from './pluginHooksWire.js';
173
+ export { hookToolAliasesOf, canonicalHookToolName } from './hookToolAliases.js';
166
174
  export * from './liveInitToolFace.js';
167
175
  export * from './printInitToolFace.js';
168
176
  export * from './model/providerPresets.js';
package/dist/index.js CHANGED
@@ -26,6 +26,7 @@ export * from './executionLaneCapability.js';
26
26
  export * from './approvalsStreamLiveCapability.js';
27
27
  export * from './peerLaneCapability.js';
28
28
  export * from './permissionRulesWriteCapability.js';
29
+ export * from './permissionsAllowGrantCapability.js';
29
30
  export * from './sessionPolicyCapability.js';
30
31
  export * from './memoryComplianceCapability.js';
31
32
  export * from './memoryOriginCapability.js';
@@ -82,6 +83,7 @@ export * from './oneShotWireCaps.js';
82
83
  export * from './rewindWireCaps.js';
83
84
  export * from './rewindArchiveCapability.js';
84
85
  export * from './fileHistoryCaptureCapability.js';
86
+ export { FILE_HISTORY_LEG_VERDICTS, fileHistoryLegNoticeVerdict, fileHistoryLegNoticeSentence, fileHistoryDeclarationOfBody, createFileHistoryDeclarationLedger, } from './fileHistoryLegNotice.js';
85
87
  export * from './sealedKeyCapability.js';
86
88
  export * from './storePostureCapability.js';
87
89
  export * from './centerWiringCapability.js';
@@ -104,6 +106,7 @@ export * from './adapter/runStream.js';
104
106
  export * from './adapter/activeRunSelfHeal.js';
105
107
  export * from './request/taskRequest.js';
106
108
  export { disableSkillsDeclaration } from './disableSkillsWire.js';
109
+ export { permissionsDeclaration } from './permissionsDeclaration.js';
107
110
  export * from './clientSlice.js';
108
111
  export * from './host.js';
109
112
  export { DEFAULT_SESSION_KEY } from './sessionSlot.js';
@@ -141,6 +144,7 @@ export { createPromptDeliveryLedger, promptKnownUndelivered, withoutUndeliveredP
141
144
  export * from './resumeRefusalCopy.js';
142
145
  export * from './wireRefusalCopy.js';
143
146
  export * from './inheritEnvWire.js';
147
+ export { SUPPORT_FLOOR, supportFloorOf, engineVersionTier } from './supportFloor.js';
144
148
  export * from './decideReceipt.js';
145
149
  export * from './sessionMap.js';
146
150
  export * from './detachWire.js';
@@ -148,6 +152,7 @@ export * from './workflowMonitor.js';
148
152
  export * from './workflowClient.js';
149
153
  export * from './hooksWireCaps.js';
150
154
  export { ENGINE_FIRED_HOOK_EVENTS, PLUGIN_HOOK_DISPOSITIONS, PLUGIN_HOOK_REASONS, PLUGIN_HOOK_EXCLUSION_REASONS, SETTINGS_HOOK_DROP_REASONS, isEngineFiredHookEvent, pluginHookVerdictOf, pluginHookReasonText, settingsHookDropText, pluginHookExecutorLabel, hooksNotRunNotice, settingsHooksKeptLocally, } from './pluginHooksWire.js';
155
+ export { hookToolAliasesOf, canonicalHookToolName } from './hookToolAliases.js';
151
156
  export * from './liveInitToolFace.js';
152
157
  export * from './printInitToolFace.js';
153
158
  export * from './model/providerPresets.js';
@@ -0,0 +1,17 @@
1
+ export type PermissionsAllowGrantReading = {
2
+ kind: 'unobserved';
3
+ } | {
4
+ kind: 'not_reported';
5
+ } | {
6
+ kind: 'present';
7
+ grants: boolean;
8
+ };
9
+ export declare function projectPermissionsAllowGrantCapability(caps: unknown): PermissionsAllowGrantReading | undefined;
10
+ export declare function noteEngineCapsForPermissionsAllowGrant(baseUrl: string, caps: unknown, opts?: {
11
+ generation?: number;
12
+ }): void;
13
+ export declare function observedPermissionsAllowGrant(baseUrl?: string | undefined): PermissionsAllowGrantReading;
14
+ export declare function permissionsAllowGrantAvailable(reading: PermissionsAllowGrantReading): boolean;
15
+ export declare function permissionsAllowGrantDoctorDetail(reading: PermissionsAllowGrantReading): string;
16
+ export declare function forgetPermissionsAllowGrantReading(baseUrl: string | undefined): void;
17
+ export declare function __resetPermissionsAllowGrantReadingsForTests(): void;
@@ -0,0 +1,75 @@
1
+ import { engineWireTarget } from './engineWireTarget.js';
2
+ import { createEngineCapReader } from './engineCapReader.js';
3
+ const ALLOW_GRANT_CAP_PATH = ['settings', 'permissions', 'allowGrant'];
4
+ export function projectPermissionsAllowGrantCapability(caps) {
5
+ if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
6
+ return undefined;
7
+ let node = caps;
8
+ for (let i = 0; i < ALLOW_GRANT_CAP_PATH.length; i++) {
9
+ const seg = ALLOW_GRANT_CAP_PATH[i];
10
+ if (!Object.hasOwn(node, seg))
11
+ return { kind: 'not_reported' };
12
+ const v = node[seg];
13
+ if (v === undefined)
14
+ return { kind: 'not_reported' };
15
+ if (i === ALLOW_GRANT_CAP_PATH.length - 1) {
16
+ return typeof v === 'boolean' ? { kind: 'present', grants: v } : undefined;
17
+ }
18
+ if (v === null || typeof v !== 'object' || Array.isArray(v))
19
+ return undefined;
20
+ node = v;
21
+ }
22
+ return undefined;
23
+ }
24
+ const reader = createEngineCapReader({
25
+ name: ALLOW_GRANT_CAP_PATH.join('.'),
26
+ project: projectPermissionsAllowGrantCapability,
27
+ makeUnobserved: () => ({ kind: 'unobserved' }),
28
+ });
29
+ export function noteEngineCapsForPermissionsAllowGrant(baseUrl, caps, opts) {
30
+ reader.note(baseUrl, caps, opts);
31
+ }
32
+ export function observedPermissionsAllowGrant(baseUrl = engineWireTarget()?.baseUrl) {
33
+ return reader.observed(baseUrl);
34
+ }
35
+ export function permissionsAllowGrantAvailable(reading) {
36
+ try {
37
+ if (reading === null || typeof reading !== 'object' || Array.isArray(reading))
38
+ return false;
39
+ if (!Object.hasOwn(reading, 'kind') || !Object.hasOwn(reading, 'grants'))
40
+ return false;
41
+ return reading.kind === 'present' && reading.grants === true;
42
+ }
43
+ catch {
44
+ return false;
45
+ }
46
+ }
47
+ function ownReadingField(reading, key) {
48
+ try {
49
+ if (reading === null || typeof reading !== 'object' || Array.isArray(reading))
50
+ return undefined;
51
+ return Object.hasOwn(reading, key) ? reading[key] : undefined;
52
+ }
53
+ catch {
54
+ return undefined;
55
+ }
56
+ }
57
+ export function permissionsAllowGrantDoctorDetail(reading) {
58
+ if (permissionsAllowGrantAvailable(reading)) {
59
+ 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
+ }
61
+ const kind = ownReadingField(reading, 'kind');
62
+ if (kind === 'present' && ownReadingField(reading, 'grants') === false) {
63
+ 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';
64
+ }
65
+ if (kind === 'not_reported') {
66
+ return 'allow rules kept on this side — this engine does not say how it reads allow rules; sending them to an engine that reads them as an exclusive list would block every tool not on it, so they are not sent and matching approvals are still answered here';
67
+ }
68
+ return 'allow rules kept on this side — no usable capabilities reading is cached for this engine (none received, or the last one was unreadable), so allow rules are not sent and matching approvals are still answered here';
69
+ }
70
+ export function forgetPermissionsAllowGrantReading(baseUrl) {
71
+ reader.forget(baseUrl);
72
+ }
73
+ export function __resetPermissionsAllowGrantReadingsForTests() {
74
+ reader.__resetForTests();
75
+ }
@@ -0,0 +1,25 @@
1
+ import { type PermissionsAllowGrantReading } from './permissionsAllowGrantCapability.js';
2
+ declare const RULE_TRUST_ORDER: readonly ["policySettings", "cliArg", "flagSettings", "session", "userSettings", "localSettings", "projectSettings"];
3
+ export type WireRuleOrigin = (typeof RULE_TRUST_ORDER)[number];
4
+ export interface WireRuleOrigins {
5
+ readonly deny?: ReadonlyMap<string, WireRuleOrigin>;
6
+ readonly ask?: ReadonlyMap<string, WireRuleOrigin>;
7
+ readonly allow?: ReadonlyMap<string, WireRuleOrigin>;
8
+ }
9
+ export interface PermissionsDeclarationInput {
10
+ readonly deny: readonly string[];
11
+ readonly ask: readonly string[];
12
+ readonly allow: readonly string[];
13
+ readonly origins?: WireRuleOrigins;
14
+ readonly cliDeny?: readonly string[];
15
+ }
16
+ export interface PermissionsDeclarationBuckets {
17
+ deny?: string[];
18
+ ask?: string[];
19
+ allow?: string[];
20
+ }
21
+ export interface PermissionsDeclaration {
22
+ permissions?: PermissionsDeclarationBuckets;
23
+ }
24
+ export declare function permissionsDeclaration(input: PermissionsDeclarationInput, caps: PermissionsAllowGrantReading): PermissionsDeclaration;
25
+ export {};
@@ -0,0 +1,227 @@
1
+ import { hasCoreValuePort, protocolOf, retiredToolNames, ruleToolGrammarOf, validatePermissionRules } from './coreValuePorts.js';
2
+ import { permissionsAllowGrantAvailable } from './permissionsAllowGrantCapability.js';
3
+ const RULE_TRUST_ORDER = [
4
+ 'policySettings',
5
+ 'cliArg',
6
+ 'flagSettings',
7
+ 'session',
8
+ 'userSettings',
9
+ 'localSettings',
10
+ 'projectSettings',
11
+ ];
12
+ function normalizeLegacyToolName(name) {
13
+ switch (name) {
14
+ case 'Task':
15
+ return 'Agent';
16
+ case 'KillShell':
17
+ return 'TaskStop';
18
+ case 'AgentOutputTool':
19
+ case 'BashOutputTool':
20
+ return 'TaskOutput';
21
+ default:
22
+ return name;
23
+ }
24
+ }
25
+ function unescapedIndex(str, char, fromEnd) {
26
+ for (let k = 0; k < str.length; k++) {
27
+ const i = fromEnd ? str.length - 1 - k : k;
28
+ if (str[i] !== char)
29
+ continue;
30
+ let backslashes = 0;
31
+ for (let j = i - 1; j >= 0 && str[j] === '\\'; j--)
32
+ backslashes++;
33
+ if (backslashes % 2 === 0)
34
+ return i;
35
+ }
36
+ return -1;
37
+ }
38
+ function unescapeRuleContent(content) {
39
+ return content.replace(/\\\(/g, '(').replace(/\\\)/g, ')').replace(/\\\\/g, '\\');
40
+ }
41
+ function parseRule(rule) {
42
+ const open = unescapedIndex(rule, '(', false);
43
+ if (open === -1)
44
+ return { toolName: normalizeLegacyToolName(rule) };
45
+ const close = unescapedIndex(rule, ')', true);
46
+ if (close === -1 || close <= open || close !== rule.length - 1)
47
+ return { toolName: normalizeLegacyToolName(rule) };
48
+ const toolName = rule.substring(0, open);
49
+ const rawContent = rule.substring(open + 1, close);
50
+ if (!toolName)
51
+ return { toolName: normalizeLegacyToolName(rule) };
52
+ if (rawContent === '' || rawContent === '*')
53
+ return { toolName: normalizeLegacyToolName(toolName) };
54
+ return { toolName: normalizeLegacyToolName(toolName), ruleContent: unescapeRuleContent(rawContent) };
55
+ }
56
+ function requireRulePorts() {
57
+ const required = ['MCP_NAMESPACE', 'RETIRED_TOOL_NAMES', 'protocolOf', 'ruleToolGrammarOf', 'validatePermissionRules'];
58
+ const missing = required.filter((p) => !hasCoreValuePort(p));
59
+ if (missing.length === 0)
60
+ return;
61
+ throw new Error(`permissionsDeclaration: 规则判据端口缺席(${missing.join(' / ')})—— 先用 installCoreValuePorts 装上 core 的同名实现。` +
62
+ '缺席不等于全部合格:不判就发会让整份请求被引擎拒收,不判就删会静默丢掉 deny 规则,所以这里拒绝装配。');
63
+ }
64
+ function engineWireSafe(name) {
65
+ if (name.includes('('))
66
+ return false;
67
+ if (retiredToolNames().has(name))
68
+ return false;
69
+ const ns = protocolOf(name);
70
+ if (ns === undefined)
71
+ return !name.includes('__');
72
+ return ns.parse(name) !== undefined;
73
+ }
74
+ function wholeServerRuleName(name) {
75
+ const read = name.trim();
76
+ const ns = protocolOf(read);
77
+ if (ns === undefined || !read.startsWith(ns.prefix))
78
+ return name;
79
+ const peer = read.slice(ns.prefix.length);
80
+ return peer !== '' && !peer.includes('__') && !peer.endsWith('_') ? `${read}__*` : name;
81
+ }
82
+ const RULE_ROOT = '/';
83
+ function compileVerdict(rule, behavior) {
84
+ try {
85
+ const issues = validatePermissionRules([{ rule, behavior }], { pathBases: { root: RULE_ROOT } });
86
+ if (!Array.isArray(issues))
87
+ return 'unknown';
88
+ return issues.length === 0 ? 'compiles' : 'refused';
89
+ }
90
+ catch {
91
+ return 'unknown';
92
+ }
93
+ }
94
+ function wireSubset(rules, behavior) {
95
+ const out = new Set();
96
+ let portsChecked = false;
97
+ for (const rule of rules) {
98
+ if (typeof rule !== 'string' || rule === '')
99
+ continue;
100
+ if (!portsChecked) {
101
+ requireRulePorts();
102
+ portsChecked = true;
103
+ }
104
+ const parsed = parseRule(rule);
105
+ if (parsed.ruleContent === undefined) {
106
+ const name = wholeServerRuleName(parsed.toolName);
107
+ if (!engineWireSafe(name))
108
+ continue;
109
+ if (out.has(name))
110
+ continue;
111
+ const verdict = compileVerdict(name, behavior);
112
+ if (verdict === 'refused' || (verdict === 'unknown' && behavior === 'allow'))
113
+ continue;
114
+ out.add(name);
115
+ continue;
116
+ }
117
+ if (!engineWireSafe(parsed.toolName))
118
+ continue;
119
+ if (ruleToolGrammarOf(parsed.toolName) === 'path' && parsed.ruleContent.startsWith('~/'))
120
+ continue;
121
+ const verdict = compileVerdict(rule, behavior);
122
+ if (verdict === 'compiles' || (verdict === 'unknown' && behavior !== 'allow'))
123
+ out.add(rule);
124
+ }
125
+ return [...out];
126
+ }
127
+ function projectBucketByTrust(raw, behavior, originOf, cliRules) {
128
+ const present = new Set(raw);
129
+ const groups = RULE_TRUST_ORDER.map(() => []);
130
+ const rankOf = (origin) => RULE_TRUST_ORDER.indexOf(origin);
131
+ if (originOf !== undefined) {
132
+ for (const [r, origin] of originOf) {
133
+ const rank = rankOf(origin);
134
+ if (rank >= 0 && typeof r === 'string' && present.has(r))
135
+ groups[rank].push(r);
136
+ }
137
+ }
138
+ const unknownOrigin = [...present].filter((r) => originOf === undefined || rankOf(originOf.get(r)) < 0);
139
+ const out = new Set();
140
+ const append = (list) => {
141
+ for (const r of list)
142
+ out.add(r);
143
+ };
144
+ RULE_TRUST_ORDER.forEach((origin, rank) => {
145
+ if (origin === 'cliArg')
146
+ append(cliRules);
147
+ append(wireSubset(groups[rank], behavior));
148
+ });
149
+ append(wireSubset(unknownOrigin, behavior));
150
+ return [...out];
151
+ }
152
+ const WHO = 'permissionsDeclaration';
153
+ function ownField(obj, key, where) {
154
+ if (Object.hasOwn(obj, key))
155
+ return obj[key];
156
+ if (key in obj)
157
+ throw new TypeError(`${WHO}: ${where}${key} 只在原型链上,不是自有字段 —— 拒绝读取(请交普通对象)。`);
158
+ return undefined;
159
+ }
160
+ function ruleList(v, key) {
161
+ if (v === undefined || v === null)
162
+ return undefined;
163
+ if (!Array.isArray(v))
164
+ throw new TypeError(`${WHO}: ${key} 必须是规则串数组或缺席 —— 收到 typeof=${typeof v}。`);
165
+ const out = [];
166
+ const n = v.length;
167
+ for (let i = 0; i < n; i++) {
168
+ if (!Object.hasOwn(v, i)) {
169
+ if (i in v)
170
+ throw new TypeError(`${WHO}: ${key}[${i}] 是空洞,而原型链上在这个下标有值 —— 拒绝读取(请交不带空洞的普通数组)。`);
171
+ continue;
172
+ }
173
+ const r = v[i];
174
+ if (typeof r === 'string')
175
+ out.push(r);
176
+ }
177
+ return out;
178
+ }
179
+ function originMap(v, key) {
180
+ if (v === undefined || v === null)
181
+ return undefined;
182
+ const ok = typeof v === 'object' &&
183
+ typeof v.get === 'function' &&
184
+ typeof v[Symbol.iterator] === 'function';
185
+ if (!ok)
186
+ throw new TypeError(`${WHO}: origins.${key} 必须是 Map(规则原串 → 来源)或缺席 —— 收到 typeof=${typeof v}。`);
187
+ const snap = new Map();
188
+ for (const entry of v) {
189
+ const [r, origin] = entry;
190
+ if (!snap.has(r))
191
+ snap.set(r, origin);
192
+ }
193
+ return snap;
194
+ }
195
+ export function permissionsDeclaration(input, caps) {
196
+ if (input === null || typeof input !== 'object' || Array.isArray(input)) {
197
+ throw new TypeError(`${WHO}: 入参必须是对象 —— 收到 ${input === null ? 'null' : Array.isArray(input) ? 'array' : `typeof=${typeof input}`}。`);
198
+ }
199
+ const deny = ruleList(ownField(input, 'deny', ''), 'deny');
200
+ const ask = ruleList(ownField(input, 'ask', ''), 'ask');
201
+ const allow = ruleList(ownField(input, 'allow', ''), 'allow');
202
+ const cliDeny = ruleList(ownField(input, 'cliDeny', ''), 'cliDeny');
203
+ const originsRaw = ownField(input, 'origins', '');
204
+ let origins = {};
205
+ if (originsRaw !== undefined && originsRaw !== null) {
206
+ if (typeof originsRaw !== 'object' || Array.isArray(originsRaw)) {
207
+ throw new TypeError(`${WHO}: origins 必须是 { deny?, ask?, allow? } 对象或缺席 —— 收到 typeof=${typeof originsRaw}。`);
208
+ }
209
+ const od = originMap(ownField(originsRaw, 'deny', 'origins.'), 'deny');
210
+ const oa = originMap(ownField(originsRaw, 'ask', 'origins.'), 'ask');
211
+ const ol = originMap(ownField(originsRaw, 'allow', 'origins.'), 'allow');
212
+ origins = { ...(od ? { deny: od } : {}), ...(oa ? { ask: oa } : {}), ...(ol ? { allow: ol } : {}) };
213
+ }
214
+ const allowOn = permissionsAllowGrantAvailable(caps);
215
+ const cliDenyWire = cliDeny === undefined ? [] : wireSubset(cliDeny, 'deny');
216
+ const denyOut = projectBucketByTrust(deny ?? [], 'deny', origins.deny, cliDenyWire);
217
+ const askOut = projectBucketByTrust(ask ?? [], 'ask', origins.ask, []);
218
+ const allowOut = allowOn && allow !== undefined ? projectBucketByTrust(allow, 'allow', origins.allow, []) : [];
219
+ const permissions = {};
220
+ if (denyOut.length > 0)
221
+ permissions.deny = denyOut;
222
+ if (askOut.length > 0)
223
+ permissions.ask = askOut;
224
+ if (allowOut.length > 0)
225
+ permissions.allow = allowOut;
226
+ return Object.keys(permissions).length > 0 ? { permissions } : {};
227
+ }
@@ -14,7 +14,10 @@ export interface PromptDeliveryLedger {
14
14
  batchOf(uuid: string): readonly string[] | undefined;
15
15
  toRecords(): readonly PromptDeliveryRecord[];
16
16
  fromRecords(records: readonly PromptDeliveryRecord[]): readonly PromptDeliveryRecord[];
17
+ knownUndeliveredMessage(message: object | null | undefined): boolean;
18
+ withoutUndelivered<T>(messages: readonly T[]): T[];
17
19
  }
20
+ export declare function frameProvesDelivery(event: object | null | undefined): boolean;
18
21
  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[];
22
+ export declare function promptKnownUndelivered(ledger: Pick<PromptDeliveryLedger, 'knownUndelivered'>, message: object | null | undefined): boolean;
23
+ export declare function withoutUndeliveredPrompts<T>(ledger: Pick<PromptDeliveryLedger, 'knownUndelivered'>, messages: readonly T[]): T[];
@@ -32,10 +32,12 @@ function readRecord(rec) {
32
32
  }
33
33
  }
34
34
  const PRE_RUN_FRAME_TYPES = new Set(['meta', 'approval_request', 'approval_revoke']);
35
- function frameProvesDelivery(event) {
36
- if (typeof event !== 'object' || event === null || Array.isArray(event))
35
+ export function frameProvesDelivery(event) {
36
+ if (typeof event !== 'object' || event === null)
37
37
  return false;
38
38
  try {
39
+ if (Array.isArray(event))
40
+ return false;
39
41
  const type = event.type;
40
42
  if (typeof type !== 'string' || type.length === 0 || PRE_RUN_FRAME_TYPES.has(type))
41
43
  return false;
@@ -83,7 +85,7 @@ export function createPromptDeliveryLedger() {
83
85
  add(u, 'd', added);
84
86
  return Object.freeze(added);
85
87
  };
86
- return Object.freeze({
88
+ const ledger = Object.freeze({
87
89
  attempted(batchUuids) {
88
90
  const batch = Object.freeze(cleanBatch(batchUuids));
89
91
  const added = [];
@@ -122,7 +124,10 @@ export function createPromptDeliveryLedger() {
122
124
  }
123
125
  return Object.freeze(added);
124
126
  },
127
+ knownUndeliveredMessage: (message) => promptKnownUndelivered(ledger, message),
128
+ withoutUndelivered: (messages) => withoutUndeliveredPrompts(ledger, messages),
125
129
  });
130
+ return ledger;
126
131
  }
127
132
  function uuidOfMessage(m) {
128
133
  if (typeof m !== 'object' || m === null)
@@ -1,6 +1,7 @@
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 providerRejectionResend(detail: RunTerminalFailureDetail | null | undefined): ProviderRejectionResend | undefined;
7
+ export declare function providerRejectionResend(detail: RunTerminalFailureDetail | null | undefined, carrier?: ToolHistoryErrorCarrier | null): ProviderRejectionResend | undefined;
@@ -1,4 +1,5 @@
1
1
  import { readRunTerminal } from './runTerminal.js';
2
+ import { classifyToolHistoryMismatch } from './toolHistoryMismatch.js';
2
3
  const PROVIDER_STATUS_TIERS = Object.freeze([
3
4
  Object.freeze([400, 400, 'invalid_request']),
4
5
  Object.freeze([401, 401, 'authentication_failed']),
@@ -24,8 +25,11 @@ const NOT_BY_EDITING_TAIL = new Map([
24
25
  ['server_error', ': the provider reported an internal error — try again later.'],
25
26
  [UNTIERED, ' for a reason that editing it will not fix — check the provider account or try again later.'],
26
27
  ]);
28
+ function toolHistoryMismatchCarrier(carrier) {
29
+ return carrier !== undefined && carrier !== null && classifyToolHistoryMismatch(carrier) !== null;
30
+ }
27
31
  const PROVIDER_REJECTED = 'provider_rejected';
28
- export function providerRejectionResend(detail) {
32
+ export function providerRejectionResend(detail, carrier) {
29
33
  let read;
30
34
  try {
31
35
  const r = readRunTerminal({ terminal: { kind: 'failed', detail } });
@@ -39,6 +43,8 @@ export function providerRejectionResend(detail) {
39
43
  const status = Object.hasOwn(read, 'status') ? read.status : undefined;
40
44
  if (status === undefined)
41
45
  return undefined;
46
+ if (toolHistoryMismatchCarrier(carrier))
47
+ return undefined;
42
48
  const tier = providerStatusTier(status);
43
49
  return tier === EDITABLE_TIER
44
50
  ? {
@@ -1,4 +1,6 @@
1
1
  import { refuseBadDisableSkills, refuseDisableSkillsConflict } from '../disableSkillsWire.js';
2
+ import { ATTACHMENTS_SPEC_KEYS, AttachmentsSpecRefused } from '../attachmentsWireCaps.js';
3
+ import { retiredAttachmentsKeyWhy } from '../taskRequestWords.js';
2
4
  export const REQUEST_FIELD_MATRIX = [
3
5
  { field: 'objective', lanes: ['interactive', 'print', 'utility'], live: false, why: '本 turn 的真实输入,每条车道都必需', whyUtilityLane: '恒座:三条车道都必须带上本次提交的模型面输入' },
4
6
  { field: 'sessionId', lanes: ['interactive', 'print', 'utility'], live: false, why: '单命名空间(TP-A Step 2):壳 id 即引擎 session id;print 在非 live 下保留 mock 常量', whyUtilityLane: '恒座:side-channel 提交走**独立**会话 id(壳另铸一个,绝不复用用户会话那一个 —— 复用会把这次提交的转录混进用户会话)' },
@@ -451,6 +453,29 @@ const refuseBadMemoryOff = (v) => {
451
453
  '`"on"`,`{ enabled: false }` 对象形也不是 wire 拼法)且对坏拼写**响亮拒**(server 400 `request.field_invalid`)。' +
452
454
  '本层若静默删键,这一次运行会照常挂上记忆面而声明人不会知道 —— 与 memoryCapture 同一条纪律。错误文本只报形状,不回显值。');
453
455
  };
456
+ const withoutRetiredAttachmentsKeys = (v) => {
457
+ if (typeof v !== 'object' || v === null)
458
+ return v;
459
+ let drop;
460
+ for (const key of Object.keys(v)) {
461
+ const why = retiredAttachmentsKeyWhy(key, 'key');
462
+ if (why === undefined)
463
+ continue;
464
+ const d = Object.getOwnPropertyDescriptor(v, key);
465
+ if (d !== undefined && Object.prototype.hasOwnProperty.call(d, 'value') && d.value === undefined) {
466
+ ;
467
+ (drop ??= []).push(key);
468
+ continue;
469
+ }
470
+ throw new AttachmentsSpecRefused(`buildTaskRequest: refusing attachments key ${JSON.stringify(key)} — ${why}`, { token: key, why, allowed: ATTACHMENTS_SPEC_KEYS, key });
471
+ }
472
+ if (drop === undefined)
473
+ return v;
474
+ const copy = { ...v };
475
+ for (const key of drop)
476
+ delete copy[key];
477
+ return copy;
478
+ };
454
479
  const refuseBadExcludeAllTools = (v) => {
455
480
  if (v === undefined || v === null || v === true || v === false)
456
481
  return;
@@ -784,6 +809,7 @@ export function assembleTaskRequest(input, lane) {
784
809
  ...(onSetting('hooks') ? { hooks: s.hooks } : {}),
785
810
  ...(s.outputStyle !== undefined ? { outputStyle: s.outputStyle } : {}),
786
811
  };
812
+ const attachmentsForWire = on('attachments') ? withoutRetiredAttachmentsKeys(i.attachments) : undefined;
787
813
  const out = {
788
814
  objective: i.objective,
789
815
  sessionId: i.sessionId,
@@ -802,7 +828,7 @@ export function assembleTaskRequest(input, lane) {
802
828
  ...(on('retainBackgroundProcesses') ? { retainBackgroundProcesses: i.retainBackgroundProcesses } : {}),
803
829
  ...(on('promptProfile') ? { promptProfile: i.promptProfile } : {}),
804
830
  ...(on('enableFork') ? { enableFork: i.enableFork } : {}),
805
- ...(on('attachments') ? { attachments: i.attachments } : {}),
831
+ ...(on('attachments') ? { attachments: attachmentsForWire } : {}),
806
832
  ...(on('repo') ? { repo: i.repo } : {}),
807
833
  ...(on('council') ? { council: true } : {}),
808
834
  ...(on('debate') ? { debate: true } : {}),
@@ -2,5 +2,6 @@ export { RegistryClient } from '@sema-agent/sdk/registry';
2
2
  export { getMeConfig, getScopeConfigDraft, getEffective } from '@sema-agent/sdk/registry';
3
3
  export { probeRegistryHealth, postFeedback } from '@sema-agent/sdk/registry';
4
4
  export { REGISTRY_AUTH_PATHS, skillContentAddress } from '@sema-agent/sdk/registry';
5
+ export { requestDeviceCode, pollUntilApproved, refreshTokens, listScopes, switchScope } from '@sema-agent/sdk/registry';
5
6
  export { RegistryApiError, OAuthFlowError } from '@sema-agent/sdk/registry';
6
7
  export type { RegistryTokens, TokenProvider, } from '@sema-agent/sdk/registry';
@@ -2,4 +2,5 @@ export { RegistryClient } from '@sema-agent/sdk/registry';
2
2
  export { getMeConfig, getScopeConfigDraft, getEffective } from '@sema-agent/sdk/registry';
3
3
  export { probeRegistryHealth, postFeedback } from '@sema-agent/sdk/registry';
4
4
  export { REGISTRY_AUTH_PATHS, skillContentAddress } from '@sema-agent/sdk/registry';
5
+ export { requestDeviceCode, pollUntilApproved, refreshTokens, listScopes, switchScope } from '@sema-agent/sdk/registry';
5
6
  export { RegistryApiError, OAuthFlowError } from '@sema-agent/sdk/registry';
@@ -0,0 +1,10 @@
1
+ export type SupportFloorComponent = 'server' | 'core' | 'sdk';
2
+ export interface SupportFloor {
3
+ readonly server: string;
4
+ readonly core: string;
5
+ readonly sdk: string;
6
+ }
7
+ export declare const SUPPORT_FLOOR: Readonly<SupportFloor>;
8
+ export declare function supportFloorOf(component: unknown): string | undefined;
9
+ export type EngineVersionTier = 'engine' | 'legacy' | 'unknown';
10
+ export declare function engineVersionTier(version: unknown, floor?: string): EngineVersionTier;