@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.
Files changed (128) hide show
  1. package/CHANGELOG.md +177 -0
  2. package/README.md +32 -19
  3. package/dist/adapt/arms.js +60 -36
  4. package/dist/adapt/panelTasks.d.ts +2 -1
  5. package/dist/adapt/panelTasks.js +32 -9
  6. package/dist/adapt/textStream.d.ts +1 -1
  7. package/dist/adapt/textStream.js +51 -28
  8. package/dist/adapter/activeRunSelfHeal.d.ts +7 -0
  9. package/dist/adapter/activeRunSelfHeal.js +58 -11
  10. package/dist/adapter/downstream/eventToSdkMessage.d.ts +25 -6
  11. package/dist/adapter/downstream/eventToSdkMessage.js +125 -9
  12. package/dist/adapter/downstream/terminalToSdkResult.js +10 -0
  13. package/dist/adapter/downstream/wiringManifestView.d.ts +3 -1
  14. package/dist/adapter/downstream/wiringManifestView.js +1 -0
  15. package/dist/adapter/runStream.js +32 -5
  16. package/dist/adapter/types.d.ts +2 -0
  17. package/dist/agentsWireCaps.d.ts +7 -0
  18. package/dist/agentsWireCaps.js +100 -15
  19. package/dist/cloudConfigWireCaps.d.ts +10 -1
  20. package/dist/cloudConfigWireCaps.js +156 -62
  21. package/dist/compactionStatus.d.ts +34 -0
  22. package/dist/compactionStatus.js +196 -0
  23. package/dist/decideFailureNote.js +3 -3
  24. package/dist/displayUntrusted.js +150 -5
  25. package/dist/engineAgentPanelStore.d.ts +7 -4
  26. package/dist/engineErrorCodes.d.ts +9 -1
  27. package/dist/engineErrorCodes.js +9 -5
  28. package/dist/engineRowRef.js +1 -0
  29. package/dist/engineToolDirectory.d.ts +3 -0
  30. package/dist/engineToolDirectory.js +4 -2
  31. package/dist/eventLeg.d.ts +1 -7
  32. package/dist/eventLeg.js +7 -8
  33. package/dist/fileHistoryCaptureCapability.d.ts +0 -3
  34. package/dist/fileHistoryCaptureCapability.js +0 -4
  35. package/dist/generated/engineFactTables.d.ts +1 -0
  36. package/dist/generated/engineFactTables.js +27 -0
  37. package/dist/generated/engineNoticeTables.js +3 -0
  38. package/dist/generated/settingsSchemaFns.d.ts +12 -0
  39. package/dist/generated/settingsSchemaFns.js +13 -0
  40. package/dist/generated/settingsSchemaMcpRequestEntry.d.ts +1 -0
  41. package/dist/generated/settingsSchemaMcpRequestEntry.js +162 -0
  42. package/dist/generated/settingsSchemaTables.d.ts +1 -0
  43. package/dist/generated/settingsSchemaTables.js +5 -0
  44. package/dist/generated/settingsSchemaTypes.d.ts +28 -0
  45. package/dist/hitl/allowRuleShadow.js +2 -2
  46. package/dist/hitl/approvalCardReasons.js +2 -2
  47. package/dist/hitl/approvalHolds.d.ts +14 -0
  48. package/dist/hitl/approvalHolds.js +41 -0
  49. package/dist/hitl/askClassPair.d.ts +2 -2
  50. package/dist/hitl/askClassPair.js +2 -2
  51. package/dist/hitl/attributionLadder.d.ts +23 -0
  52. package/dist/hitl/attributionLadder.js +67 -0
  53. package/dist/hitl/decisionLedger.d.ts +55 -0
  54. package/dist/hitl/decisionLedger.js +156 -0
  55. package/dist/hitl/decisionRefusal.d.ts +1 -0
  56. package/dist/hitl/decisionRefusal.js +1 -1
  57. package/dist/hitl/decisionSend.d.ts +113 -0
  58. package/dist/hitl/decisionSend.js +579 -0
  59. package/dist/hitl/hitlBridge.js +18 -6
  60. package/dist/hitl/planReviewWire.js +45 -76
  61. package/dist/hitl/sessionPolicyDeliverable.js +3 -6
  62. package/dist/hitl/toolApprovalWire.js +43 -109
  63. package/dist/hostTurnSettings.d.ts +46 -7
  64. package/dist/hostTurnSettings.js +408 -7
  65. package/dist/humanTurn.d.ts +2 -1
  66. package/dist/humanTurn.js +3 -3
  67. package/dist/i18n/messages.en.d.ts +131 -0
  68. package/dist/i18n/messages.en.js +143 -12
  69. package/dist/i18n/messages.zh.js +148 -12
  70. package/dist/index.d.ts +18 -2
  71. package/dist/index.js +10 -1
  72. package/dist/inheritEnvCapability.d.ts +1 -0
  73. package/dist/inheritEnvCapability.js +9 -2
  74. package/dist/inheritEnvWire.js +2 -2
  75. package/dist/injectionLaneNotice.d.ts +5 -0
  76. package/dist/injectionLaneNotice.js +46 -0
  77. package/dist/mcpPanel.js +2 -0
  78. package/dist/mcpReconnect.js +2 -2
  79. package/dist/mcpToolToggles.d.ts +6 -0
  80. package/dist/mcpToolToggles.js +46 -0
  81. package/dist/mcpWireCaps.d.ts +10 -4
  82. package/dist/mcpWireCaps.js +106 -71
  83. package/dist/mcpWireTransport.d.ts +4 -0
  84. package/dist/mcpWireTransport.js +18 -0
  85. package/dist/modelCapabilityProbe.d.ts +2 -1
  86. package/dist/modelCapabilityProbe.js +10 -9
  87. package/dist/modelIdentityResolvability.d.ts +2 -0
  88. package/dist/modelIdentityResolvability.js +23 -5
  89. package/dist/ownKey.d.ts +26 -0
  90. package/dist/ownKey.js +75 -0
  91. package/dist/panelReduce.d.ts +5 -0
  92. package/dist/panelReduce.js +45 -13
  93. package/dist/panelStateHolder.d.ts +1 -0
  94. package/dist/panelStateHolder.js +10 -1
  95. package/dist/permissionRuleIssue.d.ts +2 -1
  96. package/dist/permissionRuleIssue.js +22 -21
  97. package/dist/pluginHooksWire.js +12 -10
  98. package/dist/prepareRefusal.d.ts +8 -0
  99. package/dist/prepareRefusal.js +72 -0
  100. package/dist/request/taskRequest.d.ts +10 -10
  101. package/dist/request/taskRequest.js +106 -65
  102. package/dist/resumeRefusalCopy.d.ts +3 -1
  103. package/dist/resumeRefusalCopy.js +22 -4
  104. package/dist/retryStatus.d.ts +18 -0
  105. package/dist/retryStatus.js +76 -19
  106. package/dist/ruleRefusalSentence.d.ts +4 -0
  107. package/dist/ruleRefusalSentence.js +58 -0
  108. package/dist/sandboxWire.d.ts +2 -1
  109. package/dist/sandboxWire.js +55 -21
  110. package/dist/scenarioRefusal.d.ts +2 -1
  111. package/dist/scenarioRefusal.js +5 -5
  112. package/dist/seam.d.ts +24 -9
  113. package/dist/seam.js +44 -4
  114. package/dist/seatContract.d.ts +16 -8
  115. package/dist/seatContract.js +21 -6
  116. package/dist/settingsCredentialKeyPaths.d.ts +8 -0
  117. package/dist/settingsCredentialKeyPaths.js +219 -0
  118. package/dist/skillsWireCaps.d.ts +0 -3
  119. package/dist/skillsWireCaps.js +1 -2
  120. package/dist/subagentContentStore.d.ts +1 -0
  121. package/dist/subagentContentStore.js +135 -27
  122. package/dist/toolsFlagNames.d.ts +8 -0
  123. package/dist/toolsFlagNames.js +110 -0
  124. package/dist/turnFailurePresentation.js +15 -7
  125. package/dist/wireRefusalCopy.d.ts +51 -17
  126. package/dist/wireRefusalCopy.js +298 -135
  127. package/docs/INTEGRATION-CLIENTS.md +423 -26
  128. 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"];
@@ -45,3 +45,8 @@ export const SETTINGS_UI_LANGUAGE_VALUES = Object.freeze([
45
45
  "en",
46
46
  "zh",
47
47
  ]);
48
+ export const SETTINGS_MCP_CONTENT_ORIGINS = Object.freeze([
49
+ "local",
50
+ "execution",
51
+ "external",
52
+ ]);
@@ -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 = displayUntrusted(r.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 { CORE_MODE_ARBITRATION } from '../generated/engineFactTables.js';
2
- export declare const DEPLOYMENT_LIST_ASK_CLASSES: readonly (keyof typeof CORE_MODE_ARBITRATION)[];
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 { CORE_MODE_ARBITRATION } from '../generated/engineFactTables.js';
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 = Object.keys(CORE_MODE_ARBITRATION);
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
+ }