@sema-agent/client-core 0.108.0 → 0.110.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 +294 -0
- package/README.md +77 -40
- package/dist/adapt/arms.js +41 -13
- package/dist/adapt/textStream.d.ts +2 -0
- package/dist/adapt/textStream.js +37 -16
- package/dist/adapter/activeRunSelfHeal.js +1 -1
- package/dist/adapter/downstream/eventToSdkMessage.js +11 -5
- package/dist/agentSession/engineAgentRegistryRead.js +2 -0
- package/dist/bashExitReading.d.ts +12 -0
- package/dist/bashExitReading.js +44 -0
- package/dist/checkpointRowVerdict.d.ts +1 -1
- package/dist/checkpointRowVerdict.js +4 -2
- package/dist/compensations.js +1 -1
- package/dist/declared/supportFloorDeclared.js +1 -1
- package/dist/displayUntrusted.js +132 -14
- package/dist/engineAgentAbsence.d.ts +2 -0
- package/dist/engineAgentAbsence.js +28 -0
- package/dist/engineErrorCodes.d.ts +7 -0
- package/dist/engineErrorCodes.js +7 -0
- package/dist/engineHealthFacts.d.ts +26 -0
- package/dist/engineHealthFacts.js +63 -4
- package/dist/engineInlineTaskStats.d.ts +4 -3
- package/dist/engineInlineTaskStats.js +18 -2
- package/dist/engineNoticeCodes.d.ts +138 -0
- package/dist/engineNoticeCodes.js +662 -55
- package/dist/engineRepoint.d.ts +140 -0
- package/dist/engineRepoint.js +285 -0
- package/dist/engineRowRef.js +2 -0
- package/dist/engineSessionMessages.d.ts +2 -1
- package/dist/engineSessionMessages.js +5 -0
- package/dist/errorSupplementRoles.js +5 -1
- package/dist/fleet/fleetProjection.js +3 -2
- package/dist/fleetTaskDesc.js +3 -3
- package/dist/generated/engineFactTables.d.ts +3 -0
- package/dist/generated/engineFactTables.js +18 -0
- package/dist/generated/engineNoticeTables.js +3 -0
- package/dist/generated/sdkFactTables.d.ts +1 -0
- package/dist/generated/sdkFactTables.js +4 -0
- package/dist/hitl/approvalAskers.d.ts +6 -0
- package/dist/hitl/approvalAskers.js +30 -0
- package/dist/hitl/approvalCardReasons.js +25 -4
- package/dist/hitl/approvalRequestFrameReading.d.ts +21 -0
- package/dist/hitl/approvalRequestFrameReading.js +90 -0
- package/dist/hitl/approvalResolution.d.ts +8 -1
- package/dist/hitl/approvalResolution.js +4 -0
- package/dist/hitl/approvalsFeed.js +1 -1
- package/dist/hitl/askGateWire.js +0 -2
- package/dist/hitl/askParkRowRouting.js +5 -2
- package/dist/hitl/automaticAnswerVerdict.d.ts +5 -2
- package/dist/hitl/automaticAnswerVerdict.js +18 -4
- package/dist/hitl/decisionConflict.d.ts +2 -1
- package/dist/hitl/decisionConflict.js +10 -4
- package/dist/hitl/decisionRefusal.js +1 -0
- package/dist/hitl/decisionSend.d.ts +3 -0
- package/dist/hitl/decisionSend.js +29 -11
- package/dist/hitl/editedApprovalAcceptance.d.ts +25 -0
- package/dist/hitl/editedApprovalAcceptance.js +26 -0
- package/dist/hitl/frameRouter.js +13 -19
- package/dist/hitl/gateLedger.d.ts +0 -4
- package/dist/hitl/gateLedger.js +0 -19
- package/dist/hitl/hitlBridge.d.ts +5 -0
- package/dist/hitl/hitlBridge.js +13 -9
- package/dist/hitl/hitlHostSurface.d.ts +1 -0
- package/dist/hitl/hitlHostSurface.js +7 -2
- package/dist/hitl/hostWithdrawal.d.ts +15 -0
- package/dist/hitl/hostWithdrawal.js +38 -0
- package/dist/hitl/livePendingAsk.js +17 -16
- package/dist/hitl/localAllowRule.js +2 -1
- package/dist/hitl/noAllowCause.d.ts +1 -1
- package/dist/hitl/noAllowCause.js +4 -2
- package/dist/hitl/parkExpiryJudgements.js +26 -1
- package/dist/hitl/parkOwnership.d.ts +2 -0
- package/dist/hitl/parkOwnership.js +48 -8
- package/dist/hitl/parkResolver.js +32 -16
- package/dist/hitl/parkedUnresumable.d.ts +13 -0
- package/dist/hitl/parkedUnresumable.js +80 -0
- package/dist/hitl/permissionRuleScope.d.ts +4 -0
- package/dist/hitl/permissionRuleScope.js +31 -0
- package/dist/hitl/persistedRulesWire.d.ts +2 -0
- package/dist/hitl/persistedRulesWire.js +13 -12
- package/dist/hitl/planReviewWire.js +14 -4
- package/dist/hitl/sessionAllowRoute.js +5 -4
- package/dist/hitl/sessionPolicyWire.js +6 -4
- package/dist/hitl/standingRows.d.ts +2 -5
- package/dist/hitl/standingRows.js +30 -15
- package/dist/hitl/toolApprovalWire.d.ts +42 -9
- package/dist/hitl/toolApprovalWire.js +267 -44
- package/dist/hooksWireCaps.js +35 -4
- package/dist/hostSettingsSource.js +2 -0
- package/dist/hostTurnSettings.d.ts +10 -4
- package/dist/hostTurnSettings.js +112 -5
- package/dist/i18n/messages.en.d.ts +251 -0
- package/dist/i18n/messages.en.js +254 -2
- package/dist/i18n/messages.zh.js +264 -2
- package/dist/index.d.ts +48 -12
- package/dist/index.js +29 -7
- package/dist/inheritEnvCapability.js +2 -0
- package/dist/inputAuthor.d.ts +16 -0
- package/dist/inputAuthor.js +44 -0
- package/dist/interruptRefusalNote.d.ts +2 -0
- package/dist/interruptRefusalNote.js +17 -0
- package/dist/liveModelCatalog.d.ts +2 -0
- package/dist/liveModelCatalog.js +21 -7
- package/dist/liveQuestionStore.d.ts +4 -1
- package/dist/liveQuestionStore.js +12 -3
- package/dist/localEngineJudge.d.ts +75 -0
- package/dist/localEngineJudge.js +242 -0
- package/dist/localEngineOccupancy.d.ts +88 -0
- package/dist/localEngineOccupancy.js +269 -0
- package/dist/localEngineSeatReading.d.ts +29 -0
- package/dist/localEngineSeatReading.js +294 -0
- package/dist/localEngineSpawn.d.ts +113 -0
- package/dist/localEngineSpawn.js +486 -0
- package/dist/localEngineStep.d.ts +286 -0
- package/dist/localEngineStep.js +1086 -0
- package/dist/localEngineStopLadder.d.ts +29 -0
- package/dist/localEngineStopLadder.js +139 -0
- package/dist/localEngineSwapCard.d.ts +43 -0
- package/dist/localEngineSwapCard.js +205 -0
- package/dist/localEngineTiming.d.ts +8 -0
- package/dist/localEngineTiming.js +8 -0
- package/dist/mcpEngineLeg.d.ts +3 -0
- package/dist/mcpEngineLeg.js +38 -1
- package/dist/mcpPanel.js +2 -0
- package/dist/mcpReconnect.d.ts +9 -1
- package/dist/mcpReconnect.js +132 -20
- package/dist/mcpWireCaps.js +2 -2
- package/dist/memoryEntriesWire.d.ts +1 -1
- package/dist/memoryEntriesWire.js +5 -0
- package/dist/modelCapabilityProbe.js +4 -3
- package/dist/modsFace.d.ts +8 -1
- package/dist/modsFace.js +27 -0
- package/dist/noticePresentation.d.ts +2 -0
- package/dist/noticePresentation.js +79 -1
- package/dist/notifications.d.ts +17 -6
- package/dist/notifications.js +67 -19
- package/dist/ownKey.d.ts +5 -0
- package/dist/ownKey.js +8 -1
- package/dist/peerFrames.d.ts +40 -2
- package/dist/peerFrames.js +158 -3
- package/dist/pluginHooksWire.d.ts +2 -1
- package/dist/pluginHooksWire.js +56 -1
- package/dist/replayBody.js +10 -2
- package/dist/request/taskRequest.d.ts +5 -0
- package/dist/request/taskRequest.js +138 -21
- package/dist/resumeRefusalCopy.d.ts +1 -3
- package/dist/resumeRefusalCopy.js +2 -1
- package/dist/retryStatus.d.ts +2 -2
- package/dist/retryStatus.js +2 -0
- package/dist/ruleReadings.js +1 -1
- package/dist/seam.d.ts +24 -2
- package/dist/seam.js +15 -2
- package/dist/seatContract.d.ts +125 -10
- package/dist/seatContract.js +373 -7
- package/dist/sessionBackground.js +4 -1
- package/dist/sessionBackgroundHandoff.js +2 -1
- package/dist/sessionEngine.d.ts +2 -0
- package/dist/sessionEngine.js +111 -0
- package/dist/sessionMemoryStatus.d.ts +2 -1
- package/dist/sessionSyncFileHistory.d.ts +4 -0
- package/dist/sessionSyncFileHistory.js +28 -0
- package/dist/sseIdleTriage.d.ts +13 -1
- package/dist/sseIdleTriage.js +24 -6
- package/dist/subagent/engineSubagentTail.js +6 -0
- package/dist/subagentContentStore.d.ts +12 -0
- package/dist/subagentContentStore.js +72 -11
- package/dist/terminalStatuses.d.ts +1 -0
- package/dist/terminalStatuses.js +4 -1
- package/dist/toolResult.js +18 -5
- package/dist/turnFailurePresentation.js +9 -2
- package/dist/typePins.d.ts +11 -0
- package/dist/usageAnalytics.d.ts +17 -0
- package/dist/usageAnalytics.js +44 -2
- package/dist/wireFailureShape.d.ts +22 -5
- package/dist/wireFailureShape.js +47 -26
- package/dist/wireRefusalCopy.d.ts +31 -1
- package/dist/wireRefusalCopy.js +167 -2
- package/docs/INTEGRATION-CLIENTS.md +3269 -21
- package/package.json +5 -5
package/dist/hitl/frameRouter.js
CHANGED
|
@@ -10,13 +10,13 @@ import { guardedRead, readOwnDataValue } from '../ownKey.js';
|
|
|
10
10
|
import { parkHeldCallIdOf } from '../engineRowRef.js';
|
|
11
11
|
import { inheritFrameBinding } from '../frameBinding.js';
|
|
12
12
|
import { surfaceForCurrentSession, surfaceReplayedApprovalListUnreadable } from './hitlHostSurface.js';
|
|
13
|
-
import { liveCardHoldOf,
|
|
13
|
+
import { liveCardHoldOf, settleEngineClosedApproval } from './approvalHolds.js';
|
|
14
14
|
import { approvalCompleteParked } from './approvalCompleteParked.js';
|
|
15
15
|
import { liveFrameCallKey } from './gateIdentity.js';
|
|
16
16
|
import { readPolicyDenyCause } from './policyDenyCause.js';
|
|
17
17
|
import { msg, renderMessage } from '../i18n/translate.js';
|
|
18
18
|
import { SEMA_DENIAL_KIND_KEY, hasLedgerPositionKey } from './gateLedger.js';
|
|
19
|
-
import {
|
|
19
|
+
import { replayedApprovalDisposition, settledApprovalDisposition } from './replayedApproval.js';
|
|
20
20
|
import { settleWithinDeadline } from '../abortableSleep.js';
|
|
21
21
|
import { ccToolDenialKindForSettledBy } from '../gateVocabulary.js';
|
|
22
22
|
import { REJECT_MESSAGE } from '../adapt/wireShapes.js';
|
|
@@ -32,7 +32,11 @@ export function policyDenyReason(facts) {
|
|
|
32
32
|
const one = typeof facts.toolName === 'string' && SENTENCE_TOOL_NAME.test(facts.toolName) ? `this one "${facts.toolName}" call` : 'this one call';
|
|
33
33
|
const flag = typeof facts.allowRuleFlag === 'string' && SENTENCE_TOOL_NAME.test(facts.allowRuleFlag.replace(/^--?/, '')) ? ` (or ${facts.allowRuleFlag})` : '';
|
|
34
34
|
const budgetKnob = typeof facts.budgetSetting === 'string' && SENTENCE_TOOL_NAME.test(facts.budgetSetting) ? facts.budgetSetting : undefined;
|
|
35
|
-
const cause = facts.cause === 'no_allow_rule' || facts.cause === 'ask_rule' || facts.cause === 'engine_ask_rule' || facts.cause === 'budget'
|
|
35
|
+
const cause = facts.cause === 'no_allow_rule' || facts.cause === 'ask_rule' || facts.cause === 'engine_ask_rule' || facts.cause === 'budget' || facts.cause === 'whole_tool_rule_only'
|
|
36
|
+
? facts.cause
|
|
37
|
+
: 'other';
|
|
38
|
+
const wholeToolOnly = 'this client does not approve calls from the arguments shown to it, so here only an allow rule for the whole tool can approve calls like this ahead of time — ' +
|
|
39
|
+
'a rule that names paths, commands or other arguments applies only where the engine checks it against the real arguments';
|
|
36
40
|
if (facts.lane === 'dont_ask') {
|
|
37
41
|
const mode = 'denied by the dontAsk permission mode';
|
|
38
42
|
switch (cause) {
|
|
@@ -44,6 +48,8 @@ export function policyDenyReason(facts) {
|
|
|
44
48
|
return `${mode}: a rule on the engine side, not one of the user's permission rules, asks about ${one}, and this mode refuses anything that would ask — the user's settings cannot approve it; use another permission mode to answer it`;
|
|
45
49
|
case 'budget':
|
|
46
50
|
return `${mode}: ${one} needed approval, and this session's automatic-approval budget is used up, so saved rules no longer approve calls${budgetKnob !== undefined ? ` — raise ${budgetKnob} before launch to change the cap` : ''}`;
|
|
51
|
+
case 'whole_tool_rule_only':
|
|
52
|
+
return `${mode}: ${one} needed approval; ${wholeToolOnly} — and this mode refuses anything that would ask; use another permission mode to answer it`;
|
|
47
53
|
default:
|
|
48
54
|
return `${mode}: ${one} needed a person's approval that saved permission rules cannot approve here (the engine or your deployment requires a person for it, or it could not be checked against your rules), and this mode refuses anything that would ask — use another permission mode to answer it`;
|
|
49
55
|
}
|
|
@@ -59,6 +65,8 @@ export function policyDenyReason(facts) {
|
|
|
59
65
|
return `${lane}: a rule on the engine side, not one of the user's permission rules, asks about ${one}, so it needed a person's answer, and ${tail}; the user's settings cannot approve it - run the task interactively to answer it`;
|
|
60
66
|
case 'budget':
|
|
61
67
|
return `${lane}: ${one} needed a person's approval, and this session's automatic-approval budget is used up, so saved rules no longer approve calls; ${tail}${budgetKnob !== undefined ? `; raise ${budgetKnob} before launch to change the cap` : ''}`;
|
|
68
|
+
case 'whole_tool_rule_only':
|
|
69
|
+
return `${lane}: ${one} needed a person's approval; ${wholeToolOnly}; ${tail}; run the task interactively to answer it`;
|
|
62
70
|
default:
|
|
63
71
|
return `${lane}: ${one} needed a person's approval, and saved permission rules cannot approve it here (the engine or your deployment requires a person for it, or it could not be checked against your rules); ${tail}; run the task interactively to answer it`;
|
|
64
72
|
}
|
|
@@ -341,7 +349,7 @@ async function routeToolApprovalFrame(ev, ctx) {
|
|
|
341
349
|
return { kind: 'skip' };
|
|
342
350
|
}
|
|
343
351
|
const settledHere = settledApprovalDisposition(ev);
|
|
344
|
-
if (settledHere !== undefined ||
|
|
352
|
+
if (settledHere !== undefined || hasLedgerPositionKey(ev)) {
|
|
345
353
|
const disposition = settledHere ?? (await judgeLedgerApprovalFrame(ev, ctx));
|
|
346
354
|
if (disposition.kind !== 'live') {
|
|
347
355
|
hostLog('debug', disposition.kind === 'decided' && disposition.settled !== undefined
|
|
@@ -349,8 +357,6 @@ async function routeToolApprovalFrame(ev, ctx) {
|
|
|
349
357
|
: `liveHitlAskWire: tool_approval frame ${ev.approvalId} read back from the run event log is ${disposition.kind}` +
|
|
350
358
|
`${disposition.kind === 'unknown' ? ` (${disposition.why})` : disposition.cause !== undefined ? ` (${disposition.cause})` : ''}` +
|
|
351
359
|
' — not raised as a question, no decision sent');
|
|
352
|
-
if (settledHere === undefined && replayedApprovalClosesCardFrame(disposition))
|
|
353
|
-
noteEngineClosedApproval(ev.approvalId, false);
|
|
354
360
|
if (disposition.kind === 'unknown' && ctx.signal?.aborted !== true)
|
|
355
361
|
surfaceReplayedApprovalListUnreadable();
|
|
356
362
|
if (deps.onReplayedApproval !== undefined) {
|
|
@@ -408,7 +414,7 @@ async function judgeLedgerApprovalFrame(ev, ctx) {
|
|
|
408
414
|
.finally(release);
|
|
409
415
|
});
|
|
410
416
|
const approvals = got.kind === 'value' ? { read: 'ok', body: got.value } : { read: 'failed' };
|
|
411
|
-
return replayedApprovalDisposition(ev, {
|
|
417
|
+
return replayedApprovalDisposition(ev, { approvals });
|
|
412
418
|
}
|
|
413
419
|
function routeToolStart(ev, callId, led) {
|
|
414
420
|
led.noteStart(callId, ev);
|
|
@@ -503,17 +509,5 @@ export async function routeFrame(ev, ctx) {
|
|
|
503
509
|
if (ev.type === 'failed') {
|
|
504
510
|
return { kind: 'end', events: [...flushHeldWithInterruptRewrite(led, { terminal: ev, signal: ctx.signal }), ev] };
|
|
505
511
|
}
|
|
506
|
-
if (ev.type === 'approval_request') {
|
|
507
|
-
try {
|
|
508
|
-
if (!hasLedgerPositionKey(ev)) {
|
|
509
|
-
const id = ev.approvalId;
|
|
510
|
-
if (typeof id === 'string' && id !== '')
|
|
511
|
-
led.notePreambleApproval(id);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
catch (e) {
|
|
515
|
-
hostLog('debug', `liveHitlAskWire: approval_request frame unreadable — not counted as preamble evidence: ${String(e).slice(0, 120)}`);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
512
|
return { kind: 'yield', events: [ev] };
|
|
519
513
|
}
|
|
@@ -18,14 +18,10 @@ export interface AskAnsweredOutput {
|
|
|
18
18
|
export declare function durableSeqOf(ev: object): string | undefined;
|
|
19
19
|
export declare const BODY_SEQ_OTHER_AXIS_EVENTS: readonly string[];
|
|
20
20
|
export declare function hasLedgerPositionKey(ev: object): boolean;
|
|
21
|
-
export declare const PREAMBLE_APPROVALS_MAX_ENTRIES = 4096;
|
|
22
21
|
export interface GateLedger {
|
|
23
22
|
noteSeq(ev: AgentEvent): void;
|
|
24
23
|
lastSeq(): string | undefined;
|
|
25
24
|
forgetRunScopedState(): void;
|
|
26
|
-
notePreambleApproval(approvalId: string): void;
|
|
27
|
-
preambleApprovals(): ReadonlySet<string>;
|
|
28
|
-
beginStreamSegment(): void;
|
|
29
25
|
trackLiveCard(phaseOf: () => 'card' | 'respond', handled: Promise<void>): void;
|
|
30
26
|
answeringLiveCards(): Promise<void>[];
|
|
31
27
|
liveCardsSettled(): Promise<void>;
|
package/dist/hitl/gateLedger.js
CHANGED
|
@@ -21,7 +21,6 @@ export function hasLedgerPositionKey(ev) {
|
|
|
21
21
|
const { id, seq } = ev;
|
|
22
22
|
return (id !== undefined && id !== null) || (seq !== undefined && seq !== null);
|
|
23
23
|
}
|
|
24
|
-
export const PREAMBLE_APPROVALS_MAX_ENTRIES = 4096;
|
|
25
24
|
export const START_ARGS_MAX_ENTRIES = 256;
|
|
26
25
|
function normalizeDenyAttribution(a) {
|
|
27
26
|
if (a === undefined)
|
|
@@ -108,7 +107,6 @@ export function createGateLedger() {
|
|
|
108
107
|
let denyStampNextFsEnd = false;
|
|
109
108
|
let denyStampAttribution;
|
|
110
109
|
let seq;
|
|
111
|
-
const preambleApprovalIds = new Set();
|
|
112
110
|
const liveCards = new Set();
|
|
113
111
|
return {
|
|
114
112
|
noteSeq(ev) {
|
|
@@ -125,23 +123,6 @@ export function createGateLedger() {
|
|
|
125
123
|
startArgsByCall.clear();
|
|
126
124
|
gatedStartCalls.clear();
|
|
127
125
|
},
|
|
128
|
-
notePreambleApproval(approvalId) {
|
|
129
|
-
if (typeof approvalId !== 'string' || approvalId === '')
|
|
130
|
-
return;
|
|
131
|
-
preambleApprovalIds.delete(approvalId);
|
|
132
|
-
preambleApprovalIds.add(approvalId);
|
|
133
|
-
if (preambleApprovalIds.size > PREAMBLE_APPROVALS_MAX_ENTRIES) {
|
|
134
|
-
const oldest = preambleApprovalIds.values().next().value;
|
|
135
|
-
if (oldest !== undefined)
|
|
136
|
-
preambleApprovalIds.delete(oldest);
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
preambleApprovals() {
|
|
140
|
-
return preambleApprovalIds;
|
|
141
|
-
},
|
|
142
|
-
beginStreamSegment() {
|
|
143
|
-
preambleApprovalIds.clear();
|
|
144
|
-
},
|
|
145
126
|
trackLiveCard(phaseOf, handled) {
|
|
146
127
|
const entry = { phaseOf, handled };
|
|
147
128
|
liveCards.add(entry);
|
|
@@ -141,6 +141,10 @@ export type FindPendingOutcome = {
|
|
|
141
141
|
export declare function findPendingForTask(client: HitlClientLike, taskId: string, matches: (toolName: string | undefined) => boolean, opts?: {
|
|
142
142
|
signal?: AbortSignal;
|
|
143
143
|
}, callId?: string, identityAccepts?: (row: PendingCheckpoint) => boolean): Promise<FindPendingOutcome>;
|
|
144
|
+
export declare function checkpointQueueReadingOf(row: object): {
|
|
145
|
+
readonly r: PendingCheckpoint;
|
|
146
|
+
readonly foreign: string | null;
|
|
147
|
+
} | undefined;
|
|
144
148
|
export declare class HitlBridge {
|
|
145
149
|
private readonly client;
|
|
146
150
|
private readonly taskId;
|
|
@@ -156,6 +160,7 @@ export declare class HitlBridge {
|
|
|
156
160
|
decision: 'approve';
|
|
157
161
|
updatedInput?: unknown;
|
|
158
162
|
remember?: 'session' | undefined;
|
|
163
|
+
feedback?: string | undefined;
|
|
159
164
|
} | HitlDecideDenyOutcome, toolUseID?: string, opts?: {
|
|
160
165
|
signal?: AbortSignal;
|
|
161
166
|
operation?: DecideOperationSpan;
|
package/dist/hitl/hitlBridge.js
CHANGED
|
@@ -270,11 +270,8 @@ export async function findPendingForTask(client, taskId, matches, opts, callId,
|
|
|
270
270
|
return { ok: false, reason: `approvals.list failed: ${t.reason}` };
|
|
271
271
|
}
|
|
272
272
|
const readings = rows.flatMap((r) => {
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
return [];
|
|
276
|
-
const cp = r;
|
|
277
|
-
return [{ r: cp, foreign: sort === 'unreadable' ? QUEUE_ROW_FAMILY_UNREADABLE_GATE : askParkForeignGateKind(cp) }];
|
|
273
|
+
const x = checkpointQueueReadingOf(r);
|
|
274
|
+
return x === undefined ? [] : [x];
|
|
278
275
|
});
|
|
279
276
|
const decidable = readings.filter((x) => x.foreign === null).map((x) => x.r);
|
|
280
277
|
const identityRow = callId !== undefined
|
|
@@ -313,6 +310,13 @@ export async function findPendingForTask(client, taskId, matches, opts, callId,
|
|
|
313
310
|
return { ok: false, reason: checkpointRowBarSentence(bar), code: 'row_unresumable', rowVerdict: bar };
|
|
314
311
|
return { ok: true, pending, gatedCallId: pending.toolCallId ?? pending.boundCallId ?? undefined };
|
|
315
312
|
}
|
|
313
|
+
export function checkpointQueueReadingOf(row) {
|
|
314
|
+
const sort = checkpointQueueRowSortOf(row);
|
|
315
|
+
if (sort === 'excluded')
|
|
316
|
+
return undefined;
|
|
317
|
+
const cp = row;
|
|
318
|
+
return { r: cp, foreign: sort === 'unreadable' ? QUEUE_ROW_FAMILY_UNREADABLE_GATE : askParkForeignGateKind(cp) };
|
|
319
|
+
}
|
|
316
320
|
export class HitlBridge {
|
|
317
321
|
client;
|
|
318
322
|
taskId;
|
|
@@ -381,6 +385,7 @@ export class HitlBridge {
|
|
|
381
385
|
...this.bindingOf(pending),
|
|
382
386
|
...(outcome.updatedInput !== undefined ? { updatedInput: outcome.updatedInput } : {}),
|
|
383
387
|
...(outcome.remember !== undefined ? { remember: outcome.remember } : {}),
|
|
388
|
+
...(outcome.feedback !== undefined ? { feedback: outcome.feedback } : {}),
|
|
384
389
|
}
|
|
385
390
|
: {
|
|
386
391
|
decision: 'deny',
|
|
@@ -594,12 +599,11 @@ function bindingMismatchReading(e) {
|
|
|
594
599
|
return 'not';
|
|
595
600
|
const code = readWireErrorCodeReading(e);
|
|
596
601
|
const nameReading = protocolErrorNameReading(e);
|
|
597
|
-
const
|
|
598
|
-
|
|
602
|
+
const codeReclassified = code.state === 'code' && code.code === CHECKPOINT_INVALID_OUTCOME;
|
|
603
|
+
const family = (code.state === 'code' && code.code === APPROVAL_BINDING_MISMATCH) ||
|
|
604
|
+
(!codeReclassified && nameReading.state === 'named' && nameReading.name === PROTOCOL_ERROR_NAME.ApprovalBindingMismatchError);
|
|
599
605
|
if (family)
|
|
600
606
|
return 'mismatch';
|
|
601
|
-
if (nameReading.state === 'sdk-instance' && code.state !== 'code')
|
|
602
|
-
return 'unreadable';
|
|
603
607
|
if (code.state === 'unreadable')
|
|
604
608
|
return 'unreadable';
|
|
605
609
|
return 'not';
|
|
@@ -28,6 +28,7 @@ export declare const CANCEL_DENY_WARN_TEXT = "could not cancel the pending quest
|
|
|
28
28
|
export declare const REMEMBER_NOT_APPLIED_WARN_TEXT = "this session-wide allow was not remembered by the worker \u2014 you will be asked again for this tool";
|
|
29
29
|
export declare const EDIT_NOT_FORWARDED_WARN_TEXT = "your edited tool input was NOT forwarded \u2014 the tool is running with the ORIGINAL input you saw before editing";
|
|
30
30
|
export declare function surfaceRememberNotApplied(): void;
|
|
31
|
+
export declare function surfaceAllowNoteNotCarried(sentence: string): void;
|
|
31
32
|
export declare function surfaceEditNotForwarded(): void;
|
|
32
33
|
export declare const APPROVAL_PARKING_WARN_TEXT = "This approval is being parked; answer it again on the parked card when it appears.";
|
|
33
34
|
export declare function approvalParkingNoticeText(retryAfterMs?: number): string;
|
|
@@ -2,6 +2,7 @@ import { createSessionSlot, DEFAULT_SESSION_KEY } from '../sessionSlot.js';
|
|
|
2
2
|
import { hostLog } from '../host.js';
|
|
3
3
|
import { unrefTimer } from '../unrefTimer.js';
|
|
4
4
|
import { decideFailureNote } from '../decideFailureNote.js';
|
|
5
|
+
import { editedApprovalRefusedLine } from './editedApprovalAcceptance.js';
|
|
5
6
|
import { decisionRefusalSaysAskGone } from './decisionRefusal.js';
|
|
6
7
|
const hostSurfaceByKey = createSessionSlot();
|
|
7
8
|
const hostSurfaceMissesByKey = new Map();
|
|
@@ -73,6 +74,11 @@ const EDIT_NOT_FORWARDED_TIMEOUT_MS = 20_000;
|
|
|
73
74
|
export function surfaceRememberNotApplied() {
|
|
74
75
|
surfaceSelfClearingWarn(REMEMBER_NOT_APPLIED_KEY, REMEMBER_NOT_APPLIED_WARN_TEXT, REMEMBER_NOT_APPLIED_TIMEOUT_MS);
|
|
75
76
|
}
|
|
77
|
+
const ALLOW_NOTE_NOT_CARRIED_KEY = 'hitl-allow-note-not-carried';
|
|
78
|
+
const ALLOW_NOTE_NOT_CARRIED_TIMEOUT_MS = 10_000;
|
|
79
|
+
export function surfaceAllowNoteNotCarried(sentence) {
|
|
80
|
+
surfaceSelfClearingWarn(ALLOW_NOTE_NOT_CARRIED_KEY, sentence, ALLOW_NOTE_NOT_CARRIED_TIMEOUT_MS);
|
|
81
|
+
}
|
|
76
82
|
export function surfaceEditNotForwarded() {
|
|
77
83
|
surfaceSelfClearingWarn(EDIT_NOT_FORWARDED_KEY, EDIT_NOT_FORWARDED_WARN_TEXT, EDIT_NOT_FORWARDED_TIMEOUT_MS);
|
|
78
84
|
}
|
|
@@ -100,8 +106,7 @@ const REPLAYED_APPROVAL_LIST_UNREADABLE_TIMEOUT_MS = 10_000;
|
|
|
100
106
|
export function surfaceReplayedApprovalListUnreadable() {
|
|
101
107
|
surfaceSelfClearingWarn(REPLAYED_APPROVAL_LIST_UNREADABLE_KEY, REPLAYED_APPROVAL_LIST_UNREADABLE_WARN_TEXT, REPLAYED_APPROVAL_LIST_UNREADABLE_TIMEOUT_MS);
|
|
102
108
|
}
|
|
103
|
-
export const EDIT_REFUSED_ON_BLIND_ASK_WARN_TEXT =
|
|
104
|
-
'The request is still waiting — approve or deny it as it is.';
|
|
109
|
+
export const EDIT_REFUSED_ON_BLIND_ASK_WARN_TEXT = editedApprovalRefusedLine();
|
|
105
110
|
const EDIT_REFUSED_ON_BLIND_ASK_KEY = 'hitl-edit-refused-blind-ask';
|
|
106
111
|
export function surfaceEditRefusedOnBlindAsk() {
|
|
107
112
|
surfaceSelfClearingWarn(EDIT_REFUSED_ON_BLIND_ASK_KEY, EDIT_REFUSED_ON_BLIND_ASK_WARN_TEXT, EDIT_NOT_FORWARDED_TIMEOUT_MS);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type CheckpointRowBar } from '../checkpointRowVerdict.js';
|
|
2
|
+
export interface HostWithdrawal {
|
|
3
|
+
readonly toldBy: 'host';
|
|
4
|
+
readonly rowVerdict: CheckpointRowBar;
|
|
5
|
+
}
|
|
6
|
+
export declare function hostWithdrawalOf(v: object | null | undefined): HostWithdrawal | undefined;
|
|
7
|
+
export declare const PARK_QUESTION_WITHDRAW_REFUSALS: readonly ["malformed", "no_card", "not_withdrawable"];
|
|
8
|
+
export type ParkQuestionWithdrawRefusal = (typeof PARK_QUESTION_WITHDRAW_REFUSALS)[number];
|
|
9
|
+
export type ParkQuestionWithdrawal = {
|
|
10
|
+
readonly withdrawn: true;
|
|
11
|
+
} | {
|
|
12
|
+
readonly withdrawn: false;
|
|
13
|
+
readonly refusal: ParkQuestionWithdrawRefusal;
|
|
14
|
+
};
|
|
15
|
+
export declare function withdrawParkQuestion(questionId: string, withdrawal: HostWithdrawal): ParkQuestionWithdrawal;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { slotReadOf } from '../wireFailureShape.js';
|
|
2
|
+
import { checkpointRowBarOf } from '../checkpointRowVerdict.js';
|
|
3
|
+
import { takeLocalQuestionWithdrawHook } from '../liveQuestionStore.js';
|
|
4
|
+
import { hostLog } from '../host.js';
|
|
5
|
+
export function hostWithdrawalOf(v) {
|
|
6
|
+
try {
|
|
7
|
+
if (typeof v !== 'object' || v === null)
|
|
8
|
+
return undefined;
|
|
9
|
+
const told = slotReadOf(v, 'toldBy');
|
|
10
|
+
if (told.presence !== 'present' || told.value !== 'host')
|
|
11
|
+
return undefined;
|
|
12
|
+
if (slotReadOf(v, 'rowVerdict').presence !== 'present')
|
|
13
|
+
return undefined;
|
|
14
|
+
const bar = checkpointRowBarOf(v);
|
|
15
|
+
return bar === undefined ? undefined : Object.freeze({ toldBy: 'host', rowVerdict: bar });
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export const PARK_QUESTION_WITHDRAW_REFUSALS = Object.freeze(['malformed', 'no_card', 'not_withdrawable']);
|
|
22
|
+
const WITHDRAWN = Object.freeze({ withdrawn: true });
|
|
23
|
+
const refused = (refusal) => Object.freeze({ withdrawn: false, refusal });
|
|
24
|
+
export function withdrawParkQuestion(questionId, withdrawal) {
|
|
25
|
+
const told = hostWithdrawalOf(withdrawal);
|
|
26
|
+
if (told === undefined)
|
|
27
|
+
return refused('malformed');
|
|
28
|
+
const taken = takeLocalQuestionWithdrawHook(questionId);
|
|
29
|
+
if (taken === 'no_card' || taken === 'not_withdrawable')
|
|
30
|
+
return refused(taken);
|
|
31
|
+
try {
|
|
32
|
+
taken(told);
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
hostLog('debug', `hostWithdrawal: withdraw-hook-threw card=${questionId} thrown=${e instanceof Error ? e.name : typeof e}`);
|
|
36
|
+
}
|
|
37
|
+
return WITHDRAWN;
|
|
38
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { isApprovalRequestFrameV1 } from '@sema-agent/sdk';
|
|
2
|
-
import { appendOwnElement } from '../ownKey.js';
|
|
2
|
+
import { appendOwnElement, readOwnDataNonEmptyString } from '../ownKey.js';
|
|
3
3
|
import { readRequiresRealApproval } from './approvalCardReading.js';
|
|
4
4
|
import { pendingRowIsOfSession } from './parkRowDelegation.js';
|
|
5
5
|
import { approvalLiveFaceOf, liveFaceCarried } from './approvalLiveFace.js';
|
|
6
6
|
import { THROWN_SLOT_UNREADABLE, readThrownSlot } from '../wireFailureShape.js';
|
|
7
|
-
import { isToolApprovalFrame,
|
|
7
|
+
import { isToolApprovalFrame, surfaceApprovalFrameWithId, } from './toolApprovalWire.js';
|
|
8
8
|
function nonEmptyString(v) {
|
|
9
9
|
return typeof v === 'string' && v !== '';
|
|
10
10
|
}
|
|
@@ -24,7 +24,9 @@ function snapshotMembers(raw) {
|
|
|
24
24
|
}
|
|
25
25
|
return raw.length === length ? out : undefined;
|
|
26
26
|
}
|
|
27
|
-
function openingFrameOf(f) {
|
|
27
|
+
function openingFrameOf(f, approvalId) {
|
|
28
|
+
if (typeof f !== 'object' || f === null || readOwnDataNonEmptyString(f, 'approvalId') !== approvalId)
|
|
29
|
+
return undefined;
|
|
28
30
|
if (isToolApprovalFrame(f))
|
|
29
31
|
return f.type === 'tool_approval' ? f : undefined;
|
|
30
32
|
return isApprovalRequestFrameV1(f) ? f : undefined;
|
|
@@ -54,19 +56,19 @@ export function readLivePendingRows(body) {
|
|
|
54
56
|
continue;
|
|
55
57
|
}
|
|
56
58
|
const o = item;
|
|
57
|
-
|
|
59
|
+
const approvalId = readOwnDataNonEmptyString(o, 'approvalId');
|
|
60
|
+
if (approvalId === undefined || !nonEmptyString(o.toolName) || !nonNegativeFinite(o.ts) || !nonNegativeFinite(o.expiresAtMs)) {
|
|
58
61
|
dropped++;
|
|
59
62
|
continue;
|
|
60
63
|
}
|
|
61
|
-
if (seen.has(
|
|
64
|
+
if (seen.has(approvalId)) {
|
|
62
65
|
dropped++;
|
|
63
66
|
continue;
|
|
64
67
|
}
|
|
65
|
-
seen.add(
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
approvalId: o.approvalId,
|
|
68
|
+
seen.add(approvalId);
|
|
69
|
+
const frame = openingFrameOf(o.frame, approvalId);
|
|
70
|
+
appendOwnElement(rows, {
|
|
71
|
+
approvalId,
|
|
70
72
|
toolName: o.toolName,
|
|
71
73
|
ts: o.ts,
|
|
72
74
|
expiresAtMs: o.expiresAtMs,
|
|
@@ -147,13 +149,12 @@ export function livePendingRowToApprovalFrame(row) {
|
|
|
147
149
|
};
|
|
148
150
|
}
|
|
149
151
|
export function surfaceSuspendedAskAndRespond(row, respond, signal, lane) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
+
const frame = row.frame;
|
|
153
|
+
const approvalId = row.approvalId;
|
|
154
|
+
if (frame !== undefined) {
|
|
155
|
+
return surfaceApprovalFrameWithId(frame, approvalId, respond, undefined, signal, lane, undefined);
|
|
152
156
|
}
|
|
153
|
-
return
|
|
154
|
-
...lane,
|
|
155
|
-
argsUnavailable: true,
|
|
156
|
-
});
|
|
157
|
+
return surfaceApprovalFrameWithId(livePendingRowToApprovalFrame(row), approvalId, respond, undefined, signal, { ...lane, argsUnavailable: true }, undefined);
|
|
157
158
|
}
|
|
158
159
|
const TRACKER_UNSEEN_CLAIMS_MAX = 512;
|
|
159
160
|
export function createSuspendedAskTracker(filter) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { reasonOfThrownWithSink } from '../hostCallbackGuard.js';
|
|
2
|
+
import { isWholeToolRuleValue } from './permissionRuleScope.js';
|
|
2
3
|
const BASH_TOOL_NAME_FOR_RULES = 'Bash';
|
|
3
4
|
export function parseLocalAllowRule(rule, expectedToolName, deps) {
|
|
4
5
|
if (typeof rule !== 'string' || rule === '') {
|
|
@@ -14,7 +15,7 @@ export function parseLocalAllowRule(rule, expectedToolName, deps) {
|
|
|
14
15
|
if (typeof value.toolName !== 'string' || value.toolName === '') {
|
|
15
16
|
return { ok: false, error: 'rule text carries no tool name' };
|
|
16
17
|
}
|
|
17
|
-
if (value
|
|
18
|
+
if (isWholeToolRuleValue(value)) {
|
|
18
19
|
return {
|
|
19
20
|
ok: false,
|
|
20
21
|
error: 'candidate is a whole-tool allow rule (broader than this ask) — not offered locally',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type NoAllowCause = 'no_allow_rule' | 'ask_rule' | 'engine_ask_rule' | 'budget' | 'other';
|
|
1
|
+
export type NoAllowCause = 'no_allow_rule' | 'ask_rule' | 'engine_ask_rule' | 'budget' | 'whole_tool_rule_only' | 'other';
|
|
2
2
|
export interface RuleLadderNoAllowFacts {
|
|
3
3
|
readonly allowed?: boolean;
|
|
4
4
|
readonly denied?: boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { askLevelVerdict } from './automaticAnswerVerdict.js';
|
|
2
2
|
function ladderSays(ladder, key) {
|
|
3
3
|
if (typeof ladder !== 'object' || ladder === null)
|
|
4
4
|
return false;
|
|
@@ -17,11 +17,13 @@ export function noAllowCauseOf(card, ladder) {
|
|
|
17
17
|
return 'ask_rule';
|
|
18
18
|
if (ladderSays(ladder, 'budgetExhausted'))
|
|
19
19
|
return 'budget';
|
|
20
|
-
const standing =
|
|
20
|
+
const standing = askLevelVerdict(card, 'standing_rule');
|
|
21
21
|
if (standing.kind === 'must_ask' && (standing.askClass === 'ask_rule' || standing.origin === 'ask_rule')) {
|
|
22
22
|
return ladderSays(ladder, 'noAskRuleMatched') || ladderSays(ladder, 'noRuleMatched') ? 'engine_ask_rule' : 'ask_rule';
|
|
23
23
|
}
|
|
24
24
|
if (ladderSays(ladder, 'noRuleMatched') && standing.kind === 'may_answer')
|
|
25
25
|
return 'no_allow_rule';
|
|
26
|
+
if (standing.kind === 'may_answer')
|
|
27
|
+
return 'whole_tool_rule_only';
|
|
26
28
|
return 'other';
|
|
27
29
|
}
|
|
@@ -1,11 +1,36 @@
|
|
|
1
|
+
import { isApprovalCompleteFrameV1 } from '@sema-agent/sdk';
|
|
1
2
|
import { APPROVAL_EXPIRED } from '../engineErrorCodes.js';
|
|
2
3
|
import { approvalCompleteParked } from './approvalCompleteParked.js';
|
|
3
4
|
export function classifyApprovalCompleteFrame(frame) {
|
|
4
5
|
if (typeof frame !== 'object' || frame === null)
|
|
5
6
|
return null;
|
|
6
7
|
const f = frame;
|
|
7
|
-
|
|
8
|
+
const type = f.type;
|
|
9
|
+
if (type === 'tool_approval_complete')
|
|
10
|
+
return dispositionOf(frame, f);
|
|
11
|
+
if (type === 'approval_complete')
|
|
12
|
+
return presentedFamilyDisposition(frame);
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
function presentedFamilyDisposition(frame) {
|
|
16
|
+
try {
|
|
17
|
+
const snapshot = ownDataSnapshot(frame);
|
|
18
|
+
return isApprovalCompleteFrameV1(snapshot) ? dispositionOf(snapshot, snapshot) : null;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
8
21
|
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function ownDataSnapshot(frame) {
|
|
25
|
+
const snapshot = Object.create(null);
|
|
26
|
+
for (const key of Object.getOwnPropertyNames(frame)) {
|
|
27
|
+
const d = Object.getOwnPropertyDescriptor(frame, key);
|
|
28
|
+
if (d !== undefined && Object.hasOwn(d, 'value'))
|
|
29
|
+
snapshot[key] = d.value;
|
|
30
|
+
}
|
|
31
|
+
return snapshot;
|
|
32
|
+
}
|
|
33
|
+
function dispositionOf(frame, f) {
|
|
9
34
|
const outcome = f.outcome;
|
|
10
35
|
if (outcome === 'allowed' || outcome === 'denied')
|
|
11
36
|
return 'settled';
|
|
@@ -6,5 +6,7 @@ export interface ParkOwnershipDeps {
|
|
|
6
6
|
isOwnRun?: (taskIdTail: string) => boolean;
|
|
7
7
|
}
|
|
8
8
|
export declare function pendingRowIsOwnedByThisSession(row: PendingCheckpoint, deps?: ParkOwnershipDeps): boolean;
|
|
9
|
+
export declare function pendingRowOwnershipOf(row: object, deps?: ParkOwnershipDeps): 'owned' | 'foreign' | 'unknown';
|
|
10
|
+
export declare function usableAskQuestions(value: unknown): AskQuestion[] | null;
|
|
9
11
|
export declare function askQuestionsFromPending(pending: PendingCheckpoint): AskQuestion[] | null;
|
|
10
12
|
export declare function isHitlSafetyErrorLike(e: unknown): boolean;
|
|
@@ -3,31 +3,71 @@ import { rowIdTail } from '../workflow.js';
|
|
|
3
3
|
import { hostSessionFor } from '../host.js';
|
|
4
4
|
import { DEFAULT_SESSION_KEY } from '../sessionSlot.js';
|
|
5
5
|
import { pendingRowIsOfSession } from './parkRowDelegation.js';
|
|
6
|
+
import { slotReadOf } from '../wireFailureShape.js';
|
|
6
7
|
export function pendingRowIsOwnedByThisSession(row, deps) {
|
|
8
|
+
return ownershipVerdictOf(row, deps) === 'owned';
|
|
9
|
+
}
|
|
10
|
+
export function pendingRowOwnershipOf(row, deps) {
|
|
11
|
+
try {
|
|
12
|
+
return ownershipVerdictOf(row, deps);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return 'unknown';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function ownershipVerdictOf(row, deps) {
|
|
7
19
|
const sessionKey = deps?.sessionKey ?? DEFAULT_SESSION_KEY;
|
|
8
20
|
const rowTaskId = typeof row.taskId === 'string' ? row.taskId : '';
|
|
9
21
|
if (rowTaskId.length > 0) {
|
|
10
22
|
const tail = rowIdTail(rowTaskId);
|
|
11
23
|
if (deps?.isOwnRun !== undefined && deps.isOwnRun(tail))
|
|
12
|
-
return
|
|
24
|
+
return 'owned';
|
|
13
25
|
if (sessionKey === DEFAULT_SESSION_KEY && isOwnEngineRun(tail))
|
|
14
|
-
return
|
|
26
|
+
return 'owned';
|
|
15
27
|
}
|
|
16
28
|
const currentSessionId = deps?.currentSessionId ?? (() => hostSessionFor(sessionKey)?.currentSessionId());
|
|
17
29
|
const sessionId = currentSessionId();
|
|
18
30
|
if (typeof sessionId !== 'string' || sessionId.length === 0)
|
|
31
|
+
return 'unknown';
|
|
32
|
+
if (pendingRowIsOfSession(row, sessionId))
|
|
33
|
+
return 'owned';
|
|
34
|
+
return rowIsProvablyForeign(row, sessionId) ? 'foreign' : 'unknown';
|
|
35
|
+
}
|
|
36
|
+
function rowIsProvablyForeign(row, selfSessionId) {
|
|
37
|
+
const own = slotReadOf(row, 'sessionId');
|
|
38
|
+
if (own.presence !== 'present' || typeof own.value !== 'string' || own.value === '' || own.value === selfSessionId)
|
|
39
|
+
return false;
|
|
40
|
+
const agent = slotReadOf(row, 'agent');
|
|
41
|
+
if (agent.presence === 'absent')
|
|
42
|
+
return true;
|
|
43
|
+
if (agent.presence !== 'present' || typeof agent.value !== 'object' || agent.value === null)
|
|
19
44
|
return false;
|
|
20
|
-
|
|
45
|
+
for (const key of ['parentSessionId', 'rootSessionId']) {
|
|
46
|
+
const link = slotReadOf(agent.value, key);
|
|
47
|
+
if (link.presence === 'absent')
|
|
48
|
+
continue;
|
|
49
|
+
if (link.presence !== 'present' || typeof link.value !== 'string' || link.value === selfSessionId)
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
export function usableAskQuestions(value) {
|
|
55
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
56
|
+
return null;
|
|
57
|
+
for (let i = 0; i < value.length; i++) {
|
|
58
|
+
if (!Object.hasOwn(value, i))
|
|
59
|
+
return null;
|
|
60
|
+
const q = value[i];
|
|
61
|
+
if (typeof q !== 'object' || q === null || typeof q.question !== 'string')
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
21
65
|
}
|
|
22
66
|
export function askQuestionsFromPending(pending) {
|
|
23
67
|
const input = pending.input;
|
|
24
68
|
if (typeof input !== 'object' || input === null)
|
|
25
69
|
return null;
|
|
26
|
-
|
|
27
|
-
if (!Array.isArray(qs) || qs.length === 0)
|
|
28
|
-
return null;
|
|
29
|
-
const ok = qs.every(q => typeof q === 'object' && q !== null && typeof q.question === 'string');
|
|
30
|
-
return ok ? qs : null;
|
|
70
|
+
return usableAskQuestions(input.questions);
|
|
31
71
|
}
|
|
32
72
|
export function isHitlSafetyErrorLike(e) {
|
|
33
73
|
return typeof e === 'object' && e !== null && e.name === 'HitlSafetyError';
|