@sema-agent/client-core 0.106.0 → 0.108.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 +199 -0
- package/README.md +35 -26
- package/dist/adapter/activeRunSelfHeal.d.ts +10 -0
- package/dist/adapter/activeRunSelfHeal.js +68 -23
- package/dist/checkpointRowVerdict.d.ts +11 -0
- package/dist/checkpointRowVerdict.js +35 -0
- package/dist/cloudConfigWireCaps.d.ts +10 -0
- package/dist/cloudConfigWireCaps.js +33 -0
- package/dist/coreValuePorts.d.ts +17 -1
- package/dist/coreValuePorts.js +5 -0
- package/dist/decideReceipt.js +2 -1
- package/dist/displayUntrusted.d.ts +5 -3
- package/dist/displayUntrusted.js +50 -41
- package/dist/engineErrorCodes.d.ts +5 -0
- package/dist/engineErrorCodes.js +5 -0
- package/dist/engineNoticeCodes.d.ts +31 -1
- package/dist/engineNoticeCodes.js +182 -9
- package/dist/engineTargetSeat.js +1 -0
- package/dist/errorSupplementRoles.js +4 -1
- package/dist/eventLeg.js +1 -1
- package/dist/executionLaneCapability.js +3 -2
- package/dist/executionLaneWords.d.ts +1 -0
- package/dist/executionLaneWords.js +4 -0
- package/dist/gateOutcome.d.ts +10 -1
- package/dist/gateOutcome.js +48 -1
- package/dist/generated/engineFactTables.d.ts +1 -0
- package/dist/generated/engineFactTables.js +5 -0
- package/dist/generated/engineNoticeTables.js +12 -3
- package/dist/generated/settingsSchemaFns.d.ts +4 -2
- package/dist/generated/settingsSchemaFns.js +9 -3
- package/dist/generated/settingsSchemaMcpRequestEntry.d.ts +1 -1
- package/dist/generated/settingsSchemaMcpRequestEntry.js +9 -18
- package/dist/generated/settingsSchemaReservedMemberName.d.ts +1 -0
- package/dist/generated/settingsSchemaReservedMemberName.js +7 -0
- package/dist/generated/settingsSchemaTables.d.ts +2 -0
- package/dist/generated/settingsSchemaTables.js +10 -0
- package/dist/generated/settingsSchemaWireName.d.ts +1 -0
- package/dist/generated/settingsSchemaWireName.js +8 -0
- package/dist/headlessReconnectWire.js +12 -7
- package/dist/hitl/approvalAskers.d.ts +13 -0
- package/dist/hitl/approvalAskers.js +79 -0
- package/dist/hitl/approvalResolution.d.ts +8 -1
- package/dist/hitl/approvalResolution.js +37 -2
- package/dist/hitl/approvalsFeed.js +5 -13
- package/dist/hitl/askParkRowRouting.js +17 -1
- package/dist/hitl/decisionRefusal.d.ts +1 -0
- package/dist/hitl/decisionRefusal.js +5 -2
- package/dist/hitl/decisionSend.d.ts +2 -0
- package/dist/hitl/decisionSend.js +22 -12
- package/dist/hitl/frameRouter.d.ts +3 -2
- package/dist/hitl/frameRouter.js +28 -6
- package/dist/hitl/gateLedger.d.ts +2 -0
- package/dist/hitl/gateLedger.js +3 -1
- package/dist/hitl/hitlBridge.d.ts +5 -1
- package/dist/hitl/hitlBridge.js +26 -2
- package/dist/hitl/livePendingAsk.js +4 -2
- package/dist/hitl/parkOwnership.js +4 -5
- package/dist/hitl/parkResolver.js +34 -21
- package/dist/hitl/parkRowBirthWait.d.ts +9 -0
- package/dist/hitl/parkRowBirthWait.js +8 -1
- package/dist/hitl/parkRowDelegation.d.ts +6 -0
- package/dist/hitl/parkRowDelegation.js +33 -0
- package/dist/hitl/persistedRulesWire.d.ts +19 -3
- package/dist/hitl/persistedRulesWire.js +118 -20
- package/dist/hitl/planReviewWire.js +16 -18
- package/dist/hitl/policyDenyCause.d.ts +3 -0
- package/dist/hitl/policyDenyCause.js +16 -0
- package/dist/hitl/sessionAllowRoute.d.ts +5 -0
- package/dist/hitl/sessionAllowRoute.js +42 -0
- package/dist/hitl/sessionPolicyDeliverable.js +1 -1
- package/dist/hitl/standingRows.js +10 -10
- package/dist/hitl/toolApprovalWire.d.ts +8 -2
- package/dist/hitl/toolApprovalWire.js +44 -11
- package/dist/hooksReaderGenerations.d.ts +1 -0
- package/dist/hooksReaderGenerations.js +12 -0
- package/dist/hooksWireCaps.js +8 -4
- package/dist/i18n/messages.en.d.ts +43 -0
- package/dist/i18n/messages.en.js +47 -4
- package/dist/i18n/messages.zh.js +53 -4
- package/dist/index.d.ts +7 -1
- package/dist/index.js +5 -1
- package/dist/injectionLaneNotice.js +2 -1
- package/dist/liveModelCatalog.d.ts +4 -2
- package/dist/liveModelCatalog.js +164 -26
- package/dist/model/catalogDefault.d.ts +25 -0
- package/dist/model/catalogDefault.js +44 -0
- package/dist/noticePresentation.js +2 -0
- package/dist/permissionWireCaps.d.ts +1 -0
- package/dist/permissionWireCaps.js +11 -0
- package/dist/pluginHooksWire.d.ts +5 -3
- package/dist/pluginHooksWire.js +116 -7
- package/dist/queuedDelivery.d.ts +1 -0
- package/dist/queuedDelivery.js +12 -0
- package/dist/resultFrameKeys.js +4 -2
- package/dist/resumeRefusalCopy.js +15 -8
- package/dist/runPermissionModeSwitch.d.ts +2 -1
- package/dist/runPermissionModeSwitch.js +16 -6
- package/dist/sdkWireTransit.d.ts +1 -1
- package/dist/seatContract.d.ts +64 -10
- package/dist/seatContract.js +167 -12
- package/dist/sessionEngine.d.ts +2 -1
- package/dist/sessionEngine.js +5 -0
- package/dist/settingsHooksSalvage.d.ts +5 -0
- package/dist/settingsHooksSalvage.js +54 -0
- package/dist/subagentContentStore.js +1 -1
- package/dist/supportFloor.js +2 -0
- package/dist/taskNotificationAdmission.d.ts +19 -0
- package/dist/taskNotificationAdmission.js +84 -0
- package/dist/toolResult.js +2 -1
- package/dist/webSearchWireCaps.js +9 -4
- package/dist/wireRefusalCopy.d.ts +7 -1
- package/dist/wireRefusalCopy.js +7 -1
- package/docs/INTEGRATION-CLIENTS.md +1189 -18
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SETTINGS_MCP_REQUEST_ENTRY_CLOSURE: (MCP_CONTENT_ORIGINS: readonly ["local", "execution", "external"], startsWithHttpScheme: (raw: string) => boolean, echoCallerText: (text: string) => string, UNINSPECTABLE: "an object that could not be inspected (a proxy trap threw)", captureStringArray: (v: unknown, classification: "strict" | "wide") => string[] | undefined, dataDescriptor: (value: unknown) => PropertyDescriptor, inspect: (read: () => unknown) => object, ownFieldsOf: (record: object, keys: readonly string[]) => object, ownValueOf: (record: object, key: string) => object, readKey: (record: object, key: PropertyKey) => object, isJsonArray: (v: unknown) => v is unknown[], jsonShapeOf: (v: unknown) => "plain" | "non_plain_object" | "array" | "null" | "primitive" | "uninspectable", isReservedMcpServerName: (name: string) => boolean, RESERVED_MCP_SERVER_NAME_REASON: "the \"remote-\" prefix is reserved for the remote MCP servers a device executor offers") => any;
|
|
1
|
+
export declare const SETTINGS_MCP_REQUEST_ENTRY_CLOSURE: (MCP_CONTENT_ORIGINS: readonly ["local", "execution", "external"], startsWithHttpScheme: (raw: string) => boolean, echoCallerText: (text: string) => string, UNINSPECTABLE: "an object that could not be inspected (a proxy trap threw)", captureStringArray: (v: unknown, classification: "strict" | "wide") => string[] | undefined, dataDescriptor: (value: unknown) => PropertyDescriptor, inspect: (read: () => unknown) => object, ownFieldsOf: (record: object, keys: readonly string[]) => object, ownValueOf: (record: object, key: string) => object, readKey: (record: object, key: PropertyKey) => object, isJsonArray: (v: unknown) => v is unknown[], jsonShapeOf: (v: unknown) => "plain" | "non_plain_object" | "array" | "null" | "primitive" | "uninspectable", isReservedMcpServerName: (name: string) => boolean, RESERVED_MCP_SERVER_NAME_REASON: "the \"remote-\" prefix is reserved for the remote MCP servers a device executor offers", TOOL_WIRE_NAME_MAX_CHARS: 128, hasEdgeWhitespace: (v: string) => boolean, isUsableWireName: (v: unknown) => v is string, isReservedMemberName: (name: string) => boolean) => any;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const SETTINGS_MCP_REQUEST_ENTRY_CLOSURE = (MCP_CONTENT_ORIGINS, startsWithHttpScheme, echoCallerText, UNINSPECTABLE, captureStringArray, dataDescriptor, inspect, ownFieldsOf, ownValueOf, readKey, isJsonArray, jsonShapeOf, isReservedMcpServerName, RESERVED_MCP_SERVER_NAME_REASON) => {
|
|
1
|
+
export const SETTINGS_MCP_REQUEST_ENTRY_CLOSURE = (MCP_CONTENT_ORIGINS, startsWithHttpScheme, echoCallerText, UNINSPECTABLE, captureStringArray, dataDescriptor, inspect, ownFieldsOf, ownValueOf, readKey, isJsonArray, jsonShapeOf, isReservedMcpServerName, RESERVED_MCP_SERVER_NAME_REASON, TOOL_WIRE_NAME_MAX_CHARS, hasEdgeWhitespace, isUsableWireName, isReservedMemberName) => {
|
|
2
2
|
function describeKindWord(v) {
|
|
3
3
|
const shape = jsonShapeOf(v);
|
|
4
4
|
switch (shape) {
|
|
@@ -46,11 +46,6 @@ export const SETTINGS_MCP_REQUEST_ENTRY_CLOSURE = (MCP_CONTENT_ORIGINS, startsWi
|
|
|
46
46
|
function quotedCallerText(text) {
|
|
47
47
|
return `"${echoCallerText(text)}"`;
|
|
48
48
|
}
|
|
49
|
-
const TOOL_WIRE_NAME_MAX_CHARS = 128;
|
|
50
|
-
const hasEdgeWhitespace = (v) => v.trim() !== v;
|
|
51
|
-
function isUsableWireName(v) {
|
|
52
|
-
return typeof v === "string" && v.length > 0 && v.length <= TOOL_WIRE_NAME_MAX_CHARS && !hasEdgeWhitespace(v);
|
|
53
|
-
}
|
|
54
49
|
function describeBadWireName(v) {
|
|
55
50
|
if (typeof v !== "string")
|
|
56
51
|
return describeKindWord(v);
|
|
@@ -202,8 +197,7 @@ export const SETTINGS_MCP_REQUEST_ENTRY_CLOSURE = (MCP_CONTENT_ORIGINS, startsWi
|
|
|
202
197
|
}
|
|
203
198
|
return { ok: true, value: { kind: "other", value: kind.value } };
|
|
204
199
|
}
|
|
205
|
-
const
|
|
206
|
-
const RESERVED_MEMBER_FAULT = `no member of a declaration may be named "${PLAIN_STRING_RECORD_RESERVED_MEMBER}"`;
|
|
200
|
+
const reservedMemberFault = (name) => `no member of a declaration may be named "${name}"`;
|
|
207
201
|
function notPlainDetail(shape, v) {
|
|
208
202
|
switch (shape) {
|
|
209
203
|
case "null":
|
|
@@ -233,28 +227,25 @@ export const SETTINGS_MCP_REQUEST_ENTRY_CLOSURE = (MCP_CONTENT_ORIGINS, startsWi
|
|
|
233
227
|
return { ok: false, fault: "not_plain_record", detail: `got ${namesRead.sentence}` };
|
|
234
228
|
const names = namesRead.value;
|
|
235
229
|
const members = Object.create(null);
|
|
236
|
-
|
|
230
|
+
const reservedMembers = names.filter(isReservedMemberName);
|
|
237
231
|
for (const name of names) {
|
|
238
232
|
const read = readKey(v, name);
|
|
239
233
|
const unreadable = read.presence === "unreadable";
|
|
240
234
|
const value = read.presence === "own" ? read.value : undefined;
|
|
241
235
|
if (unreadable || typeof value !== "string") {
|
|
242
236
|
const valueFault = unreadable ? "could not be read (its getter threw)" : `must be a string — got ${describeValueShape(value)}`;
|
|
243
|
-
const
|
|
244
|
-
const fault = reserved ? "member_name_reserved" : "member_not_string";
|
|
237
|
+
const fault = isReservedMemberName(name) ? "member_name_reserved" : "member_not_string";
|
|
245
238
|
return {
|
|
246
239
|
ok: false,
|
|
247
240
|
fault,
|
|
248
241
|
member: echoCallerText(name),
|
|
249
|
-
detail: `its member "${echoCallerText(name)}" ${
|
|
242
|
+
detail: `its member "${echoCallerText(name)}" ${isReservedMemberName(name) ? `is not allowed (${reservedMemberFault(name)}) and its value ${valueFault}` : valueFault}`,
|
|
250
243
|
};
|
|
251
244
|
}
|
|
252
|
-
if (name
|
|
253
|
-
reservedMember = true;
|
|
254
|
-
else
|
|
245
|
+
if (!isReservedMemberName(name))
|
|
255
246
|
Object.defineProperty(members, name, dataDescriptor(value));
|
|
256
247
|
}
|
|
257
|
-
return { ok: true, record: { ...members },
|
|
248
|
+
return { ok: true, record: { ...members }, reservedMembers };
|
|
258
249
|
}
|
|
259
250
|
const issuesOf = (...candidates) => candidates.filter((c) => c !== undefined);
|
|
260
251
|
function readTransportRecord(path, raw) {
|
|
@@ -269,8 +260,8 @@ export const SETTINGS_MCP_REQUEST_ENTRY_CLOSURE = (MCP_CONTENT_ORIGINS, startsWi
|
|
|
269
260
|
}
|
|
270
261
|
return {
|
|
271
262
|
record: read.record,
|
|
272
|
-
issues: read.
|
|
263
|
+
issues: read.reservedMembers.map((name) => ({ path: `${path}.${name}`, reason: reservedMemberFault(name), member: name, fault: "member_name_reserved" })),
|
|
273
264
|
};
|
|
274
265
|
}
|
|
275
|
-
return { readRequestMcpTransport, readMcpIdentityHeaders,
|
|
266
|
+
return { readRequestMcpTransport, readMcpIdentityHeaders, describeBadWireName, isUsableMcpServerName, describeBadMcpServerName, isContentOriginWord, describeBadContentOrigin };
|
|
276
267
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const SETTINGS_RESERVED_MEMBER_NAME_CLOSURE: () => any;
|
|
@@ -13,3 +13,5 @@ export declare const SETTINGS_UI_LANGUAGE_VALUES: readonly ["en", "zh"];
|
|
|
13
13
|
export declare const SETTINGS_MCP_CONTENT_ORIGINS: readonly ["local", "execution", "external"];
|
|
14
14
|
export declare const SETTINGS_MIN_TRUST_WORDS: readonly ["local", "session", "project", "global", "managed", "task"];
|
|
15
15
|
export declare const SETTINGS_WORKSPACE_TRUST_STATES: readonly ["accepted", "declined", "no_trust_step"];
|
|
16
|
+
export declare const SETTINGS_MODEL_PLANE_REFUSAL_CODES: readonly ["config.default_model_missing", "config.model_refused_name_minted", "config.model_route_endpoint_missing", "config.gateway_fallback_without_primary", "config.catalog_replaces_env_models"];
|
|
17
|
+
export declare const SETTINGS_NO_EXECUTION_TARGET_LANES: readonly ["none"];
|
|
@@ -63,3 +63,13 @@ export const SETTINGS_WORKSPACE_TRUST_STATES = Object.freeze([
|
|
|
63
63
|
"declined",
|
|
64
64
|
"no_trust_step",
|
|
65
65
|
]);
|
|
66
|
+
export const SETTINGS_MODEL_PLANE_REFUSAL_CODES = Object.freeze([
|
|
67
|
+
"config.default_model_missing",
|
|
68
|
+
"config.model_refused_name_minted",
|
|
69
|
+
"config.model_route_endpoint_missing",
|
|
70
|
+
"config.gateway_fallback_without_primary",
|
|
71
|
+
"config.catalog_replaces_env_models",
|
|
72
|
+
]);
|
|
73
|
+
export const SETTINGS_NO_EXECUTION_TARGET_LANES = Object.freeze([
|
|
74
|
+
"none",
|
|
75
|
+
]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const SETTINGS_WIRE_NAME_CLOSURE: () => any;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const SETTINGS_WIRE_NAME_CLOSURE = () => {
|
|
2
|
+
const TOOL_WIRE_NAME_MAX_CHARS = 128;
|
|
3
|
+
const hasEdgeWhitespace = (v) => v.trim() !== v;
|
|
4
|
+
function isUsableWireName(v) {
|
|
5
|
+
return typeof v === "string" && v.length > 0 && v.length <= TOOL_WIRE_NAME_MAX_CHARS && !hasEdgeWhitespace(v);
|
|
6
|
+
}
|
|
7
|
+
return { isUsableWireName, TOOL_WIRE_NAME_MAX_CHARS, hasEdgeWhitespace };
|
|
8
|
+
};
|
|
@@ -53,7 +53,7 @@ export async function* withHeadlessR1Reconnect(live, deps) {
|
|
|
53
53
|
let attempts = 0;
|
|
54
54
|
let lastSeenSeq;
|
|
55
55
|
const probe = deps.probeAlive ?? probeEngineAlive;
|
|
56
|
-
let
|
|
56
|
+
let tearShape;
|
|
57
57
|
const drains = createStreamDrainingLegs();
|
|
58
58
|
let drainReopenAfterMs;
|
|
59
59
|
let lastBreak;
|
|
@@ -62,6 +62,11 @@ export async function* withHeadlessR1Reconnect(live, deps) {
|
|
|
62
62
|
registerRunNotFollowed(originalTearErr, fact);
|
|
63
63
|
throw originalTearErr;
|
|
64
64
|
};
|
|
65
|
+
const unboundHandle = (cause) => {
|
|
66
|
+
if (tearShape === 'transport')
|
|
67
|
+
throw originalTearErr;
|
|
68
|
+
return giveUp(mintRunNotFollowed(cause));
|
|
69
|
+
};
|
|
65
70
|
const terminalFromRunRow = async (taskId) => {
|
|
66
71
|
const row = await deps.getRun(taskId).catch(() => null);
|
|
67
72
|
const terminal = terminalEventFromRunRow(row);
|
|
@@ -72,21 +77,21 @@ export async function* withHeadlessR1Reconnect(live, deps) {
|
|
|
72
77
|
for (;;) {
|
|
73
78
|
if (deps.signal?.aborted)
|
|
74
79
|
throw originalTearErr;
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
if (
|
|
80
|
+
if (tearShape === undefined)
|
|
81
|
+
tearShape = isSseIdleError(originalTearErr) ? 'idle' : guardedTransport(originalTearErr) ? 'transport' : 'other';
|
|
82
|
+
if (tearShape === 'other')
|
|
78
83
|
throw originalTearErr;
|
|
79
84
|
let handle;
|
|
80
85
|
try {
|
|
81
86
|
handle = deps.getTaskId();
|
|
82
87
|
}
|
|
83
88
|
catch {
|
|
84
|
-
return
|
|
89
|
+
return unboundHandle('bad_input');
|
|
85
90
|
}
|
|
86
91
|
if (handle === null || handle === undefined)
|
|
87
|
-
return
|
|
92
|
+
return unboundHandle('no_run_id');
|
|
88
93
|
if (typeof handle !== 'string' || handle === '')
|
|
89
|
-
return
|
|
94
|
+
return unboundHandle('bad_input');
|
|
90
95
|
const taskId = handle;
|
|
91
96
|
if (drainReopenAfterMs !== undefined) {
|
|
92
97
|
const waitMs = drainReopenAfterMs;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ApprovalAskReasonByHook {
|
|
2
|
+
readonly by: 'hook';
|
|
3
|
+
readonly cause: string;
|
|
4
|
+
readonly registration?: string;
|
|
5
|
+
readonly words?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ApprovalAskReasonByPolicy {
|
|
8
|
+
readonly by: string;
|
|
9
|
+
readonly words: string;
|
|
10
|
+
}
|
|
11
|
+
export type ApprovalAskReason = ApprovalAskReasonByHook | ApprovalAskReasonByPolicy;
|
|
12
|
+
export declare function approvalAskReasonsOf(carrier: object | null | undefined): readonly ApprovalAskReason[] | undefined;
|
|
13
|
+
export declare function approvalAgentIdOf(carrier: object | null | undefined): string | undefined;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { appendOwnElement } from '../ownKey.js';
|
|
2
|
+
import { slotReadOf } from '../wireFailureShape.js';
|
|
3
|
+
function ownNonEmptyText(o, key) {
|
|
4
|
+
const r = slotReadOf(o, key);
|
|
5
|
+
return r.presence === 'present' && typeof r.value === 'string' && r.value !== '' ? r.value : undefined;
|
|
6
|
+
}
|
|
7
|
+
function optionalText(o, key) {
|
|
8
|
+
const r = slotReadOf(o, key);
|
|
9
|
+
if (r.presence === 'absent')
|
|
10
|
+
return { state: 'absent' };
|
|
11
|
+
if (r.presence === 'present' && typeof r.value === 'string')
|
|
12
|
+
return { state: 'text', value: r.value };
|
|
13
|
+
return { state: 'bad' };
|
|
14
|
+
}
|
|
15
|
+
function askReasonOf(item) {
|
|
16
|
+
if (typeof item !== 'object' || item === null)
|
|
17
|
+
return undefined;
|
|
18
|
+
const by = ownNonEmptyText(item, 'by');
|
|
19
|
+
if (by === undefined)
|
|
20
|
+
return undefined;
|
|
21
|
+
const words = optionalText(item, 'words');
|
|
22
|
+
if (words.state === 'bad')
|
|
23
|
+
return undefined;
|
|
24
|
+
if (by === 'hook') {
|
|
25
|
+
const cause = ownNonEmptyText(item, 'cause');
|
|
26
|
+
if (cause === undefined)
|
|
27
|
+
return undefined;
|
|
28
|
+
const registration = optionalText(item, 'registration');
|
|
29
|
+
if (registration.state === 'bad')
|
|
30
|
+
return undefined;
|
|
31
|
+
return Object.freeze({
|
|
32
|
+
by: 'hook',
|
|
33
|
+
cause,
|
|
34
|
+
...(registration.state === 'text' ? { registration: registration.value } : {}),
|
|
35
|
+
...(words.state === 'text' ? { words: words.value } : {}),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
if (words.state !== 'text')
|
|
39
|
+
return undefined;
|
|
40
|
+
return Object.freeze({ by, words: words.value });
|
|
41
|
+
}
|
|
42
|
+
export function approvalAskReasonsOf(carrier) {
|
|
43
|
+
try {
|
|
44
|
+
if (typeof carrier !== 'object' || carrier === null)
|
|
45
|
+
return undefined;
|
|
46
|
+
const slot = slotReadOf(carrier, 'askReasons');
|
|
47
|
+
if (slot.presence !== 'present')
|
|
48
|
+
return undefined;
|
|
49
|
+
const raw = slot.value;
|
|
50
|
+
if (typeof raw !== 'object' || raw === null || !Array.isArray(raw))
|
|
51
|
+
return undefined;
|
|
52
|
+
const len = slotReadOf(raw, 'length');
|
|
53
|
+
if (len.presence !== 'present' || typeof len.value !== 'number' || !Number.isSafeInteger(len.value) || len.value < 0)
|
|
54
|
+
return undefined;
|
|
55
|
+
const out = [];
|
|
56
|
+
for (let i = 0; i < len.value; i++) {
|
|
57
|
+
const el = slotReadOf(raw, String(i));
|
|
58
|
+
if (el.presence !== 'present')
|
|
59
|
+
return undefined;
|
|
60
|
+
const reason = askReasonOf(el.value);
|
|
61
|
+
if (reason !== undefined)
|
|
62
|
+
appendOwnElement(out, reason);
|
|
63
|
+
}
|
|
64
|
+
return out.length === 0 ? undefined : Object.freeze(out);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export function approvalAgentIdOf(carrier) {
|
|
71
|
+
try {
|
|
72
|
+
if (typeof carrier !== 'object' || carrier === null)
|
|
73
|
+
return undefined;
|
|
74
|
+
return ownNonEmptyText(carrier, 'agentId');
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import type { DecideReceiptView } from '../decideReceipt.js';
|
|
2
2
|
import { type HitlSafetyCode } from './hitlBridge.js';
|
|
3
3
|
import type { AskAnsweredOutput } from './gateLedger.js';
|
|
4
|
+
import type { PolicyDenyCause } from './policyDenyCause.js';
|
|
4
5
|
import type { ApprovalDenySettledBy, ToolApprovalRespondAckView, ToolApprovalRespondDecision, ToolApprovalRespondParkingView } from './toolApprovalWire.js';
|
|
5
6
|
import { type DecisionRefusalReading, type RefusalRouteContext } from './decisionRefusal.js';
|
|
6
7
|
import { type DeliveredReading, type LadderSafetyStop } from './attributionLadder.js';
|
|
8
|
+
import { type CheckpointRowVerdictReading } from '../checkpointRowVerdict.js';
|
|
7
9
|
export declare const APPROVAL_RESOLUTION_LANES: readonly ["respond", "decide", "ask"];
|
|
8
10
|
export type ApprovalResolutionLane = (typeof APPROVAL_RESOLUTION_LANES)[number];
|
|
9
|
-
export declare const APPROVAL_NOT_SENT_CAUSES: readonly ["retracted", "edit_refused", "no_pending", "wrong_gate", "fetch_failed", "no_input", "card_unavailable", "safety_stop"];
|
|
11
|
+
export declare const APPROVAL_NOT_SENT_CAUSES: readonly ["retracted", "edit_refused", "no_pending", "wrong_gate", "fetch_failed", "no_input", "card_unavailable", "safety_stop", "row_unresumable"];
|
|
10
12
|
export type ApprovalNotSentCause = (typeof APPROVAL_NOT_SENT_CAUSES)[number];
|
|
11
13
|
export declare const APPROVAL_UNSETTLED_CAUSES: readonly ["engine_refused", "engine_error", "no_answer", "transport_exhausted", "parking_in_progress", "interrupted", "orchestration", "unreadable"];
|
|
12
14
|
export type ApprovalUnsettledCause = (typeof APPROVAL_UNSETTLED_CAUSES)[number];
|
|
13
15
|
export interface ApprovalDenyFacts {
|
|
14
16
|
readonly settledBy?: ApprovalDenySettledBy;
|
|
15
17
|
readonly reason?: string;
|
|
18
|
+
readonly policyCause?: PolicyDenyCause;
|
|
16
19
|
readonly attributionKey?: 'dropped' | 'renamed';
|
|
17
20
|
}
|
|
18
21
|
interface ApprovalResolutionBase {
|
|
@@ -32,6 +35,9 @@ export interface ApprovalResolutionNotSent extends ApprovalResolutionBase {
|
|
|
32
35
|
readonly kind: 'not_sent';
|
|
33
36
|
readonly cause: ApprovalNotSentCause;
|
|
34
37
|
readonly safetyCode?: Exclude<HitlSafetyCode, 'binding_mismatch' | 'no_pending' | 'wrong_gate'>;
|
|
38
|
+
readonly rowVerdict?: Exclude<CheckpointRowVerdictReading, {
|
|
39
|
+
readonly kind: 'ok';
|
|
40
|
+
}>;
|
|
35
41
|
readonly detail?: string;
|
|
36
42
|
}
|
|
37
43
|
export interface ApprovalResolutionUnsettled extends ApprovalResolutionBase {
|
|
@@ -49,6 +55,7 @@ export declare function approvalUnsettledCause(r: ApprovalResolution): ApprovalU
|
|
|
49
55
|
export declare const APPROVAL_NOTE_RETRACTED_DETAIL = "the card was retracted (this host no longer has a decision port for it)";
|
|
50
56
|
export declare const APPROVAL_NOTE_EDIT_REFUSED_DETAIL = "edits are not accepted on this card";
|
|
51
57
|
export declare function approvalResolutionDetail(r: ApprovalResolution): string | undefined;
|
|
58
|
+
export declare function approvalResolutionStatusTag(r: ApprovalResolution): string;
|
|
52
59
|
export declare function unsettledCauseOfReading(reading: DecisionRefusalReading): 'engine_refused' | 'engine_error' | 'no_answer';
|
|
53
60
|
export interface DecisionFailureMint {
|
|
54
61
|
readonly lane: ApprovalResolutionLane;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { DecideTransportRetryExhaustedError, refusalCarrierOf, safetyStopHeadOf, safetyStopNamed } from './hitlBridge.js';
|
|
2
|
-
import { mintDecisionRefusalReading } from './decisionRefusal.js';
|
|
2
|
+
import { decisionStatusTag, mintDecisionRefusalReading } from './decisionRefusal.js';
|
|
3
3
|
import { isSlotCarrier, thrownIsInstanceOf } from '../wireFailureShape.js';
|
|
4
4
|
import { carrierReadOf, isLocalSafetyStop, localCodeReadOf, topCauseReadOf } from './attributionLadder.js';
|
|
5
5
|
import { directFailureReadingOf, transportReadingOf } from './approvalDelivery.js';
|
|
6
6
|
import { ownReadingSlot } from '../engineWireFor.js';
|
|
7
|
+
import { CHECKPOINT_ROW_BAR_SENTENCE, unboundWayOutContent } from '../checkpointRowVerdict.js';
|
|
7
8
|
export const APPROVAL_RESOLUTION_LANES = Object.freeze(['respond', 'decide', 'ask']);
|
|
8
9
|
export const APPROVAL_NOT_SENT_CAUSES = Object.freeze([
|
|
9
10
|
'retracted',
|
|
@@ -14,6 +15,7 @@ export const APPROVAL_NOT_SENT_CAUSES = Object.freeze([
|
|
|
14
15
|
'no_input',
|
|
15
16
|
'card_unavailable',
|
|
16
17
|
'safety_stop',
|
|
18
|
+
'row_unresumable',
|
|
17
19
|
]);
|
|
18
20
|
export const APPROVAL_UNSETTLED_CAUSES = Object.freeze([
|
|
19
21
|
'engine_refused',
|
|
@@ -49,6 +51,7 @@ const NOT_SENT_SENTENCES = Object.freeze({
|
|
|
49
51
|
no_input: 'the approval has no tool input or question to show, so no card was shown; nothing was sent',
|
|
50
52
|
card_unavailable: 'no approval card could be shown on this host; nothing was sent',
|
|
51
53
|
safety_stop: 'the decision was stopped by a local safety check before it was sent; nothing was sent',
|
|
54
|
+
row_unresumable: CHECKPOINT_ROW_BAR_SENTENCE,
|
|
52
55
|
});
|
|
53
56
|
function nonEmpty(v) {
|
|
54
57
|
return typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
@@ -77,8 +80,21 @@ export function approvalResolutionDetail(r) {
|
|
|
77
80
|
if (cause === undefined || !Object.hasOwn(NOT_SENT_SENTENCES, cause))
|
|
78
81
|
return undefined;
|
|
79
82
|
const sentence = NOT_SENT_SENTENCES[cause];
|
|
83
|
+
if (cause === 'row_unresumable') {
|
|
84
|
+
let verdict;
|
|
85
|
+
try {
|
|
86
|
+
const d = Object.getOwnPropertyDescriptor(r, 'rowVerdict');
|
|
87
|
+
verdict = d !== undefined && Object.hasOwn(d, 'value') ? d.value : undefined;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
verdict = undefined;
|
|
91
|
+
}
|
|
92
|
+
return ownStr(verdict, 'kind') !== 'unreadable' ? (ownStr(verdict, 'sentence') ?? sentence) : sentence;
|
|
93
|
+
}
|
|
80
94
|
const code = cause === 'safety_stop' ? ownStr(r, 'safetyCode') : undefined;
|
|
81
|
-
|
|
95
|
+
if (code === undefined)
|
|
96
|
+
return sentence;
|
|
97
|
+
return code === 'binding_missing' ? `${sentence} (${code}) · ${unboundWayOutContent('unknown')}` : `${sentence} (${code})`;
|
|
82
98
|
}
|
|
83
99
|
if (kind !== 'unsettled')
|
|
84
100
|
return undefined;
|
|
@@ -93,6 +109,25 @@ export function approvalResolutionDetail(r) {
|
|
|
93
109
|
const parts = [ownStr(refusal, 'errorCode'), ownStr(r, 'detail') ?? ownStr(refusal, 'message')].filter((x) => x !== undefined);
|
|
94
110
|
return parts.length > 0 ? parts.join(' · ') : undefined;
|
|
95
111
|
}
|
|
112
|
+
export function approvalResolutionStatusTag(r) {
|
|
113
|
+
try {
|
|
114
|
+
if (typeof r !== 'object' || r === null)
|
|
115
|
+
return '';
|
|
116
|
+
const kd = Object.getOwnPropertyDescriptor(r, 'kind');
|
|
117
|
+
if (kd === undefined || !Object.hasOwn(kd, 'value') || kd.value !== 'unsettled')
|
|
118
|
+
return '';
|
|
119
|
+
const rd = Object.getOwnPropertyDescriptor(r, 'refusal');
|
|
120
|
+
const refusal = rd !== undefined && Object.hasOwn(rd, 'value') ? rd.value : undefined;
|
|
121
|
+
if (typeof refusal !== 'object' || refusal === null)
|
|
122
|
+
return '';
|
|
123
|
+
const sd = Object.getOwnPropertyDescriptor(refusal, 'status');
|
|
124
|
+
const status = sd !== undefined && Object.hasOwn(sd, 'value') ? sd.value : undefined;
|
|
125
|
+
return typeof status === 'number' && Number.isInteger(status) && status >= 400 && status <= 599 ? decisionStatusTag(status) : '';
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return '';
|
|
129
|
+
}
|
|
130
|
+
}
|
|
96
131
|
export function unsettledCauseOfReading(reading) {
|
|
97
132
|
const standing = ownReadingSlot(reading, 'standing');
|
|
98
133
|
if (standing === 'ambiguous')
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { hostLog } from '../host.js';
|
|
2
|
+
import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
|
|
2
3
|
import { unrefTimer } from '../unrefTimer.js';
|
|
3
4
|
import { readLivePendingRows } from './livePendingAsk.js';
|
|
4
5
|
import { createStreamDrainingLegs } from '../streamDrainingLegs.js';
|
|
5
6
|
import { approvalQueueRowFamilyOf, peerInboundQueueRowOf } from './approvalQueueRow.js';
|
|
7
|
+
import { parkRowDelegationLinksOf } from './parkRowDelegation.js';
|
|
6
8
|
import { checkpointRowVerdictOf } from '../checkpointRowVerdict.js';
|
|
7
9
|
const _realClientFitsFeedSlice = true;
|
|
8
10
|
void _realClientFitsFeedSlice;
|
|
@@ -49,7 +51,7 @@ function readDurablePendingRows(raw) {
|
|
|
49
51
|
}
|
|
50
52
|
function durableDigestOf(rows) {
|
|
51
53
|
return rows
|
|
52
|
-
.map(r => [r.sessionId ?? '', r.taskId ?? '', r.toolCallId ?? '', r.boundCallId ?? '', r.boundInputHash ?? '', r.toolName ?? '', JSON.stringify(checkpointRowVerdictOf(r) ?? null)]
|
|
54
|
+
.map(r => [r.sessionId ?? '', r.taskId ?? '', r.toolCallId ?? '', r.boundCallId ?? '', r.boundInputHash ?? '', r.toolName ?? '', JSON.stringify(checkpointRowVerdictOf(r) ?? null), JSON.stringify(parkRowDelegationLinksOf(r) ?? null)]
|
|
53
55
|
.join(''))
|
|
54
56
|
.sort()
|
|
55
57
|
.join('');
|
|
@@ -175,12 +177,7 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
|
|
|
175
177
|
const at = Date.now();
|
|
176
178
|
blind = { why, at };
|
|
177
179
|
stats.unknowns++;
|
|
178
|
-
|
|
179
|
-
onSnapshot({ kind: 'unknown', why, at, mode: via });
|
|
180
|
-
}
|
|
181
|
-
catch (e) {
|
|
182
|
-
hostLog('debug', `approvalsFeed: subscriber threw on the unknown arm (swallowed): ${String(e)}`);
|
|
183
|
-
}
|
|
180
|
+
invokeHostCallbackIsolated('approvalsFeed: subscriber (unknown arm)', () => onSnapshot({ kind: 'unknown', why, at, mode: via }));
|
|
184
181
|
}
|
|
185
182
|
async function take(via, leg) {
|
|
186
183
|
if (stopped)
|
|
@@ -292,12 +289,7 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
|
|
|
292
289
|
};
|
|
293
290
|
last = snap;
|
|
294
291
|
stats.snapshots++;
|
|
295
|
-
|
|
296
|
-
onSnapshot(snap);
|
|
297
|
-
}
|
|
298
|
-
catch (e) {
|
|
299
|
-
hostLog('debug', `approvalsFeed: subscriber threw (swallowed): ${String(e)}`);
|
|
300
|
-
}
|
|
292
|
+
invokeHostCallbackIsolated('approvalsFeed: subscriber', () => onSnapshot(snap));
|
|
301
293
|
}
|
|
302
294
|
function schedulePoll() {
|
|
303
295
|
if (stopped || ac.signal.aborted)
|
|
@@ -3,6 +3,8 @@ import { isAskTool, questionCardVerdict } from './frameRouter.js';
|
|
|
3
3
|
import { approvalCallKey } from './gateIdentity.js';
|
|
4
4
|
import { askQuestionsFromPending, pendingRowIsOwnedByThisSession } from './parkOwnership.js';
|
|
5
5
|
import { approvalQueueRowFamilyOf, checkpointQueueRowSortOf, QUEUE_ROW_FAMILY_UNREADABLE_GATE } from './approvalQueueRow.js';
|
|
6
|
+
import { checkpointRowBarOf, checkpointRowBarSentence } from '../checkpointRowVerdict.js';
|
|
7
|
+
import { msg, renderMessage } from '../i18n/translate.js';
|
|
6
8
|
export const ASK_PARK_ROW_WAIT_MS = 15000;
|
|
7
9
|
export const ASK_PARK_ROW_POLL_MS = 500;
|
|
8
10
|
export function classifyAskParkRows(queueRows, taskId, deps) {
|
|
@@ -29,6 +31,15 @@ export function classifyAskParkRows(queueRows, taskId, deps) {
|
|
|
29
31
|
};
|
|
30
32
|
}
|
|
31
33
|
const foreignKind = familyUnreadable.has(pending) ? QUEUE_ROW_FAMILY_UNREADABLE_GATE : askParkForeignGateKind(pending);
|
|
34
|
+
const bar = foreignKind === null || foreignKind === QUEUE_ROW_FAMILY_UNREADABLE_GATE ? checkpointRowBarOf(pending) : undefined;
|
|
35
|
+
if (bar !== undefined) {
|
|
36
|
+
return {
|
|
37
|
+
kind: 'settled',
|
|
38
|
+
reason: renderMessage(msg('approval.rowUnresumable.askNotSurfaced', { taskId: String(pending.taskId), sentence: checkpointRowBarSentence(bar) })),
|
|
39
|
+
rowVerdict: bar,
|
|
40
|
+
rowTaskId: askParkRowTaskIdOf(taskId, pending),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
32
43
|
if (foreignKind !== null) {
|
|
33
44
|
return {
|
|
34
45
|
kind: 'unborn',
|
|
@@ -40,8 +51,11 @@ export function classifyAskParkRows(queueRows, taskId, deps) {
|
|
|
40
51
|
function askParkRowGateKind(row) {
|
|
41
52
|
return typeof row.gateKind === 'string' && row.gateKind.length > 0 ? row.gateKind : null;
|
|
42
53
|
}
|
|
54
|
+
function askParkRowTaskIdOf(taskId, row) {
|
|
55
|
+
return typeof row.taskId === 'string' && row.taskId.length > 0 ? row.taskId : taskId;
|
|
56
|
+
}
|
|
43
57
|
export function askParkRowIdentity(taskId, row) {
|
|
44
|
-
const rowTaskId =
|
|
58
|
+
const rowTaskId = askParkRowTaskIdOf(taskId, row);
|
|
45
59
|
const gatedCallId = row.toolCallId ?? row.boundCallId ?? undefined;
|
|
46
60
|
return { rowTaskId, gatedCallId, armedKey: approvalCallKey(gatedCallId, rowTaskId) };
|
|
47
61
|
}
|
|
@@ -129,6 +143,8 @@ export function pendingRowIsOwnedAndDecidable(row, deps) {
|
|
|
129
143
|
return true;
|
|
130
144
|
if (leg !== 'approval')
|
|
131
145
|
return false;
|
|
146
|
+
if (checkpointRowBarOf(row) !== undefined)
|
|
147
|
+
return false;
|
|
132
148
|
return askParkRowArm(row) === 'question' ? askQuestionsFromPending(row) !== null : true;
|
|
133
149
|
}
|
|
134
150
|
export function countOwnedPendingRows(listed, deps) {
|
|
@@ -78,5 +78,6 @@ export interface UndeliverableDecisionFacts {
|
|
|
78
78
|
readonly end: UndeliverableDecisionEnd;
|
|
79
79
|
readonly runId?: string;
|
|
80
80
|
}
|
|
81
|
+
export declare function decisionStatusTag(status: number, errorCode?: string): string;
|
|
81
82
|
export declare function undeliverableDecisionContent(facts: UndeliverableDecisionFacts): string;
|
|
82
83
|
export declare function decideRowUnrevivableNotice(lang?: UiLanguage | (string & {})): string;
|
|
@@ -314,9 +314,12 @@ export function decisionRefusalClosesOut(refusal, pending) {
|
|
|
314
314
|
return false;
|
|
315
315
|
return standing === 'ambiguous' || standing === 'refused-pending';
|
|
316
316
|
}
|
|
317
|
+
export function decisionStatusTag(status, errorCode) {
|
|
318
|
+
const code = typeof errorCode === 'string' && isDisplayableWireCode(errorCode) ? ` ${errorCode}` : '';
|
|
319
|
+
return `(HTTP ${String(status)}${code})`;
|
|
320
|
+
}
|
|
317
321
|
export function undeliverableDecisionContent(facts) {
|
|
318
|
-
const
|
|
319
|
-
const head = `The engine refused the approval decision this client sent (HTTP ${String(facts.status)}${code}), so the call it asked about could not be answered from here and did not run`;
|
|
322
|
+
const head = `The engine refused the approval decision this client sent ${decisionStatusTag(facts.status, facts.errorCode)}, so the call it asked about could not be answered from here and did not run`;
|
|
320
323
|
const runId = typeof facts.runId === 'string' && isDisplayableWireCode(facts.runId) ? ` ${facts.runId}` : '';
|
|
321
324
|
switch (facts.end) {
|
|
322
325
|
case 'run-stopped':
|
|
@@ -20,6 +20,7 @@ export interface HitlApprovalDecisionRequest {
|
|
|
20
20
|
readonly taskId?: string;
|
|
21
21
|
readonly toolCallId?: string;
|
|
22
22
|
readonly answer: ApprovalCardDecision;
|
|
23
|
+
readonly card?: object;
|
|
23
24
|
}
|
|
24
25
|
export interface HitlAskDecisionRequest {
|
|
25
26
|
readonly family: 'ask';
|
|
@@ -28,6 +29,7 @@ export interface HitlAskDecisionRequest {
|
|
|
28
29
|
readonly approvalId?: string;
|
|
29
30
|
readonly toolCallId?: string;
|
|
30
31
|
readonly answer: ApprovalCardDecision;
|
|
32
|
+
readonly card?: object;
|
|
31
33
|
}
|
|
32
34
|
export interface HitlElicitationDecisionRequest {
|
|
33
35
|
readonly family: 'elicitation';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { settleWithinDeadline } from '../abortableSleep.js';
|
|
2
2
|
import { hostLog } from '../host.js';
|
|
3
|
+
import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
|
|
3
4
|
import { msg, renderMessage } from '../i18n/translate.js';
|
|
4
5
|
import { askGoneFromError } from '../decideReceipt.js';
|
|
5
6
|
import { appendOwnElement, guardedRead, readOwnDataNonEmptyString, readOwnDataValue } from '../ownKey.js';
|
|
@@ -10,7 +11,7 @@ import { readDecisionNoteAudit } from './approvalDecisionNoteAudit.js';
|
|
|
10
11
|
import { carrierReadOf, deliverWithAttributionLadder, isLocalSafetyStop } from './attributionLadder.js';
|
|
11
12
|
import { budgetSpent, deliverApprovalDecision, deliveryTiming, isAborted, newSendTally, PARKING_WAIT_CAP_MS, setDeliveryTimingForTests, sleepFor, settledTransportReadingOf, withTransportRetry, deliveryReadingOf, } from './approvalDelivery.js';
|
|
12
13
|
import { ASK_DECIDED_CODE, classifyDeliveredDecisionRefusal, decisionRefusalClosesOut, notPendingAfterRetry } from './decisionRefusal.js';
|
|
13
|
-
import {
|
|
14
|
+
import { sessionAllowEligible } from './sessionAllowRoute.js';
|
|
14
15
|
import { addDecisionOpKeys, closeDecisionOp, decisionLedgerKeysOf, decisionOpsWithKey, openDecisionOp } from './decisionLedger.js';
|
|
15
16
|
import { hostHoldIsActive, takeHostHoldLedgerOp, tryHoldApprovalNow } from './approvalHolds.js';
|
|
16
17
|
import { answerPayloadFor, decisionAttributionKeyOf, isToolApprovalRespondReplayed, readApprovalDenySettledBy, readToolApprovalRespondAck, readToolApprovalRespondParking, } from './toolApprovalWire.js';
|
|
@@ -195,7 +196,7 @@ async function sendAskFamily(respondForFallback, decideAsk, req, ctx) {
|
|
|
195
196
|
if (payload === undefined)
|
|
196
197
|
return { family: 'ask', attempts: ctx.tally.attempts, kind: 'not_sent', cause: 'not_an_answer' };
|
|
197
198
|
let fallbackRespond = respondForFallback;
|
|
198
|
-
if (req.answer.kind === 'allow' && req.answer.allowSession === true && respondForFallback !== undefined && req.approvalId !== undefined
|
|
199
|
+
if (req.answer.kind === 'allow' && req.answer.allowSession === true && respondForFallback !== undefined && req.approvalId !== undefined) {
|
|
199
200
|
const viaAnswerGate = await sendApprovalFamily(respondForFallback, req.approvalId, req.answer, ctx);
|
|
200
201
|
if (!answerGateCannotTake(viaAnswerGate))
|
|
201
202
|
return { ...viaAnswerGate, family: 'ask', allowSessionCarried: true };
|
|
@@ -343,12 +344,7 @@ function holdFits(handle, req) {
|
|
|
343
344
|
}
|
|
344
345
|
}
|
|
345
346
|
function callHold(call) {
|
|
346
|
-
|
|
347
|
-
call();
|
|
348
|
-
}
|
|
349
|
-
catch (e) {
|
|
350
|
-
hostLog('debug', `decisionSend.callHold: ${String(e).slice(0, 120)}`);
|
|
351
|
-
}
|
|
347
|
+
invokeHostCallbackIsolated('decisionSend: hold verb', call);
|
|
352
348
|
}
|
|
353
349
|
function hostField(holder, key) {
|
|
354
350
|
if (typeof holder !== 'object' || holder === null)
|
|
@@ -386,6 +382,14 @@ function snapshotAnswer(raw) {
|
|
|
386
382
|
}
|
|
387
383
|
return typeof kind === 'string' ? Object.freeze({ kind }) : undefined;
|
|
388
384
|
}
|
|
385
|
+
function sessionGatedAnswer(answer, card) {
|
|
386
|
+
if (answer.kind !== 'allow' || answer.allowSession !== true)
|
|
387
|
+
return { answer };
|
|
388
|
+
const why = card === undefined ? 'card_absent' : typeof card === 'object' && card !== null ? sessionAllowEligible(card) : 'card_unreadable';
|
|
389
|
+
if (why === 'given')
|
|
390
|
+
return { answer };
|
|
391
|
+
return { answer: Object.freeze({ ...answer, allowSession: false }), dropped: why };
|
|
392
|
+
}
|
|
389
393
|
function readRequest(req) {
|
|
390
394
|
if (typeof req !== 'object' || req === null)
|
|
391
395
|
return undefined;
|
|
@@ -399,17 +403,19 @@ function readRequest(req) {
|
|
|
399
403
|
const rawAnswer = guardedRead(() => readOwnDataValue(req, 'answer'));
|
|
400
404
|
if (typeof rawAnswer !== 'object' || rawAnswer === null)
|
|
401
405
|
return undefined;
|
|
402
|
-
const
|
|
403
|
-
if (
|
|
406
|
+
const snapshot = snapshotAnswer(rawAnswer);
|
|
407
|
+
if (snapshot === undefined)
|
|
404
408
|
return undefined;
|
|
409
|
+
const { answer, dropped } = sessionGatedAnswer(snapshot, guardedRead(() => readOwnDataValue(req, 'card')));
|
|
410
|
+
const droppedFact = dropped !== undefined ? { sessionAllowDropped: dropped } : {};
|
|
405
411
|
if (family === 'approval') {
|
|
406
412
|
const approvalId = s('approvalId');
|
|
407
|
-
return approvalId !== undefined ? { family, approvalId, taskId: s('taskId'), toolCallId: s('toolCallId'), answer } : undefined;
|
|
413
|
+
return approvalId !== undefined ? { family, approvalId, taskId: s('taskId'), toolCallId: s('toolCallId'), answer, ...droppedFact } : undefined;
|
|
408
414
|
}
|
|
409
415
|
if (family === 'ask') {
|
|
410
416
|
const taskId = s('taskId');
|
|
411
417
|
const askId = s('askId');
|
|
412
|
-
return taskId !== undefined && askId !== undefined ? { family, taskId, askId, approvalId: s('approvalId'), toolCallId: s('toolCallId'), answer } : undefined;
|
|
418
|
+
return taskId !== undefined && askId !== undefined ? { family, taskId, askId, approvalId: s('approvalId'), toolCallId: s('toolCallId'), answer, ...droppedFact } : undefined;
|
|
413
419
|
}
|
|
414
420
|
return undefined;
|
|
415
421
|
}
|
|
@@ -519,6 +525,10 @@ export async function sendHitlDecision(request, deps) {
|
|
|
519
525
|
out = outcomeOfThrown(e, 'elicitation', undefined, signal, ctx.tally, false);
|
|
520
526
|
}
|
|
521
527
|
}
|
|
528
|
+
if (req.family !== 'elicitation' && req.sessionAllowDropped !== undefined) {
|
|
529
|
+
hostLog('debug', `decisionSend: session-allow-not-eligible ${req.family}=${req.family === 'ask' ? req.askId : req.approvalId} why=${req.sessionAllowDropped} via=allow-once`);
|
|
530
|
+
out = { ...out, rememberDropped: true };
|
|
531
|
+
}
|
|
522
532
|
const ambiguous = out.kind === 'unconfirmed' || (out.kind === 'not_applied' && (out.cause === 'parking' || out.cause === 'refused' || out.cause === 'idempotency_crossed'));
|
|
523
533
|
if (ambiguous && out.cause !== 'aborted') {
|
|
524
534
|
const gate = req.family === 'elicitation'
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { AgentEvent } from '@sema-agent/sdk';
|
|
2
2
|
import type { DecideOperationObserver, HitlClientLike } from './hitlBridge.js';
|
|
3
3
|
import { type RespondToolApprovalFn, type ToolApprovalFrame, type ToolApprovalFrameLaneOpts } from './toolApprovalWire.js';
|
|
4
|
-
import type { ApprovalResolution } from './approvalResolution.js';
|
|
5
|
-
import type { GateLedger } from './gateLedger.js';
|
|
4
|
+
import type { ApprovalDenyFacts, ApprovalResolution } from './approvalResolution.js';
|
|
5
|
+
import type { GateLedger, GateDenyAttribution } from './gateLedger.js';
|
|
6
6
|
import type { NoAllowCause } from './noAllowCause.js';
|
|
7
7
|
import { type ReplayedApprovalDisposition } from './replayedApproval.js';
|
|
8
8
|
import type { ReattachDrainingEvent } from '../runReattachPolicy.js';
|
|
@@ -17,6 +17,7 @@ export interface PolicyDenyReasonFacts {
|
|
|
17
17
|
readonly budgetSetting?: string;
|
|
18
18
|
}
|
|
19
19
|
export declare function policyDenyReason(facts: PolicyDenyReasonFacts): string;
|
|
20
|
+
export declare function denyAttributionOf(deny: ApprovalDenyFacts | undefined): GateDenyAttribution;
|
|
20
21
|
export declare const HITL_INTERRUPT_MESSAGE_FOR_TOOL_USE = "[Request interrupted by user for tool use]";
|
|
21
22
|
export declare const ENGINE_ABORT_TOOL_RESULT = "Operation aborted";
|
|
22
23
|
export interface ParkReattachNotice {
|