@sema-agent/client-core 0.91.0 → 0.93.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 +351 -0
- package/README.md +51 -34
- package/dist/adapt/arms.js +6 -12
- package/dist/adapt/panelTasks.d.ts +0 -2
- package/dist/adapt/panelTasks.js +6 -26
- package/dist/adapt/toolCards.js +6 -4
- package/dist/adapt.d.ts +1 -1
- package/dist/adapt.js +3 -3
- package/dist/adapter/activeRunSelfHeal.d.ts +0 -2
- package/dist/adapter/activeRunSelfHeal.js +5 -27
- package/dist/adapter/downstream/terminalToSdkResult.js +16 -7
- package/dist/adapter/runStream.js +6 -2
- package/dist/agentsWireCaps.js +1 -1
- package/dist/approvalDecidedByCapability.d.ts +16 -0
- package/dist/approvalDecidedByCapability.js +47 -0
- package/dist/approvalsStreamLiveCapability.js +3 -3
- package/dist/askSurvivesStandingAnswer.d.ts +4 -1
- package/dist/askSurvivesStandingAnswer.js +13 -0
- package/dist/autoModeUnavailable.js +3 -2
- package/dist/centerWiringCapability.js +1 -1
- package/dist/cloudConfigWireCaps.js +10 -9
- package/dist/compensations.js +7 -7
- package/dist/controlRouter.d.ts +23 -23
- package/dist/controlRouter.js +114 -49
- package/dist/coreValuePorts.d.ts +4 -2
- package/dist/coreValuePorts.js +5 -0
- package/dist/decideFailureNote.js +4 -2
- package/dist/decideReceipt.d.ts +11 -0
- package/dist/decideReceipt.js +95 -9
- package/dist/detachWire.d.ts +2 -14
- package/dist/detachWire.js +1 -47
- package/dist/deviceExecutorManagementCapability.d.ts +6 -2
- package/dist/deviceExecutorManagementCapability.js +61 -15
- package/dist/displayUntrusted.js +2 -1
- package/dist/effectiveFacts.d.ts +14 -4
- package/dist/effectiveFacts.js +45 -5
- package/dist/engineAgentAbsence.js +2 -1
- package/dist/engineCapReader.js +16 -10
- package/dist/engineCapsCache.d.ts +2 -0
- package/dist/engineCapsCache.js +13 -2
- package/dist/engineConfigDrift.d.ts +20 -0
- package/dist/engineConfigDrift.js +54 -0
- package/dist/engineErrorCodes.d.ts +17 -0
- package/dist/engineErrorCodes.js +45 -0
- package/dist/engineInlineTaskStats.d.ts +1 -0
- package/dist/engineNoticeCodes.d.ts +16 -2
- package/dist/engineNoticeCodes.js +71 -0
- package/dist/engineWireFor.d.ts +22 -0
- package/dist/engineWireFor.js +154 -9
- package/dist/engineWireSdk.d.ts +0 -1
- package/dist/engineWireSdk.js +0 -3
- package/dist/executionLaneCapability.js +4 -2
- package/dist/fileHistoryCaptureCapability.d.ts +1 -1
- package/dist/fileHistoryCaptureCapability.js +1 -6
- package/dist/fileHistoryLegNotice.d.ts +1 -1
- package/dist/fileHistoryLegNotice.js +1 -6
- package/dist/gateOutcome.js +2 -17
- package/dist/gateVocabulary.d.ts +2 -8
- package/dist/gateVocabulary.js +6 -32
- package/dist/generated/engineFactTables.d.ts +17 -1
- package/dist/generated/engineFactTables.js +91 -0
- package/dist/generated/engineNoticeTables.d.ts +1 -1
- package/dist/generated/engineNoticeTables.js +4 -1
- package/dist/generated/settingsSchemaTables.d.ts +8 -0
- package/dist/generated/settingsSchemaTables.js +40 -0
- package/dist/goalStopHook.d.ts +1 -8
- package/dist/goalStopHook.js +2 -58
- package/dist/headlessPermissionModeWire.d.ts +3 -2
- package/dist/headlessPermissionModeWire.js +46 -5
- package/dist/hitl/allowRuleShadow.d.ts +21 -0
- package/dist/hitl/allowRuleShadow.js +167 -0
- package/dist/hitl/askClassPair.d.ts +14 -0
- package/dist/hitl/askClassPair.js +42 -0
- package/dist/hitl/askGateWire.js +1 -0
- package/dist/hitl/askParkRowRouting.js +2 -2
- package/dist/hitl/askSurvivesPosture.d.ts +5 -0
- package/dist/hitl/askSurvivesPosture.js +17 -0
- package/dist/hitl/automaticAnswerVerdict.d.ts +21 -0
- package/dist/hitl/automaticAnswerVerdict.js +143 -0
- package/dist/hitl/crashConverged.d.ts +1 -0
- package/dist/hitl/crashConverged.js +1 -1
- package/dist/hitl/frameRouter.d.ts +2 -0
- package/dist/hitl/frameRouter.js +73 -16
- package/dist/hitl/gateLedger.d.ts +7 -0
- package/dist/hitl/gateLedger.js +37 -5
- package/dist/hitl/hitlBridge.d.ts +1 -0
- package/dist/hitl/hitlBridge.js +6 -1
- package/dist/hitl/hitlHostSurface.d.ts +5 -2
- package/dist/hitl/hitlHostSurface.js +25 -3
- package/dist/hitl/livePendingAsk.js +24 -5
- package/dist/hitl/parkResolver.js +24 -4
- package/dist/hitl/persistedRulesWire.d.ts +4 -1
- package/dist/hitl/persistedRulesWire.js +38 -19
- package/dist/hitl/planReviewWire.d.ts +0 -2
- package/dist/hitl/planReviewWire.js +3 -20
- package/dist/hitl/replayedApproval.d.ts +21 -0
- package/dist/hitl/replayedApproval.js +89 -0
- package/dist/hitl/sessionPolicyDeliverable.js +20 -2
- package/dist/hitl/sessionPolicyWire.d.ts +10 -0
- package/dist/hitl/sessionPolicyWire.js +19 -6
- package/dist/hitl/toolApprovalWire.d.ts +27 -7
- package/dist/hitl/toolApprovalWire.js +153 -94
- package/dist/hookMatcherVerdict.js +2 -1
- package/dist/hooksWireCaps.d.ts +1 -1
- package/dist/hooksWireCaps.js +1 -1
- package/dist/humanTurn.js +3 -4
- package/dist/index.d.ts +21 -6
- package/dist/index.js +17 -4
- package/dist/inheritEnvWire.d.ts +1 -4
- package/dist/inheritEnvWire.js +3 -22
- package/dist/limitsWire.d.ts +0 -6
- package/dist/limitsWire.js +0 -38
- package/dist/mcpLiveness.js +2 -1
- package/dist/mcpProbeCapability.js +1 -1
- package/dist/mcpProbeWire.js +10 -12
- package/dist/mcpReconnect.d.ts +5 -0
- package/dist/mcpReconnect.js +28 -0
- package/dist/memoryComplianceCapability.js +2 -2
- package/dist/memoryOriginCapability.js +2 -2
- package/dist/memoryVerbsWire.d.ts +1 -1
- package/dist/memoryVerbsWire.js +17 -13
- package/dist/model/catalog.js +2 -1
- package/dist/model/providerPresets.d.ts +3 -1
- package/dist/model/tierVocabulary.d.ts +2 -1
- package/dist/model/tierVocabulary.js +3 -8
- package/dist/ownKey.d.ts +2 -0
- package/dist/ownKey.js +15 -0
- package/dist/peerFrames.js +2 -2
- package/dist/peerLaneCapability.js +2 -2
- package/dist/permissionModesCapability.d.ts +16 -0
- package/dist/permissionModesCapability.js +71 -0
- package/dist/permissionRuleIssue.js +10 -7
- package/dist/permissionRulesWriteCapability.js +2 -2
- package/dist/permissionWireCaps.d.ts +2 -0
- package/dist/permissionWireCaps.js +8 -1
- package/dist/permissionsAllowGrantCapability.js +7 -2
- package/dist/permissionsDeclaration.d.ts +7 -1
- package/dist/permissionsDeclaration.js +29 -20
- package/dist/pluginHooksWire.js +2 -11
- package/dist/postureKnob.js +1 -1
- package/dist/promptProfileWireCaps.d.ts +2 -1
- package/dist/protocolSegment.d.ts +11 -0
- package/dist/protocolSegment.js +31 -0
- package/dist/readFacePosture.js +1 -1
- package/dist/request/taskRequest.js +1 -15
- package/dist/resumeRefusalCopy.d.ts +1 -0
- package/dist/resumeRefusalCopy.js +26 -3
- package/dist/retryStatus.js +2 -1
- package/dist/rewindArchiveCapability.js +2 -2
- package/dist/ruleCompileVerdict.d.ts +2 -0
- package/dist/ruleCompileVerdict.js +13 -0
- package/dist/ruleRemovalConsequence.d.ts +2 -1
- package/dist/runCancelContext.d.ts +12 -0
- package/dist/runCancelContext.js +83 -1
- package/dist/runTerminal.d.ts +16 -0
- package/dist/runTerminal.js +32 -3
- package/dist/seam.d.ts +1 -0
- package/dist/seam.js +1 -1
- package/dist/seatContract.d.ts +2 -1
- package/dist/sessionMemoryStatus.d.ts +2 -4
- package/dist/sessionPolicyCapability.js +1 -1
- package/dist/sessionSyncFileHistoryCapability.d.ts +18 -0
- package/dist/sessionSyncFileHistoryCapability.js +50 -0
- package/dist/skillToolListsCapability.d.ts +18 -0
- package/dist/skillToolListsCapability.js +50 -0
- package/dist/sqlEngineCapability.js +2 -9
- package/dist/steerPriorityCapability.d.ts +18 -0
- package/dist/steerPriorityCapability.js +80 -0
- package/dist/steerReceipt.d.ts +9 -0
- package/dist/steerReceipt.js +25 -0
- package/dist/storePostureCapability.js +2 -2
- package/dist/subagent/engineSubagentSteer.js +2 -9
- package/dist/supportFloor.d.ts +1 -0
- package/dist/supportFloor.js +15 -5
- package/dist/taskRequestWords.d.ts +4 -2
- package/dist/taskRequestWords.js +1 -1
- package/dist/toolHistoryBadTurn.js +1 -2
- package/dist/toolResult.d.ts +10 -1
- package/dist/toolResult.js +80 -6
- package/dist/types/engineState.d.ts +2 -12
- package/dist/userInvokedSkillsCapability.d.ts +18 -0
- package/dist/userInvokedSkillsCapability.js +50 -0
- package/dist/webSearchBackendCapability.js +2 -2
- package/dist/webSearchWireCaps.d.ts +1 -1
- package/dist/webSearchWireCaps.js +40 -2
- package/dist/wireFailureShape.d.ts +2 -0
- package/dist/wireFailureShape.js +6 -2
- package/dist/wireRefusalCopy.d.ts +9 -3
- package/dist/wireRefusalCopy.js +41 -4
- package/dist/workflowsJournalCapability.d.ts +18 -0
- package/dist/workflowsJournalCapability.js +50 -0
- package/dist/writeProtectionCapability.d.ts +2 -4
- package/dist/writeProtectionCapability.js +1 -1
- package/docs/INTEGRATION-CLIENTS.md +1516 -18
- package/package.json +2 -2
|
@@ -15,9 +15,9 @@ export function classifyAskParkRows(rows, taskId, deps) {
|
|
|
15
15
|
kind: 'unborn',
|
|
16
16
|
reason: rows.length === 0
|
|
17
17
|
?
|
|
18
|
-
`no pending row at all for task ${taskId} — either an orphan park (a seat held with no decision outstanding) or the next gate's row is not minted yet`
|
|
18
|
+
`no pending row at all for task ${taskId} — either an orphan park (a seat held with no decision outstanding) or the next gate's row is not minted yet; this client cannot tell which`
|
|
19
19
|
: soleUnowned
|
|
20
|
-
? `the only pending row (task ${String(sole?.taskId)} / session ${String(sole?.sessionId)}) could not be proven to belong to this client (not in its own-run ledger, session id absent or different) — refusing to surface an approval that may be another session's`
|
|
20
|
+
? `the only pending row (task ${String(sole?.taskId)} / session ${String(sole?.sessionId)}) could not be proven to belong to this client (not in its own-run ledger, session id absent or different; this client cannot tell which) — refusing to surface an approval that may be another session's`
|
|
21
21
|
: `${String(rows.length)} pending rows but none for task ${taskId} — ambiguous queue, refusing to surface an unrelated row`,
|
|
22
22
|
};
|
|
23
23
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AutomaticAnswerRoad, type AutomaticAnswerUnknownWhy } from './automaticAnswerVerdict.js';
|
|
1
2
|
export interface AskPostureFacts {
|
|
2
3
|
readonly settingsAskRuleMatched?: boolean;
|
|
3
4
|
}
|
|
@@ -5,10 +6,14 @@ export type AskPostureSurvivalRoad = 'origin' | 'settings_ask_rule';
|
|
|
5
6
|
export type AskPostureVerdict = {
|
|
6
7
|
readonly kind: 'must_ask';
|
|
7
8
|
readonly by: AskPostureSurvivalRoad;
|
|
9
|
+
readonly road?: AutomaticAnswerRoad;
|
|
10
|
+
readonly askClass?: string;
|
|
8
11
|
} | {
|
|
9
12
|
readonly kind: 'posture_may_answer';
|
|
13
|
+
readonly releaseReason?: string;
|
|
10
14
|
} | {
|
|
11
15
|
readonly kind: 'unknown';
|
|
16
|
+
readonly why?: AutomaticAnswerUnknownWhy;
|
|
12
17
|
};
|
|
13
18
|
export declare function askSurvivesPosture(req: {
|
|
14
19
|
readonly origin?: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readOwnNonEmptyString } from '../ownKey.js';
|
|
2
|
+
import { automaticAnswerVerdict } from './automaticAnswerVerdict.js';
|
|
2
3
|
const ORIGIN_SURVIVES_POSTURE = Object.freeze({
|
|
3
4
|
content_question: true,
|
|
4
5
|
ancestor_marked: false,
|
|
@@ -17,6 +18,22 @@ const MUST_ASK_BY_SETTINGS = Object.freeze({ kind: 'must_ask', by: 'settings_ask
|
|
|
17
18
|
const POSTURE_MAY_ANSWER = Object.freeze({ kind: 'posture_may_answer' });
|
|
18
19
|
const UNKNOWN = Object.freeze({ kind: 'unknown' });
|
|
19
20
|
export function askSurvivesPosture(req, facts) {
|
|
21
|
+
const old = legacyPostureVerdict(req, facts);
|
|
22
|
+
const neu = automaticAnswerVerdict(req, 'mode_posture', facts ?? undefined);
|
|
23
|
+
if (old.kind === 'must_ask' || neu.kind === 'must_ask') {
|
|
24
|
+
const by = old.kind === 'must_ask' ? old.by : neu.kind === 'must_ask' && neu.road === 'settings_ask_rule' ? 'settings_ask_rule' : 'origin';
|
|
25
|
+
const road = neu.kind === 'must_ask' ? neu.road : by === 'settings_ask_rule' ? 'settings_ask_rule' : 'origin';
|
|
26
|
+
const askClass = neu.kind === 'must_ask' ? neu.askClass : undefined;
|
|
27
|
+
return Object.freeze({ kind: 'must_ask', by, road, ...(askClass !== undefined ? { askClass } : {}) });
|
|
28
|
+
}
|
|
29
|
+
if (old.kind === 'unknown' || neu.kind === 'unknown') {
|
|
30
|
+
return neu.kind === 'unknown' ? Object.freeze({ kind: 'unknown', why: neu.why }) : UNKNOWN;
|
|
31
|
+
}
|
|
32
|
+
return neu.kind === 'may_answer' && neu.releaseReason !== undefined
|
|
33
|
+
? Object.freeze({ kind: 'posture_may_answer', releaseReason: neu.releaseReason })
|
|
34
|
+
: POSTURE_MAY_ANSWER;
|
|
35
|
+
}
|
|
36
|
+
function legacyPostureVerdict(req, facts) {
|
|
20
37
|
const origin = readOwnNonEmptyString(req, 'origin');
|
|
21
38
|
const rawMatched = typeof facts === 'object' && facts !== null ? facts.settingsAskRuleMatched : undefined;
|
|
22
39
|
const matched = typeof rawMatched === 'boolean' ? rawMatched : undefined;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type CoreModeArbitrationRow } from '../generated/engineFactTables.js';
|
|
2
|
+
export type AutomaticAnswerLane = 'mode_posture' | 'standing_rule';
|
|
3
|
+
export interface AutomaticAnswerFacts {
|
|
4
|
+
readonly settingsAskRuleMatched?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export type AutomaticAnswerRoad = 'origin' | 'requires_real_approval' | 'governance' | 'mandate_word' | 'mandated' | 'allow_rule_shadow' | 'ask_class' | 'settings_ask_rule';
|
|
7
|
+
export type AutomaticAnswerUnknownWhy = 'card_unreadable' | 'lane_not_recognised' | 'classes_malformed' | 'classes_absent' | 'class_not_in_table' | 'host_not_reported';
|
|
8
|
+
export type AutomaticAnswerVerdict = {
|
|
9
|
+
readonly kind: 'must_ask';
|
|
10
|
+
readonly road: AutomaticAnswerRoad;
|
|
11
|
+
readonly askClass?: string;
|
|
12
|
+
readonly origin?: string;
|
|
13
|
+
} | {
|
|
14
|
+
readonly kind: 'may_answer';
|
|
15
|
+
readonly releaseReason?: string;
|
|
16
|
+
} | {
|
|
17
|
+
readonly kind: 'unknown';
|
|
18
|
+
readonly why: AutomaticAnswerUnknownWhy;
|
|
19
|
+
};
|
|
20
|
+
export declare function foldModeArbitrationRow(classes: Iterable<string>): CoreModeArbitrationRow;
|
|
21
|
+
export declare function automaticAnswerVerdict(card: object | null | undefined, lane: AutomaticAnswerLane, facts?: AutomaticAnswerFacts | null): AutomaticAnswerVerdict;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { CORE_MODE_ARBITRATION, CORE_MODE_RELEASE_REASONS } from '../generated/engineFactTables.js';
|
|
2
|
+
import { readOwnNonEmptyString } from '../ownKey.js';
|
|
3
|
+
import { readAskClassPairState } from './askClassPair.js';
|
|
4
|
+
import { approvalAllowRuleShadowed } from './allowRuleShadow.js';
|
|
5
|
+
import { approvalIsMandated, readApprovalMandate } from './toolApprovalWire.js';
|
|
6
|
+
const TABLE = CORE_MODE_ARBITRATION;
|
|
7
|
+
const ORIGIN_IMPLIES_CLASS = Object.freeze({
|
|
8
|
+
content_question: 'content_question',
|
|
9
|
+
org_unavailable: 'governance_unavailable',
|
|
10
|
+
org_rule: 'org_rule',
|
|
11
|
+
hook: 'hook',
|
|
12
|
+
ask_rule: 'ask_rule',
|
|
13
|
+
denial_limit_fallback: 'denial_limit_fallback',
|
|
14
|
+
});
|
|
15
|
+
const MANDATE_WORD_IMPLIES_CLASS = Object.freeze({
|
|
16
|
+
operator_always: 'shell_floor',
|
|
17
|
+
write_protection_unresolved: 'write_protection_unresolved',
|
|
18
|
+
});
|
|
19
|
+
const ORDINARY_ROW = CORE_MODE_ARBITRATION.ordinary;
|
|
20
|
+
export function foldModeArbitrationRow(classes) {
|
|
21
|
+
let any = false;
|
|
22
|
+
let bypassImmune = false;
|
|
23
|
+
let classifierEligible = true;
|
|
24
|
+
let sandboxAdmission = true;
|
|
25
|
+
let ruleMayClear = true;
|
|
26
|
+
let hookAskFloor = false;
|
|
27
|
+
for (const c of classes) {
|
|
28
|
+
if (!Object.hasOwn(TABLE, c))
|
|
29
|
+
continue;
|
|
30
|
+
const row = TABLE[c];
|
|
31
|
+
any = true;
|
|
32
|
+
bypassImmune ||= row.bypassImmune;
|
|
33
|
+
classifierEligible &&= row.classifierEligible;
|
|
34
|
+
sandboxAdmission &&= row.sandboxAdmission;
|
|
35
|
+
ruleMayClear &&= row.ruleMayClear;
|
|
36
|
+
hookAskFloor ||= row.hookAskFloor;
|
|
37
|
+
}
|
|
38
|
+
if (!any)
|
|
39
|
+
return ORDINARY_ROW;
|
|
40
|
+
return Object.freeze({ bypassImmune, classifierEligible, sandboxAdmission, ruleMayClear, hookAskFloor });
|
|
41
|
+
}
|
|
42
|
+
function laneBlocks(lane, row) {
|
|
43
|
+
return lane === 'mode_posture' ? row.bypassImmune : !row.ruleMayClear;
|
|
44
|
+
}
|
|
45
|
+
function mustAsk(road, extra) {
|
|
46
|
+
return Object.freeze({ kind: 'must_ask', road, ...(extra?.askClass !== undefined ? { askClass: extra.askClass } : {}), ...(extra?.origin !== undefined ? { origin: extra.origin } : {}) });
|
|
47
|
+
}
|
|
48
|
+
const UNKNOWN = Object.freeze({
|
|
49
|
+
card_unreadable: Object.freeze({ kind: 'unknown', why: 'card_unreadable' }),
|
|
50
|
+
lane_not_recognised: Object.freeze({ kind: 'unknown', why: 'lane_not_recognised' }),
|
|
51
|
+
classes_malformed: Object.freeze({ kind: 'unknown', why: 'classes_malformed' }),
|
|
52
|
+
classes_absent: Object.freeze({ kind: 'unknown', why: 'classes_absent' }),
|
|
53
|
+
class_not_in_table: Object.freeze({ kind: 'unknown', why: 'class_not_in_table' }),
|
|
54
|
+
host_not_reported: Object.freeze({ kind: 'unknown', why: 'host_not_reported' }),
|
|
55
|
+
});
|
|
56
|
+
const MAY_ANSWER = Object.freeze({ kind: 'may_answer' });
|
|
57
|
+
function evidenceView(card, ...keys) {
|
|
58
|
+
const out = {};
|
|
59
|
+
for (const k of keys) {
|
|
60
|
+
const v = card[k];
|
|
61
|
+
if (v !== undefined)
|
|
62
|
+
out[k] = v;
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
function readHostAskRuleMatched(facts) {
|
|
67
|
+
if (typeof facts !== 'object' || facts === null)
|
|
68
|
+
return undefined;
|
|
69
|
+
try {
|
|
70
|
+
const d = Object.getOwnPropertyDescriptor(facts, 'settingsAskRuleMatched');
|
|
71
|
+
return d !== undefined && 'value' in d && typeof d.value === 'boolean' ? d.value : undefined;
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export function automaticAnswerVerdict(card, lane, facts) {
|
|
78
|
+
if (lane !== 'mode_posture' && lane !== 'standing_rule')
|
|
79
|
+
return UNKNOWN.lane_not_recognised;
|
|
80
|
+
try {
|
|
81
|
+
if (typeof card !== 'object' || card === null)
|
|
82
|
+
return UNKNOWN.card_unreadable;
|
|
83
|
+
const origin = readOwnNonEmptyString(evidenceView(card, 'origin'), 'origin');
|
|
84
|
+
if (origin !== undefined && Object.hasOwn(ORIGIN_IMPLIES_CLASS, origin)) {
|
|
85
|
+
const cls = ORIGIN_IMPLIES_CLASS[origin];
|
|
86
|
+
if (laneBlocks(lane, TABLE[cls]))
|
|
87
|
+
return mustAsk('origin', { origin, askClass: cls });
|
|
88
|
+
}
|
|
89
|
+
if (lane === 'standing_rule' && evidenceView(card, 'requiresRealApproval').requiresRealApproval === true)
|
|
90
|
+
return mustAsk('requires_real_approval');
|
|
91
|
+
if (evidenceView(card, 'governanceForced').governanceForced === true)
|
|
92
|
+
return mustAsk('governance');
|
|
93
|
+
if (lane === 'mode_posture') {
|
|
94
|
+
const word = readApprovalMandate(evidenceView(card, 'mandate'));
|
|
95
|
+
if (word !== undefined && Object.hasOwn(MANDATE_WORD_IMPLIES_CLASS, word)) {
|
|
96
|
+
const cls = MANDATE_WORD_IMPLIES_CLASS[word];
|
|
97
|
+
if (laneBlocks(lane, TABLE[cls]))
|
|
98
|
+
return mustAsk('mandate_word', { askClass: cls });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
const mandateView = evidenceView(card, 'mandated', 'ruleOffersAbsence', 'mandate');
|
|
103
|
+
if (approvalIsMandated(mandateView))
|
|
104
|
+
return mustAsk('mandated');
|
|
105
|
+
if (approvalAllowRuleShadowed(card) || readOwnNonEmptyString(mandateView, 'ruleOffersAbsence') === 'shadowed')
|
|
106
|
+
return mustAsk('allow_rule_shadow');
|
|
107
|
+
}
|
|
108
|
+
const pair = readAskClassPairState(card);
|
|
109
|
+
if (pair.state === 'malformed')
|
|
110
|
+
return UNKNOWN.classes_malformed;
|
|
111
|
+
if (pair.state === 'absent')
|
|
112
|
+
return UNKNOWN.classes_absent;
|
|
113
|
+
const folded = foldModeArbitrationRow(pair.classes);
|
|
114
|
+
let anyKnown = false;
|
|
115
|
+
let firstBlocking;
|
|
116
|
+
let anyUnknown = false;
|
|
117
|
+
for (const c of pair.classes) {
|
|
118
|
+
if (!Object.hasOwn(TABLE, c)) {
|
|
119
|
+
anyUnknown = true;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
anyKnown = true;
|
|
123
|
+
if (firstBlocking === undefined && laneBlocks(lane, TABLE[c]))
|
|
124
|
+
firstBlocking = c;
|
|
125
|
+
}
|
|
126
|
+
if (anyKnown && laneBlocks(lane, folded))
|
|
127
|
+
return mustAsk('ask_class', firstBlocking !== undefined ? { askClass: firstBlocking } : undefined);
|
|
128
|
+
if (anyUnknown)
|
|
129
|
+
return UNKNOWN.class_not_in_table;
|
|
130
|
+
if (lane === 'standing_rule')
|
|
131
|
+
return MAY_ANSWER;
|
|
132
|
+
const matched = readHostAskRuleMatched(facts);
|
|
133
|
+
if (matched === true)
|
|
134
|
+
return mustAsk('settings_ask_rule');
|
|
135
|
+
if (matched !== false)
|
|
136
|
+
return UNKNOWN.host_not_reported;
|
|
137
|
+
const releaseReason = CORE_MODE_RELEASE_REASONS.find((r) => pair.classes.includes(r));
|
|
138
|
+
return releaseReason !== undefined ? Object.freeze({ kind: 'may_answer', releaseReason }) : MAY_ANSWER;
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return UNKNOWN.card_unreadable;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -16,6 +16,7 @@ export interface CrashConvergedRow {
|
|
|
16
16
|
readonly resumeSafe: boolean;
|
|
17
17
|
readonly needsHumanReason?: 'approved_then_interrupted' | 'pending_unsafe' | 'unstable_row' | 'contradictory';
|
|
18
18
|
}
|
|
19
|
+
export declare const isCrashConvergedCause: (v: unknown) => v is CrashConvergedRow["cause"];
|
|
19
20
|
export interface ApprovalsListEnvelope {
|
|
20
21
|
pending: PendingCheckpoint[];
|
|
21
22
|
livePending?: unknown[];
|
|
@@ -2,7 +2,7 @@ const CRASH_CONVERGED_CAUSE_WORDS = Object.freeze({
|
|
|
2
2
|
crashed_before_park: true,
|
|
3
3
|
shutdown_before_park: true,
|
|
4
4
|
});
|
|
5
|
-
const isCrashConvergedCause = (v) => typeof v === 'string' && Object.hasOwn(CRASH_CONVERGED_CAUSE_WORDS, v);
|
|
5
|
+
export const isCrashConvergedCause = (v) => typeof v === 'string' && Object.hasOwn(CRASH_CONVERGED_CAUSE_WORDS, v);
|
|
6
6
|
const isString = (v) => typeof v === 'string';
|
|
7
7
|
const isNumber = (v) => typeof v === 'number';
|
|
8
8
|
const nonEmptyString = (v) => typeof v === 'string' && v.length > 0;
|
|
@@ -2,6 +2,7 @@ import type { AgentEvent } from '@sema-agent/sdk';
|
|
|
2
2
|
import type { DecideOperationObserver, HitlClientLike } from './hitlBridge.js';
|
|
3
3
|
import { type RespondToolApprovalFn, type ToolApprovalFrame, type ToolApprovalFrameLaneOpts, type ToolApprovalFrameOutcome } from './toolApprovalWire.js';
|
|
4
4
|
import type { GateLedger } from './gateLedger.js';
|
|
5
|
+
import { type ReplayedApprovalDisposition } from './replayedApproval.js';
|
|
5
6
|
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.";
|
|
6
7
|
export declare const HITL_POLICY_DENY_MESSAGE: string;
|
|
7
8
|
export declare const HITL_INTERRUPT_MESSAGE_FOR_TOOL_USE = "[Request interrupted by user for tool use]";
|
|
@@ -23,6 +24,7 @@ export interface AskGateWireDeps {
|
|
|
23
24
|
approvalLane?: ToolApprovalFrameLaneOpts;
|
|
24
25
|
onToolApprovalOutcome?: (frame: ToolApprovalFrame, outcome: ToolApprovalFrameOutcome) => void | Promise<void>;
|
|
25
26
|
onDecideOperation?: DecideOperationObserver;
|
|
27
|
+
onReplayedApproval?: (frame: ToolApprovalFrame, disposition: ReplayedApprovalDisposition) => void | Promise<void>;
|
|
26
28
|
}
|
|
27
29
|
type GateKind = 'ask' | 'fs';
|
|
28
30
|
export interface GatePark {
|
package/dist/hitl/frameRouter.js
CHANGED
|
@@ -3,8 +3,10 @@ import { isToolApprovalGate, toolNameIsFsWrite, toolNameIsShellExec, isToolAppro
|
|
|
3
3
|
import { classifierDenyFromToolEnd } from '../classifierVerdictWire.js';
|
|
4
4
|
import { GATE_PARKED_ERROR_CODE } from '../engineErrorCodes.js';
|
|
5
5
|
import { readRunTerminal, runTerminalCode, runTerminalGateKind, runTerminalGateToolName } from '../runTerminal.js';
|
|
6
|
-
import { surfaceForCurrentSession } from './hitlHostSurface.js';
|
|
7
|
-
import { SEMA_DENIAL_KIND_KEY } from './gateLedger.js';
|
|
6
|
+
import { surfaceForCurrentSession, surfaceReplayedApprovalListUnreadable } from './hitlHostSurface.js';
|
|
7
|
+
import { SEMA_DENIAL_KIND_KEY, hasLedgerPositionKey } from './gateLedger.js';
|
|
8
|
+
import { replayedApprovalDisposition } from './replayedApproval.js';
|
|
9
|
+
import { settleWithinDeadline } from '../abortableSleep.js';
|
|
8
10
|
import { ccToolDenialKindForSettledBy } from '../gateVocabulary.js';
|
|
9
11
|
import { REJECT_MESSAGE } from '../adapt/wireShapes.js';
|
|
10
12
|
import { RUN_CANCELLED_CODE } from '../runCancelContext.js';
|
|
@@ -64,6 +66,11 @@ function isGatedToolName(name) {
|
|
|
64
66
|
function isFsOrShellToolName(name) {
|
|
65
67
|
return typeof name === 'string' && (toolNameIsFsWrite(name) || toolNameIsShellExec(name));
|
|
66
68
|
}
|
|
69
|
+
function parkFamilyEvidence(led, gateToolName, gatedCallId) {
|
|
70
|
+
if (!isFsOrShellToolName(gateToolName))
|
|
71
|
+
return false;
|
|
72
|
+
return gatedCallId === undefined || led.isFsOrShellGatedCall(gatedCallId);
|
|
73
|
+
}
|
|
67
74
|
const ENGINE_GATE_PARKED_ERROR_CODE = GATE_PARKED_ERROR_CODE;
|
|
68
75
|
function isEngineAbortToolEnd(ev) {
|
|
69
76
|
if (ev.errorCode === ENGINE_GATE_PARKED_ERROR_CODE)
|
|
@@ -233,22 +240,36 @@ async function routeToolApprovalFrame(ev, ctx) {
|
|
|
233
240
|
hostLog('debug', `liveHitlAskWire: tool_approval frame ${ev.approvalId} but no respond wire — engine self-settles (TTL deny)`);
|
|
234
241
|
return { kind: 'skip' };
|
|
235
242
|
}
|
|
236
|
-
if (
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
243
|
+
if (hasLedgerPositionKey(ev) && !led.preambleApprovals().has(ev.approvalId)) {
|
|
244
|
+
const disposition = await judgeLedgerApprovalFrame(ev, ctx);
|
|
245
|
+
if (disposition.kind !== 'live') {
|
|
246
|
+
hostLog('debug', `liveHitlAskWire: tool_approval frame ${ev.approvalId} read back from the run event log is ${disposition.kind}` +
|
|
247
|
+
`${disposition.kind === 'unknown' ? ` (${disposition.why})` : disposition.cause !== undefined ? ` (${disposition.cause})` : ''}` +
|
|
248
|
+
' — not raised as a question, no decision sent');
|
|
249
|
+
if (disposition.kind === 'unknown' && ctx.signal?.aborted !== true)
|
|
250
|
+
surfaceReplayedApprovalListUnreadable();
|
|
251
|
+
if (deps.onReplayedApproval !== undefined) {
|
|
252
|
+
try {
|
|
253
|
+
const out = deps.onReplayedApproval(ev, disposition);
|
|
254
|
+
if (out && typeof out.then === 'function') {
|
|
255
|
+
void Promise.resolve(out).catch((e) => {
|
|
256
|
+
hostLog('debug', `liveHitlAskWire: onReplayedApproval rejected: ${String(e).slice(0, 160)}`);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
catch (e) {
|
|
261
|
+
hostLog('debug', `liveHitlAskWire: onReplayedApproval threw: ${String(e).slice(0, 160)}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return { kind: 'skip' };
|
|
245
265
|
}
|
|
246
|
-
return { kind: 'skip' };
|
|
247
266
|
}
|
|
248
267
|
const fromSubagent = isFromSubagent(ev);
|
|
249
268
|
hostLog('debug', `liveHitlAskWire: tool_approval frame ${ev.approvalId} tool=${String(ev.toolName)}${fromSubagent ? ` from-subagent=${String(ev.sourceTaskId ?? ev.sourceAgentName ?? 'explicit')}` : ''}`);
|
|
250
|
-
const
|
|
251
|
-
const
|
|
269
|
+
const frameCallId = !fromSubagent && typeof ev.toolCallId === 'string' && ev.toolCallId !== '' ? ev.toolCallId : undefined;
|
|
270
|
+
const gatedCallId = fromSubagent ? undefined : frameCallId ?? led.lastPendingFsCall();
|
|
271
|
+
const argsCallId = frameCallId === undefined ? gatedCallId : typeof ev.toolName === 'string' && toolNameIsFsWrite(ev.toolName) ? frameCallId : undefined;
|
|
272
|
+
const outcome = await surfaceToolApprovalFrameAndRespond(ev, deps.respondToolApproval, argsOfGatedStart(led, argsCallId), ctx.signal, deps.approvalLane);
|
|
252
273
|
const { decision } = outcome;
|
|
253
274
|
if (deps.onToolApprovalOutcome !== undefined) {
|
|
254
275
|
try {
|
|
@@ -275,6 +296,30 @@ async function routeToolApprovalFrame(ev, ctx) {
|
|
|
275
296
|
}
|
|
276
297
|
return { kind: 'skip' };
|
|
277
298
|
}
|
|
299
|
+
const REPLAYED_APPROVAL_LIST_DEADLINE_MS = 3000;
|
|
300
|
+
async function judgeLedgerApprovalFrame(ev, ctx) {
|
|
301
|
+
const outer = ctx.signal;
|
|
302
|
+
const got = await settleWithinDeadline(REPLAYED_APPROVAL_LIST_DEADLINE_MS, (deadline) => {
|
|
303
|
+
const ac = new AbortController();
|
|
304
|
+
const release = () => {
|
|
305
|
+
deadline.removeEventListener('abort', forward);
|
|
306
|
+
outer?.removeEventListener('abort', forward);
|
|
307
|
+
};
|
|
308
|
+
const forward = () => {
|
|
309
|
+
ac.abort();
|
|
310
|
+
release();
|
|
311
|
+
};
|
|
312
|
+
deadline.addEventListener('abort', forward, { once: true });
|
|
313
|
+
outer?.addEventListener('abort', forward, { once: true });
|
|
314
|
+
if (outer?.aborted === true)
|
|
315
|
+
forward();
|
|
316
|
+
return Promise.resolve()
|
|
317
|
+
.then(() => ctx.deps.client.approvals.list({ signal: ac.signal }))
|
|
318
|
+
.finally(release);
|
|
319
|
+
});
|
|
320
|
+
const approvals = got.kind === 'value' ? { read: 'ok', body: got.value } : { read: 'failed' };
|
|
321
|
+
return replayedApprovalDisposition(ev, { preamble: ctx.led.preambleApprovals(), approvals });
|
|
322
|
+
}
|
|
278
323
|
function routeToolStart(ev, callId, led) {
|
|
279
324
|
led.noteStart(callId, ev);
|
|
280
325
|
if (!led.markStarted(callId))
|
|
@@ -313,7 +358,7 @@ function routeSuspended(ev, ctx) {
|
|
|
313
358
|
if (ev.gate &&
|
|
314
359
|
isToolApprovalGate(ev.gate) &&
|
|
315
360
|
ctx.taskId.current) {
|
|
316
|
-
ctx.led.noteParkGate({ fsOrShellFamily:
|
|
361
|
+
ctx.led.noteParkGate({ fsOrShellFamily: parkFamilyEvidence(ctx.led, ev.gate.toolName, gatedCallId) });
|
|
317
362
|
return { kind: 'park', gate: 'fs', ...(gatedCallId !== undefined ? { gatedCallId } : {}) };
|
|
318
363
|
}
|
|
319
364
|
return { kind: 'yield', events: [ev] };
|
|
@@ -324,7 +369,7 @@ function routeDone(ev, ctx) {
|
|
|
324
369
|
const park = parkGateOf(ev.result);
|
|
325
370
|
if (park !== null) {
|
|
326
371
|
ctx.taskId.current = ctx.taskId.current ?? park.taskId;
|
|
327
|
-
led.noteParkGate({ fsOrShellFamily: park.gate === 'fs' &&
|
|
372
|
+
led.noteParkGate({ fsOrShellFamily: park.gate === 'fs' && parkFamilyEvidence(led, park.toolName, gatedCallId) });
|
|
328
373
|
return { kind: 'park', gate: park.gate, pendingDone: ev, ...(gatedCallId !== undefined ? { gatedCallId } : {}) };
|
|
329
374
|
}
|
|
330
375
|
const events = [...flushHeldWithInterruptRewrite(led, { terminal: ev, signal: ctx.signal })];
|
|
@@ -355,5 +400,17 @@ export async function routeFrame(ev, ctx) {
|
|
|
355
400
|
if (ev.type === 'failed') {
|
|
356
401
|
return { kind: 'end', events: [...flushHeldWithInterruptRewrite(led, { terminal: ev, signal: ctx.signal }), ev] };
|
|
357
402
|
}
|
|
403
|
+
if (ev.type === 'approval_request') {
|
|
404
|
+
try {
|
|
405
|
+
if (!hasLedgerPositionKey(ev)) {
|
|
406
|
+
const id = ev.approvalId;
|
|
407
|
+
if (typeof id === 'string' && id !== '')
|
|
408
|
+
led.notePreambleApproval(id);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
catch (e) {
|
|
412
|
+
hostLog('debug', `liveHitlAskWire: approval_request frame unreadable — not counted as preamble evidence: ${String(e).slice(0, 120)}`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
358
415
|
return { kind: 'yield', events: [ev] };
|
|
359
416
|
}
|
|
@@ -13,10 +13,16 @@ export interface AskAnsweredOutput {
|
|
|
13
13
|
notes?: string;
|
|
14
14
|
}>;
|
|
15
15
|
}
|
|
16
|
+
export declare function durableSeqOf(ev: object): string | undefined;
|
|
17
|
+
export declare function hasLedgerPositionKey(ev: object): boolean;
|
|
18
|
+
export declare const PREAMBLE_APPROVALS_MAX_ENTRIES = 4096;
|
|
16
19
|
export interface GateLedger {
|
|
17
20
|
noteSeq(ev: AgentEvent): void;
|
|
18
21
|
lastSeq(): string | undefined;
|
|
19
22
|
forgetRunScopedState(): void;
|
|
23
|
+
notePreambleApproval(approvalId: string): void;
|
|
24
|
+
preambleApprovals(): ReadonlySet<string>;
|
|
25
|
+
beginStreamSegment(): void;
|
|
20
26
|
markStarted(callId: string): boolean;
|
|
21
27
|
noteGatedStart(callId: string, ev: AgentEvent): void;
|
|
22
28
|
gatedStartArgs(): ReadonlyMap<string, unknown>;
|
|
@@ -40,6 +46,7 @@ export interface GateLedger {
|
|
|
40
46
|
lastPendingFsCall(): string | undefined;
|
|
41
47
|
noteFsOrShellGate(callId: string): void;
|
|
42
48
|
lastFsOrShellGatedCallId(): string | undefined;
|
|
49
|
+
isFsOrShellGatedCall(callId: string): boolean;
|
|
43
50
|
noteParkGate(evidence: {
|
|
44
51
|
fsOrShellFamily: boolean;
|
|
45
52
|
}): void;
|
package/dist/hitl/gateLedger.js
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
export const SEMA_COLLATERAL_ABORT_KEY = '_sema_collateral_abort';
|
|
2
2
|
export const SEMA_DENIAL_KIND_KEY = '_sema_denial_kind';
|
|
3
|
+
export function durableSeqOf(ev) {
|
|
4
|
+
const rawSeq = ev.id ?? ev.seq;
|
|
5
|
+
if (typeof rawSeq === 'string' && rawSeq.length > 0)
|
|
6
|
+
return rawSeq;
|
|
7
|
+
if (typeof rawSeq === 'number' && Number.isFinite(rawSeq))
|
|
8
|
+
return String(rawSeq);
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
export function hasLedgerPositionKey(ev) {
|
|
12
|
+
const { id, seq } = ev;
|
|
13
|
+
return (id !== undefined && id !== null) || (seq !== undefined && seq !== null);
|
|
14
|
+
}
|
|
15
|
+
export const PREAMBLE_APPROVALS_MAX_ENTRIES = 4096;
|
|
3
16
|
export const START_ARGS_MAX_ENTRIES = 256;
|
|
4
17
|
function normalizeDenyAttribution(a) {
|
|
5
18
|
if (a === undefined)
|
|
@@ -74,13 +87,12 @@ export function createGateLedger() {
|
|
|
74
87
|
let denyStampNextFsEnd = false;
|
|
75
88
|
let denyStampAttribution;
|
|
76
89
|
let seq;
|
|
90
|
+
const preambleApprovalIds = new Set();
|
|
77
91
|
return {
|
|
78
92
|
noteSeq(ev) {
|
|
79
|
-
const
|
|
80
|
-
if (
|
|
81
|
-
seq =
|
|
82
|
-
else if (typeof rawSeq === 'number' && Number.isFinite(rawSeq))
|
|
83
|
-
seq = String(rawSeq);
|
|
93
|
+
const s = durableSeqOf(ev);
|
|
94
|
+
if (s !== undefined)
|
|
95
|
+
seq = s;
|
|
84
96
|
},
|
|
85
97
|
lastSeq() {
|
|
86
98
|
return seq;
|
|
@@ -91,6 +103,23 @@ export function createGateLedger() {
|
|
|
91
103
|
gatedStartArgsByCall.clear();
|
|
92
104
|
startArgsByCall.clear();
|
|
93
105
|
},
|
|
106
|
+
notePreambleApproval(approvalId) {
|
|
107
|
+
if (typeof approvalId !== 'string' || approvalId === '')
|
|
108
|
+
return;
|
|
109
|
+
preambleApprovalIds.delete(approvalId);
|
|
110
|
+
preambleApprovalIds.add(approvalId);
|
|
111
|
+
if (preambleApprovalIds.size > PREAMBLE_APPROVALS_MAX_ENTRIES) {
|
|
112
|
+
const oldest = preambleApprovalIds.values().next().value;
|
|
113
|
+
if (oldest !== undefined)
|
|
114
|
+
preambleApprovalIds.delete(oldest);
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
preambleApprovals() {
|
|
118
|
+
return preambleApprovalIds;
|
|
119
|
+
},
|
|
120
|
+
beginStreamSegment() {
|
|
121
|
+
preambleApprovalIds.clear();
|
|
122
|
+
},
|
|
94
123
|
markStarted(callId) {
|
|
95
124
|
if (startedCalls.has(callId))
|
|
96
125
|
return false;
|
|
@@ -206,6 +235,9 @@ export function createGateLedger() {
|
|
|
206
235
|
lastFsOrShellGatedCallId() {
|
|
207
236
|
return lastFsOrShellGatedCall;
|
|
208
237
|
},
|
|
238
|
+
isFsOrShellGatedCall(callId) {
|
|
239
|
+
return fsOrShellGatedCalls.has(callId);
|
|
240
|
+
},
|
|
209
241
|
noteParkGate(evidence) {
|
|
210
242
|
if (batchParkCount > 0 && !heldSinceLastPark)
|
|
211
243
|
return;
|
|
@@ -56,6 +56,7 @@ export interface HitlDecideDenyOutcome {
|
|
|
56
56
|
decision: 'deny';
|
|
57
57
|
reason?: string | undefined;
|
|
58
58
|
settledBy?: 'policy' | undefined;
|
|
59
|
+
decidedBy?: 'policy' | undefined;
|
|
59
60
|
}
|
|
60
61
|
export declare class HitlSafetyError extends Error {
|
|
61
62
|
readonly code: HitlSafetyCode;
|
package/dist/hitl/hitlBridge.js
CHANGED
|
@@ -2,6 +2,7 @@ import { askParkForeignGateKind } from '../adapter/activeRunSelfHeal.js';
|
|
|
2
2
|
import { eventSeq } from '../adapter/types.js';
|
|
3
3
|
import { hostLog } from '../host.js';
|
|
4
4
|
import { abortableSleep } from '../abortableSleep.js';
|
|
5
|
+
import { readOwnDataValue } from '../ownKey.js';
|
|
5
6
|
import { suspendedReopenOf } from './suspendedReopen.js';
|
|
6
7
|
export const DEFAULT_DENY_REASON = 'The user rejected this tool use';
|
|
7
8
|
const MAX_DENY_REASON_CHARS = 4096;
|
|
@@ -291,7 +292,11 @@ export class HitlBridge {
|
|
|
291
292
|
decision: 'deny',
|
|
292
293
|
...this.bindingOf(pending),
|
|
293
294
|
...(outcome.reason !== undefined ? { reason: outcome.reason } : {}),
|
|
294
|
-
...(outcome
|
|
295
|
+
...(readOwnDataValue(outcome, 'decidedBy') === 'policy'
|
|
296
|
+
? { decidedBy: 'policy' }
|
|
297
|
+
: readOwnDataValue(outcome, 'settledBy') === 'policy'
|
|
298
|
+
? { settledBy: 'policy' }
|
|
299
|
+
: {}),
|
|
295
300
|
};
|
|
296
301
|
return this.decideRaw(pending.sessionId, decision, opts);
|
|
297
302
|
}
|
|
@@ -28,10 +28,13 @@ export declare const REMEMBER_NOT_APPLIED_WARN_TEXT = "this session-wide allow w
|
|
|
28
28
|
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";
|
|
29
29
|
export declare function surfaceRememberNotApplied(): void;
|
|
30
30
|
export declare function surfaceEditNotForwarded(): void;
|
|
31
|
+
export declare const REPLAYED_APPROVAL_LIST_UNREADABLE_WARN_TEXT: string;
|
|
32
|
+
export declare function surfaceReplayedApprovalListUnreadable(): void;
|
|
31
33
|
export declare const EDIT_REFUSED_ON_BLIND_ASK_WARN_TEXT: string;
|
|
32
34
|
export declare function surfaceEditRefusedOnBlindAsk(): void;
|
|
33
35
|
export declare const RULE_NOT_SENT_WARN_TEXT = "your \"don't ask again\" choice was NOT sent to this engine \u2014 support for that rule form could not be confirmed. The approval itself went through, but you will be asked again";
|
|
34
36
|
export declare function surfaceRuleArmNotSent(): void;
|
|
35
|
-
export
|
|
36
|
-
export declare
|
|
37
|
+
export type RuleArmRejectedWhy = 'both_arms' | 'index_not_a_batch_offer' | 'text_not_offered';
|
|
38
|
+
export declare const RULE_NOT_SENT_REJECTED_WARN_TEXT: string;
|
|
39
|
+
export declare function surfaceRuleArmRejected(why?: RuleArmRejectedWhy): void;
|
|
37
40
|
export declare function observeCancelByDeny(settle: Promise<unknown>, taskId: string): void;
|
|
@@ -72,6 +72,13 @@ export function surfaceRememberNotApplied() {
|
|
|
72
72
|
export function surfaceEditNotForwarded() {
|
|
73
73
|
surfaceSelfClearingWarn(EDIT_NOT_FORWARDED_KEY, EDIT_NOT_FORWARDED_WARN_TEXT, EDIT_NOT_FORWARDED_TIMEOUT_MS);
|
|
74
74
|
}
|
|
75
|
+
export const REPLAYED_APPROVAL_LIST_UNREADABLE_WARN_TEXT = "Could not read the pending-approval list, so an approval request read back from this run's event log was not raised as a question, " +
|
|
76
|
+
'and no decision was sent for it.';
|
|
77
|
+
const REPLAYED_APPROVAL_LIST_UNREADABLE_KEY = 'hitl-replayed-approval-list-unreadable';
|
|
78
|
+
const REPLAYED_APPROVAL_LIST_UNREADABLE_TIMEOUT_MS = 10_000;
|
|
79
|
+
export function surfaceReplayedApprovalListUnreadable() {
|
|
80
|
+
surfaceSelfClearingWarn(REPLAYED_APPROVAL_LIST_UNREADABLE_KEY, REPLAYED_APPROVAL_LIST_UNREADABLE_WARN_TEXT, REPLAYED_APPROVAL_LIST_UNREADABLE_TIMEOUT_MS);
|
|
81
|
+
}
|
|
75
82
|
export const EDIT_REFUSED_ON_BLIND_ASK_WARN_TEXT = 'That approval was NOT sent: this request does not expose its tool arguments here, so an edited approval cannot be applied. ' +
|
|
76
83
|
'The request is still waiting — approve or deny it as it is.';
|
|
77
84
|
const EDIT_REFUSED_ON_BLIND_ASK_KEY = 'hitl-edit-refused-blind-ask';
|
|
@@ -84,11 +91,26 @@ const RULE_NOT_SENT_TIMEOUT_MS = 10_000;
|
|
|
84
91
|
export function surfaceRuleArmNotSent() {
|
|
85
92
|
surfaceSelfClearingWarn(RULE_NOT_SENT_KEY, RULE_NOT_SENT_WARN_TEXT, RULE_NOT_SENT_TIMEOUT_MS);
|
|
86
93
|
}
|
|
87
|
-
|
|
94
|
+
function ruleNotSentRejectedReason(why) {
|
|
95
|
+
switch (why) {
|
|
96
|
+
case 'both_arms':
|
|
97
|
+
return 'the card returned two conflicting choices at once, a rule text and a batch index';
|
|
98
|
+
case 'index_not_a_batch_offer':
|
|
99
|
+
return 'the card returned an index that is not a batch offer';
|
|
100
|
+
case 'text_not_offered':
|
|
101
|
+
return 'the card returned a rule text that is not one of the single rules the engine offered';
|
|
102
|
+
default:
|
|
103
|
+
return 'the card returned a rule text that is not one of the single rules the engine offered, an index that is not a batch offer, or two conflicting choices at once; this client cannot tell which';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function ruleNotSentRejectedText(why) {
|
|
107
|
+
return `your "don't ask again" choice was NOT sent — it did not pass the client-side check (${ruleNotSentRejectedReason(why)}). The approval itself went through, but you will be asked again`;
|
|
108
|
+
}
|
|
109
|
+
export const RULE_NOT_SENT_REJECTED_WARN_TEXT = ruleNotSentRejectedText();
|
|
88
110
|
const RULE_NOT_SENT_REJECTED_KEY = 'hitl-rule-arm-rejected';
|
|
89
111
|
const RULE_NOT_SENT_REJECTED_TIMEOUT_MS = 10_000;
|
|
90
|
-
export function surfaceRuleArmRejected() {
|
|
91
|
-
surfaceSelfClearingWarn(RULE_NOT_SENT_REJECTED_KEY,
|
|
112
|
+
export function surfaceRuleArmRejected(why) {
|
|
113
|
+
surfaceSelfClearingWarn(RULE_NOT_SENT_REJECTED_KEY, ruleNotSentRejectedText(why), RULE_NOT_SENT_REJECTED_TIMEOUT_MS);
|
|
92
114
|
}
|
|
93
115
|
function isNoPendingError(e) {
|
|
94
116
|
if (e instanceof HitlSafetyError)
|
|
@@ -5,20 +5,39 @@ function nonEmptyString(v) {
|
|
|
5
5
|
function nonNegativeFinite(v) {
|
|
6
6
|
return typeof v === 'number' && Number.isFinite(v) && v >= 0;
|
|
7
7
|
}
|
|
8
|
+
function snapshotMembers(raw) {
|
|
9
|
+
const length = raw.length;
|
|
10
|
+
if (typeof length !== 'number' || !Number.isSafeInteger(length) || length < 0)
|
|
11
|
+
return undefined;
|
|
12
|
+
const out = [];
|
|
13
|
+
for (let i = 0; i < length; i++) {
|
|
14
|
+
const d = Object.getOwnPropertyDescriptor(raw, i);
|
|
15
|
+
if (d === undefined || !('value' in d))
|
|
16
|
+
return undefined;
|
|
17
|
+
out.push(d.value);
|
|
18
|
+
}
|
|
19
|
+
return raw.length === length ? out : undefined;
|
|
20
|
+
}
|
|
8
21
|
export function readLivePendingRows(body) {
|
|
9
22
|
if (body === null || typeof body !== 'object' || Array.isArray(body))
|
|
10
23
|
return { kind: 'malformed' };
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
24
|
+
const field = Object.getOwnPropertyDescriptor(body, 'livePending');
|
|
25
|
+
if (field === undefined)
|
|
26
|
+
return 'livePending' in body ? { kind: 'malformed' } : { kind: 'not_reported' };
|
|
27
|
+
if (!('value' in field))
|
|
28
|
+
return { kind: 'malformed' };
|
|
29
|
+
const raw = field.value;
|
|
14
30
|
if (raw === undefined)
|
|
15
31
|
return { kind: 'not_reported' };
|
|
16
32
|
if (!Array.isArray(raw))
|
|
17
33
|
return { kind: 'malformed' };
|
|
34
|
+
const members = snapshotMembers(raw);
|
|
35
|
+
if (members === undefined)
|
|
36
|
+
return { kind: 'malformed' };
|
|
18
37
|
const rows = [];
|
|
19
38
|
const seen = new Set();
|
|
20
39
|
let dropped = 0;
|
|
21
|
-
for (const item of
|
|
40
|
+
for (const item of members) {
|
|
22
41
|
if (item === null || typeof item !== 'object' || Array.isArray(item)) {
|
|
23
42
|
dropped++;
|
|
24
43
|
continue;
|
|
@@ -46,7 +65,7 @@ export function readLivePendingRows(body) {
|
|
|
46
65
|
...(isToolApprovalFrame(o.frame) && o.frame.type === 'tool_approval' && o.frame.approvalId === o.approvalId ? { frame: o.frame } : {}),
|
|
47
66
|
});
|
|
48
67
|
}
|
|
49
|
-
if (
|
|
68
|
+
if (members.length > 0 && rows.length === 0)
|
|
50
69
|
return { kind: 'malformed' };
|
|
51
70
|
return { kind: 'present', rows, dropped };
|
|
52
71
|
}
|