@sema-agent/client-core 0.105.0 → 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 +220 -0
- package/README.md +26 -11
- package/dist/adapt/arms.js +10 -1
- package/dist/adapter/activeRunSelfHeal.d.ts +13 -2
- package/dist/adapter/activeRunSelfHeal.js +80 -7
- package/dist/adapter/downstream/eventToSdkMessage.d.ts +0 -5
- package/dist/adapter/downstream/eventToSdkMessage.js +20 -19
- package/dist/adapter/downstream/terminalToSdkResult.js +2 -3
- package/dist/adapter/downstream/wiringManifestView.d.ts +1 -3
- package/dist/adapter/downstream/wiringManifestView.js +0 -1
- package/dist/adapter/runStream.js +3 -4
- 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/coreValuePorts.d.ts +12 -1
- package/dist/coreValuePorts.js +10 -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/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/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 +2 -2
- package/dist/eventLeg.js +2 -1
- package/dist/fleet/fleetProjection.js +26 -5
- package/dist/generated/engineFactTables.d.ts +2 -1
- package/dist/generated/engineFactTables.js +3 -1
- package/dist/generated/engineNoticeTables.js +19 -4
- package/dist/generated/sdkFactTables.d.ts +3 -0
- package/dist/generated/sdkFactTables.js +15 -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/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/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.js +4 -5
- 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 +2 -0
- package/dist/hitl/planReviewCard.js +21 -0
- package/dist/hitl/planReviewWire.d.ts +2 -2
- package/dist/hitl/planReviewWire.js +101 -36
- 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 +4 -1
- package/dist/hostSettingsSource.js +11 -6
- package/dist/hostTurnSettings.d.ts +4 -5
- package/dist/hostTurnSettings.js +80 -19
- package/dist/i18n/messages.en.d.ts +35 -12
- package/dist/i18n/messages.en.js +36 -13
- package/dist/i18n/messages.zh.js +38 -13
- package/dist/index.d.ts +21 -2
- package/dist/index.js +11 -2
- package/dist/liveInitToolFace.js +0 -1
- package/dist/liveModelCatalog.d.ts +15 -0
- package/dist/liveModelCatalog.js +40 -0
- package/dist/mcpWireCaps.js +1 -1
- package/dist/modelCapabilityProbe.js +66 -33
- package/dist/ownKey.d.ts +0 -1
- package/dist/ownKey.js +0 -2
- package/dist/permissionsDeclaration.d.ts +2 -2
- package/dist/permissionsDeclaration.js +12 -14
- package/dist/projectSeed.d.ts +39 -0
- package/dist/projectSeed.js +80 -0
- package/dist/promptDelivery.js +1 -1
- 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/runReattachPolicy.d.ts +3 -0
- package/dist/runReattachPolicy.js +15 -2
- package/dist/runTerminal.js +30 -8
- package/dist/sandboxWire.d.ts +0 -3
- package/dist/sandboxWire.js +0 -9
- package/dist/seam.d.ts +14 -7
- package/dist/seam.js +12 -0
- package/dist/seatContract.d.ts +57 -7
- package/dist/seatContract.js +132 -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/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/toolResult.js +1 -1
- package/dist/wireRefusalCopy.js +2 -6
- package/docs/INTEGRATION-CLIENTS.md +1022 -16
- package/package.json +5 -5
|
@@ -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
|
}
|
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 ' +
|
|
@@ -135,13 +136,11 @@ export function parkGateArmOf(gate) {
|
|
|
135
136
|
return null;
|
|
136
137
|
}
|
|
137
138
|
const leg = durableDecideLegOf(typeof kind === 'string' ? { gateKind: kind } : null);
|
|
138
|
-
if (leg !== 'approval'
|
|
139
|
+
if (leg !== 'approval')
|
|
139
140
|
return null;
|
|
140
141
|
if (questionCardVerdict(gate) ?? isAskToolLoose(toolName))
|
|
141
142
|
return 'ask';
|
|
142
|
-
|
|
143
|
-
return 'fs';
|
|
144
|
-
return isFsOrShellToolName(toolName) ? 'fs' : null;
|
|
143
|
+
return 'fs';
|
|
145
144
|
}
|
|
146
145
|
const ENGINE_GATE_PARKED_ERROR_CODE = GATE_PARKED_ERROR_CODE;
|
|
147
146
|
function isEngineAbortToolEnd(ev) {
|
|
@@ -336,7 +335,7 @@ async function routeToolApprovalFrame(ev, ctx) {
|
|
|
336
335
|
}
|
|
337
336
|
}
|
|
338
337
|
const fromSubagent = isFromSubagent(ev);
|
|
339
|
-
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')}` : ''}`);
|
|
340
339
|
const frameCallId = !fromSubagent && typeof ev.toolCallId === 'string' && ev.toolCallId !== '' ? ev.toolCallId : undefined;
|
|
341
340
|
const gatedCallId = fromSubagent ? undefined : frameCallId ?? led.lastPendingFsCall();
|
|
342
341
|
const argsCallId = frameCallId === undefined ? gatedCallId : typeof ev.toolName === 'string' && toolNameIsFsWrite(ev.toolName) ? frameCallId : undefined;
|
|
@@ -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
|
}
|
package/dist/hitl/hitlBridge.js
CHANGED
|
@@ -10,6 +10,7 @@ import { APPROVAL_BINDING_MISMATCH, CHECKPOINT_INVALID_OUTCOME, RESUME_POLICY_FO
|
|
|
10
10
|
import { suspendedReopenOf } from './suspendedReopen.js';
|
|
11
11
|
import { closeDecisionOp, decisionLedgerKeysOf, openDecisionOp } from './decisionLedger.js';
|
|
12
12
|
import { askParkBlockGeneration, noteAskParkDecideSettled } from './askParkAutoReopenBlock.js';
|
|
13
|
+
import { checkpointQueueRowSortOf, QUEUE_ROW_FAMILY_UNREADABLE_GATE } from './approvalQueueRow.js';
|
|
13
14
|
export const DEFAULT_DENY_REASON = 'The user rejected this tool use';
|
|
14
15
|
export const MAX_DENY_REASON_CHARS = 4096;
|
|
15
16
|
export function denyReasonForWire(reason, tag) {
|
|
@@ -28,6 +29,15 @@ export function denyReasonForWire(reason, tag) {
|
|
|
28
29
|
`(server caps approval reason at ${MAX_DENY_REASON_CHARS}; over-cap is a 413 reason_too_large that would drop the DECISION, not just the reason)`);
|
|
29
30
|
return cut;
|
|
30
31
|
}
|
|
32
|
+
function resumeTakesRestatementBody(client) {
|
|
33
|
+
if (typeof Reflect.get(client.approvals, 'decidePeerInbound') === 'function')
|
|
34
|
+
return true;
|
|
35
|
+
return client.assistant.resume.length >= 3;
|
|
36
|
+
}
|
|
37
|
+
const _realClientFitsHitlSlice = true;
|
|
38
|
+
void _realClientFitsHitlSlice;
|
|
39
|
+
const _bindingKeysEchoed = true;
|
|
40
|
+
void _bindingKeysEchoed;
|
|
31
41
|
export const PLAN_REVIEW_MODE_AFTER_WORDS = Object.freeze(['default', 'acceptEdits']);
|
|
32
42
|
export function isPlanReviewModeAfter(v) {
|
|
33
43
|
return v !== undefined && PLAN_REVIEW_MODE_AFTER_WORDS.includes(v);
|
|
@@ -258,7 +268,13 @@ export async function findPendingForTask(client, taskId, matches, opts, callId,
|
|
|
258
268
|
hostLog('debug', `hitlBridge: approvals.list failed: ${t.log}`);
|
|
259
269
|
return { ok: false, reason: `approvals.list failed: ${t.reason}` };
|
|
260
270
|
}
|
|
261
|
-
const readings = rows.
|
|
271
|
+
const readings = rows.flatMap((r) => {
|
|
272
|
+
const sort = checkpointQueueRowSortOf(r);
|
|
273
|
+
if (sort === 'excluded')
|
|
274
|
+
return [];
|
|
275
|
+
const cp = r;
|
|
276
|
+
return [{ r: cp, foreign: sort === 'unreadable' ? QUEUE_ROW_FAMILY_UNREADABLE_GATE : askParkForeignGateKind(cp) }];
|
|
277
|
+
});
|
|
262
278
|
const decidable = readings.filter((x) => x.foreign === null).map((x) => x.r);
|
|
263
279
|
const identityRow = callId !== undefined
|
|
264
280
|
? decidable.find((r) => r.taskId === taskId && (r.boundCallId === callId || r.toolCallId === callId))
|
|
@@ -328,12 +344,14 @@ export class HitlBridge {
|
|
|
328
344
|
throw new Error(found.reason);
|
|
329
345
|
}
|
|
330
346
|
bindingOf(p) {
|
|
331
|
-
|
|
347
|
+
const boundCallId = readOwnDataValue(p, 'boundCallId');
|
|
348
|
+
const boundInputHash = readOwnDataValue(p, 'boundInputHash');
|
|
349
|
+
if (typeof boundCallId !== 'string' || boundCallId.length === 0 || typeof boundInputHash !== 'string' || boundInputHash.length === 0) {
|
|
332
350
|
throw new HitlSafetyError(`pending checkpoint ${p.id ?? '<no-id>'} is missing D-1 binding fields ` +
|
|
333
|
-
`(boundCallId=${String(
|
|
351
|
+
`(boundCallId=${String(boundCallId)}, boundInputHash=${String(boundInputHash)}) — ` +
|
|
334
352
|
'pre-D-1 workers are no longer supported (server 5.x requires the verbatim two-tuple echo)', 'binding_missing');
|
|
335
353
|
}
|
|
336
|
-
return { boundCallId
|
|
354
|
+
return { boundCallId, boundInputHash };
|
|
337
355
|
}
|
|
338
356
|
async decideTool(outcome, toolUseID, opts, preResolvedPending) {
|
|
339
357
|
const pending = preResolvedPending ?? (await this.pendingForDecide(toolUseID, wireOptsOf(opts)));
|
|
@@ -406,7 +424,10 @@ export class HitlBridge {
|
|
|
406
424
|
if (kind && kind !== 'resource_limit') {
|
|
407
425
|
throw new HitlSafetyError(`cannot resume a "${kind}" gate — "continue" must never bypass a human approval; use decide`, 'wrong_gate');
|
|
408
426
|
}
|
|
409
|
-
|
|
427
|
+
const assistant = this.client.assistant;
|
|
428
|
+
if (resumeTakesRestatementBody(this.client))
|
|
429
|
+
return assistant.resume(this.taskId, undefined, opts);
|
|
430
|
+
return Reflect.apply(assistant.resume, assistant, [this.taskId, opts]);
|
|
410
431
|
}
|
|
411
432
|
async decideRaw(sessionId, decision, optsIn) {
|
|
412
433
|
const opts = wireOptsOf(optsIn);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ApprovalRequestFrame } from '@sema-agent/sdk';
|
|
2
2
|
import type { ApprovalResolution } from './approvalResolution.js';
|
|
3
3
|
import type { ApprovalsFeedEmission } from './approvalsFeed.js';
|
|
4
|
+
import type { ApprovalMandateWord } from '../gateVocabulary.js';
|
|
4
5
|
import { type RespondToolApprovalFn, type ToolApprovalFrame, type ToolApprovalFrameLaneOpts } from './toolApprovalWire.js';
|
|
5
6
|
export interface LivePendingAskView {
|
|
6
7
|
approvalId: string;
|
|
@@ -13,6 +14,10 @@ export interface LivePendingAskView {
|
|
|
13
14
|
fromSubagent?: true;
|
|
14
15
|
originTaskId?: string;
|
|
15
16
|
frame?: ToolApprovalFrame | ApprovalRequestFrame;
|
|
17
|
+
mandated?: true;
|
|
18
|
+
mandate?: ApprovalMandateWord;
|
|
19
|
+
origin?: string;
|
|
20
|
+
liveFactsUnknown?: true;
|
|
16
21
|
}
|
|
17
22
|
export type LivePendingReading = {
|
|
18
23
|
kind: 'not_reported';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isApprovalRequestFrameV1 } from '@sema-agent/sdk';
|
|
2
2
|
import { appendOwnElement } from '../ownKey.js';
|
|
3
3
|
import { readRequiresRealApproval } from './approvalCardReading.js';
|
|
4
|
+
import { approvalLiveFaceOf, liveFaceCarried } from './approvalLiveFace.js';
|
|
4
5
|
import { THROWN_SLOT_UNREADABLE, readThrownSlot } from '../wireFailureShape.js';
|
|
5
6
|
import { isToolApprovalFrame, surfaceToolApprovalFrameAndRespond, } from './toolApprovalWire.js';
|
|
6
7
|
function nonEmptyString(v) {
|
|
@@ -61,6 +62,8 @@ export function readLivePendingRows(body) {
|
|
|
61
62
|
continue;
|
|
62
63
|
}
|
|
63
64
|
seen.add(o.approvalId);
|
|
65
|
+
const opening = openingFrameOf(o.frame);
|
|
66
|
+
const frame = opening !== undefined && opening.approvalId === o.approvalId ? opening : undefined;
|
|
64
67
|
rows.push({
|
|
65
68
|
approvalId: o.approvalId,
|
|
66
69
|
toolName: o.toolName,
|
|
@@ -71,7 +74,8 @@ export function readLivePendingRows(body) {
|
|
|
71
74
|
...(o.governanceForced === true ? { governanceForced: true } : {}),
|
|
72
75
|
...(o.fromSubagent === true ? { fromSubagent: true } : {}),
|
|
73
76
|
...(nonEmptyString(o.originTaskId) ? { originTaskId: o.originTaskId } : {}),
|
|
74
|
-
...(
|
|
77
|
+
...(frame !== undefined ? { frame } : {}),
|
|
78
|
+
...(frame === undefined ? liveFaceCarried(approvalLiveFaceOf(o)) : {}),
|
|
75
79
|
});
|
|
76
80
|
}
|
|
77
81
|
if (members.length > 0 && rows.length === 0)
|
|
@@ -137,6 +141,7 @@ export function livePendingRowToApprovalFrame(row) {
|
|
|
137
141
|
...(row.originTaskId !== undefined ? { sourceTaskId: row.originTaskId } : {}),
|
|
138
142
|
...(row.governanceForced === true ? { governanceForced: true } : {}),
|
|
139
143
|
...(readRequiresRealApproval(row) ? { requiresRealApproval: true } : {}),
|
|
144
|
+
...liveFaceCarried(approvalLiveFaceOf(row)),
|
|
140
145
|
};
|
|
141
146
|
}
|
|
142
147
|
export function surfaceSuspendedAskAndRespond(row, respond, signal, lane) {
|
|
@@ -6,6 +6,8 @@ export declare function planReviewCardNoteRef(facts: PlanReviewCardFacts): Messa
|
|
|
6
6
|
export declare function planReviewUnreadableNoteRef(): MessageRef;
|
|
7
7
|
export declare function planReviewCardNote(facts: PlanReviewCardFacts, lang?: UiLanguage): string | undefined;
|
|
8
8
|
export declare function planReviewAskAgainKey(facts: PlanReviewCardFacts | undefined): MessageKey;
|
|
9
|
+
export type PlanReviewDecisionReason = 'card_missing' | 'card_withholds' | 'gate_moved' | 'gate_untrusted';
|
|
10
|
+
export declare function planReviewDecisionReasonRef(reason: PlanReviewDecisionReason, card?: PlanReviewCardFacts): MessageRef;
|
|
9
11
|
export interface PlanReviewLedgerSource {
|
|
10
12
|
readonly runRow: (taskId: string, signal: AbortSignal) => Promise<object>;
|
|
11
13
|
readonly frames: (taskId: string, signal: AbortSignal) => AsyncIterable<object>;
|
|
@@ -46,6 +46,27 @@ export function planReviewAskAgainKey(facts) {
|
|
|
46
46
|
return body.cause === 'not_carried' ? 'approval.planReview.askAgain.notCarried' : 'approval.planReview.askAgain.unknown';
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
+
export function planReviewDecisionReasonRef(reason, card) {
|
|
50
|
+
switch (reason) {
|
|
51
|
+
case 'card_missing':
|
|
52
|
+
return msg('approval.planReview.decision.cardMissing');
|
|
53
|
+
case 'card_withholds': {
|
|
54
|
+
let ref;
|
|
55
|
+
try {
|
|
56
|
+
const read = card === undefined ? undefined : planReviewCardFactsFrom(card);
|
|
57
|
+
ref = read === undefined ? undefined : planReviewCardNoteRef(read);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
ref = undefined;
|
|
61
|
+
}
|
|
62
|
+
return ref ?? planReviewUnreadableNoteRef();
|
|
63
|
+
}
|
|
64
|
+
case 'gate_moved':
|
|
65
|
+
return msg('approval.planReview.decision.gateMoved');
|
|
66
|
+
case 'gate_untrusted':
|
|
67
|
+
return msg('approval.planReview.decision.gateUntrusted');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
49
70
|
const PLAN_REVIEW_LEDGER_BUDGET_MS = 3_000;
|
|
50
71
|
const planReviewFactsFlights = new Map();
|
|
51
72
|
export function _resetPlanReviewFactsFlightsForTest() {
|
|
@@ -100,7 +100,7 @@ export interface PlanReviewDeliveryOptions {
|
|
|
100
100
|
}
|
|
101
101
|
export interface DecidePlanReviewOptions extends PlanReviewDeliveryOptions {
|
|
102
102
|
reason?: string;
|
|
103
|
-
|
|
103
|
+
card?: PlanReviewCardFacts;
|
|
104
104
|
}
|
|
105
105
|
export interface PlanReviewDecisionHandle {
|
|
106
106
|
settle(): void;
|
|
@@ -124,7 +124,7 @@ export interface ReopenPlanReviewSharedOpts {
|
|
|
124
124
|
}
|
|
125
125
|
export interface ReopenPlanReviewFreshOpts extends ReopenPlanReviewSharedOpts {
|
|
126
126
|
mintFreshQuestionId?: true;
|
|
127
|
-
deliverDecision?: (taskId: string, decision: Parameters<typeof decidePlanReview>[1], reason
|
|
127
|
+
deliverDecision?: (taskId: string, decision: Parameters<typeof decidePlanReview>[1], reason: string | undefined, card: PlanReviewCardFacts) => void | Promise<void>;
|
|
128
128
|
wire?: PlanReviewWire;
|
|
129
129
|
onOutcome?: PlanReviewDeliveryOptions['onOutcome'];
|
|
130
130
|
}
|