@sema-agent/client-core 0.104.1 → 0.106.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 +310 -0
- package/README.md +36 -14
- package/dist/adapt/arms.js +23 -6
- package/dist/adapter/activeRunSelfHeal.d.ts +14 -2
- package/dist/adapter/activeRunSelfHeal.js +90 -12
- package/dist/adapter/downstream/eventToSdkMessage.d.ts +0 -5
- package/dist/adapter/downstream/eventToSdkMessage.js +24 -23
- package/dist/adapter/downstream/terminalToSdkResult.js +2 -3
- package/dist/adapter/downstream/wiringManifestView.d.ts +7 -3
- package/dist/adapter/downstream/wiringManifestView.js +25 -1
- package/dist/adapter/runStream.js +8 -5
- package/dist/approvalReachWire.d.ts +1 -0
- package/dist/approvalReachWire.js +4 -1
- package/dist/approvalSeat.d.ts +21 -0
- package/dist/approvalSeat.js +73 -0
- package/dist/assistantInbox.d.ts +2 -0
- package/dist/assistantInbox.js +3 -0
- package/dist/checkpointRowVerdict.d.ts +23 -0
- package/dist/checkpointRowVerdict.js +89 -0
- package/dist/cloudConfigWireCaps.js +3 -0
- package/dist/compensations.js +6 -6
- package/dist/coreValuePorts.d.ts +14 -1
- package/dist/coreValuePorts.js +14 -0
- package/dist/decideFailureNote.js +3 -1
- package/dist/decideReceipt.d.ts +9 -0
- package/dist/decideReceipt.js +29 -1
- package/dist/declared/supportFloorDeclared.js +2 -2
- package/dist/deviceRebind.d.ts +7 -0
- package/dist/deviceRebind.js +39 -5
- package/dist/displayUntrusted.d.ts +4 -2
- package/dist/displayUntrusted.js +45 -22
- package/dist/engineAddress.d.ts +3 -0
- package/dist/engineAddress.js +25 -0
- package/dist/engineDirectory.js +9 -17
- package/dist/engineErrorCodes.d.ts +8 -0
- package/dist/engineErrorCodes.js +8 -0
- package/dist/engineGenerationPort.d.ts +11 -2
- package/dist/engineGenerationPort.js +63 -9
- package/dist/engineHealthFacts.d.ts +5 -0
- package/dist/engineHealthFacts.js +11 -0
- package/dist/engineNoticeCodes.d.ts +26 -3
- package/dist/engineNoticeCodes.js +52 -7
- package/dist/engineRowRef.js +1 -1
- package/dist/errorSupplementRoles.js +2 -0
- package/dist/eventLeg.d.ts +3 -3
- package/dist/eventLeg.js +25 -4
- package/dist/fleet/fleetProjection.js +26 -5
- package/dist/generated/engineFactTables.d.ts +3 -1
- package/dist/generated/engineFactTables.js +4 -1
- package/dist/generated/engineNoticeTables.js +19 -4
- package/dist/generated/sdkFactTables.d.ts +5 -0
- package/dist/generated/sdkFactTables.js +24 -0
- package/dist/generated/settingsSchemaFns.d.ts +11 -0
- package/dist/generated/settingsSchemaFns.js +2 -1
- package/dist/generated/settingsSchemaSafetyMergeSpec.js +1 -1
- package/dist/generated/settingsSchemaSettingsSources.d.ts +1 -1
- package/dist/generated/settingsSchemaSettingsSources.js +22 -2
- package/dist/headlessReconnectWire.js +31 -4
- package/dist/hitl/approvalLiveFace.d.ts +17 -0
- package/dist/hitl/approvalLiveFace.js +35 -0
- package/dist/hitl/approvalQueueRow.d.ts +21 -0
- package/dist/hitl/approvalQueueRow.js +69 -0
- package/dist/hitl/approvalsFeed.d.ts +5 -4
- package/dist/hitl/approvalsFeed.js +38 -6
- package/dist/hitl/askGateWire.js +26 -2
- package/dist/hitl/askParkRowRouting.d.ts +4 -3
- package/dist/hitl/askParkRowRouting.js +15 -3
- package/dist/hitl/automaticAnswerVerdict.js +13 -10
- package/dist/hitl/crashConverged.d.ts +5 -1
- package/dist/hitl/decisionConflict.js +2 -1
- package/dist/hitl/decisionRefusal.js +1 -1
- package/dist/hitl/decisionSend.d.ts +1 -0
- package/dist/hitl/decisionSend.js +14 -2
- package/dist/hitl/frameRouter.d.ts +4 -0
- package/dist/hitl/frameRouter.js +18 -5
- package/dist/hitl/gateIdentity.d.ts +1 -0
- package/dist/hitl/gateIdentity.js +3 -0
- package/dist/hitl/hitlBridge.d.ts +1 -1
- package/dist/hitl/hitlBridge.js +26 -5
- package/dist/hitl/livePendingAsk.d.ts +5 -0
- package/dist/hitl/livePendingAsk.js +6 -1
- package/dist/hitl/planReviewCard.d.ts +23 -0
- package/dist/hitl/planReviewCard.js +250 -0
- package/dist/hitl/planReviewWire.d.ts +39 -14
- package/dist/hitl/planReviewWire.js +450 -163
- package/dist/hitl/sessionAllowRoute.d.ts +2 -0
- package/dist/hitl/sessionAllowRoute.js +7 -0
- package/dist/hitl/sessionPolicyWire.d.ts +1 -7
- package/dist/hitl/sessionPolicyWire.js +3 -43
- package/dist/hitl/subagentSource.d.ts +1 -0
- package/dist/hitl/subagentSource.js +13 -0
- package/dist/hitl/toolApprovalWire.d.ts +3 -2
- package/dist/hitl/toolApprovalWire.js +79 -37
- package/dist/hostSettingsSource.d.ts +5 -2
- package/dist/hostSettingsSource.js +19 -10
- package/dist/hostTurnSettings.d.ts +5 -6
- package/dist/hostTurnSettings.js +106 -26
- package/dist/i18n/messages.en.d.ts +85 -12
- package/dist/i18n/messages.en.js +85 -12
- package/dist/i18n/messages.zh.js +88 -12
- package/dist/index.d.ts +35 -4
- package/dist/index.js +19 -2
- package/dist/liveInitToolFace.js +0 -1
- package/dist/liveModelCatalog.d.ts +15 -0
- package/dist/liveModelCatalog.js +40 -0
- package/dist/mcpTransportKindsCapability.d.ts +18 -0
- package/dist/mcpTransportKindsCapability.js +75 -0
- package/dist/mcpWireCaps.d.ts +4 -1
- package/dist/mcpWireCaps.js +25 -14
- package/dist/modelCapabilityProbe.js +66 -33
- package/dist/notifications.d.ts +9 -0
- package/dist/notifications.js +11 -2
- package/dist/ownKey.d.ts +4 -1
- package/dist/ownKey.js +8 -2
- package/dist/permissionModeWriteCapability.d.ts +13 -0
- package/dist/permissionModeWriteCapability.js +35 -0
- package/dist/permissionsAllowGrantCapability.d.ts +3 -0
- package/dist/permissionsAllowGrantCapability.js +26 -3
- package/dist/permissionsDeclaration.d.ts +25 -3
- package/dist/permissionsDeclaration.js +69 -41
- package/dist/planReviewCardFacts.d.ts +116 -0
- package/dist/planReviewCardFacts.js +469 -0
- package/dist/projectSeed.d.ts +39 -0
- package/dist/projectSeed.js +80 -0
- package/dist/promptDelivery.js +1 -1
- package/dist/reattachWindow.d.ts +8 -1
- package/dist/reattachWindow.js +44 -12
- package/dist/replayBody.d.ts +7 -6
- package/dist/replayBody.js +62 -7
- package/dist/request/taskRequest.js +3 -4
- package/dist/ruleCompileVerdict.d.ts +0 -2
- package/dist/ruleCompileVerdict.js +0 -31
- package/dist/ruleRefusalSentence.js +0 -3
- package/dist/runFollow.d.ts +30 -0
- package/dist/runFollow.js +117 -0
- package/dist/runLedgerSnapshot.d.ts +20 -0
- package/dist/runLedgerSnapshot.js +272 -0
- package/dist/runPermissionModeSwitch.d.ts +57 -0
- package/dist/runPermissionModeSwitch.js +135 -0
- package/dist/runReattachPolicy.d.ts +16 -5
- package/dist/runReattachPolicy.js +72 -24
- package/dist/runTerminal.js +30 -8
- package/dist/sandboxWire.d.ts +0 -3
- package/dist/sandboxWire.js +0 -9
- package/dist/seam.d.ts +17 -7
- package/dist/seam.js +13 -1
- package/dist/seatContract.d.ts +57 -7
- package/dist/seatContract.js +133 -3
- package/dist/serverSupportRange.d.ts +18 -0
- package/dist/serverSupportRange.js +25 -0
- package/dist/sessionEngine.d.ts +156 -0
- package/dist/sessionEngine.js +464 -0
- package/dist/sessionMap.d.ts +11 -1
- package/dist/sessionMap.js +35 -9
- package/dist/sessionMemoryErase.d.ts +1 -1
- package/dist/sessionMemoryErase.js +5 -0
- package/dist/sessionMemoryStatus.d.ts +27 -1
- package/dist/sessionMemoryStatus.js +117 -0
- package/dist/sessionMemorySync.d.ts +31 -0
- package/dist/sessionMemorySync.js +95 -0
- package/dist/sessionRowTag.d.ts +4 -0
- package/dist/sessionRowTag.js +18 -0
- package/dist/settingsLayerAdmission.d.ts +1 -0
- package/dist/settingsLayerAdmission.js +4 -1
- package/dist/sseIdleTriage.d.ts +3 -1
- package/dist/sseIdleTriage.js +19 -4
- package/dist/subagent/engineSubagentTail.js +2 -0
- package/dist/subagentContentStore.d.ts +3 -0
- package/dist/subagentContentStore.js +10 -1
- package/dist/toolResult.js +1 -1
- package/dist/turnFailurePresentation.d.ts +5 -2
- package/dist/turnFailurePresentation.js +12 -4
- package/dist/wireRefusalCopy.js +2 -6
- package/docs/INTEGRATION-CLIENTS.md +1507 -18
- package/package.json +5 -5
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { slotReadOf } from '../wireFailureShape.js';
|
|
2
|
+
import { SDK_APPROVAL_QUEUE_KINDS } from '../generated/sdkFactTables.js';
|
|
3
|
+
const _queueKindsPin = true;
|
|
4
|
+
void _queueKindsPin;
|
|
5
|
+
const isApprovalQueueKind = (v) => SDK_APPROVAL_QUEUE_KINDS.includes(v);
|
|
6
|
+
export function approvalQueueRowFamilyOf(row) {
|
|
7
|
+
const kind = slotReadOf(row, 'kind');
|
|
8
|
+
if (kind.presence === 'absent')
|
|
9
|
+
return 'checkpoint';
|
|
10
|
+
if (kind.presence !== 'present' || typeof kind.value !== 'string')
|
|
11
|
+
return 'unreadable';
|
|
12
|
+
return isApprovalQueueKind(kind.value) ? kind.value : 'unknown';
|
|
13
|
+
}
|
|
14
|
+
export function checkpointQueueRowSortOf(row) {
|
|
15
|
+
try {
|
|
16
|
+
if (typeof row !== 'object' || row === null || Array.isArray(row))
|
|
17
|
+
return 'excluded';
|
|
18
|
+
const family = approvalQueueRowFamilyOf(row);
|
|
19
|
+
return family === 'checkpoint' ? 'checkpoint' : family === 'unreadable' ? 'unreadable' : 'excluded';
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return 'unreadable';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export const QUEUE_ROW_FAMILY_UNREADABLE_GATE = '(unreadable)';
|
|
26
|
+
const ownString = (row, key, nonEmpty) => {
|
|
27
|
+
const r = slotReadOf(row, key);
|
|
28
|
+
return r.presence === 'present' && typeof r.value === 'string' && (!nonEmpty || r.value !== '') ? r.value : undefined;
|
|
29
|
+
};
|
|
30
|
+
const ownTime = (row, key) => {
|
|
31
|
+
const r = slotReadOf(row, key);
|
|
32
|
+
return r.presence === 'present' && typeof r.value === 'number' && Number.isFinite(r.value) && r.value >= 0 ? r.value : undefined;
|
|
33
|
+
};
|
|
34
|
+
export function peerInboundQueueRowOf(row) {
|
|
35
|
+
try {
|
|
36
|
+
const id = ownString(row, 'id', true);
|
|
37
|
+
const recipientSessionId = ownString(row, 'recipientSessionId', true);
|
|
38
|
+
if (id === undefined || recipientSessionId === undefined)
|
|
39
|
+
return undefined;
|
|
40
|
+
const out = { id, recipientSessionId };
|
|
41
|
+
const lane = ownString(row, 'lane', true);
|
|
42
|
+
if (lane !== undefined)
|
|
43
|
+
out.lane = lane;
|
|
44
|
+
const cause = ownString(row, 'cause', true);
|
|
45
|
+
if (cause !== undefined)
|
|
46
|
+
out.cause = cause;
|
|
47
|
+
const content = ownString(row, 'content', false);
|
|
48
|
+
if (content !== undefined)
|
|
49
|
+
out.content = content;
|
|
50
|
+
const sentAt = ownTime(row, 'sentAt');
|
|
51
|
+
if (sentAt !== undefined)
|
|
52
|
+
out.sentAt = sentAt;
|
|
53
|
+
const heldAt = ownTime(row, 'heldAt');
|
|
54
|
+
if (heldAt !== undefined)
|
|
55
|
+
out.heldAt = heldAt;
|
|
56
|
+
const deadline = ownTime(row, 'deadline');
|
|
57
|
+
if (deadline !== undefined)
|
|
58
|
+
out.deadline = deadline;
|
|
59
|
+
for (const k of ['fromSession', 'fromName', 'fromScope']) {
|
|
60
|
+
const v = ownString(row, k, true);
|
|
61
|
+
if (v !== undefined)
|
|
62
|
+
out[k] = v;
|
|
63
|
+
}
|
|
64
|
+
return Object.freeze(out);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { PendingCheckpoint } from '@sema-agent/sdk';
|
|
2
2
|
import { type LivePendingAskView } from './livePendingAsk.js';
|
|
3
|
+
import { type ApprovalQueuePeerInboundRow } from './approvalQueueRow.js';
|
|
4
|
+
import type { ApprovalsListEnvelope } from './crashConverged.js';
|
|
5
|
+
export type { ApprovalQueuePeerInboundRow } from './approvalQueueRow.js';
|
|
3
6
|
export interface ApprovalsFeedClientLike {
|
|
4
7
|
approvals: {
|
|
5
8
|
list(opts?: {
|
|
6
9
|
signal?: AbortSignal;
|
|
7
|
-
}): Promise<
|
|
8
|
-
pending: PendingCheckpoint[];
|
|
9
|
-
livePending?: unknown[];
|
|
10
|
-
}>;
|
|
10
|
+
}): Promise<ApprovalsListEnvelope>;
|
|
11
11
|
stream?(opts?: {
|
|
12
12
|
signal?: AbortSignal;
|
|
13
13
|
}): AsyncGenerator<{
|
|
@@ -20,6 +20,7 @@ export type ApprovalsFeedMode = 'push' | 'poll' | 'idle';
|
|
|
20
20
|
export interface ApprovalsFeedSnapshot {
|
|
21
21
|
kind: 'snapshot';
|
|
22
22
|
pending: PendingCheckpoint[];
|
|
23
|
+
peerInbound?: ApprovalQueuePeerInboundRow[];
|
|
23
24
|
livePending?: LivePendingAskView[];
|
|
24
25
|
livePendingDropped?: number;
|
|
25
26
|
pendingDropped?: number;
|
|
@@ -2,17 +2,40 @@ import { hostLog } from '../host.js';
|
|
|
2
2
|
import { unrefTimer } from '../unrefTimer.js';
|
|
3
3
|
import { readLivePendingRows } from './livePendingAsk.js';
|
|
4
4
|
import { createStreamDrainingLegs } from '../streamDrainingLegs.js';
|
|
5
|
+
import { approvalQueueRowFamilyOf, peerInboundQueueRowOf } from './approvalQueueRow.js';
|
|
6
|
+
import { checkpointRowVerdictOf } from '../checkpointRowVerdict.js';
|
|
7
|
+
const _realClientFitsFeedSlice = true;
|
|
8
|
+
void _realClientFitsFeedSlice;
|
|
5
9
|
const MAX_CONSECUTIVE_POLL_ESCAPES = 5;
|
|
6
10
|
function readDurablePendingRows(raw) {
|
|
7
11
|
if (!Array.isArray(raw))
|
|
8
12
|
return { kind: 'malformed' };
|
|
9
13
|
const rows = [];
|
|
14
|
+
const peer = [];
|
|
10
15
|
let dropped = 0;
|
|
16
|
+
let skipped = 0;
|
|
11
17
|
for (const item of raw) {
|
|
12
18
|
if (item === null || typeof item !== 'object' || Array.isArray(item)) {
|
|
13
19
|
dropped++;
|
|
14
20
|
continue;
|
|
15
21
|
}
|
|
22
|
+
const family = approvalQueueRowFamilyOf(item);
|
|
23
|
+
if (family === 'unknown') {
|
|
24
|
+
skipped++;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (family === 'peer_inbound') {
|
|
28
|
+
const view = peerInboundQueueRowOf(item);
|
|
29
|
+
if (view === undefined)
|
|
30
|
+
dropped++;
|
|
31
|
+
else
|
|
32
|
+
peer.push(view);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (family === 'unreadable') {
|
|
36
|
+
dropped++;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
16
39
|
const sid = item.sessionId;
|
|
17
40
|
if (typeof sid !== 'string' || sid === '') {
|
|
18
41
|
dropped++;
|
|
@@ -20,13 +43,13 @@ function readDurablePendingRows(raw) {
|
|
|
20
43
|
}
|
|
21
44
|
rows.push(item);
|
|
22
45
|
}
|
|
23
|
-
if (raw.length > 0 && rows.length === 0)
|
|
46
|
+
if (raw.length > 0 && rows.length + peer.length + skipped === 0)
|
|
24
47
|
return { kind: 'malformed' };
|
|
25
|
-
return { kind: 'present', rows, dropped };
|
|
48
|
+
return { kind: 'present', rows, peer, dropped };
|
|
26
49
|
}
|
|
27
50
|
function durableDigestOf(rows) {
|
|
28
51
|
return rows
|
|
29
|
-
.map(r => [r.sessionId ?? '', r.taskId ?? '', r.toolCallId ?? '', r.boundCallId ?? '', r.boundInputHash ?? '', r.toolName ?? '']
|
|
52
|
+
.map(r => [r.sessionId ?? '', r.taskId ?? '', r.toolCallId ?? '', r.boundCallId ?? '', r.boundInputHash ?? '', r.toolName ?? '', JSON.stringify(checkpointRowVerdictOf(r) ?? null)]
|
|
30
53
|
.join(''))
|
|
31
54
|
.sort()
|
|
32
55
|
.join('');
|
|
@@ -34,14 +57,20 @@ function durableDigestOf(rows) {
|
|
|
34
57
|
const LIVE_FIELD_SEP = String.fromCharCode(1);
|
|
35
58
|
const LIVE_ROW_SEP = String.fromCharCode(2);
|
|
36
59
|
const LIVE_SECTION_SEP = String.fromCharCode(3);
|
|
37
|
-
function
|
|
60
|
+
function peerDigestOf(peer) {
|
|
61
|
+
return peer
|
|
62
|
+
.map(r => JSON.stringify(r))
|
|
63
|
+
.sort()
|
|
64
|
+
.join(LIVE_ROW_SEP);
|
|
65
|
+
}
|
|
66
|
+
function digestOf(rows, live, dropped = 0, droppedDurable = 0, peer = []) {
|
|
38
67
|
const liveDigest = live === undefined
|
|
39
68
|
? 'absent'
|
|
40
69
|
: live
|
|
41
70
|
.map(r => [r.approvalId, r.toolName, String(r.expiresAtMs), r.sessionId ?? '', r.originTaskId ?? '', r.frame !== undefined ? 'f' : ''].join(LIVE_FIELD_SEP))
|
|
42
71
|
.sort()
|
|
43
72
|
.join(LIVE_ROW_SEP);
|
|
44
|
-
return `${durableDigestOf(rows)}${LIVE_SECTION_SEP}${liveDigest}${LIVE_SECTION_SEP}${dropped}${LIVE_SECTION_SEP}${droppedDurable}`;
|
|
73
|
+
return `${durableDigestOf(rows)}${LIVE_SECTION_SEP}${liveDigest}${LIVE_SECTION_SEP}${dropped}${LIVE_SECTION_SEP}${droppedDurable}${LIVE_SECTION_SEP}${peerDigestOf(peer)}`;
|
|
45
74
|
}
|
|
46
75
|
export function startApprovalsFeed(client, onSnapshot, opts) {
|
|
47
76
|
const pollIntervalMs = opts?.pollIntervalMs ?? 5000;
|
|
@@ -158,6 +187,7 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
|
|
|
158
187
|
return;
|
|
159
188
|
const seq = ++takeSeq;
|
|
160
189
|
let rows;
|
|
190
|
+
let peer;
|
|
161
191
|
let live;
|
|
162
192
|
let dropped = 0;
|
|
163
193
|
let droppedDurable = 0;
|
|
@@ -212,6 +242,7 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
|
|
|
212
242
|
throw new TypeError('approvalsFeed: unreadable pending section');
|
|
213
243
|
}
|
|
214
244
|
rows = durableRead.rows;
|
|
245
|
+
peer = durableRead.peer;
|
|
215
246
|
droppedDurable = durableRead.dropped;
|
|
216
247
|
if (durableRead.dropped > 0)
|
|
217
248
|
hostLog('debug', `approvalsFeed: dropped ${durableRead.dropped} malformed pending row(s) — snapshot carries pendingDropped`);
|
|
@@ -220,7 +251,7 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
|
|
|
220
251
|
return;
|
|
221
252
|
let d;
|
|
222
253
|
try {
|
|
223
|
-
d = digestOf(rows, live, dropped, droppedDurable);
|
|
254
|
+
d = digestOf(rows, live, dropped, droppedDurable, peer);
|
|
224
255
|
}
|
|
225
256
|
catch (e) {
|
|
226
257
|
noteTakeMissed(via, seq);
|
|
@@ -252,6 +283,7 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
|
|
|
252
283
|
const snap = {
|
|
253
284
|
kind: 'snapshot',
|
|
254
285
|
pending: rows,
|
|
286
|
+
...(peer.length > 0 ? { peerInbound: peer } : {}),
|
|
255
287
|
...(live !== undefined ? { livePending: live } : {}),
|
|
256
288
|
...(dropped > 0 ? { livePendingDropped: dropped } : {}),
|
|
257
289
|
...(droppedDurable > 0 ? { pendingDropped: droppedDurable } : {}),
|
package/dist/hitl/askGateWire.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { createGateLedger } from './gateLedger.js';
|
|
2
|
-
import { flushHeldWithInterruptRewrite, isHostProgressFrame, routeFrame, } from './frameRouter.js';
|
|
2
|
+
import { bridgeRunHandleFallbackOf, flushHeldWithInterruptRewrite, isHostProgressFrame, routeFrame, } from './frameRouter.js';
|
|
3
3
|
import { resolvePark, nextHopBudget, stalledTerminal, MAX_GATE_HOPS } from './parkResolver.js';
|
|
4
4
|
import { createStreamDrainingLegs } from '../streamDrainingLegs.js';
|
|
5
5
|
import { abortableSleep } from '../abortableSleep.js';
|
|
6
6
|
import { hostLog } from '../host.js';
|
|
7
|
+
import { invokeHostCallbackIsolated } from '../hostCallbackGuard.js';
|
|
7
8
|
import { decideAcceptedNotResolved, readDecideReceipt } from '../decideReceipt.js';
|
|
8
9
|
import { APPROVAL_EXPIRED } from '../engineErrorCodes.js';
|
|
9
10
|
import { isNotFoundFailure } from '../wireErrorTriage.js';
|
|
@@ -37,11 +38,14 @@ export async function* bridgeAskUserQuestionGates(source, deps, opts) {
|
|
|
37
38
|
let lastRoundDecided = false;
|
|
38
39
|
let unverifiableParks = 0;
|
|
39
40
|
const drains = createStreamDrainingLegs();
|
|
41
|
+
let lastDrainFrame;
|
|
40
42
|
while (true) {
|
|
41
43
|
let park = null;
|
|
42
44
|
for await (const ev of stream) {
|
|
43
|
-
if (drains.see(ev))
|
|
45
|
+
if (drains.see(ev)) {
|
|
46
|
+
lastDrainFrame = ev;
|
|
44
47
|
continue;
|
|
48
|
+
}
|
|
45
49
|
const answering = led.answeringLiveCards();
|
|
46
50
|
if (answering.length > 0)
|
|
47
51
|
await Promise.all(answering);
|
|
@@ -77,6 +81,11 @@ export async function* bridgeAskUserQuestionGates(source, deps, opts) {
|
|
|
77
81
|
const drained = drains.legEnded('returned');
|
|
78
82
|
if (drained !== undefined) {
|
|
79
83
|
const reopenAfterMs = 'reconnectAfterMs' in drained ? drained.reconnectAfterMs : undefined;
|
|
84
|
+
if (taskId.current === undefined) {
|
|
85
|
+
const bound = bridgeRunHandleFallbackOf(deps);
|
|
86
|
+
if (bound !== undefined && reopenAfterMs !== undefined)
|
|
87
|
+
taskId.current = bound;
|
|
88
|
+
}
|
|
80
89
|
if (reopenAfterMs !== undefined && taskId.current !== undefined && !opts?.signal?.aborted) {
|
|
81
90
|
hostLog('debug', `liveHitlAskWire: runs.events(${taskId.current}) ended on the drain end frame — the engine is going down; reopening in ${reopenAfterMs}ms`);
|
|
82
91
|
await abortableSleep(reopenAfterMs, opts?.signal ?? new AbortController().signal);
|
|
@@ -91,6 +100,13 @@ export async function* bridgeAskUserQuestionGates(source, deps, opts) {
|
|
|
91
100
|
}
|
|
92
101
|
}
|
|
93
102
|
hostLog('debug', `liveHitlAskWire: runs.events ended on the drain end frame and is not reopened (${'giveUp' in drained ? drained.giveUp : taskId.current === undefined ? 'no run handle' : 'aborted'}) — ending this leg like a dropped stream`);
|
|
103
|
+
if (!opts?.signal?.aborted) {
|
|
104
|
+
const runId = taskId.current;
|
|
105
|
+
const event = 'giveUp' in drained
|
|
106
|
+
? { cause: 'stream_draining', ladder: 'exhausted', ...(runId !== undefined ? { runId } : {}) }
|
|
107
|
+
: { cause: 'stream_draining', frame: lastDrainFrame ?? {}, ...(runId !== undefined ? { runId } : {}) };
|
|
108
|
+
notifyBreak(deps, Object.freeze(event), opts?.signal);
|
|
109
|
+
}
|
|
94
110
|
}
|
|
95
111
|
yield* flushHeldWithInterruptRewrite(led, { signal: opts?.signal });
|
|
96
112
|
return;
|
|
@@ -161,6 +177,14 @@ export async function* bridgeAskUserQuestionGates(source, deps, opts) {
|
|
|
161
177
|
});
|
|
162
178
|
}
|
|
163
179
|
}
|
|
180
|
+
function notifyBreak(deps, event, signal) {
|
|
181
|
+
invokeHostCallbackIsolated('liveHitlAskWire: onBreak', () => {
|
|
182
|
+
const cb = deps.onBreak;
|
|
183
|
+
if (typeof cb !== 'function' || signal?.aborted === true)
|
|
184
|
+
return;
|
|
185
|
+
return cb.call(deps, event);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
164
188
|
export const LATE_DECIDE_OUTCOMES = Object.freeze(['accepted-not-resolved', 'resumed', 'expired-reaped', 'settled-elsewhere', 'unknown']);
|
|
165
189
|
const LATE_DECIDE_TASK_ID_MAX = 64;
|
|
166
190
|
export function lateDecideOutcomeOf(reply, opts) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PendingCheckpoint } from '@sema-agent/sdk';
|
|
2
|
+
import type { ApprovalQueueRow } from './crashConverged.js';
|
|
2
3
|
import type { ParkRowProbe } from './parkRowBirthWait.js';
|
|
3
4
|
import { type ParkOwnershipDeps } from './parkOwnership.js';
|
|
4
5
|
export declare const ASK_PARK_ROW_WAIT_MS = 15000;
|
|
@@ -7,7 +8,7 @@ export interface AskParkRowScanDeps {
|
|
|
7
8
|
isOwned?: (row: PendingCheckpoint) => boolean;
|
|
8
9
|
ownership?: ParkOwnershipDeps;
|
|
9
10
|
}
|
|
10
|
-
export declare function classifyAskParkRows(
|
|
11
|
+
export declare function classifyAskParkRows(queueRows: readonly ApprovalQueueRow[], taskId: string, deps?: AskParkRowScanDeps): ParkRowProbe<PendingCheckpoint>;
|
|
11
12
|
export interface AskParkRowIdentity {
|
|
12
13
|
rowTaskId: string;
|
|
13
14
|
gatedCallId: string | undefined;
|
|
@@ -15,7 +16,7 @@ export interface AskParkRowIdentity {
|
|
|
15
16
|
}
|
|
16
17
|
export declare function askParkRowIdentity(taskId: string, row: PendingCheckpoint): AskParkRowIdentity;
|
|
17
18
|
export declare function askParkRowArm(row: PendingCheckpoint): 'question' | 'tool-gate';
|
|
18
|
-
export declare function askParkRowStillPending(rows: readonly
|
|
19
|
+
export declare function askParkRowStillPending(rows: readonly ApprovalQueueRow[], identity: AskParkRowIdentity): boolean;
|
|
19
20
|
export type AskParkChainFailureDisposition = 'retry' | 'row-gone-after-card' | 'row-unborn';
|
|
20
21
|
export declare function classifyAskParkChainFailure(i: {
|
|
21
22
|
rowStillPending: boolean;
|
|
@@ -29,6 +30,6 @@ export interface RowArmSingleFlight<T> {
|
|
|
29
30
|
export declare function createRowArmSingleFlight<T>(): RowArmSingleFlight<T>;
|
|
30
31
|
export declare function pendingRowIsOwnedAndDecidable(row: PendingCheckpoint, deps?: ParkOwnershipDeps): boolean;
|
|
31
32
|
export interface OwnedPendingListView {
|
|
32
|
-
readonly pending?: readonly
|
|
33
|
+
readonly pending?: readonly ApprovalQueueRow[] | null;
|
|
33
34
|
}
|
|
34
35
|
export declare function countOwnedPendingRows(listed: OwnedPendingListView | null | undefined, deps?: ParkOwnershipDeps): number;
|
|
@@ -2,10 +2,17 @@ import { askParkForeignGateKind, durableDecideLegOf } from '../adapter/activeRun
|
|
|
2
2
|
import { isAskTool, questionCardVerdict } from './frameRouter.js';
|
|
3
3
|
import { approvalCallKey } from './gateIdentity.js';
|
|
4
4
|
import { askQuestionsFromPending, pendingRowIsOwnedByThisSession } from './parkOwnership.js';
|
|
5
|
+
import { approvalQueueRowFamilyOf, checkpointQueueRowSortOf, QUEUE_ROW_FAMILY_UNREADABLE_GATE } from './approvalQueueRow.js';
|
|
5
6
|
export const ASK_PARK_ROW_WAIT_MS = 15000;
|
|
6
7
|
export const ASK_PARK_ROW_POLL_MS = 500;
|
|
7
|
-
export function classifyAskParkRows(
|
|
8
|
+
export function classifyAskParkRows(queueRows, taskId, deps) {
|
|
8
9
|
const isOwned = deps?.isOwned ?? ((row) => pendingRowIsOwnedByThisSession(row, deps?.ownership));
|
|
10
|
+
const sorted = queueRows.flatMap((r) => {
|
|
11
|
+
const sort = checkpointQueueRowSortOf(r);
|
|
12
|
+
return sort === 'excluded' ? [] : [{ r: r, unreadable: sort === 'unreadable' }];
|
|
13
|
+
});
|
|
14
|
+
const rows = sorted.map((x) => x.r);
|
|
15
|
+
const familyUnreadable = new Set(sorted.filter((x) => x.unreadable).map((x) => x.r));
|
|
9
16
|
const own = rows.find(r => r.taskId === taskId);
|
|
10
17
|
const sole = rows.length === 1 ? rows[0] : undefined;
|
|
11
18
|
const soleUnowned = sole !== undefined && !isOwned(sole);
|
|
@@ -21,7 +28,7 @@ export function classifyAskParkRows(rows, taskId, deps) {
|
|
|
21
28
|
: `${String(rows.length)} pending rows but none for task ${taskId} — ambiguous queue, refusing to surface an unrelated row`,
|
|
22
29
|
};
|
|
23
30
|
}
|
|
24
|
-
const foreignKind = askParkForeignGateKind(pending);
|
|
31
|
+
const foreignKind = familyUnreadable.has(pending) ? QUEUE_ROW_FAMILY_UNREADABLE_GATE : askParkForeignGateKind(pending);
|
|
25
32
|
if (foreignKind !== null) {
|
|
26
33
|
return {
|
|
27
34
|
kind: 'unborn',
|
|
@@ -120,7 +127,7 @@ export function pendingRowIsOwnedAndDecidable(row, deps) {
|
|
|
120
127
|
const leg = durableDecideLegOf(row);
|
|
121
128
|
if (leg === 'plan_review')
|
|
122
129
|
return true;
|
|
123
|
-
if (leg !== 'approval'
|
|
130
|
+
if (leg !== 'approval')
|
|
124
131
|
return false;
|
|
125
132
|
return askParkRowArm(row) === 'question' ? askQuestionsFromPending(row) !== null : true;
|
|
126
133
|
}
|
|
@@ -137,6 +144,11 @@ export function countOwnedPendingRows(listed, deps) {
|
|
|
137
144
|
if (typeof row !== 'object' || row === null) {
|
|
138
145
|
throw new Error('approvals.list answered with a pending row that is not an object — the owned pending count is unknown');
|
|
139
146
|
}
|
|
147
|
+
const family = approvalQueueRowFamilyOf(row);
|
|
148
|
+
if (family === 'unreadable')
|
|
149
|
+
throw new Error('approvals.list answered with a pending row whose family (`kind`) cannot be read — the owned pending count is unknown');
|
|
150
|
+
if (family !== 'checkpoint')
|
|
151
|
+
continue;
|
|
140
152
|
if (pendingRowIsOwnedAndDecidable(row, deps))
|
|
141
153
|
n++;
|
|
142
154
|
}
|
|
@@ -125,6 +125,17 @@ function evidenceUnreadable(card) {
|
|
|
125
125
|
const nested = evidenceSlotOf(risk.value, 'requiresRealApproval');
|
|
126
126
|
return nested.kind !== 'data' || typeof nested.value !== 'boolean';
|
|
127
127
|
}
|
|
128
|
+
function registryUnreadable(card) {
|
|
129
|
+
const slot = evidenceSlotOf(card, 'unreadableEvidence');
|
|
130
|
+
if (slot.kind === 'absent' || (slot.kind === 'data' && slot.value === undefined))
|
|
131
|
+
return false;
|
|
132
|
+
if (slot.kind === 'unreadable')
|
|
133
|
+
return true;
|
|
134
|
+
const list = slot.value;
|
|
135
|
+
if (!Array.isArray(list) || list.some((k) => typeof k !== 'string'))
|
|
136
|
+
return true;
|
|
137
|
+
return list.some((k) => k !== 'rememberRefused' && k !== 'ruleOffers');
|
|
138
|
+
}
|
|
128
139
|
function readHostAskRuleMatched(facts) {
|
|
129
140
|
if (typeof facts !== 'object' || facts === null)
|
|
130
141
|
return undefined;
|
|
@@ -137,17 +148,9 @@ export function automaticAnswerVerdict(card, lane, facts) {
|
|
|
137
148
|
try {
|
|
138
149
|
if (typeof card !== 'object' || card === null)
|
|
139
150
|
return UNKNOWN.card_unreadable;
|
|
151
|
+
const unreadableAtEntry = registryUnreadable(card) || evidenceUnreadable(card) || (retiredAllowRuleShadowKeyPresent(card, 'frame') && !approvalAllowRuleShadowed(card));
|
|
140
152
|
if (card.cardPayloadUnreadable === true)
|
|
141
153
|
return UNKNOWN.card_unreadable;
|
|
142
|
-
const unreadable = card.unreadableEvidence;
|
|
143
|
-
if (unreadable !== undefined) {
|
|
144
|
-
if (!Array.isArray(unreadable) || unreadable.some((k) => typeof k !== 'string'))
|
|
145
|
-
return UNKNOWN.card_unreadable;
|
|
146
|
-
if (unreadable.some((k) => k !== 'rememberRefused' && k !== 'ruleOffers'))
|
|
147
|
-
return UNKNOWN.card_unreadable;
|
|
148
|
-
}
|
|
149
|
-
if (!approvalAllowRuleShadowed(card) && retiredAllowRuleShadowKeyPresent(card, 'frame'))
|
|
150
|
-
return UNKNOWN.card_unreadable;
|
|
151
154
|
const face = lane === 'mode_posture' ? askFaceOfCard(card) : undefined;
|
|
152
155
|
const origin = readAskOrigin(evidenceView(card, 'origin'));
|
|
153
156
|
if (origin !== undefined && Object.hasOwn(ORIGIN_IMPLIES_CLASS, origin)) {
|
|
@@ -175,7 +178,7 @@ export function automaticAnswerVerdict(card, lane, facts) {
|
|
|
175
178
|
return mustAsk('allow_rule_shadow');
|
|
176
179
|
}
|
|
177
180
|
}
|
|
178
|
-
if (
|
|
181
|
+
if (unreadableAtEntry)
|
|
179
182
|
return UNKNOWN.card_unreadable;
|
|
180
183
|
const pair = readAskClassPairState(card);
|
|
181
184
|
if (pair.state === 'malformed')
|
|
@@ -4,8 +4,12 @@ export interface CrashConvergedRowReading extends Readonly<CrashConvergedRow> {
|
|
|
4
4
|
readonly needsHumanReason?: 'approved_then_interrupted' | 'pending_unsafe' | 'unstable_row' | 'contradictory';
|
|
5
5
|
}
|
|
6
6
|
export declare const isCrashConvergedCause: (v: unknown) => v is CrashConvergedRowReading["cause"];
|
|
7
|
+
export interface ApprovalQueueKindedRow {
|
|
8
|
+
readonly kind: string;
|
|
9
|
+
}
|
|
10
|
+
export type ApprovalQueueRow = PendingCheckpoint | ApprovalQueueKindedRow;
|
|
7
11
|
export interface ApprovalsListEnvelope {
|
|
8
|
-
pending:
|
|
12
|
+
pending: ApprovalQueueRow[];
|
|
9
13
|
livePending?: unknown[];
|
|
10
14
|
crashConverged?: CrashConvergedRowReading[];
|
|
11
15
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { APPROVAL_BINDING_MISMATCH, CHECKPOINT_UNSUPPORTED_VERSION, RESUME_OUTCOME_INVALID } from '../engineErrorCodes.js';
|
|
2
2
|
import { msg, renderMessage } from '../i18n/translate.js';
|
|
3
3
|
import { ownDataField } from '../ownKey.js';
|
|
4
|
+
import { SDK_DECIDE_BINDING_KEYS } from '../generated/sdkFactTables.js';
|
|
4
5
|
import { isSlotCarrier, PROTOCOL_ERROR_NAME, protocolErrorNameReading, readRefusalSlots, readThrownExtraSnapshot } from '../wireFailureShape.js';
|
|
5
|
-
const BINDING_SEAT_FIELDS = new Set(
|
|
6
|
+
const BINDING_SEAT_FIELDS = new Set(SDK_DECIDE_BINDING_KEYS);
|
|
6
7
|
const ANSWER_FIELD = 'answer';
|
|
7
8
|
const REFUSAL_WORD_KEYS = Object.freeze(['field', 'reason']);
|
|
8
9
|
const GRANT_DIGEST_FIELD = 'boundGrantDigest';
|
|
@@ -146,7 +146,7 @@ export function readDecisionRefusalAsk(failure, answer, read) {
|
|
|
146
146
|
if (name === 'HitlSafetyError')
|
|
147
147
|
return facts('not_settled');
|
|
148
148
|
const code = typeof errorCode === 'string' && errorCode.length > 0 ? errorCode : undefined;
|
|
149
|
-
if (code === RESUME_PREFLIGHT_REJECTED &&
|
|
149
|
+
if (code === RESUME_PREFLIGHT_REJECTED && preflightSettledBitsPresent(carrier))
|
|
150
150
|
return facts('not_settled', true);
|
|
151
151
|
if (isCodeClassifiedGateFailure(code, retriable))
|
|
152
152
|
return facts('not_settled');
|
|
@@ -63,6 +63,7 @@ interface HitlDecisionOutcomeBase {
|
|
|
63
63
|
readonly fallback?: 'approval';
|
|
64
64
|
readonly noteDropped?: true;
|
|
65
65
|
readonly rememberDropped?: true;
|
|
66
|
+
readonly allowSessionCarried?: true;
|
|
66
67
|
readonly attributionDropped?: true;
|
|
67
68
|
readonly attributionRenamed?: true;
|
|
68
69
|
readonly pending?: LiveAskPendingReading;
|
|
@@ -10,6 +10,7 @@ import { readDecisionNoteAudit } from './approvalDecisionNoteAudit.js';
|
|
|
10
10
|
import { carrierReadOf, deliverWithAttributionLadder, isLocalSafetyStop } from './attributionLadder.js';
|
|
11
11
|
import { budgetSpent, deliverApprovalDecision, deliveryTiming, isAborted, newSendTally, PARKING_WAIT_CAP_MS, setDeliveryTimingForTests, sleepFor, settledTransportReadingOf, withTransportRetry, deliveryReadingOf, } from './approvalDelivery.js';
|
|
12
12
|
import { ASK_DECIDED_CODE, classifyDeliveredDecisionRefusal, decisionRefusalClosesOut, notPendingAfterRetry } from './decisionRefusal.js';
|
|
13
|
+
import { sessionAllowCarried } from './sessionAllowRoute.js';
|
|
13
14
|
import { addDecisionOpKeys, closeDecisionOp, decisionLedgerKeysOf, decisionOpsWithKey, openDecisionOp } from './decisionLedger.js';
|
|
14
15
|
import { hostHoldIsActive, takeHostHoldLedgerOp, tryHoldApprovalNow } from './approvalHolds.js';
|
|
15
16
|
import { answerPayloadFor, decisionAttributionKeyOf, isToolApprovalRespondReplayed, readApprovalDenySettledBy, readToolApprovalRespondAck, readToolApprovalRespondParking, } from './toolApprovalWire.js';
|
|
@@ -183,6 +184,9 @@ function bodyFacts(answer, payload, rememberUnsupported) {
|
|
|
183
184
|
...(rememberUnsupported && answer.kind === 'allow' && answer.allowSession === true ? { rememberDropped: true } : {}),
|
|
184
185
|
};
|
|
185
186
|
}
|
|
187
|
+
function answerGateCannotTake(out) {
|
|
188
|
+
return out.kind === 'not_applied' && (out.cause === 'gone' || out.cause === 'parking' || out.cause === 'lane_disabled');
|
|
189
|
+
}
|
|
186
190
|
function ownTrue(raw, key) {
|
|
187
191
|
return typeof raw === 'object' && raw !== null && guardedRead(() => readOwnDataValue(raw, key)) === true;
|
|
188
192
|
}
|
|
@@ -190,6 +194,14 @@ async function sendAskFamily(respondForFallback, decideAsk, req, ctx) {
|
|
|
190
194
|
const payload = answerPayloadFor(req.answer, 'respond', { approvalDecisionNoteCapable: ctx.noteCapable });
|
|
191
195
|
if (payload === undefined)
|
|
192
196
|
return { family: 'ask', attempts: ctx.tally.attempts, kind: 'not_sent', cause: 'not_an_answer' };
|
|
197
|
+
let fallbackRespond = respondForFallback;
|
|
198
|
+
if (req.answer.kind === 'allow' && req.answer.allowSession === true && respondForFallback !== undefined && req.approvalId !== undefined && sessionAllowCarried('v1', req.approvalId)) {
|
|
199
|
+
const viaAnswerGate = await sendApprovalFamily(respondForFallback, req.approvalId, req.answer, ctx);
|
|
200
|
+
if (!answerGateCannotTake(viaAnswerGate))
|
|
201
|
+
return { ...viaAnswerGate, family: 'ask', allowSessionCarried: true };
|
|
202
|
+
hostLog('debug', `decisionSend: session-allow-fallback ask=${req.askId} cause=${String(viaAnswerGate.cause)} via=ask-decide-once`);
|
|
203
|
+
fallbackRespond = undefined;
|
|
204
|
+
}
|
|
193
205
|
const verdict = verdictOf(payload.decision) ?? 'deny';
|
|
194
206
|
const attribution = req.answer.kind === 'deny' ? decisionAttributionKeyOf(ctx.decidedBy, readApprovalDenySettledBy(req.answer)) : {};
|
|
195
207
|
const body = {
|
|
@@ -232,8 +244,8 @@ async function sendAskFamily(respondForFallback, decideAsk, req, ctx) {
|
|
|
232
244
|
}
|
|
233
245
|
const out = outcomeOfThrown(e, 'ask', verdict, ctx.signal, ctx.tally, ctx.noteCapable, settled);
|
|
234
246
|
if (out.kind === 'not_applied' && out.cause === 'lane_disabled' && code === ASK_DECISION_LANE_DISABLED && req.approvalId !== undefined) {
|
|
235
|
-
if (
|
|
236
|
-
const fb = await sendApprovalFamily(
|
|
247
|
+
if (fallbackRespond !== undefined && ctx.signal?.aborted !== true) {
|
|
248
|
+
const fb = await sendApprovalFamily(fallbackRespond, req.approvalId, req.answer, ctx);
|
|
237
249
|
return { ...fb, family: 'ask', fallback: 'approval' };
|
|
238
250
|
}
|
|
239
251
|
}
|
|
@@ -5,6 +5,7 @@ import type { ApprovalResolution } from './approvalResolution.js';
|
|
|
5
5
|
import type { GateLedger } from './gateLedger.js';
|
|
6
6
|
import type { NoAllowCause } from './noAllowCause.js';
|
|
7
7
|
import { type ReplayedApprovalDisposition } from './replayedApproval.js';
|
|
8
|
+
import type { ReattachDrainingEvent } from '../runReattachPolicy.js';
|
|
8
9
|
export declare const HITL_REJECT_MESSAGE = "The user doesn't want to proceed with this tool use. The tool use was rejected (eg. if it was a file edit, the new_string was NOT written to the file). STOP what you are doing and wait for the user to tell you how to proceed.";
|
|
9
10
|
export declare const HITL_POLICY_DENY_MESSAGE: string;
|
|
10
11
|
export type PolicyDenyLane = 'no_approval_ui' | 'dont_ask';
|
|
@@ -31,11 +32,14 @@ export interface AskGateWireDeps {
|
|
|
31
32
|
}) => AsyncGenerator<AgentEvent>;
|
|
32
33
|
respondToolApproval?: RespondToolApprovalFn;
|
|
33
34
|
onParkReattach?: (e: ParkReattachNotice) => void;
|
|
35
|
+
onBreak?: (event: ReattachDrainingEvent) => void;
|
|
34
36
|
approvalLane?: ToolApprovalFrameLaneOpts;
|
|
35
37
|
onToolApprovalOutcome?: (frame: ToolApprovalFrame, resolution: ApprovalResolution) => void | Promise<void>;
|
|
36
38
|
onDecideOperation?: DecideOperationObserver;
|
|
37
39
|
onReplayedApproval?: (frame: ToolApprovalFrame, disposition: ReplayedApprovalDisposition) => void | Promise<void>;
|
|
38
40
|
}
|
|
41
|
+
export declare function setBridgeRunHandleFallback(deps: object, read: () => string | undefined): void;
|
|
42
|
+
export declare function bridgeRunHandleFallbackOf(deps: object): string | undefined;
|
|
39
43
|
type GateKind = 'ask' | 'fs';
|
|
40
44
|
export interface GatePark {
|
|
41
45
|
gate: GateKind;
|
package/dist/hitl/frameRouter.js
CHANGED
|
@@ -20,6 +20,7 @@ import { ccToolDenialKindForSettledBy } from '../gateVocabulary.js';
|
|
|
20
20
|
import { REJECT_MESSAGE } from '../adapt/wireShapes.js';
|
|
21
21
|
import { RUN_CANCELLED_CODE } from '../runCancelContext.js';
|
|
22
22
|
import { durableDecideLegOf } from '../adapter/activeRunSelfHeal.js';
|
|
23
|
+
import { subagentSourceSessionOf } from './subagentSource.js';
|
|
23
24
|
export const HITL_REJECT_MESSAGE = REJECT_MESSAGE;
|
|
24
25
|
export const HITL_POLICY_DENY_MESSAGE = 'Permission for this tool use was denied: it requires interactive approval, and permission prompts are ' +
|
|
25
26
|
'not available in this session. The action was NOT performed. Do not claim it succeeded, and do not retry ' +
|
|
@@ -83,6 +84,20 @@ function denyStampedEnd(ev, attribution) {
|
|
|
83
84
|
export const HITL_INTERRUPT_MESSAGE_FOR_TOOL_USE = '[Request interrupted by user for tool use]';
|
|
84
85
|
const RUN_CANCELLED_ERROR_CODE = RUN_CANCELLED_CODE;
|
|
85
86
|
export const ENGINE_ABORT_TOOL_RESULT = 'Operation aborted';
|
|
87
|
+
const bridgeRunHandleFallbacks = new WeakMap();
|
|
88
|
+
export function setBridgeRunHandleFallback(deps, read) {
|
|
89
|
+
bridgeRunHandleFallbacks.set(deps, read);
|
|
90
|
+
}
|
|
91
|
+
export function bridgeRunHandleFallbackOf(deps) {
|
|
92
|
+
try {
|
|
93
|
+
const read = bridgeRunHandleFallbacks.get(deps);
|
|
94
|
+
const v = read?.();
|
|
95
|
+
return typeof v === 'string' && v !== '' ? v : undefined;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
86
101
|
function isAskToolLoose(name) {
|
|
87
102
|
return typeof name === 'string' && name.replace(/[\s_-]+/g, '').toLowerCase() === 'askuserquestion';
|
|
88
103
|
}
|
|
@@ -121,13 +136,11 @@ export function parkGateArmOf(gate) {
|
|
|
121
136
|
return null;
|
|
122
137
|
}
|
|
123
138
|
const leg = durableDecideLegOf(typeof kind === 'string' ? { gateKind: kind } : null);
|
|
124
|
-
if (leg !== 'approval'
|
|
139
|
+
if (leg !== 'approval')
|
|
125
140
|
return null;
|
|
126
141
|
if (questionCardVerdict(gate) ?? isAskToolLoose(toolName))
|
|
127
142
|
return 'ask';
|
|
128
|
-
|
|
129
|
-
return 'fs';
|
|
130
|
-
return isFsOrShellToolName(toolName) ? 'fs' : null;
|
|
143
|
+
return 'fs';
|
|
131
144
|
}
|
|
132
145
|
const ENGINE_GATE_PARKED_ERROR_CODE = GATE_PARKED_ERROR_CODE;
|
|
133
146
|
function isEngineAbortToolEnd(ev) {
|
|
@@ -322,7 +335,7 @@ async function routeToolApprovalFrame(ev, ctx) {
|
|
|
322
335
|
}
|
|
323
336
|
}
|
|
324
337
|
const fromSubagent = isFromSubagent(ev);
|
|
325
|
-
hostLog('debug', `liveHitlAskWire: tool_approval frame ${ev.approvalId} tool=${String(ev.toolName)}${fromSubagent ? ` from-subagent=${String(ev
|
|
338
|
+
hostLog('debug', `liveHitlAskWire: tool_approval frame ${ev.approvalId} tool=${String(ev.toolName)}${fromSubagent ? ` from-subagent=${String(subagentSourceSessionOf(ev) ?? ev.sourceAgentName ?? 'explicit')}` : ''}`);
|
|
326
339
|
const frameCallId = !fromSubagent && typeof ev.toolCallId === 'string' && ev.toolCallId !== '' ? ev.toolCallId : undefined;
|
|
327
340
|
const gatedCallId = fromSubagent ? undefined : frameCallId ?? led.lastPendingFsCall();
|
|
328
341
|
const argsCallId = frameCallId === undefined ? gatedCallId : typeof ev.toolName === 'string' && toolNameIsFsWrite(ev.toolName) ? frameCallId : undefined;
|
|
@@ -3,6 +3,7 @@ export declare const REOPEN_ID_TAIL = "#reopen-";
|
|
|
3
3
|
export declare function approvalCallKey(gatedCallId: string | undefined, taskId: string): string;
|
|
4
4
|
export declare function askGateQuestionId(callKey: string): string;
|
|
5
5
|
export declare function planReviewQuestionId(taskId: string): string;
|
|
6
|
+
export declare function freshPlanReviewQuestionId(taskId: string, suffix: string): string;
|
|
6
7
|
export declare function liveFrameCallKey(approvalId: string): string;
|
|
7
8
|
export declare function taskIdFromPlanReviewQuestionId(questionId: string): string | undefined;
|
|
8
9
|
export declare function armedKeyFromQuestionId(questionId: string): string;
|
|
@@ -11,6 +11,9 @@ export function askGateQuestionId(callKey) {
|
|
|
11
11
|
export function planReviewQuestionId(taskId) {
|
|
12
12
|
return `${PLAN_REVIEW_QUESTION_ID_PREFIX}${taskId}`;
|
|
13
13
|
}
|
|
14
|
+
export function freshPlanReviewQuestionId(taskId, suffix) {
|
|
15
|
+
return `${planReviewQuestionId(taskId)}${REOPEN_ID_TAIL}${suffix}`;
|
|
16
|
+
}
|
|
14
17
|
export function liveFrameCallKey(approvalId) {
|
|
15
18
|
return `${HITL_FRAME_CALL_KEY_PREFIX}${approvalId}`;
|
|
16
19
|
}
|
|
@@ -26,7 +26,7 @@ export interface AssistantResourceLike {
|
|
|
26
26
|
planReview(taskId: string, req: PlanReviewRequest, opts?: {
|
|
27
27
|
signal?: AbortSignal;
|
|
28
28
|
}): Promise<AssistantTaskStatus>;
|
|
29
|
-
resume(taskId: string, opts?: {
|
|
29
|
+
resume(taskId: string, body?: undefined, opts?: {
|
|
30
30
|
signal?: AbortSignal;
|
|
31
31
|
}): Promise<AssistantTaskStatus>;
|
|
32
32
|
}
|