@sema-agent/client-core 0.99.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 +177 -0
- package/README.md +32 -19
- package/dist/adapt/arms.js +60 -36
- package/dist/adapt/panelTasks.d.ts +2 -1
- package/dist/adapt/panelTasks.js +32 -9
- package/dist/adapt/textStream.d.ts +1 -1
- package/dist/adapt/textStream.js +51 -28
- package/dist/adapter/activeRunSelfHeal.d.ts +7 -0
- package/dist/adapter/activeRunSelfHeal.js +58 -11
- package/dist/adapter/downstream/eventToSdkMessage.d.ts +25 -6
- package/dist/adapter/downstream/eventToSdkMessage.js +125 -9
- 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 +32 -5
- package/dist/adapter/types.d.ts +2 -0
- package/dist/agentsWireCaps.d.ts +7 -0
- package/dist/agentsWireCaps.js +100 -15
- package/dist/cloudConfigWireCaps.d.ts +10 -1
- package/dist/cloudConfigWireCaps.js +156 -62
- package/dist/compactionStatus.d.ts +34 -0
- package/dist/compactionStatus.js +196 -0
- package/dist/decideFailureNote.js +3 -3
- package/dist/displayUntrusted.js +150 -5
- package/dist/engineAgentPanelStore.d.ts +7 -4
- package/dist/engineErrorCodes.d.ts +9 -1
- package/dist/engineErrorCodes.js +9 -5
- package/dist/engineRowRef.js +1 -0
- package/dist/engineToolDirectory.d.ts +3 -0
- package/dist/engineToolDirectory.js +4 -2
- package/dist/eventLeg.d.ts +1 -7
- package/dist/eventLeg.js +7 -8
- package/dist/fileHistoryCaptureCapability.d.ts +0 -3
- package/dist/fileHistoryCaptureCapability.js +0 -4
- package/dist/generated/engineFactTables.d.ts +1 -0
- package/dist/generated/engineFactTables.js +27 -0
- package/dist/generated/engineNoticeTables.js +3 -0
- package/dist/generated/settingsSchemaFns.d.ts +12 -0
- package/dist/generated/settingsSchemaFns.js +13 -0
- package/dist/generated/settingsSchemaMcpRequestEntry.d.ts +1 -0
- package/dist/generated/settingsSchemaMcpRequestEntry.js +162 -0
- package/dist/generated/settingsSchemaTables.d.ts +1 -0
- package/dist/generated/settingsSchemaTables.js +5 -0
- package/dist/generated/settingsSchemaTypes.d.ts +28 -0
- package/dist/hitl/allowRuleShadow.js +2 -2
- package/dist/hitl/approvalCardReasons.js +2 -2
- package/dist/hitl/approvalHolds.d.ts +14 -0
- package/dist/hitl/approvalHolds.js +41 -0
- package/dist/hitl/askClassPair.d.ts +2 -2
- package/dist/hitl/askClassPair.js +2 -2
- package/dist/hitl/attributionLadder.d.ts +23 -0
- package/dist/hitl/attributionLadder.js +67 -0
- 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/hitlBridge.js +18 -6
- package/dist/hitl/planReviewWire.js +45 -76
- package/dist/hitl/sessionPolicyDeliverable.js +3 -6
- package/dist/hitl/toolApprovalWire.js +43 -109
- package/dist/hostTurnSettings.d.ts +46 -7
- package/dist/hostTurnSettings.js +408 -7
- package/dist/humanTurn.d.ts +2 -1
- package/dist/humanTurn.js +3 -3
- package/dist/i18n/messages.en.d.ts +131 -0
- package/dist/i18n/messages.en.js +143 -12
- package/dist/i18n/messages.zh.js +148 -12
- package/dist/index.d.ts +18 -2
- package/dist/index.js +10 -1
- package/dist/inheritEnvCapability.d.ts +1 -0
- package/dist/inheritEnvCapability.js +9 -2
- package/dist/inheritEnvWire.js +2 -2
- package/dist/injectionLaneNotice.d.ts +5 -0
- package/dist/injectionLaneNotice.js +46 -0
- package/dist/mcpPanel.js +2 -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 +10 -4
- package/dist/mcpWireCaps.js +106 -71
- package/dist/mcpWireTransport.d.ts +4 -0
- package/dist/mcpWireTransport.js +18 -0
- package/dist/modelCapabilityProbe.d.ts +2 -1
- package/dist/modelCapabilityProbe.js +10 -9
- package/dist/modelIdentityResolvability.d.ts +2 -0
- package/dist/modelIdentityResolvability.js +23 -5
- package/dist/ownKey.d.ts +26 -0
- package/dist/ownKey.js +75 -0
- package/dist/panelReduce.d.ts +5 -0
- package/dist/panelReduce.js +45 -13
- package/dist/panelStateHolder.d.ts +1 -0
- package/dist/panelStateHolder.js +10 -1
- package/dist/permissionRuleIssue.d.ts +2 -1
- package/dist/permissionRuleIssue.js +22 -21
- package/dist/pluginHooksWire.js +12 -10
- package/dist/prepareRefusal.d.ts +8 -0
- package/dist/prepareRefusal.js +72 -0
- package/dist/request/taskRequest.d.ts +10 -10
- package/dist/request/taskRequest.js +106 -65
- package/dist/resumeRefusalCopy.d.ts +3 -1
- package/dist/resumeRefusalCopy.js +22 -4
- package/dist/retryStatus.d.ts +18 -0
- package/dist/retryStatus.js +76 -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 +2 -1
- package/dist/scenarioRefusal.js +5 -5
- package/dist/seam.d.ts +24 -9
- package/dist/seam.js +44 -4
- package/dist/seatContract.d.ts +16 -8
- package/dist/seatContract.js +21 -6
- package/dist/settingsCredentialKeyPaths.d.ts +8 -0
- package/dist/settingsCredentialKeyPaths.js +219 -0
- package/dist/skillsWireCaps.d.ts +0 -3
- package/dist/skillsWireCaps.js +1 -2
- package/dist/subagentContentStore.d.ts +1 -0
- package/dist/subagentContentStore.js +135 -27
- package/dist/toolsFlagNames.d.ts +8 -0
- package/dist/toolsFlagNames.js +110 -0
- package/dist/turnFailurePresentation.js +15 -7
- package/dist/wireRefusalCopy.d.ts +51 -17
- package/dist/wireRefusalCopy.js +298 -135
- package/docs/INTEGRATION-CLIENTS.md +423 -26
- package/package.json +4 -4
|
@@ -14,6 +14,7 @@ export declare const CORE_ASK_EVIDENCE_ABSENCE_VALUES: readonly ["not_wired", "n
|
|
|
14
14
|
export declare const CORE_SYSTEM_INJECTION_PRIORITIES: readonly ["now", "next", "later"];
|
|
15
15
|
export declare const CORE_READ_DENY_BUILTIN_TIERS: readonly ["credentials", "shell-history", "browser", "wallet", "agent-config"];
|
|
16
16
|
export declare const CORE_READ_DENY_DEFAULT_TIERS: readonly ["credentials", "agent-config"];
|
|
17
|
+
export declare const CORE_ASK_CLASSES: readonly ["content_question", "hook", "ancestor", "dangerous_delete", "unverifiable_delete", "unverifiable_shell_text", "transcript_write", "org_rule", "rule_unreadable", "ask_rule", "denial_limit_fallback", "classifier_no_ruling", "requires_user_interaction", "operator_forced", "shell_floor", "target_unresolved", "probe", "write_protection", "tool_marks_mcp", "tool_marks_a2a", "policy_rra", "artifact_shared_read", "mode", "unstated", "ordinary"];
|
|
17
18
|
export interface CoreModeArbitrationRow {
|
|
18
19
|
readonly bypassImmune: boolean;
|
|
19
20
|
readonly classifierEligible: boolean;
|
|
@@ -140,6 +140,33 @@ export const CORE_READ_DENY_DEFAULT_TIERS = Object.freeze([
|
|
|
140
140
|
"credentials",
|
|
141
141
|
"agent-config",
|
|
142
142
|
]);
|
|
143
|
+
export const CORE_ASK_CLASSES = Object.freeze([
|
|
144
|
+
"content_question",
|
|
145
|
+
"hook",
|
|
146
|
+
"ancestor",
|
|
147
|
+
"dangerous_delete",
|
|
148
|
+
"unverifiable_delete",
|
|
149
|
+
"unverifiable_shell_text",
|
|
150
|
+
"transcript_write",
|
|
151
|
+
"org_rule",
|
|
152
|
+
"rule_unreadable",
|
|
153
|
+
"ask_rule",
|
|
154
|
+
"denial_limit_fallback",
|
|
155
|
+
"classifier_no_ruling",
|
|
156
|
+
"requires_user_interaction",
|
|
157
|
+
"operator_forced",
|
|
158
|
+
"shell_floor",
|
|
159
|
+
"target_unresolved",
|
|
160
|
+
"probe",
|
|
161
|
+
"write_protection",
|
|
162
|
+
"tool_marks_mcp",
|
|
163
|
+
"tool_marks_a2a",
|
|
164
|
+
"policy_rra",
|
|
165
|
+
"artifact_shared_read",
|
|
166
|
+
"mode",
|
|
167
|
+
"unstated",
|
|
168
|
+
"ordinary",
|
|
169
|
+
]);
|
|
143
170
|
export const CORE_MODE_ARBITRATION = Object.freeze({
|
|
144
171
|
"content_question": Object.freeze({ bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: true }),
|
|
145
172
|
"hook": Object.freeze({ bypassImmune: true, classifierEligible: false, sandboxAdmission: false, ruleMayClear: false, hookAskFloor: false }),
|
|
@@ -31,6 +31,7 @@ export const CORE_ENGINE_NOTICE_CODES = Object.freeze([
|
|
|
31
31
|
"permission.read_deny_unapplied",
|
|
32
32
|
"permission.rule_store_unavailable",
|
|
33
33
|
"permission.rule_row_dropped",
|
|
34
|
+
"config.session_rules_earlier_grammar",
|
|
34
35
|
"permission.allowed_tool_not_granted",
|
|
35
36
|
"mods.registration_refused",
|
|
36
37
|
"peer.inbound_disposition",
|
|
@@ -128,6 +129,7 @@ export const CORE_NOTICE_AUDIENCE = Object.freeze({
|
|
|
128
129
|
"permission.read_deny_unapplied": "user",
|
|
129
130
|
"permission.rule_store_unavailable": "user",
|
|
130
131
|
"permission.rule_row_dropped": "user",
|
|
132
|
+
"config.session_rules_earlier_grammar": "user",
|
|
131
133
|
"permission.allowed_tool_not_granted": "user",
|
|
132
134
|
"mods.registration_refused": "user",
|
|
133
135
|
"peer.inbound_disposition": "user",
|
|
@@ -231,6 +233,7 @@ export const CORE_NOTICE_MINTER = Object.freeze({
|
|
|
231
233
|
"permission.read_deny_unapplied": "engine",
|
|
232
234
|
"permission.rule_store_unavailable": "engine",
|
|
233
235
|
"permission.rule_row_dropped": "engine",
|
|
236
|
+
"config.session_rules_earlier_grammar": "engine",
|
|
234
237
|
"permission.allowed_tool_not_granted": "engine",
|
|
235
238
|
"mods.registration_refused": "engine",
|
|
236
239
|
"peer.inbound_disposition": "engine",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { HooksClassification } from './settingsSchemaTypes.js';
|
|
2
|
+
import type { McpContentOrigin, RequestMcpTransportRead } from './settingsSchemaTypes.js';
|
|
2
3
|
export declare const SETTINGS_AT_MENTION_MODES: readonly ["all", "none", "only"];
|
|
3
4
|
export type SettingsAtMention = {
|
|
4
5
|
readonly mode: Exclude<(typeof SETTINGS_AT_MENTION_MODES)[number], 'only'>;
|
|
@@ -12,8 +13,19 @@ export declare const SETTINGS_IS_HIERARCHICAL_URL: (raw: string) => boolean;
|
|
|
12
13
|
export declare const SETTINGS_NARROW_AT_MENTION: (atMention: SettingsAtMention, available: ReadonlySet<string>) => SettingsAtMention;
|
|
13
14
|
export declare const SETTINGS_HOOK_MATCHER_SELECTS: (matcher: string | undefined, name: string) => boolean | 'invalid';
|
|
14
15
|
export declare const SETTINGS_HOOK_MATCHER_FORM: (matcher: string | undefined) => SettingsHookMatcherForm;
|
|
16
|
+
export declare const SETTINGS_STARTS_WITH_HTTP_SCHEME: (raw: string) => boolean;
|
|
15
17
|
export declare const SETTINGS_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"];
|
|
16
18
|
export declare const SETTINGS_HOOKS_CLASSIFICATION_OUTCOMES: readonly ["loaded", "loaded-partial", "rejected"];
|
|
17
19
|
export declare const SETTINGS_HOOKS_DROPPED_PIECE_CODES: readonly ["hooks-not-object", "unknown-event", "event-not-array", "matcher-invalid", "entry-invalid"];
|
|
18
20
|
export declare const SETTINGS_HOOKS_REJECTION_CODES: readonly ["guard-outside-hooks", "hooks-is-matcher", "guard-under-unknown-event", "guard-event-not-array", "guard-matcher-invalid", "guard-entry-invalid"];
|
|
19
21
|
export declare const SETTINGS_CLASSIFY_HOOKS_CONFIG: (settingsDoc: unknown) => HooksClassification;
|
|
22
|
+
export declare const SETTINGS_READ_REQUEST_MCP_TRANSPORT: (t: unknown) => RequestMcpTransportRead;
|
|
23
|
+
export declare const SETTINGS_READ_MCP_IDENTITY_HEADERS: (transport: Record<string, unknown>) => {
|
|
24
|
+
principalHeader?: string;
|
|
25
|
+
assertionHeader?: string;
|
|
26
|
+
};
|
|
27
|
+
export declare const SETTINGS_IS_USABLE_WIRE_NAME: (v: unknown) => v is string;
|
|
28
|
+
export declare const SETTINGS_DESCRIBE_BAD_WIRE_NAME: (v: unknown) => string;
|
|
29
|
+
export declare const SETTINGS_IS_CONTENT_ORIGIN_WORD: (v: unknown) => v is McpContentOrigin;
|
|
30
|
+
export declare const SETTINGS_DESCRIBE_BAD_CONTENT_ORIGIN: (v: unknown) => string;
|
|
31
|
+
export declare const SETTINGS_TOOL_WIRE_NAME_MAX_CHARS: 128;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { SETTINGS_CLASSIFY_HOOKS_CONFIG_CLOSURE } from './settingsSchemaHooksClassifier.js';
|
|
2
|
+
import { SETTINGS_MCP_REQUEST_ENTRY_CLOSURE } from './settingsSchemaMcpRequestEntry.js';
|
|
3
|
+
import { SETTINGS_MCP_CONTENT_ORIGINS } from './settingsSchemaTables.js';
|
|
2
4
|
export const SETTINGS_AT_MENTION_MODES = Object.freeze([
|
|
3
5
|
"all",
|
|
4
6
|
"none",
|
|
@@ -74,6 +76,9 @@ export const SETTINGS_HOOK_MATCHER_FORM = function hookMatcherForm(matcher) {
|
|
|
74
76
|
}
|
|
75
77
|
}
|
|
76
78
|
};
|
|
79
|
+
export const SETTINGS_STARTS_WITH_HTTP_SCHEME = function startsWithHttpScheme(raw) {
|
|
80
|
+
return /^https?:\/\//i.test(raw);
|
|
81
|
+
};
|
|
77
82
|
export const SETTINGS_HOOK_EVENTS = Object.freeze([
|
|
78
83
|
"PreToolUse",
|
|
79
84
|
"PostToolUse",
|
|
@@ -130,3 +135,11 @@ export const SETTINGS_HOOKS_REJECTION_CODES = Object.freeze([
|
|
|
130
135
|
"guard-entry-invalid",
|
|
131
136
|
]);
|
|
132
137
|
export const SETTINGS_CLASSIFY_HOOKS_CONFIG = SETTINGS_CLASSIFY_HOOKS_CONFIG_CLOSURE(SETTINGS_HOOK_EVENTS);
|
|
138
|
+
const SETTINGS_MCP_REQUEST_ENTRY = SETTINGS_MCP_REQUEST_ENTRY_CLOSURE(SETTINGS_MCP_CONTENT_ORIGINS, SETTINGS_STARTS_WITH_HTTP_SCHEME);
|
|
139
|
+
export const SETTINGS_READ_REQUEST_MCP_TRANSPORT = SETTINGS_MCP_REQUEST_ENTRY.readRequestMcpTransport;
|
|
140
|
+
export const SETTINGS_READ_MCP_IDENTITY_HEADERS = SETTINGS_MCP_REQUEST_ENTRY.readMcpIdentityHeaders;
|
|
141
|
+
export const SETTINGS_IS_USABLE_WIRE_NAME = SETTINGS_MCP_REQUEST_ENTRY.isUsableWireName;
|
|
142
|
+
export const SETTINGS_DESCRIBE_BAD_WIRE_NAME = SETTINGS_MCP_REQUEST_ENTRY.describeBadWireName;
|
|
143
|
+
export const SETTINGS_IS_CONTENT_ORIGIN_WORD = SETTINGS_MCP_REQUEST_ENTRY.isContentOriginWord;
|
|
144
|
+
export const SETTINGS_DESCRIBE_BAD_CONTENT_ORIGIN = SETTINGS_MCP_REQUEST_ENTRY.describeBadContentOrigin;
|
|
145
|
+
export const SETTINGS_TOOL_WIRE_NAME_MAX_CHARS = SETTINGS_MCP_REQUEST_ENTRY.TOOL_WIRE_NAME_MAX_CHARS;
|
|
@@ -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
|
+
};
|
|
@@ -10,3 +10,4 @@ export declare const SETTINGS_BUDGET_PER_MODEL_NUMBER_KEYS: readonly ["maxBudget
|
|
|
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
12
|
export declare const SETTINGS_UI_LANGUAGE_VALUES: readonly ["en", "zh"];
|
|
13
|
+
export declare const SETTINGS_MCP_CONTENT_ORIGINS: readonly ["local", "execution", "external"];
|
|
@@ -102,4 +102,32 @@ export type HooksClassification = {
|
|
|
102
102
|
readonly dropped: readonly [];
|
|
103
103
|
readonly rejections: readonly HooksRejection[];
|
|
104
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];
|
|
105
133
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
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
3
|
import { msg, renderMessage } from '../i18n/translate.js';
|
|
4
4
|
const KNOWN_MEMBERS = new Set(CORE_ALLOW_RULE_MEMBERS);
|
|
5
5
|
function freezeMember(m) {
|
|
@@ -207,7 +207,7 @@ export function allowRuleShadowSentence(card, lang) {
|
|
|
207
207
|
const single = singlePersistedEntry(shadow);
|
|
208
208
|
if (single !== undefined)
|
|
209
209
|
return renderMessage(msg('approval.card.shadow.single', { entry: single }), lang);
|
|
210
|
-
return renderMessage(msg('approval.card.shadow.several', { members: shadow.members.map((m) => memberPhrase(m, lang)).join(' · ') }), lang);
|
|
210
|
+
return renderMessage(msg('approval.card.shadow.several', { members: displayUntrusted(shadow.members.map((m) => memberPhrase(m, lang)).join(' · ')) }), lang);
|
|
211
211
|
}
|
|
212
212
|
catch {
|
|
213
213
|
return renderMessage(msg('approval.card.shadow.unreadable'), lang);
|
|
@@ -104,8 +104,8 @@ export function approvalCardReasons(card, opts, lang) {
|
|
|
104
104
|
for (const r of raw) {
|
|
105
105
|
if (r.text === undefined)
|
|
106
106
|
continue;
|
|
107
|
-
const text =
|
|
108
|
-
if (text === '' || seen.has(text))
|
|
107
|
+
const text = r.text;
|
|
108
|
+
if (text.trim() === '' || seen.has(text))
|
|
109
109
|
continue;
|
|
110
110
|
seen.add(text);
|
|
111
111
|
lines.push(Object.freeze({ source: r.source, text }));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ApprovalHolderIdentity } from './toolApprovalWire.js';
|
|
2
2
|
import type { ApprovalResolution, ApprovalResolutionLane } from './approvalResolution.js';
|
|
3
|
+
import { type DecisionLedgerOp } from './decisionLedger.js';
|
|
3
4
|
export declare const APPROVAL_SETTLED_HOWS: readonly ["answered", "interrupted", "closed_by_engine"];
|
|
4
5
|
export type ApprovalSettledHow = (typeof APPROVAL_SETTLED_HOWS)[number];
|
|
5
6
|
export declare const SETTLED_APPROVALS_MAX_ENTRIES = 4096;
|
|
@@ -29,6 +30,7 @@ export interface LiveCardHold {
|
|
|
29
30
|
readonly cardSignal: AbortSignal;
|
|
30
31
|
readonly interrupt: () => void;
|
|
31
32
|
trailing?: Promise<boolean>;
|
|
33
|
+
ledgerOp?: DecisionLedgerOp;
|
|
32
34
|
}
|
|
33
35
|
export interface HostCardHold {
|
|
34
36
|
readonly lane: 'host';
|
|
@@ -61,6 +63,17 @@ export interface HostApprovalCardHold {
|
|
|
61
63
|
sending(): void;
|
|
62
64
|
release(settled?: 'answered' | 'interrupted'): void;
|
|
63
65
|
}
|
|
66
|
+
export declare function hostHoldIsActive(handle: HostApprovalCardHold): boolean;
|
|
67
|
+
export declare function takeHostHoldLedgerOp(handle: HostApprovalCardHold): DecisionLedgerOp | undefined;
|
|
68
|
+
export type ApprovalHoldAttempt = {
|
|
69
|
+
readonly kind: 'settled';
|
|
70
|
+
} | {
|
|
71
|
+
readonly kind: 'busy';
|
|
72
|
+
} | {
|
|
73
|
+
readonly kind: 'held';
|
|
74
|
+
readonly hold: HostApprovalCardHold;
|
|
75
|
+
};
|
|
76
|
+
export declare function tryHoldApprovalNow(who: HoldIdentity, signal: AbortSignal | undefined): ApprovalHoldAttempt;
|
|
64
77
|
export type ApprovalCardClaim = {
|
|
65
78
|
readonly kind: 'held';
|
|
66
79
|
readonly hold: HostApprovalCardHold;
|
|
@@ -93,4 +106,5 @@ export declare function settledLiveCardOutcome(approvalId: string): ApprovalReso
|
|
|
93
106
|
export declare function liveCardHoldOf(key: string): LiveCardHold | undefined;
|
|
94
107
|
export declare function holdLiveCard(identity: LiveCardIdentity, callerSignal: AbortSignal | undefined, present: (hold: LiveCardHold) => Promise<ApprovalResolution>): Promise<ApprovalResolution>;
|
|
95
108
|
export declare function joinLiveCard(hold: LiveCardHold, signal: AbortSignal | undefined): Promise<ApprovalResolution>;
|
|
109
|
+
export declare function enterLiveRespondPhase(hold: LiveCardHold): void;
|
|
96
110
|
export declare function supersedeLiveCard(key: string, parked: boolean): boolean;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { hostLog } from '../host.js';
|
|
2
2
|
import { liveFrameCallKey } from './gateIdentity.js';
|
|
3
3
|
import { decideReceiptReopen, readDecideReceipt } from '../decideReceipt.js';
|
|
4
|
+
import { closeDecisionOp, decisionLedgerKeysOf, openDecisionOp, settleDecisionOp } from './decisionLedger.js';
|
|
4
5
|
export const APPROVAL_SETTLED_HOWS = Object.freeze(['answered', 'interrupted', 'closed_by_engine']);
|
|
5
6
|
export const SETTLED_APPROVALS_MAX_ENTRIES = 4096;
|
|
6
7
|
const nonEmpty = (v) => typeof v === 'string' && v !== '';
|
|
@@ -110,11 +111,16 @@ function holdHostCard(identity, signal) {
|
|
|
110
111
|
h.inFlight.set(key, hold);
|
|
111
112
|
let done = false;
|
|
112
113
|
let stopFollowing = () => { };
|
|
114
|
+
let ledgerOp;
|
|
113
115
|
const release = (settled) => {
|
|
114
116
|
if (done)
|
|
115
117
|
return;
|
|
116
118
|
done = true;
|
|
117
119
|
stopFollowing();
|
|
120
|
+
if (ledgerOp !== undefined) {
|
|
121
|
+
settleDecisionOp(ledgerOp, 'host');
|
|
122
|
+
ledgerOp.ownCallOpen = false;
|
|
123
|
+
}
|
|
118
124
|
const how = settled === 'answered' || settled === 'interrupted' ? settled : undefined;
|
|
119
125
|
if (how !== undefined && nonEmpty(frozen.approvalId))
|
|
120
126
|
noteSettled(frozen.approvalId, how, false, how === 'interrupted' ? interruptedThere('decide') : nothingSentHere('decide'), true);
|
|
@@ -127,6 +133,8 @@ function holdHostCard(identity, signal) {
|
|
|
127
133
|
sending: () => {
|
|
128
134
|
if (done)
|
|
129
135
|
return;
|
|
136
|
+
if (hold.phase !== 'respond')
|
|
137
|
+
ledgerOp = openDecisionOp('tool_approval', ledgerKeysOfHold(frozen), 'handed', true);
|
|
130
138
|
hold.phase = 'respond';
|
|
131
139
|
stopFollowing();
|
|
132
140
|
},
|
|
@@ -136,8 +144,31 @@ function holdHostCard(identity, signal) {
|
|
|
136
144
|
if (!done && hold.phase === 'card')
|
|
137
145
|
release();
|
|
138
146
|
});
|
|
147
|
+
hostHoldControls.set(handle, {
|
|
148
|
+
active: () => !done && h.inFlight.get(key) === hold,
|
|
149
|
+
takeLedgerOp: () => {
|
|
150
|
+
const op = ledgerOp;
|
|
151
|
+
ledgerOp = undefined;
|
|
152
|
+
return op;
|
|
153
|
+
},
|
|
154
|
+
});
|
|
139
155
|
return handle;
|
|
140
156
|
}
|
|
157
|
+
const hostHoldControls = new WeakMap();
|
|
158
|
+
export function hostHoldIsActive(handle) {
|
|
159
|
+
return hostHoldControls.get(handle)?.active() === true;
|
|
160
|
+
}
|
|
161
|
+
export function takeHostHoldLedgerOp(handle) {
|
|
162
|
+
return hostHoldControls.get(handle)?.takeLedgerOp();
|
|
163
|
+
}
|
|
164
|
+
export function tryHoldApprovalNow(who, signal) {
|
|
165
|
+
const entry = settledApprovalOf(who.approvalId);
|
|
166
|
+
if (entry !== undefined && settledForDurableLane(entry))
|
|
167
|
+
return Object.freeze({ kind: 'settled' });
|
|
168
|
+
if (approvalHoldFor(who) !== undefined)
|
|
169
|
+
return Object.freeze({ kind: 'busy' });
|
|
170
|
+
return Object.freeze({ kind: 'held', hold: holdHostCard(who, signal) });
|
|
171
|
+
}
|
|
141
172
|
function callerSignalAborted(signal) {
|
|
142
173
|
if (signal === undefined || signal === null)
|
|
143
174
|
return false;
|
|
@@ -254,6 +285,8 @@ export function holdLiveCard(identity, callerSignal, present) {
|
|
|
254
285
|
const hold = { lane: 'live', identity: Object.freeze({ ...identity }), outcome, phase: 'card', cardSignal: cut.signal, interrupt };
|
|
255
286
|
h.inFlight.set(key, hold);
|
|
256
287
|
const release = () => {
|
|
288
|
+
if (hold.ledgerOp !== undefined)
|
|
289
|
+
closeDecisionOp(hold.ledgerOp, 'package');
|
|
257
290
|
if (h.inFlight.get(key) === hold)
|
|
258
291
|
h.inFlight.delete(key);
|
|
259
292
|
};
|
|
@@ -320,6 +353,14 @@ function followCallerSignal(signal, onAbort) {
|
|
|
320
353
|
}
|
|
321
354
|
};
|
|
322
355
|
}
|
|
356
|
+
function ledgerKeysOfHold(identity) {
|
|
357
|
+
return decisionLedgerKeysOf({ taskId: identity.taskId, approvalId: identity.approvalId, callId: identity.toolCallId });
|
|
358
|
+
}
|
|
359
|
+
export function enterLiveRespondPhase(hold) {
|
|
360
|
+
if (hold.ledgerOp === undefined)
|
|
361
|
+
hold.ledgerOp = openDecisionOp('tool_approval', ledgerKeysOfHold(hold.identity), 'posting', true);
|
|
362
|
+
hold.phase = 'respond';
|
|
363
|
+
}
|
|
323
364
|
export function supersedeLiveCard(key, parked) {
|
|
324
365
|
const hold = liveCardHoldOf(key);
|
|
325
366
|
if (hold === undefined || hold.phase !== 'card' || hold.superseded !== undefined)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare const DEPLOYMENT_LIST_ASK_CLASSES: readonly (
|
|
1
|
+
import { CORE_ASK_CLASSES } from '../generated/engineFactTables.js';
|
|
2
|
+
export declare const DEPLOYMENT_LIST_ASK_CLASSES: readonly (typeof CORE_ASK_CLASSES)[number][];
|
|
3
3
|
export type AskClassPairReading = {
|
|
4
4
|
readonly state: 'pair';
|
|
5
5
|
readonly askClass: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CORE_ASK_CLASSES } from '../generated/engineFactTables.js';
|
|
2
2
|
export const DEPLOYMENT_LIST_ASK_CLASSES = Object.freeze(['ask_rule']);
|
|
3
3
|
const ABSENT = Object.freeze({ state: 'absent' });
|
|
4
4
|
const MALFORMED = Object.freeze({ state: 'malformed' });
|
|
@@ -41,7 +41,7 @@ export function readAskClassPairState(v) {
|
|
|
41
41
|
return Object.freeze({ state: 'pair', askClass, classes: Object.freeze(classes) });
|
|
42
42
|
}
|
|
43
43
|
function inCanonicalOrder(classes) {
|
|
44
|
-
const order =
|
|
44
|
+
const order = CORE_ASK_CLASSES;
|
|
45
45
|
let last = -1;
|
|
46
46
|
for (const c of classes) {
|
|
47
47
|
const rank = order.indexOf(c);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare function shouldResendWithoutAttribution(e: {} | null | undefined, sentAttributionKey: boolean, signal?: AbortSignal): boolean;
|
|
2
|
+
export declare function shouldResendUnderSuccessorKey(e: {} | null | undefined, lastSendCarriedRetiredKey: boolean, signal?: AbortSignal): boolean;
|
|
3
|
+
export declare function carriesAttributionKey(body: object): boolean;
|
|
4
|
+
export declare function withoutAttributionKey<T extends {
|
|
5
|
+
settledBy?: unknown;
|
|
6
|
+
decidedBy?: unknown;
|
|
7
|
+
}>(body: T): Omit<T, 'settledBy' | 'decidedBy'>;
|
|
8
|
+
export declare function withAttributionKeyRenamed<T extends {
|
|
9
|
+
settledBy?: unknown;
|
|
10
|
+
decidedBy?: unknown;
|
|
11
|
+
}>(body: T): Omit<T, 'settledBy' | 'decidedBy'> & {
|
|
12
|
+
decidedBy?: Exclude<T['settledBy'], undefined>;
|
|
13
|
+
};
|
|
14
|
+
export interface AttributionLadderState {
|
|
15
|
+
lastCarried: boolean;
|
|
16
|
+
dropped: boolean;
|
|
17
|
+
renamed: boolean;
|
|
18
|
+
}
|
|
19
|
+
export type AttributionLadderStep = 'dropped' | 'renamed' | 'renamed_then_dropped';
|
|
20
|
+
export declare function deliverWithAttributionLadder<B extends {
|
|
21
|
+
settledBy?: unknown;
|
|
22
|
+
decidedBy?: unknown;
|
|
23
|
+
}, R>(body: B, send: (b: B) => Promise<R>, signal: AbortSignal | undefined, state: AttributionLadderState, onStep?: (step: AttributionLadderStep) => void): Promise<R>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { denyAttributionRefusalFromError } from '../wireRefusalCopy.js';
|
|
2
|
+
export function shouldResendWithoutAttribution(e, sentAttributionKey, signal) {
|
|
3
|
+
if (!sentAttributionKey)
|
|
4
|
+
return false;
|
|
5
|
+
if (signal?.aborted === true)
|
|
6
|
+
return false;
|
|
7
|
+
return denyAttributionRefusalFromError(e, true)?.kind === 'not_in_proof';
|
|
8
|
+
}
|
|
9
|
+
export function shouldResendUnderSuccessorKey(e, lastSendCarriedRetiredKey, signal) {
|
|
10
|
+
if (!lastSendCarriedRetiredKey)
|
|
11
|
+
return false;
|
|
12
|
+
if (signal?.aborted === true)
|
|
13
|
+
return false;
|
|
14
|
+
return denyAttributionRefusalFromError(e, true)?.kind === 'retired_key';
|
|
15
|
+
}
|
|
16
|
+
export function carriesAttributionKey(body) {
|
|
17
|
+
return Object.hasOwn(body, 'settledBy') || Object.hasOwn(body, 'decidedBy');
|
|
18
|
+
}
|
|
19
|
+
export function withoutAttributionKey(body) {
|
|
20
|
+
const { settledBy: _droppedSettledBy, decidedBy: _droppedDecidedBy, ...rest } = body;
|
|
21
|
+
void _droppedSettledBy;
|
|
22
|
+
void _droppedDecidedBy;
|
|
23
|
+
return rest;
|
|
24
|
+
}
|
|
25
|
+
export function withAttributionKeyRenamed(body) {
|
|
26
|
+
const { settledBy, decidedBy: _replaced, ...rest } = body;
|
|
27
|
+
void _replaced;
|
|
28
|
+
return settledBy === undefined ? rest : { ...rest, decidedBy: settledBy };
|
|
29
|
+
}
|
|
30
|
+
export async function deliverWithAttributionLadder(body, send, signal, state, onStep) {
|
|
31
|
+
state.lastCarried = carriesAttributionKey(body);
|
|
32
|
+
state.dropped = false;
|
|
33
|
+
state.renamed = false;
|
|
34
|
+
try {
|
|
35
|
+
return await send(body);
|
|
36
|
+
}
|
|
37
|
+
catch (first) {
|
|
38
|
+
if (shouldResendWithoutAttribution(first, state.lastCarried, signal)) {
|
|
39
|
+
const withoutAttribution = withoutAttributionKey(body);
|
|
40
|
+
state.lastCarried = false;
|
|
41
|
+
onStep?.('dropped');
|
|
42
|
+
const r = await send(withoutAttribution);
|
|
43
|
+
state.dropped = true;
|
|
44
|
+
return r;
|
|
45
|
+
}
|
|
46
|
+
if (shouldResendUnderSuccessorKey(first, Object.hasOwn(body, 'settledBy'), signal)) {
|
|
47
|
+
const renamed = withAttributionKeyRenamed(body);
|
|
48
|
+
onStep?.('renamed');
|
|
49
|
+
try {
|
|
50
|
+
const r = await send(renamed);
|
|
51
|
+
state.renamed = true;
|
|
52
|
+
return r;
|
|
53
|
+
}
|
|
54
|
+
catch (second) {
|
|
55
|
+
if (!shouldResendWithoutAttribution(second, true, signal))
|
|
56
|
+
throw second;
|
|
57
|
+
const renamedWithoutAttribution = withoutAttributionKey(renamed);
|
|
58
|
+
state.lastCarried = false;
|
|
59
|
+
onStep?.('renamed_then_dropped');
|
|
60
|
+
const r = await send(renamedWithoutAttribution);
|
|
61
|
+
state.dropped = true;
|
|
62
|
+
return r;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
throw first;
|
|
66
|
+
}
|
|
67
|
+
}
|