@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.
- package/CHANGELOG.md +104 -0
- package/README.md +18 -13
- package/dist/adapter/runStream.d.ts +2 -0
- package/dist/adapter/runStream.js +157 -32
- package/dist/attachmentsWireCaps.d.ts +13 -2
- package/dist/attachmentsWireCaps.js +25 -5
- package/dist/coreValuePorts.d.ts +1 -0
- package/dist/engineCapReader.js +12 -1
- 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 +1 -0
- package/dist/generated/engineFactTables.js +3 -0
- package/dist/generated/engineNoticeTables.js +0 -3
- package/dist/hookToolAliases.d.ts +3 -0
- package/dist/hookToolAliases.js +10 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +5 -0
- package/dist/permissionsAllowGrantCapability.d.ts +17 -0
- package/dist/permissionsAllowGrantCapability.js +75 -0
- package/dist/permissionsDeclaration.d.ts +25 -0
- package/dist/permissionsDeclaration.js +227 -0
- package/dist/promptDelivery.d.ts +5 -2
- package/dist/promptDelivery.js +8 -3
- package/dist/providerRejection.d.ts +2 -1
- package/dist/providerRejection.js +7 -1
- package/dist/request/taskRequest.js +27 -1
- package/dist/sdkRegistryTransit.d.ts +1 -0
- package/dist/sdkRegistryTransit.js +1 -0
- package/dist/supportFloor.d.ts +10 -0
- package/dist/supportFloor.js +42 -0
- package/dist/taskRequestWords.d.ts +1 -0
- package/dist/taskRequestWords.js +14 -0
- package/docs/INTEGRATION-CLIENTS.md +503 -13
- 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,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
|
+
}
|
package/dist/promptDelivery.d.ts
CHANGED
|
@@ -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[];
|
package/dist/promptDelivery.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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:
|
|
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;
|