@sema-agent/client-core 0.98.0 → 0.100.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 +376 -0
- package/README.md +61 -35
- package/dist/adapt/arms.js +120 -55
- package/dist/adapt/panelTasks.d.ts +3 -2
- package/dist/adapt/panelTasks.js +34 -11
- package/dist/adapt/textStream.d.ts +1 -1
- package/dist/adapt/textStream.js +51 -28
- package/dist/adapter/activeRunSelfHeal.d.ts +11 -3
- package/dist/adapter/activeRunSelfHeal.js +210 -294
- package/dist/adapter/downstream/eventToSdkMessage.d.ts +25 -6
- package/dist/adapter/downstream/eventToSdkMessage.js +176 -12
- package/dist/adapter/downstream/terminalToSdkResult.js +10 -0
- package/dist/adapter/downstream/wiringManifestView.d.ts +3 -1
- package/dist/adapter/downstream/wiringManifestView.js +1 -0
- package/dist/adapter/runStream.js +82 -40
- package/dist/adapter/types.d.ts +2 -0
- package/dist/adapter/types.js +2 -1
- package/dist/agentSession/engineSessionBackgroundStop.js +16 -8
- package/dist/agentsWireCaps.d.ts +7 -3
- package/dist/agentsWireCaps.js +133 -39
- package/dist/cloudConfigWireCaps.d.ts +10 -1
- package/dist/cloudConfigWireCaps.js +181 -65
- package/dist/compactionStatus.d.ts +34 -0
- package/dist/compactionStatus.js +196 -0
- package/dist/controlRouter.d.ts +4 -2
- package/dist/controlRouter.js +39 -51
- package/dist/coreValuePorts.d.ts +26 -1
- package/dist/coreValuePorts.js +10 -0
- package/dist/corruptSessionCopy.d.ts +2 -1
- package/dist/corruptSessionCopy.js +3 -3
- package/dist/decideFailureNote.js +3 -3
- package/dist/declared/supportFloorDeclared.js +1 -1
- package/dist/detachWire.js +3 -2
- package/dist/devicesFace.d.ts +34 -0
- package/dist/devicesFace.js +90 -0
- package/dist/displayUntrusted.d.ts +1 -0
- package/dist/displayUntrusted.js +153 -5
- package/dist/engineAgentPanelStore.d.ts +14 -4
- package/dist/engineAgentPanelStore.js +14 -0
- package/dist/engineCapReader.d.ts +2 -1
- package/dist/engineCapReader.js +7 -5
- package/dist/engineCapsCache.d.ts +8 -0
- package/dist/engineCapsCache.js +59 -6
- package/dist/engineCapsChange.d.ts +2 -0
- package/dist/engineCapsChange.js +9 -0
- package/dist/engineErrorCodes.d.ts +11 -2
- package/dist/engineErrorCodes.js +11 -12
- package/dist/engineRowRef.js +4 -0
- package/dist/engineSessionMessages.d.ts +1 -0
- package/dist/engineSessionMessages.js +7 -1
- package/dist/engineTargetSeat.d.ts +27 -0
- package/dist/engineTargetSeat.js +199 -0
- package/dist/engineToolDirectory.d.ts +3 -0
- package/dist/engineToolDirectory.js +4 -2
- package/dist/engineWireFor.js +3 -8
- package/dist/env/uiLanguage.d.ts +2 -1
- package/dist/env/uiLanguage.js +3 -2
- package/dist/eventLeg.d.ts +7 -5
- package/dist/eventLeg.js +68 -49
- package/dist/fileHistoryCaptureCapability.d.ts +2 -4
- package/dist/fileHistoryCaptureCapability.js +12 -22
- package/dist/fileHistoryLegNotice.d.ts +2 -1
- package/dist/fileHistoryLegNotice.js +3 -3
- package/dist/frameBinding.d.ts +3 -0
- package/dist/frameBinding.js +12 -0
- package/dist/gateOutcome.js +16 -2
- package/dist/gateVocabulary.d.ts +8 -7
- package/dist/gateVocabulary.js +71 -73
- package/dist/generated/engineFactTables.d.ts +18 -1
- package/dist/generated/engineFactTables.js +56 -0
- package/dist/generated/engineNoticeTables.js +12 -3
- package/dist/generated/settingsSchemaFns.d.ts +18 -0
- package/dist/generated/settingsSchemaFns.js +70 -0
- package/dist/generated/settingsSchemaHooksClassifier.d.ts +2 -0
- package/dist/generated/settingsSchemaHooksClassifier.js +254 -0
- package/dist/generated/settingsSchemaMcpRequestEntry.d.ts +1 -0
- package/dist/generated/settingsSchemaMcpRequestEntry.js +162 -0
- package/dist/generated/settingsSchemaTables.d.ts +2 -0
- package/dist/generated/settingsSchemaTables.js +9 -0
- package/dist/generated/settingsSchemaTypes.d.ts +133 -0
- package/dist/generated/settingsSchemaTypes.js +1 -0
- package/dist/hitl/allowRuleShadow.d.ts +2 -1
- package/dist/hitl/allowRuleShadow.js +18 -15
- package/dist/hitl/approvalCardReasons.d.ts +3 -2
- package/dist/hitl/approvalCardReasons.js +29 -30
- package/dist/hitl/approvalHolds.d.ts +17 -0
- package/dist/hitl/approvalHolds.js +114 -29
- package/dist/hitl/askClassPair.d.ts +2 -2
- package/dist/hitl/askClassPair.js +16 -0
- package/dist/hitl/askGateWire.js +2 -1
- package/dist/hitl/askSettlementLine.d.ts +14 -4
- package/dist/hitl/askSettlementLine.js +67 -47
- package/dist/hitl/attributionLadder.d.ts +23 -0
- package/dist/hitl/attributionLadder.js +67 -0
- package/dist/hitl/automaticAnswerVerdict.d.ts +2 -1
- package/dist/hitl/automaticAnswerVerdict.js +8 -8
- package/dist/hitl/crashConvergedNotice.d.ts +5 -4
- package/dist/hitl/crashConvergedNotice.js +28 -41
- package/dist/hitl/decisionLedger.d.ts +55 -0
- package/dist/hitl/decisionLedger.js +156 -0
- package/dist/hitl/decisionRefusal.d.ts +1 -0
- package/dist/hitl/decisionRefusal.js +1 -1
- package/dist/hitl/decisionSend.d.ts +113 -0
- package/dist/hitl/decisionSend.js +579 -0
- package/dist/hitl/frameRouter.js +7 -8
- package/dist/hitl/hitlBridge.d.ts +1 -0
- package/dist/hitl/hitlBridge.js +21 -9
- package/dist/hitl/persistedRulesWire.d.ts +6 -5
- package/dist/hitl/persistedRulesWire.js +55 -73
- package/dist/hitl/planReviewWire.d.ts +5 -0
- package/dist/hitl/planReviewWire.js +53 -76
- package/dist/hitl/sessionPolicyDeliverable.d.ts +2 -0
- package/dist/hitl/sessionPolicyDeliverable.js +49 -9
- package/dist/hitl/sessionPolicyWire.d.ts +2 -1
- package/dist/hitl/sessionPolicyWire.js +23 -24
- package/dist/hitl/suspendedReopen.d.ts +2 -1
- package/dist/hitl/suspendedReopen.js +7 -10
- package/dist/hitl/toolApprovalWire.d.ts +17 -5
- package/dist/hitl/toolApprovalWire.js +120 -147
- package/dist/hooksWireCaps.js +145 -56
- package/dist/host.d.ts +21 -0
- package/dist/hostTurnSettings.d.ts +110 -0
- package/dist/hostTurnSettings.js +692 -0
- package/dist/humanTurn.d.ts +15 -1
- package/dist/humanTurn.js +100 -12
- package/dist/humanTurnRead.d.ts +2 -0
- package/dist/humanTurnRead.js +22 -1
- package/dist/i18n/messages.en.d.ts +713 -0
- package/dist/i18n/messages.en.js +713 -0
- package/dist/i18n/messages.zh.js +854 -1
- package/dist/index.d.ts +45 -13
- package/dist/index.js +27 -9
- package/dist/inheritEnvCapability.d.ts +4 -2
- package/dist/inheritEnvCapability.js +12 -5
- package/dist/inheritEnvWire.d.ts +3 -2
- package/dist/inheritEnvWire.js +14 -16
- package/dist/injectionLaneNotice.d.ts +5 -0
- package/dist/injectionLaneNotice.js +46 -0
- package/dist/interactiveHalt.js +17 -6
- package/dist/liveModelCatalog.d.ts +2 -1
- package/dist/liveModelCatalog.js +14 -8
- package/dist/mcpPanel.d.ts +10 -1
- package/dist/mcpPanel.js +42 -13
- package/dist/mcpProbeCapability.d.ts +2 -1
- package/dist/mcpProbeCapability.js +7 -11
- package/dist/mcpProbeWire.d.ts +3 -1
- package/dist/mcpProbeWire.js +5 -0
- package/dist/mcpReconnect.js +2 -2
- package/dist/mcpToolToggles.d.ts +6 -0
- package/dist/mcpToolToggles.js +46 -0
- package/dist/mcpWireCaps.d.ts +43 -3
- package/dist/mcpWireCaps.js +194 -59
- package/dist/mcpWireTransport.d.ts +4 -0
- package/dist/mcpWireTransport.js +18 -0
- package/dist/memoryEntriesWire.js +5 -10
- package/dist/model/onboardJudgement.d.ts +93 -0
- package/dist/model/onboardJudgement.js +415 -0
- package/dist/model/tierVocabulary.d.ts +2 -0
- package/dist/model/tierVocabulary.js +17 -5
- package/dist/modelCapabilityProbe.d.ts +65 -0
- package/dist/modelCapabilityProbe.js +601 -0
- package/dist/modelIdentityResolvability.d.ts +5 -0
- package/dist/modelIdentityResolvability.js +49 -22
- package/dist/ownKey.d.ts +33 -0
- package/dist/ownKey.js +109 -0
- package/dist/panelReduce.d.ts +11 -1
- package/dist/panelReduce.js +95 -16
- package/dist/panelStateHolder.d.ts +1 -0
- package/dist/panelStateHolder.js +10 -1
- package/dist/permissionRuleIssue.d.ts +2 -1
- package/dist/permissionRuleIssue.js +23 -20
- package/dist/permissionRulesWriteCapability.d.ts +2 -1
- package/dist/permissionRulesWriteCapability.js +8 -6
- package/dist/pluginHooksWire.d.ts +46 -3
- package/dist/pluginHooksWire.js +220 -12
- package/dist/prepareRefusal.d.ts +8 -0
- package/dist/prepareRefusal.js +72 -0
- package/dist/printModelStatus.d.ts +63 -0
- package/dist/printModelStatus.js +155 -0
- package/dist/request/taskRequest.d.ts +16 -10
- package/dist/request/taskRequest.js +195 -58
- package/dist/resumeRefusalCopy.d.ts +14 -9
- package/dist/resumeRefusalCopy.js +89 -144
- package/dist/retryStatus.d.ts +55 -0
- package/dist/retryStatus.js +155 -19
- package/dist/ruleRefusalSentence.d.ts +4 -0
- package/dist/ruleRefusalSentence.js +58 -0
- package/dist/sandboxWire.d.ts +2 -1
- package/dist/sandboxWire.js +55 -21
- package/dist/scenarioRefusal.d.ts +10 -0
- package/dist/scenarioRefusal.js +111 -0
- package/dist/seam.d.ts +48 -9
- package/dist/seam.js +54 -5
- package/dist/seatContract.d.ts +272 -14
- package/dist/seatContract.js +711 -23
- package/dist/selfOrchestrationDenial.js +7 -19
- package/dist/sessionIdSegment.d.ts +6 -0
- package/dist/sessionIdSegment.js +24 -0
- package/dist/sessionMemoryStatus.js +6 -11
- package/dist/sessionPolicyCapability.d.ts +2 -1
- package/dist/sessionPolicyCapability.js +8 -6
- package/dist/settingsCredentialKeyPaths.d.ts +8 -0
- package/dist/settingsCredentialKeyPaths.js +219 -0
- package/dist/settingsHooksSalvage.d.ts +44 -0
- package/dist/settingsHooksSalvage.js +211 -0
- package/dist/skillToolListsCapability.d.ts +2 -1
- package/dist/skillToolListsCapability.js +8 -6
- package/dist/skillsWireCaps.d.ts +0 -3
- package/dist/skillsWireCaps.js +1 -2
- package/dist/sseIdleTriage.js +3 -2
- package/dist/subagent/engineSubagentResume.js +15 -9
- package/dist/subagentContentStore.d.ts +4 -0
- package/dist/subagentContentStore.js +157 -27
- package/dist/toolHistoryMismatch.js +3 -2
- package/dist/toolRoster.d.ts +3 -2
- package/dist/toolRoster.js +21 -20
- package/dist/toolsFlagNames.d.ts +8 -0
- package/dist/toolsFlagNames.js +110 -0
- package/dist/turnFailurePresentation.d.ts +12 -1
- package/dist/turnFailurePresentation.js +220 -15
- package/dist/usageAnalytics.js +6 -11
- package/dist/userAttachmentEnvelope.d.ts +1 -0
- package/dist/userAttachmentEnvelope.js +37 -7
- package/dist/userAttachmentSeat.d.ts +21 -0
- package/dist/userAttachmentSeat.js +75 -0
- package/dist/userInvokedSkillsCapability.d.ts +11 -1
- package/dist/userInvokedSkillsCapability.js +31 -7
- package/dist/wireErrorTriage.d.ts +7 -0
- package/dist/wireErrorTriage.js +23 -1
- package/dist/wireFailureShape.js +2 -1
- package/dist/wireRefusalCopy.d.ts +62 -16
- package/dist/wireRefusalCopy.js +307 -148
- package/dist/workflowsJournalCapability.d.ts +2 -1
- package/dist/workflowsJournalCapability.js +8 -6
- package/docs/INTEGRATION-CLIENTS.md +951 -42
- package/package.json +5 -5
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
export const SETTINGS_CLASSIFY_HOOKS_CONFIG_CLOSURE = (HOOK_EVENTS) => {
|
|
2
|
+
const HOOK_GUARD_EVENTS = ["PreToolUse", "PermissionRequest"];
|
|
3
|
+
const HOOK_SHELLS = ["bash", "powershell"];
|
|
4
|
+
const HOOK_ENTRY_COMMON_FIELDS = { if: "string?", timeout: "seconds?", statusMessage: "string?", once: "boolean?" };
|
|
5
|
+
const HOOK_ENTRY_FIELDS = {
|
|
6
|
+
command: { command: "text", shell: "shell?", async: "boolean?", asyncRewake: "boolean?", ...HOOK_ENTRY_COMMON_FIELDS },
|
|
7
|
+
prompt: { prompt: "text", model: "string?", ...HOOK_ENTRY_COMMON_FIELDS },
|
|
8
|
+
agent: { prompt: "text", model: "string?", ...HOOK_ENTRY_COMMON_FIELDS },
|
|
9
|
+
http: { url: "text", headers: "stringRecord?", allowedEnvVars: "stringList?", ...HOOK_ENTRY_COMMON_FIELDS },
|
|
10
|
+
mcp_tool: { server: "string", tool: "string", input: "record?", ...HOOK_ENTRY_COMMON_FIELDS },
|
|
11
|
+
};
|
|
12
|
+
function hookIsRecord(value) {
|
|
13
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
14
|
+
return false;
|
|
15
|
+
const proto = Object.getPrototypeOf(value);
|
|
16
|
+
return proto === Object.prototype || proto === null;
|
|
17
|
+
}
|
|
18
|
+
function hookGuardKeyOf(node) {
|
|
19
|
+
for (const key of Object.keys(node)) {
|
|
20
|
+
if (!HOOK_GUARD_EVENTS.includes(key))
|
|
21
|
+
continue;
|
|
22
|
+
const value = node[key];
|
|
23
|
+
if (value !== null && value !== undefined && !(Array.isArray(value) && value.length === 0))
|
|
24
|
+
return key;
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
function hookLooksLikeMatcher(value) {
|
|
29
|
+
if (!hookIsRecord(value))
|
|
30
|
+
return false;
|
|
31
|
+
const inner = Object.hasOwn(value, "hooks") ? value.hooks : undefined;
|
|
32
|
+
if (Array.isArray(inner))
|
|
33
|
+
return inner.length > 0;
|
|
34
|
+
return hookIsRecord(inner) && (Object.hasOwn(value, "matcher") || typeof inner.type === "string");
|
|
35
|
+
}
|
|
36
|
+
function hookGuardSignIn(root, rootPath, place) {
|
|
37
|
+
const frames = [];
|
|
38
|
+
const pending = [[root, -1, place]];
|
|
39
|
+
const seen = { matcherList: new Set(), matcher: new Set(), other: new Set() };
|
|
40
|
+
const pathTo = (frame, tail) => {
|
|
41
|
+
const segments = [...tail];
|
|
42
|
+
for (let f = frame; f >= 0; f = frames[f].parent)
|
|
43
|
+
segments.unshift(frames[f].key);
|
|
44
|
+
return [...rootPath, ...segments];
|
|
45
|
+
};
|
|
46
|
+
while (pending.length > 0) {
|
|
47
|
+
const [node, frame, at] = pending.pop();
|
|
48
|
+
if (node === null || typeof node !== "object" || seen[at].has(node))
|
|
49
|
+
continue;
|
|
50
|
+
seen[at].add(node);
|
|
51
|
+
if (Array.isArray(node)) {
|
|
52
|
+
for (let i = node.length - 1; i >= 0; i--) {
|
|
53
|
+
frames.push({ key: i, parent: frame });
|
|
54
|
+
pending.push([node[i], frames.length - 1, at === "matcherList" ? "matcher" : "other"]);
|
|
55
|
+
}
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const guardKey = hookGuardKeyOf(node);
|
|
59
|
+
if (guardKey !== undefined)
|
|
60
|
+
return pathTo(frame, [guardKey]);
|
|
61
|
+
if (at !== "matcher" && hookLooksLikeMatcher(node))
|
|
62
|
+
return pathTo(frame, []);
|
|
63
|
+
const keys = Object.keys(node);
|
|
64
|
+
for (let i = keys.length - 1; i >= 0; i--) {
|
|
65
|
+
frames.push({ key: keys[i], parent: frame });
|
|
66
|
+
pending.push([node[keys[i]], frames.length - 1, "other"]);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
function hookSignKeyOf(at, sign) {
|
|
72
|
+
const key = sign[at.length];
|
|
73
|
+
return key === undefined ? "hooks" : String(key);
|
|
74
|
+
}
|
|
75
|
+
function hookFieldFits(base, value) {
|
|
76
|
+
switch (base) {
|
|
77
|
+
case "text":
|
|
78
|
+
return typeof value === "string" && value.length > 0 && !value.includes("\0");
|
|
79
|
+
case "string":
|
|
80
|
+
return typeof value === "string";
|
|
81
|
+
case "shell":
|
|
82
|
+
return typeof value === "string" && HOOK_SHELLS.includes(value);
|
|
83
|
+
case "boolean":
|
|
84
|
+
return typeof value === "boolean";
|
|
85
|
+
case "seconds":
|
|
86
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
87
|
+
case "stringRecord":
|
|
88
|
+
return hookIsRecord(value) && Object.values(value).every((v) => typeof v === "string");
|
|
89
|
+
case "stringList":
|
|
90
|
+
if (!Array.isArray(value))
|
|
91
|
+
return false;
|
|
92
|
+
for (let i = 0; i < value.length; i++)
|
|
93
|
+
if (typeof value[i] !== "string")
|
|
94
|
+
return false;
|
|
95
|
+
return true;
|
|
96
|
+
case "record":
|
|
97
|
+
return hookIsRecord(value);
|
|
98
|
+
default: {
|
|
99
|
+
const unknownKind = base;
|
|
100
|
+
throw new Error(`hook entry field table: unknown field kind '${String(unknownKind)}'`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function hookEntryRead(entry) {
|
|
105
|
+
if (!hookIsRecord(entry))
|
|
106
|
+
return {};
|
|
107
|
+
const type = Object.hasOwn(entry, "type") ? entry.type : undefined;
|
|
108
|
+
if (typeof type !== "string" || !Object.hasOwn(HOOK_ENTRY_FIELDS, type))
|
|
109
|
+
return { field: "type" };
|
|
110
|
+
const fields = HOOK_ENTRY_FIELDS[type];
|
|
111
|
+
const out = { type };
|
|
112
|
+
for (const field of Object.keys(fields)) {
|
|
113
|
+
const kind = fields[field];
|
|
114
|
+
const optional = kind.endsWith("?");
|
|
115
|
+
const value = Object.hasOwn(entry, field) ? entry[field] : undefined;
|
|
116
|
+
if (value === undefined) {
|
|
117
|
+
if (optional)
|
|
118
|
+
continue;
|
|
119
|
+
return { field };
|
|
120
|
+
}
|
|
121
|
+
const base = (optional ? kind.slice(0, -1) : kind);
|
|
122
|
+
if (!hookFieldFits(base, value))
|
|
123
|
+
return { field };
|
|
124
|
+
out[field] = Array.isArray(value) ? value.slice() : hookIsRecord(value) ? Object.fromEntries(Object.entries(value)) : value;
|
|
125
|
+
}
|
|
126
|
+
return { entry: out };
|
|
127
|
+
}
|
|
128
|
+
function hookMatcherRead(matcher, at, guardEvent, reject, drop) {
|
|
129
|
+
if (!hookIsRecord(matcher)) {
|
|
130
|
+
if (guardEvent || hookGuardSignIn(matcher, at, "matcher") !== undefined)
|
|
131
|
+
reject("guard-matcher-invalid", at);
|
|
132
|
+
else
|
|
133
|
+
drop("matcher-invalid", at);
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
const entries = Object.hasOwn(matcher, "hooks") ? matcher.hooks : undefined;
|
|
137
|
+
const signField = hookGuardKeyOf(matcher) ??
|
|
138
|
+
Object.keys(matcher).find((key) => !(key === "hooks" && Array.isArray(entries)) && hookGuardSignIn(matcher[key], [...at, key], "other") !== undefined);
|
|
139
|
+
if (signField !== undefined) {
|
|
140
|
+
reject("guard-matcher-invalid", at, signField);
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
const entrySigns = [];
|
|
144
|
+
if (Array.isArray(entries))
|
|
145
|
+
for (let j = 0; j < entries.length; j++)
|
|
146
|
+
entrySigns.push(hookGuardSignIn(entries[j], [...at, "hooks", j], "other"));
|
|
147
|
+
const name = Object.hasOwn(matcher, "matcher") ? matcher.matcher : undefined;
|
|
148
|
+
const badField = name !== undefined && typeof name !== "string" ? "matcher" : !Array.isArray(entries) ? "hooks" : undefined;
|
|
149
|
+
if (badField !== undefined || !Array.isArray(entries)) {
|
|
150
|
+
if (guardEvent || entrySigns.some((sign) => sign !== undefined))
|
|
151
|
+
reject("guard-matcher-invalid", at, badField);
|
|
152
|
+
else
|
|
153
|
+
drop("matcher-invalid", at, badField);
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
const kept = [];
|
|
157
|
+
for (let j = 0; j < entries.length; j++) {
|
|
158
|
+
const where = [...at, "hooks", j];
|
|
159
|
+
const sign = entrySigns[j];
|
|
160
|
+
if (sign !== undefined) {
|
|
161
|
+
reject("guard-entry-invalid", where, hookIsRecord(entries[j]) ? hookSignKeyOf(where, sign) : undefined);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const read = hookEntryRead(entries[j]);
|
|
165
|
+
if (read.entry !== undefined)
|
|
166
|
+
kept.push(read.entry);
|
|
167
|
+
else if (guardEvent)
|
|
168
|
+
reject("guard-entry-invalid", where, read.field);
|
|
169
|
+
else
|
|
170
|
+
drop("entry-invalid", where, read.field);
|
|
171
|
+
}
|
|
172
|
+
return typeof name === "string" ? { matcher: name, hooks: kept } : { hooks: kept };
|
|
173
|
+
}
|
|
174
|
+
function hookBlockRead(hooks, reject, drop) {
|
|
175
|
+
if (!hookIsRecord(hooks)) {
|
|
176
|
+
if (Array.isArray(hooks) && hooks.some((x) => x !== null && typeof x === "object"))
|
|
177
|
+
reject("hooks-is-matcher", ["hooks"]);
|
|
178
|
+
else
|
|
179
|
+
drop("hooks-not-object", ["hooks"]);
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
if (hookLooksLikeMatcher(hooks) || typeof hooks.type === "string") {
|
|
183
|
+
reject("hooks-is-matcher", ["hooks"]);
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
const loaded = {};
|
|
187
|
+
for (const event of Object.keys(hooks)) {
|
|
188
|
+
const value = hooks[event];
|
|
189
|
+
const at = ["hooks", event];
|
|
190
|
+
if (!HOOK_EVENTS.includes(event)) {
|
|
191
|
+
if (hookGuardSignIn(value, at, Array.isArray(value) ? "matcherList" : "other") !== undefined)
|
|
192
|
+
reject("guard-under-unknown-event", at);
|
|
193
|
+
else
|
|
194
|
+
drop("unknown-event", at);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const guardEvent = HOOK_GUARD_EVENTS.includes(event);
|
|
198
|
+
if (!Array.isArray(value)) {
|
|
199
|
+
if ((guardEvent && value !== null) || hookGuardSignIn(value, at, "other") !== undefined)
|
|
200
|
+
reject("guard-event-not-array", at);
|
|
201
|
+
else
|
|
202
|
+
drop("event-not-array", at);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
const matchers = [];
|
|
206
|
+
for (let i = 0; i < value.length; i++) {
|
|
207
|
+
const read = hookMatcherRead(value[i], [...at, i], guardEvent, reject, drop);
|
|
208
|
+
if (read !== undefined)
|
|
209
|
+
matchers.push(read);
|
|
210
|
+
}
|
|
211
|
+
loaded[event] = matchers;
|
|
212
|
+
}
|
|
213
|
+
return loaded;
|
|
214
|
+
}
|
|
215
|
+
function classifyHooksConfig(settingsDoc) {
|
|
216
|
+
const rejections = [];
|
|
217
|
+
const dropped = [];
|
|
218
|
+
const reject = (code, path, field) => {
|
|
219
|
+
rejections.push(field === undefined ? { code, path } : { code, path, field });
|
|
220
|
+
};
|
|
221
|
+
const drop = (code, path, field) => {
|
|
222
|
+
dropped.push(field === undefined ? { code, path } : { code, path, field });
|
|
223
|
+
};
|
|
224
|
+
let hooks;
|
|
225
|
+
if (settingsDoc !== null && typeof settingsDoc === "object" && !Array.isArray(settingsDoc)) {
|
|
226
|
+
const doc = settingsDoc;
|
|
227
|
+
for (const key of Object.keys(doc)) {
|
|
228
|
+
const value = doc[key];
|
|
229
|
+
if (key === "hooks") {
|
|
230
|
+
hooks = hookBlockRead(value, reject, drop);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const sign = key === "matcher" && hookLooksLikeMatcher(doc)
|
|
234
|
+
? [key]
|
|
235
|
+
: HOOK_GUARD_EVENTS.includes(key) && value !== null && value !== undefined && !(Array.isArray(value) && value.length === 0)
|
|
236
|
+
? [key]
|
|
237
|
+
: hookGuardSignIn(value, [key], "other");
|
|
238
|
+
if (sign !== undefined)
|
|
239
|
+
reject("guard-outside-hooks", sign);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
const sign = hookGuardSignIn(settingsDoc, [], "other");
|
|
244
|
+
if (sign !== undefined)
|
|
245
|
+
reject("guard-outside-hooks", sign);
|
|
246
|
+
}
|
|
247
|
+
if (rejections.length > 0)
|
|
248
|
+
return { outcome: "rejected", hooks: undefined, dropped: [], rejections };
|
|
249
|
+
if (dropped.length > 0)
|
|
250
|
+
return { outcome: "loaded-partial", hooks, dropped, rejections: [] };
|
|
251
|
+
return { outcome: "loaded", hooks, dropped: [], rejections: [] };
|
|
252
|
+
}
|
|
253
|
+
return classifyHooksConfig;
|
|
254
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const SETTINGS_MCP_REQUEST_ENTRY_CLOSURE: (MCP_CONTENT_ORIGINS: readonly ["local", "execution", "external"], startsWithHttpScheme: (raw: string) => boolean) => any;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
export const SETTINGS_MCP_REQUEST_ENTRY_CLOSURE = (MCP_CONTENT_ORIGINS, startsWithHttpScheme) => {
|
|
2
|
+
function describeValueShape(v) {
|
|
3
|
+
if (v === undefined)
|
|
4
|
+
return "nothing (the key is absent)";
|
|
5
|
+
if (v === null)
|
|
6
|
+
return "null";
|
|
7
|
+
if (Array.isArray(v))
|
|
8
|
+
return "an array";
|
|
9
|
+
if (typeof v === "string")
|
|
10
|
+
return v.length === 0 ? "an empty string" : `a string of ${v.length} characters`;
|
|
11
|
+
if (typeof v === "object")
|
|
12
|
+
return "an object";
|
|
13
|
+
return `a ${typeof v}`;
|
|
14
|
+
}
|
|
15
|
+
function describeNotStringArray(v) {
|
|
16
|
+
return Array.isArray(v) ? "an array with a non-string element" : describeValueShape(v);
|
|
17
|
+
}
|
|
18
|
+
function isStringArray(v) {
|
|
19
|
+
return Array.isArray(v) && v.every((a) => typeof a === "string");
|
|
20
|
+
}
|
|
21
|
+
const CONTROL_AND_BIDI_STRIP_RE = /[\u0000-\u001f\u007f\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/g;
|
|
22
|
+
const CALLER_TEXT_ECHO_MAX_CHARS = 64;
|
|
23
|
+
function echoCallerText(text) {
|
|
24
|
+
const clean = text.replace(CONTROL_AND_BIDI_STRIP_RE, "");
|
|
25
|
+
return clean.length > CALLER_TEXT_ECHO_MAX_CHARS ? `${clean.slice(0, CALLER_TEXT_ECHO_MAX_CHARS)}…` : clean;
|
|
26
|
+
}
|
|
27
|
+
function quotedCallerText(text) {
|
|
28
|
+
return `"${echoCallerText(text)}"`;
|
|
29
|
+
}
|
|
30
|
+
const TOOL_WIRE_NAME_MAX_CHARS = 128;
|
|
31
|
+
const hasEdgeWhitespace = (v) => v.trim() !== v;
|
|
32
|
+
function isUsableWireName(v) {
|
|
33
|
+
return typeof v === "string" && v.length > 0 && v.length <= TOOL_WIRE_NAME_MAX_CHARS && !hasEdgeWhitespace(v);
|
|
34
|
+
}
|
|
35
|
+
function describeBadWireName(v) {
|
|
36
|
+
if (typeof v !== "string")
|
|
37
|
+
return v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
|
|
38
|
+
if (v.length === 0)
|
|
39
|
+
return "empty";
|
|
40
|
+
const head = quotedCallerText(v);
|
|
41
|
+
const tooLong = `${v.length} chars > ${TOOL_WIRE_NAME_MAX_CHARS}`;
|
|
42
|
+
if (!hasEdgeWhitespace(v))
|
|
43
|
+
return `${head} (${tooLong})`;
|
|
44
|
+
return `${head} (${v.length > TOOL_WIRE_NAME_MAX_CHARS ? `${tooLong}, ` : ""}leading or trailing whitespace)`;
|
|
45
|
+
}
|
|
46
|
+
const CONTENT_ORIGIN_WORDS = new Set(MCP_CONTENT_ORIGINS);
|
|
47
|
+
function isContentOriginWord(v) {
|
|
48
|
+
return typeof v === "string" && CONTENT_ORIGIN_WORDS.has(v);
|
|
49
|
+
}
|
|
50
|
+
function describeBadContentOrigin(v) {
|
|
51
|
+
return typeof v === "string" ? quotedCallerText(v) : v === null ? "null" : Array.isArray(v) ? "array" : typeof v;
|
|
52
|
+
}
|
|
53
|
+
function readMcpIdentityHeaders(transport) {
|
|
54
|
+
return {
|
|
55
|
+
...(typeof transport.principalHeader === "string" ? { principalHeader: transport.principalHeader } : {}),
|
|
56
|
+
...(transport.principalHeader === undefined && typeof transport.assertionHeader === "string" ? { assertionHeader: transport.assertionHeader } : {}),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const transportBad = (path, reason) => ({ ok: false, issue: { path, reason } });
|
|
60
|
+
function readRequestMcpTransport(t) {
|
|
61
|
+
if (!t || typeof t !== "object")
|
|
62
|
+
return transportBad("transport", `must be an object with a "kind" of "stdio" or "http" — got ${describeValueShape(t)}`);
|
|
63
|
+
const tr = t;
|
|
64
|
+
const lost = [];
|
|
65
|
+
if (tr.kind === "stdio") {
|
|
66
|
+
if (typeof tr.command !== "string" || tr.command.length === 0)
|
|
67
|
+
return transportBad("transport.command", `must be a non-empty string (the command to run) — got ${describeValueShape(tr.command)}`);
|
|
68
|
+
if (tr.args !== undefined && !isStringArray(tr.args))
|
|
69
|
+
lost.push({ path: "transport.args", reason: `must be an array of strings — got ${describeNotStringArray(tr.args)}` });
|
|
70
|
+
const env = readTransportRecord("transport.env", tr.env, lost);
|
|
71
|
+
return {
|
|
72
|
+
ok: true,
|
|
73
|
+
transport: {
|
|
74
|
+
kind: "stdio",
|
|
75
|
+
command: tr.command,
|
|
76
|
+
...(isStringArray(tr.args) ? { args: tr.args } : {}),
|
|
77
|
+
...(env !== undefined ? { env } : {}),
|
|
78
|
+
},
|
|
79
|
+
lost,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (tr.kind === "http") {
|
|
83
|
+
if (typeof tr.url !== "string" || !startsWithHttpScheme(tr.url))
|
|
84
|
+
return transportBad("transport.url", "must be an absolute http(s) URL (put credentials in transport.headers, never in the URL)");
|
|
85
|
+
const ids = readMcpIdentityHeaders(tr);
|
|
86
|
+
const headers = readTransportRecord("transport.headers", tr.headers, lost);
|
|
87
|
+
if (tr.principalHeader !== undefined && ids.principalHeader === undefined) {
|
|
88
|
+
lost.push({ path: "transport.principalHeader", reason: `must be a string (the HTTP header NAME the end-user principal is injected into) — got ${describeValueShape(tr.principalHeader)}` });
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
ok: true,
|
|
92
|
+
transport: {
|
|
93
|
+
kind: "http",
|
|
94
|
+
url: tr.url,
|
|
95
|
+
...(headers !== undefined ? { headers } : {}),
|
|
96
|
+
...ids,
|
|
97
|
+
},
|
|
98
|
+
lost,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return transportBad("transport.kind", `must be "stdio" or "http" — got ${describeValueShape(tr.kind)}`);
|
|
102
|
+
}
|
|
103
|
+
const RESERVED_MEMBER_NAME = "__proto__";
|
|
104
|
+
const RESERVED_MEMBER_FAULT = `no member of a declaration may be named "${RESERVED_MEMBER_NAME}"`;
|
|
105
|
+
function readPlainStringRecord(v) {
|
|
106
|
+
if (v === null || typeof v !== "object" || Array.isArray(v))
|
|
107
|
+
return { ok: false, fault: "not_plain_record", detail: v === null ? "got null" : Array.isArray(v) ? "got an array" : `got a ${typeof v}` };
|
|
108
|
+
let proto;
|
|
109
|
+
let names;
|
|
110
|
+
try {
|
|
111
|
+
proto = Object.getPrototypeOf(v);
|
|
112
|
+
names = Object.getOwnPropertyNames(v);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return { ok: false, fault: "not_plain_record", detail: "got an object that could not be inspected (a proxy trap threw)" };
|
|
116
|
+
}
|
|
117
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
118
|
+
return { ok: false, fault: "not_plain_record", detail: "got an object that is not plain data (a class instance, a Map, process.env, or a record built on a prototype; copy it into a plain object)" };
|
|
119
|
+
}
|
|
120
|
+
const record = {};
|
|
121
|
+
let reservedMember = false;
|
|
122
|
+
for (const name of names) {
|
|
123
|
+
let value;
|
|
124
|
+
let unreadable = false;
|
|
125
|
+
try {
|
|
126
|
+
const d = Object.getOwnPropertyDescriptor(v, name);
|
|
127
|
+
value = d === undefined ? undefined : Object.hasOwn(d, "value") ? d.value : d.get === undefined ? undefined : Reflect.apply(d.get, v, []);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
unreadable = true;
|
|
131
|
+
}
|
|
132
|
+
if (unreadable || typeof value !== "string") {
|
|
133
|
+
const valueFault = unreadable ? "could not be read (its getter threw)" : `must be a string — got ${describeValueShape(value)}`;
|
|
134
|
+
const reserved = name === RESERVED_MEMBER_NAME;
|
|
135
|
+
return {
|
|
136
|
+
ok: false,
|
|
137
|
+
fault: reserved ? "member_name_reserved" : "member_not_string",
|
|
138
|
+
member: echoCallerText(name),
|
|
139
|
+
detail: `its member "${echoCallerText(name)}" ${reserved ? `is not allowed (${RESERVED_MEMBER_FAULT}) and its value ${valueFault}` : valueFault}`,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
if (name === RESERVED_MEMBER_NAME)
|
|
143
|
+
reservedMember = true;
|
|
144
|
+
else
|
|
145
|
+
record[name] = value;
|
|
146
|
+
}
|
|
147
|
+
return { ok: true, record, reservedMember };
|
|
148
|
+
}
|
|
149
|
+
function readTransportRecord(path, raw, lost) {
|
|
150
|
+
if (raw === undefined)
|
|
151
|
+
return undefined;
|
|
152
|
+
const read = readPlainStringRecord(raw);
|
|
153
|
+
if (!read.ok) {
|
|
154
|
+
lost.push({ path, reason: `must be a plain object of string values — ${read.detail}`, ...(read.member !== undefined ? { member: read.member } : {}), fault: read.fault });
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
if (read.reservedMember)
|
|
158
|
+
lost.push({ path: `${path}.${RESERVED_MEMBER_NAME}`, reason: RESERVED_MEMBER_FAULT, member: RESERVED_MEMBER_NAME, fault: "member_name_reserved" });
|
|
159
|
+
return read.record;
|
|
160
|
+
}
|
|
161
|
+
return { readRequestMcpTransport, readMcpIdentityHeaders, isUsableWireName, describeBadWireName, isContentOriginWord, describeBadContentOrigin, TOOL_WIRE_NAME_MAX_CHARS };
|
|
162
|
+
};
|
|
@@ -9,3 +9,5 @@ export declare const SETTINGS_BUDGET_STRING_KEYS: readonly ["mode", "budgetDurat
|
|
|
9
9
|
export declare const SETTINGS_BUDGET_PER_MODEL_NUMBER_KEYS: readonly ["maxBudgetUsd", "tpmLimit", "rpmLimit"];
|
|
10
10
|
export declare const SETTINGS_MODEL_API_WORDS: readonly ["openai-completions", "anthropic-messages"];
|
|
11
11
|
export declare const SETTINGS_TOOL_NAME_MATCHER_EVENTS: readonly ["PreToolUse", "PostToolUse", "PostToolUseFailure"];
|
|
12
|
+
export declare const SETTINGS_UI_LANGUAGE_VALUES: readonly ["en", "zh"];
|
|
13
|
+
export declare const SETTINGS_MCP_CONTENT_ORIGINS: readonly ["local", "execution", "external"];
|
|
@@ -41,3 +41,12 @@ export const SETTINGS_TOOL_NAME_MATCHER_EVENTS = Object.freeze([
|
|
|
41
41
|
"PostToolUse",
|
|
42
42
|
"PostToolUseFailure",
|
|
43
43
|
]);
|
|
44
|
+
export const SETTINGS_UI_LANGUAGE_VALUES = Object.freeze([
|
|
45
|
+
"en",
|
|
46
|
+
"zh",
|
|
47
|
+
]);
|
|
48
|
+
export const SETTINGS_MCP_CONTENT_ORIGINS = Object.freeze([
|
|
49
|
+
"local",
|
|
50
|
+
"execution",
|
|
51
|
+
"external",
|
|
52
|
+
]);
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
export type ConfigPath = readonly (string | number)[];
|
|
2
|
+
declare const HOOK_EVENTS: readonly ["PreToolUse", "PostToolUse", "PostToolUseFailure", "PostToolBatch", "Notification", "UserPromptSubmit", "UserPromptExpansion", "SessionStart", "SessionEnd", "Stop", "StopFailure", "SubagentStart", "SubagentStop", "PreCompact", "PostCompact", "PermissionRequest", "PermissionDenied", "Setup", "TeammateIdle", "TaskCreated", "TaskCompleted", "Elicitation", "ElicitationResult", "ConfigChange", "WorktreeCreate", "WorktreeRemove", "InstructionsLoaded", "CwdChanged", "FileChanged", "MessageDisplay", "DirectoryAdded", "PreModelSwitch", "PostModelSwitch"];
|
|
3
|
+
type HookEventWord = (typeof HOOK_EVENTS)[number];
|
|
4
|
+
declare const HOOK_ENTRY_TYPES: readonly ["command", "prompt", "agent", "http", "mcp_tool"];
|
|
5
|
+
export type HookEntryType = (typeof HOOK_ENTRY_TYPES)[number];
|
|
6
|
+
declare const HOOK_SHELLS: readonly ["bash", "powershell"];
|
|
7
|
+
declare const HOOK_ENTRY_FIELDS: {
|
|
8
|
+
readonly command: {
|
|
9
|
+
readonly if: "string?";
|
|
10
|
+
readonly timeout: "seconds?";
|
|
11
|
+
readonly statusMessage: "string?";
|
|
12
|
+
readonly once: "boolean?";
|
|
13
|
+
readonly command: "text";
|
|
14
|
+
readonly shell: "shell?";
|
|
15
|
+
readonly async: "boolean?";
|
|
16
|
+
readonly asyncRewake: "boolean?";
|
|
17
|
+
};
|
|
18
|
+
readonly prompt: {
|
|
19
|
+
readonly if: "string?";
|
|
20
|
+
readonly timeout: "seconds?";
|
|
21
|
+
readonly statusMessage: "string?";
|
|
22
|
+
readonly once: "boolean?";
|
|
23
|
+
readonly prompt: "text";
|
|
24
|
+
readonly model: "string?";
|
|
25
|
+
};
|
|
26
|
+
readonly agent: {
|
|
27
|
+
readonly if: "string?";
|
|
28
|
+
readonly timeout: "seconds?";
|
|
29
|
+
readonly statusMessage: "string?";
|
|
30
|
+
readonly once: "boolean?";
|
|
31
|
+
readonly prompt: "text";
|
|
32
|
+
readonly model: "string?";
|
|
33
|
+
};
|
|
34
|
+
readonly http: {
|
|
35
|
+
readonly if: "string?";
|
|
36
|
+
readonly timeout: "seconds?";
|
|
37
|
+
readonly statusMessage: "string?";
|
|
38
|
+
readonly once: "boolean?";
|
|
39
|
+
readonly url: "text";
|
|
40
|
+
readonly headers: "stringRecord?";
|
|
41
|
+
readonly allowedEnvVars: "stringList?";
|
|
42
|
+
};
|
|
43
|
+
readonly mcp_tool: {
|
|
44
|
+
readonly if: "string?";
|
|
45
|
+
readonly timeout: "seconds?";
|
|
46
|
+
readonly statusMessage: "string?";
|
|
47
|
+
readonly once: "boolean?";
|
|
48
|
+
readonly server: "string";
|
|
49
|
+
readonly tool: "string";
|
|
50
|
+
readonly input: "record?";
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
type HookFieldBaseValue<B> = B extends "text" | "string" ? string : B extends "shell" ? (typeof HOOK_SHELLS)[number] : B extends "boolean" ? boolean : B extends "seconds" ? number : B extends "stringRecord" ? Record<string, string> : B extends "stringList" ? string[] : B extends "record" ? Record<string, unknown> : never;
|
|
54
|
+
type HookFieldValue<K> = K extends `${infer B}?` ? HookFieldBaseValue<B> : HookFieldBaseValue<K>;
|
|
55
|
+
type Flat<O> = {
|
|
56
|
+
[K in keyof O]: O[K];
|
|
57
|
+
};
|
|
58
|
+
type HookEntryOf<T extends HookEntryType, F = (typeof HOOK_ENTRY_FIELDS)[T]> = Flat<{
|
|
59
|
+
type: T;
|
|
60
|
+
} & {
|
|
61
|
+
-readonly [K in keyof F as F[K] extends `${string}?` ? never : K]: HookFieldValue<F[K]>;
|
|
62
|
+
} & {
|
|
63
|
+
-readonly [K in keyof F as F[K] extends `${string}?` ? K : never]?: HookFieldValue<F[K]>;
|
|
64
|
+
}>;
|
|
65
|
+
export type HookEntry = {
|
|
66
|
+
[T in HookEntryType]: HookEntryOf<T>;
|
|
67
|
+
}[HookEntryType];
|
|
68
|
+
export type HookMatcherEntry = {
|
|
69
|
+
matcher?: string;
|
|
70
|
+
hooks: HookEntry[];
|
|
71
|
+
};
|
|
72
|
+
export type HooksConfig = Partial<Record<HookEventWord, HookMatcherEntry[]>>;
|
|
73
|
+
declare const HOOKS_CLASSIFICATION_OUTCOMES: readonly ["loaded", "loaded-partial", "rejected"];
|
|
74
|
+
export type HooksClassificationOutcome = (typeof HOOKS_CLASSIFICATION_OUTCOMES)[number];
|
|
75
|
+
declare const HOOKS_DROPPED_PIECE_CODES: readonly ["hooks-not-object", "unknown-event", "event-not-array", "matcher-invalid", "entry-invalid"];
|
|
76
|
+
export type HooksDroppedPieceCode = (typeof HOOKS_DROPPED_PIECE_CODES)[number];
|
|
77
|
+
declare const HOOKS_REJECTION_CODES: readonly ["guard-outside-hooks", "hooks-is-matcher", "guard-under-unknown-event", "guard-event-not-array", "guard-matcher-invalid", "guard-entry-invalid"];
|
|
78
|
+
export type HooksRejectionCode = (typeof HOOKS_REJECTION_CODES)[number];
|
|
79
|
+
export interface HooksDroppedPiece {
|
|
80
|
+
readonly code: HooksDroppedPieceCode;
|
|
81
|
+
readonly path: ConfigPath;
|
|
82
|
+
readonly field?: string;
|
|
83
|
+
}
|
|
84
|
+
export interface HooksRejection {
|
|
85
|
+
readonly code: HooksRejectionCode;
|
|
86
|
+
readonly path: ConfigPath;
|
|
87
|
+
readonly field?: string;
|
|
88
|
+
}
|
|
89
|
+
export type HooksClassification = {
|
|
90
|
+
readonly outcome: "loaded";
|
|
91
|
+
readonly hooks: HooksConfig | undefined;
|
|
92
|
+
readonly dropped: readonly [];
|
|
93
|
+
readonly rejections: readonly [];
|
|
94
|
+
} | {
|
|
95
|
+
readonly outcome: "loaded-partial";
|
|
96
|
+
readonly hooks: HooksConfig | undefined;
|
|
97
|
+
readonly dropped: readonly HooksDroppedPiece[];
|
|
98
|
+
readonly rejections: readonly [];
|
|
99
|
+
} | {
|
|
100
|
+
readonly outcome: "rejected";
|
|
101
|
+
readonly hooks: undefined;
|
|
102
|
+
readonly dropped: readonly [];
|
|
103
|
+
readonly rejections: readonly HooksRejection[];
|
|
104
|
+
};
|
|
105
|
+
export type RequestMcpTransport = {
|
|
106
|
+
kind: "stdio";
|
|
107
|
+
command: string;
|
|
108
|
+
args?: string[];
|
|
109
|
+
env?: Record<string, string>;
|
|
110
|
+
} | {
|
|
111
|
+
kind: "http";
|
|
112
|
+
url: string;
|
|
113
|
+
headers?: Record<string, string>;
|
|
114
|
+
principalHeader?: string;
|
|
115
|
+
assertionHeader?: string;
|
|
116
|
+
};
|
|
117
|
+
export interface McpEntryKeyIssue {
|
|
118
|
+
readonly path: string;
|
|
119
|
+
readonly reason: string;
|
|
120
|
+
readonly member?: string;
|
|
121
|
+
readonly fault?: "not_plain_record" | "member_not_string" | "member_name_reserved";
|
|
122
|
+
}
|
|
123
|
+
export type RequestMcpTransportRead = {
|
|
124
|
+
ok: true;
|
|
125
|
+
transport: RequestMcpTransport;
|
|
126
|
+
lost: readonly McpEntryKeyIssue[];
|
|
127
|
+
} | {
|
|
128
|
+
ok: false;
|
|
129
|
+
issue: McpEntryKeyIssue;
|
|
130
|
+
};
|
|
131
|
+
declare const MCP_CONTENT_ORIGINS: readonly ["local", "execution", "external"];
|
|
132
|
+
export type McpContentOrigin = (typeof MCP_CONTENT_ORIGINS)[number];
|
|
133
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { UiLanguage } from '../env/uiLanguage.js';
|
|
1
2
|
export interface ApprovalAllowRuleShadowMember {
|
|
2
3
|
readonly member?: string;
|
|
3
4
|
readonly known: boolean;
|
|
@@ -23,4 +24,4 @@ export declare function retiredAllowRuleShadowKeyPresent(carrier: object | null
|
|
|
23
24
|
export declare function readAllowRuleShadowFromWire(carrier: object | null | undefined, leg: 'frame' | 'row'): ApprovalAllowRuleShadow | undefined;
|
|
24
25
|
export declare function readApprovalAllowRuleShadow(card: object | null | undefined): ApprovalAllowRuleShadow | undefined;
|
|
25
26
|
export declare function approvalAllowRuleShadowed(card: object | null | undefined): boolean;
|
|
26
|
-
export declare function allowRuleShadowSentence(card: object | null | undefined): string | undefined;
|
|
27
|
+
export declare function allowRuleShadowSentence(card: object | null | undefined, lang?: UiLanguage): string | undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CORE_ALLOW_RULE_MEMBERS } from '../generated/engineFactTables.js';
|
|
2
|
-
import { quoteUntrustedForSentence } from '../displayUntrusted.js';
|
|
2
|
+
import { displayUntrusted, quoteUntrustedForSentence } from '../displayUntrusted.js';
|
|
3
|
+
import { msg, renderMessage } from '../i18n/translate.js';
|
|
3
4
|
const KNOWN_MEMBERS = new Set(CORE_ALLOW_RULE_MEMBERS);
|
|
4
5
|
function freezeMember(m) {
|
|
5
6
|
return Object.freeze({ ...m });
|
|
@@ -162,7 +163,6 @@ export function approvalAllowRuleShadowed(card) {
|
|
|
162
163
|
const ENTRY_MAX = 4096;
|
|
163
164
|
const MEMBER_MAX = 64;
|
|
164
165
|
const ENTRIES_SHOWN = 5;
|
|
165
|
-
const UNREADABLE_LINE = 'An allow rule matched this call but cannot clear it; which rule could not be read, so this one is confirmed individually.';
|
|
166
166
|
function singlePersistedEntry(shadow) {
|
|
167
167
|
if (shadow.unreadable === true || shadow.members.length !== 1)
|
|
168
168
|
return undefined;
|
|
@@ -173,12 +173,12 @@ function singlePersistedEntry(shadow) {
|
|
|
173
173
|
return undefined;
|
|
174
174
|
return quoteUntrustedForSentence(m.entries[0], ENTRY_MAX);
|
|
175
175
|
}
|
|
176
|
-
function memberPhrase(m) {
|
|
176
|
+
function memberPhrase(m, lang) {
|
|
177
177
|
const memberWord = m.member === undefined ? undefined : KNOWN_MEMBERS.has(m.member) ? m.member : quoteUntrustedForSentence(m.member, MEMBER_MAX);
|
|
178
|
-
const word = memberWord ?? '
|
|
178
|
+
const word = memberWord ?? msg('approval.card.shadow.unrecognisedSource');
|
|
179
179
|
const entries = m.entries ?? [];
|
|
180
180
|
if (m.malformed === true && entries.length === 0)
|
|
181
|
-
return
|
|
181
|
+
return renderMessage(msg('approval.card.shadow.memberUnreadable', { word }), lang);
|
|
182
182
|
const listed = entries
|
|
183
183
|
.map((e) => quoteUntrustedForSentence(e, ENTRY_MAX))
|
|
184
184
|
.filter((e) => e !== undefined)
|
|
@@ -188,25 +188,28 @@ function memberPhrase(m) {
|
|
|
188
188
|
if (listed.length > 0)
|
|
189
189
|
parts.push(listed.join(', '));
|
|
190
190
|
if (notListed > 0)
|
|
191
|
-
parts.push(listed.length > 0 ?
|
|
191
|
+
parts.push(renderMessage(listed.length > 0 ? msg('approval.card.shadow.moreNotListed', { count: notListed }) : msg('approval.card.shadow.notListed', { count: notListed }), lang));
|
|
192
192
|
if (m.malformed === true)
|
|
193
|
-
parts.push('
|
|
194
|
-
return parts.length > 0
|
|
193
|
+
parts.push(renderMessage(msg('approval.card.shadow.partUnreadable'), lang));
|
|
194
|
+
return parts.length > 0
|
|
195
|
+
? renderMessage(msg('approval.card.shadow.memberWithParts', { word, parts: parts.join(renderMessage(msg('approval.card.shadow.partJoin'), lang)) }), lang)
|
|
196
|
+
: typeof word === 'string'
|
|
197
|
+
? word
|
|
198
|
+
: renderMessage(word, lang);
|
|
195
199
|
}
|
|
196
|
-
export function allowRuleShadowSentence(card) {
|
|
200
|
+
export function allowRuleShadowSentence(card, lang) {
|
|
197
201
|
const shadow = readApprovalAllowRuleShadow(card);
|
|
198
202
|
if (shadow === undefined)
|
|
199
203
|
return undefined;
|
|
200
204
|
try {
|
|
201
205
|
if (shadow.unreadable === true || shadow.members.length === 0)
|
|
202
|
-
return
|
|
206
|
+
return renderMessage(msg('approval.card.shadow.unreadable'), lang);
|
|
203
207
|
const single = singlePersistedEntry(shadow);
|
|
204
|
-
if (single !== undefined)
|
|
205
|
-
return
|
|
206
|
-
}
|
|
207
|
-
return `Allow rules matched this call but cannot clear it, so this one is confirmed individually — ${shadow.members.map(memberPhrase).join(' · ')}.`;
|
|
208
|
+
if (single !== undefined)
|
|
209
|
+
return renderMessage(msg('approval.card.shadow.single', { entry: single }), lang);
|
|
210
|
+
return renderMessage(msg('approval.card.shadow.several', { members: displayUntrusted(shadow.members.map((m) => memberPhrase(m, lang)).join(' · ')) }), lang);
|
|
208
211
|
}
|
|
209
212
|
catch {
|
|
210
|
-
return
|
|
213
|
+
return renderMessage(msg('approval.card.shadow.unreadable'), lang);
|
|
211
214
|
}
|
|
212
215
|
}
|