@sema-agent/client-core 0.90.0 → 0.92.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 +365 -0
- package/README.md +48 -32
- package/dist/adapt/arms.js +13 -12
- package/dist/adapt/panelTasks.d.ts +0 -2
- package/dist/adapt/panelTasks.js +6 -26
- package/dist/adapt.js +2 -2
- package/dist/adapter/activeRunSelfHeal.d.ts +0 -2
- package/dist/adapter/activeRunSelfHeal.js +5 -27
- package/dist/adapter/downstream/eventToSdkMessage.js +4 -2
- package/dist/adapter/downstream/terminalToSdkResult.js +16 -7
- package/dist/adapter/downstream/wiringManifestView.d.ts +6 -0
- package/dist/adapter/downstream/wiringManifestView.js +16 -0
- package/dist/adapter/runStream.js +39 -20
- package/dist/askSurvivesStandingAnswer.d.ts +13 -0
- package/dist/askSurvivesStandingAnswer.js +53 -0
- package/dist/attachmentSeatCapability.d.ts +11 -0
- package/dist/attachmentSeatCapability.js +27 -0
- package/dist/attachmentsWireCaps.d.ts +9 -0
- package/dist/attachmentsWireCaps.js +74 -21
- package/dist/autoModeUnavailable.js +3 -2
- package/dist/cloudConfigWireCaps.js +5 -4
- package/dist/compensations.js +13 -17
- package/dist/controlRouter.d.ts +32 -25
- package/dist/controlRouter.js +166 -63
- package/dist/coreValuePorts.d.ts +4 -2
- package/dist/coreValuePorts.js +5 -0
- package/dist/decideFailureNote.js +7 -3
- package/dist/decideReceipt.d.ts +9 -0
- package/dist/decideReceipt.js +62 -1
- package/dist/disableSkillsWire.d.ts +1 -0
- package/dist/disableSkillsWire.js +8 -1
- package/dist/displayUntrusted.js +155 -5
- 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 +5 -1
- package/dist/engineCapsCache.js +69 -8
- package/dist/engineClosedSets.d.ts +2 -0
- package/dist/engineClosedSets.js +3 -0
- package/dist/engineErrorCodes.d.ts +17 -0
- package/dist/engineErrorCodes.js +47 -0
- package/dist/engineNoticeCodes.d.ts +14 -0
- package/dist/engineNoticeCodes.js +71 -0
- package/dist/executionLaneCapability.js +2 -0
- package/dist/gateOutcome.js +2 -17
- package/dist/gateVocabulary.d.ts +2 -4
- package/dist/gateVocabulary.js +3 -11
- package/dist/generated/engineFactTables.d.ts +27 -0
- package/dist/generated/engineFactTables.js +128 -0
- package/dist/generated/engineNoticeTables.js +3 -0
- package/dist/generated/settingsSchemaTables.d.ts +8 -0
- package/dist/generated/settingsSchemaTables.js +40 -0
- 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.d.ts +1 -1
- package/dist/hitl/askGateWire.js +2 -1
- 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 +8 -0
- package/dist/hitl/crashConverged.js +58 -1
- package/dist/hitl/crashConvergedNotice.d.ts +4 -0
- package/dist/hitl/crashConvergedNotice.js +57 -0
- package/dist/hitl/frameRouter.d.ts +2 -0
- package/dist/hitl/frameRouter.js +66 -14
- package/dist/hitl/gateLedger.d.ts +6 -0
- package/dist/hitl/gateLedger.js +34 -5
- package/dist/hitl/hitlBridge.d.ts +7 -4
- package/dist/hitl/hitlHostSurface.d.ts +2 -0
- package/dist/hitl/hitlHostSurface.js +7 -0
- package/dist/hitl/livePendingAsk.js +24 -5
- package/dist/hitl/parkResolver.d.ts +7 -2
- package/dist/hitl/parkResolver.js +21 -7
- package/dist/hitl/persistedRulesWire.d.ts +4 -1
- package/dist/hitl/persistedRulesWire.js +9 -2
- package/dist/hitl/planReviewWire.d.ts +1 -1
- package/dist/hitl/planReviewWire.js +19 -5
- 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 +13 -0
- package/dist/hitl/sessionPolicyWire.js +75 -2
- package/dist/hitl/suspendedReopen.js +2 -2
- package/dist/hitl/toolApprovalWire.d.ts +9 -1
- package/dist/hitl/toolApprovalWire.js +40 -40
- package/dist/hookMatcherVerdict.d.ts +29 -0
- package/dist/hookMatcherVerdict.js +266 -0
- package/dist/humanTurn.d.ts +91 -0
- package/dist/humanTurn.js +252 -0
- package/dist/humanTurnRead.d.ts +105 -0
- package/dist/humanTurnRead.js +331 -0
- package/dist/index.d.ts +38 -11
- package/dist/index.js +27 -8
- package/dist/inheritEnvWire.js +4 -5
- package/dist/liveModelCatalog.d.ts +2 -0
- package/dist/liveModelCatalog.js +51 -3
- package/dist/mcpLiveness.js +2 -1
- package/dist/memoryVerbsWire.d.ts +1 -1
- package/dist/memoryVerbsWire.js +3 -0
- 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/peerFrames.js +2 -2
- package/dist/permissionRuleIssue.d.ts +1 -1
- package/dist/permissionRuleIssue.js +31 -9
- package/dist/permissionsAllowGrantCapability.d.ts +2 -1
- package/dist/permissionsAllowGrantCapability.js +11 -2
- package/dist/permissionsDeclaration.d.ts +29 -0
- package/dist/permissionsDeclaration.js +155 -66
- package/dist/pluginHooksWire.js +2 -11
- package/dist/promptDelivery.d.ts +11 -3
- package/dist/promptDelivery.js +164 -29
- package/dist/promptProfileWireCaps.d.ts +2 -1
- package/dist/protocolSegment.d.ts +9 -0
- package/dist/protocolSegment.js +28 -0
- package/dist/providerRejection.d.ts +1 -0
- package/dist/providerRejection.js +20 -0
- package/dist/readDenyTiers.d.ts +10 -0
- package/dist/readDenyTiers.js +29 -0
- package/dist/request/taskRequest.js +3 -17
- package/dist/resumeRefusalCopy.d.ts +11 -0
- package/dist/resumeRefusalCopy.js +61 -4
- package/dist/rewindArchiveCapability.js +4 -4
- package/dist/rewindWireCaps.d.ts +2 -1
- package/dist/rewindWireCaps.js +19 -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/seam.d.ts +2 -0
- package/dist/seam.js +1 -1
- package/dist/seatContract.d.ts +2 -1
- package/dist/sessionMemoryStatus.d.ts +2 -4
- 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 +1 -8
- 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/subagent/engineSubagentSteer.js +2 -9
- package/dist/supportFloor.js +2 -2
- package/dist/taskRequestWords.d.ts +7 -1
- package/dist/taskRequestWords.js +18 -8
- package/dist/toolHistoryBadTurn.d.ts +1 -0
- package/dist/toolHistoryBadTurn.js +89 -0
- package/dist/toolHistoryMismatch.d.ts +2 -1
- package/dist/toolHistoryMismatch.js +19 -6
- package/dist/toolResult.d.ts +8 -0
- package/dist/toolResult.js +47 -3
- package/dist/types/engineState.d.ts +2 -12
- package/dist/userAttachmentEnvelope.d.ts +61 -0
- package/dist/userAttachmentEnvelope.js +204 -0
- package/dist/userInvokedSkillsCapability.d.ts +18 -0
- package/dist/userInvokedSkillsCapability.js +50 -0
- package/dist/wireFailureShape.d.ts +3 -0
- package/dist/wireFailureShape.js +8 -0
- package/dist/wireRefusalCopy.d.ts +6 -0
- package/dist/wireRefusalCopy.js +26 -3
- package/dist/workflowsJournalCapability.d.ts +18 -0
- package/dist/workflowsJournalCapability.js +50 -0
- package/dist/writeProtectionCapability.d.ts +2 -4
- package/docs/INTEGRATION-CLIENTS.md +1489 -16
- package/package.json +3 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AgentEvent, ApprovalDecision, ApprovalStaleError, PendingCheckpoint, CheckpointGate, PlanReviewRequest, AssistantTaskStatus } from '@sema-agent/sdk';
|
|
2
|
+
import type { PermissionResult } from '@sema-agent/agent-types';
|
|
2
3
|
import type { ApprovalsListEnvelope } from './crashConverged.js';
|
|
3
4
|
import { type SuspendedReopenReading } from './suspendedReopen.js';
|
|
4
5
|
export declare const DEFAULT_DENY_REASON = "The user rejected this tool use";
|
|
@@ -8,9 +9,9 @@ export interface HitlPermissionDecisionLike {
|
|
|
8
9
|
message?: string;
|
|
9
10
|
updatedInput?: unknown;
|
|
10
11
|
}
|
|
11
|
-
export type HitlCanUseToolFn<D extends HitlPermissionDecisionLike = HitlPermissionDecisionLike> = (tool: {
|
|
12
|
+
export type HitlCanUseToolFn<D extends HitlPermissionDecisionLike = HitlPermissionDecisionLike, Ctx = never, Msg = never> = (tool: {
|
|
12
13
|
name: string;
|
|
13
|
-
}, input: Record<string, unknown>, toolUseContext:
|
|
14
|
+
}, input: Record<string, unknown>, toolUseContext: Ctx, assistantMessage: Msg, toolUseID: string, forceDecision?: D) => Promise<D>;
|
|
14
15
|
export interface ApprovalsResourceLike {
|
|
15
16
|
list(opts?: {
|
|
16
17
|
signal?: AbortSignal;
|
|
@@ -147,8 +148,10 @@ export type GatePrompt<D extends HitlPermissionDecisionLike = HitlPermissionDeci
|
|
|
147
148
|
toolUseID: string;
|
|
148
149
|
gate: CheckpointGate | null;
|
|
149
150
|
}) => Promise<D>;
|
|
150
|
-
export declare function makeHitlCanUseTool<D extends HitlPermissionDecisionLike = HitlPermissionDecisionLike>(bridge: HitlBridge, prompt: GatePrompt<D>): HitlCanUseToolFn<D>;
|
|
151
|
-
export interface HitlBackendDenyDecision {
|
|
151
|
+
export declare function makeHitlCanUseTool<D extends HitlPermissionDecisionLike = HitlPermissionDecisionLike, Ctx = unknown, Msg = unknown>(bridge: HitlBridge, prompt: GatePrompt<D>): HitlCanUseToolFn<D, Ctx, Msg>;
|
|
152
|
+
export interface HitlBackendDenyDecision extends Extract<PermissionResult, {
|
|
153
|
+
behavior: 'deny';
|
|
154
|
+
}> {
|
|
152
155
|
behavior: 'deny';
|
|
153
156
|
message: string;
|
|
154
157
|
decisionReason: {
|
|
@@ -28,6 +28,8 @@ 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";
|
|
@@ -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';
|
|
@@ -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
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AgentEvent } from '@sema-agent/sdk';
|
|
2
2
|
import { type GateCurrentPending, type HitlFailureStage } from './hitlBridge.js';
|
|
3
3
|
import { type QuestionAnswer } from '../liveQuestionStore.js';
|
|
4
|
+
import { type FsApprovalOutcome } from './toolApprovalWire.js';
|
|
4
5
|
import type { AskAnsweredOutput, GateLedger } from './gateLedger.js';
|
|
5
6
|
import { type AskGateWireDeps, type GatePark } from './frameRouter.js';
|
|
6
7
|
import { type DecideReceiptView } from '../decideReceipt.js';
|
|
@@ -29,12 +30,16 @@ export type GateOutcome = {
|
|
|
29
30
|
reason: string;
|
|
30
31
|
code?: GateFailureCode | undefined;
|
|
31
32
|
errorCode?: string | undefined;
|
|
33
|
+
retriable?: true;
|
|
32
34
|
retryExhausted?: true;
|
|
33
35
|
currentPending?: GateCurrentPending | undefined;
|
|
34
36
|
};
|
|
35
37
|
export declare function isAlreadyResolvedGateReason(reason: unknown): boolean;
|
|
36
|
-
export declare function
|
|
37
|
-
|
|
38
|
+
export declare function isAlreadyResolvedFailure(outcome: Extract<GateOutcome | FsApprovalOutcome, {
|
|
39
|
+
kind: 'failed';
|
|
40
|
+
}>): boolean;
|
|
41
|
+
export declare function isGateStandingErrorCode(code: string | null | undefined, retriable?: boolean): boolean;
|
|
42
|
+
export declare function isCodeClassifiedGateFailure(code: string | null | undefined, retriable?: boolean): boolean;
|
|
38
43
|
export declare function toAnsweredOutput(questions: unknown[], answer: QuestionAnswer): AskAnsweredOutput;
|
|
39
44
|
export type ParkResolution = {
|
|
40
45
|
kind: 'reattach';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { putOwnKey } from '../ownKey.js';
|
|
2
2
|
import { DecideTransportRetryExhaustedError, HitlBridge, HitlSafetyError, NO_QUESTION_OVERLAY_REASON, findPendingForTask, isNoDecisionSurfaceReason, readDecideCurrentPending, readWireErrorCode, } from './hitlBridge.js';
|
|
3
|
-
import { PARKED_RESUME_STARTUP_FAILED, RESUME_POLICY_FOLD_CODE, isDecideParkMovedCode } from '../engineErrorCodes.js';
|
|
3
|
+
import { ASK_GONE, CHECKPOINT_UNSUPPORTED_VERSION, PARKED_RESUME_STARTUP_FAILED, RESUME_POLICY_FOLD_CODE, isDecideParkMovedCode, refusalSaysStillPending } from '../engineErrorCodes.js';
|
|
4
4
|
import { publishQuestionFrame, registerLocalQuestionResponder, hasQuestionOverlay, } from '../liveQuestionStore.js';
|
|
5
5
|
import { hostLog } from '../host.js';
|
|
6
6
|
import { surfaceFsApprovalAndDecide } from './toolApprovalWire.js';
|
|
@@ -40,21 +40,33 @@ function alreadyResolvedReasonToken(reason) {
|
|
|
40
40
|
const r = reason.toLowerCase();
|
|
41
41
|
return ALREADY_RESOLVED_REASON_TOKENS.find((t) => r.includes(t));
|
|
42
42
|
}
|
|
43
|
-
function isAlreadyResolvedFailure(outcome) {
|
|
43
|
+
export function isAlreadyResolvedFailure(outcome) {
|
|
44
|
+
try {
|
|
45
|
+
return alreadyResolvedFailureVerdict(outcome);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function alreadyResolvedFailureVerdict(outcome) {
|
|
52
|
+
if (typeof outcome !== 'object' || outcome === null)
|
|
53
|
+
return false;
|
|
44
54
|
const code = 'code' in outcome && isGateFailureCode(outcome.code) ? outcome.code : undefined;
|
|
45
55
|
if (code !== undefined)
|
|
46
56
|
return code === 'no_pending';
|
|
47
|
-
if ('errorCode' in outcome && isCodeClassifiedGateFailure(outcome.errorCode))
|
|
57
|
+
if ('errorCode' in outcome && isCodeClassifiedGateFailure(outcome.errorCode, Object.hasOwn(outcome, 'retriable') && outcome.retriable === true))
|
|
48
58
|
return false;
|
|
49
59
|
if ('errorCode' in outcome && isDecideParkMovedCode(outcome.errorCode))
|
|
50
60
|
return true;
|
|
61
|
+
if ('errorCode' in outcome && outcome.errorCode === ASK_GONE)
|
|
62
|
+
return true;
|
|
51
63
|
return isAlreadyResolvedGateReason(outcome.reason);
|
|
52
64
|
}
|
|
53
|
-
export function isGateStandingErrorCode(code) {
|
|
54
|
-
return code === RESUME_POLICY_FOLD_CODE || code === 'corrupt';
|
|
65
|
+
export function isGateStandingErrorCode(code, retriable) {
|
|
66
|
+
return code === RESUME_POLICY_FOLD_CODE || code === 'corrupt' || (code === CHECKPOINT_UNSUPPORTED_VERSION && retriable === true);
|
|
55
67
|
}
|
|
56
|
-
export function isCodeClassifiedGateFailure(code) {
|
|
57
|
-
return isGateStandingErrorCode(code) || code === PARKED_RESUME_STARTUP_FAILED;
|
|
68
|
+
export function isCodeClassifiedGateFailure(code, retriable) {
|
|
69
|
+
return isGateStandingErrorCode(code, retriable) || code === PARKED_RESUME_STARTUP_FAILED;
|
|
58
70
|
}
|
|
59
71
|
export function toAnsweredOutput(questions, answer) {
|
|
60
72
|
const answers = {};
|
|
@@ -153,6 +165,7 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
|
|
|
153
165
|
const code = e instanceof HitlSafetyError && isGateFailureCode(e.code) ? e.code : undefined;
|
|
154
166
|
const currentPending = readDecideCurrentPending(e);
|
|
155
167
|
const wireCode = readWireErrorCode(e);
|
|
168
|
+
const wireRetriable = typeof e === 'object' && e !== null && refusalSaysStillPending(e);
|
|
156
169
|
return {
|
|
157
170
|
kind: 'failed',
|
|
158
171
|
stage: 'decide',
|
|
@@ -162,6 +175,7 @@ async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGat
|
|
|
162
175
|
...(e instanceof DecideTransportRetryExhaustedError ? { retryExhausted: true } : {}),
|
|
163
176
|
...(currentPending !== undefined ? { currentPending } : {}),
|
|
164
177
|
...(wireCode !== undefined ? { errorCode: wireCode } : {}),
|
|
178
|
+
...(wireRetriable ? { retriable: true } : {}),
|
|
165
179
|
};
|
|
166
180
|
}
|
|
167
181
|
}
|
|
@@ -50,6 +50,9 @@ export type RulesFailure = {
|
|
|
50
50
|
} | {
|
|
51
51
|
kind: 'forbidden';
|
|
52
52
|
message: string;
|
|
53
|
+
} | {
|
|
54
|
+
kind: 'operators-unconfigured';
|
|
55
|
+
message: string;
|
|
53
56
|
} | {
|
|
54
57
|
kind: 'body-shape';
|
|
55
58
|
message: string;
|
|
@@ -100,7 +103,7 @@ export type PersistedRuleWriteBehavior = (typeof PERSISTED_RULE_WRITE_BEHAVIORS)
|
|
|
100
103
|
export type PersistedRuleWriteRequest = RuleWriteRequest;
|
|
101
104
|
export type PersistedRuleWriteWireResult = RuleWriteResult;
|
|
102
105
|
export type PersistedRuleLiveness = RemovalLiveness;
|
|
103
|
-
export declare const PERSISTED_RULE_WRITE_REFUSAL_CAUSES: readonly ["unwritable_behavior", "unreadable_identity", "unusable_principal", "client_too_old", "field_refused", "body_shape", "operator_only", "store_absent", "lane_too_old", "route_absent", "not_committed"];
|
|
106
|
+
export declare const PERSISTED_RULE_WRITE_REFUSAL_CAUSES: readonly ["unwritable_behavior", "unreadable_identity", "unusable_principal", "client_too_old", "field_refused", "body_shape", "operator_only", "operators_unconfigured", "store_absent", "lane_too_old", "route_absent", "not_committed"];
|
|
104
107
|
export type PersistedRuleWriteRefusalCause = (typeof PERSISTED_RULE_WRITE_REFUSAL_CAUSES)[number];
|
|
105
108
|
export declare const PERSISTED_RULE_WRITE_UNKNOWN_REASONS: readonly ["indeterminate", "malformed_result", "identity_mismatch", "unattributed_method_refusal", "unclassified"];
|
|
106
109
|
export type PersistedRuleWriteUnknownReason = (typeof PERSISTED_RULE_WRITE_UNKNOWN_REASONS)[number];
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { engineCapTrue } from '../engineCapsCache.js';
|
|
2
|
+
import { CORE_RULE_BEHAVIORS } from '../generated/engineFactTables.js';
|
|
2
3
|
import { wireFailureShapeOf } from '../wireFailureShape.js';
|
|
4
|
+
import { CAPABILITY_OPERATOR_PRINCIPALS_REQUIRED } from '../engineErrorCodes.js';
|
|
3
5
|
import { hostLog } from '../host.js';
|
|
4
6
|
import { observedPermissionRulesWrite, permissionRulesWriteAvailable } from '../permissionRulesWriteCapability.js';
|
|
5
7
|
import { readToolApprovalRespondAck } from './toolApprovalWire.js';
|
|
@@ -7,7 +9,7 @@ export function persistedRulesLaneAvailable(baseUrl) {
|
|
|
7
9
|
return engineCapTrue(baseUrl, 'permissionRules');
|
|
8
10
|
}
|
|
9
11
|
export function persistedRulesGovernanceAvailable(baseUrl) {
|
|
10
|
-
return engineCapTrue(baseUrl, '
|
|
12
|
+
return engineCapTrue(baseUrl, 'permissionRules');
|
|
11
13
|
}
|
|
12
14
|
export function persistedRulesWriteAvailable(baseUrl) {
|
|
13
15
|
return permissionRulesWriteAvailable(observedPermissionRulesWrite(baseUrl ?? ''));
|
|
@@ -66,6 +68,8 @@ export function classifyRulesFailure(e) {
|
|
|
66
68
|
return { kind: 'too-many-candidates', message };
|
|
67
69
|
if (status === 403 && errorCode === 'auth.operator_only')
|
|
68
70
|
return { kind: 'forbidden', message };
|
|
71
|
+
if (errorCode === CAPABILITY_OPERATOR_PRINCIPALS_REQUIRED)
|
|
72
|
+
return { kind: 'operators-unconfigured', message };
|
|
69
73
|
if (status === 405) {
|
|
70
74
|
return errorCode === 'method_not_allowed'
|
|
71
75
|
? { kind: 'method-unsupported', message }
|
|
@@ -73,7 +77,7 @@ export function classifyRulesFailure(e) {
|
|
|
73
77
|
}
|
|
74
78
|
return { kind: 'error', message };
|
|
75
79
|
}
|
|
76
|
-
export const PERSISTED_RULE_BEHAVIORS =
|
|
80
|
+
export const PERSISTED_RULE_BEHAVIORS = CORE_RULE_BEHAVIORS;
|
|
77
81
|
const BEHAVIOR_SET = new Set(PERSISTED_RULE_BEHAVIORS);
|
|
78
82
|
function rowStr(row, key) {
|
|
79
83
|
if (typeof row !== 'object' || row === null || Array.isArray(row))
|
|
@@ -221,6 +225,7 @@ export const PERSISTED_RULE_WRITE_REFUSAL_CAUSES = Object.freeze([
|
|
|
221
225
|
'field_refused',
|
|
222
226
|
'body_shape',
|
|
223
227
|
'operator_only',
|
|
228
|
+
'operators_unconfigured',
|
|
224
229
|
'store_absent',
|
|
225
230
|
'lane_too_old',
|
|
226
231
|
'route_absent',
|
|
@@ -242,6 +247,8 @@ function writeOutcomeFromFailure(failure) {
|
|
|
242
247
|
return { status: 'refused', cause: 'body_shape', message };
|
|
243
248
|
case 'forbidden':
|
|
244
249
|
return { status: 'refused', cause: 'operator_only', message };
|
|
250
|
+
case 'operators-unconfigured':
|
|
251
|
+
return { status: 'refused', cause: 'operators_unconfigured', message };
|
|
245
252
|
case 'lane-unavailable':
|
|
246
253
|
return { status: 'refused', cause: 'store_absent', message };
|
|
247
254
|
case 'method-unsupported':
|
|
@@ -43,7 +43,7 @@ export interface PlanReviewOutcomeMeta {
|
|
|
43
43
|
decision: 'approve' | 'reject';
|
|
44
44
|
effect: PlanReviewDecisionEffect;
|
|
45
45
|
}
|
|
46
|
-
export declare function planReviewOutcomeTail(taskId: string, decision: 'approve' | 'reject', effect: PlanReviewDecisionEffect): string;
|
|
46
|
+
export declare function planReviewOutcomeTail(taskId: string, decision: 'approve' | 'reject', effect: PlanReviewDecisionEffect, cause?: 'stored_form_unsupported'): string;
|
|
47
47
|
export type PlanReviewWireClient = {
|
|
48
48
|
assistant: Pick<AgentClient['assistant'], 'planReview'>;
|
|
49
49
|
runs: Pick<AgentClient['runs'], 'get'>;
|
|
@@ -6,6 +6,8 @@ import { engineWireTarget } from '../engineWireTarget.js';
|
|
|
6
6
|
import { enqueuePlanReviewOutcome } from '../notifications.js';
|
|
7
7
|
import { corruptStoredRowContent, corruptStoredRowWhere } from '../wireErrorTriage.js';
|
|
8
8
|
import { decideFailureNote } from '../decideFailureNote.js';
|
|
9
|
+
import { checkpointUnsupportedVersionContent, checkpointUnsupportedVersionFromError } from '../resumeRefusalCopy.js';
|
|
10
|
+
import { REQUEST_FIELD_CONFLICT, isUnavailableFaceCode } from '../engineErrorCodes.js';
|
|
9
11
|
import { denyReasonForWire, isPlanReviewModeAfter } from './hitlBridge.js';
|
|
10
12
|
import { engineCapString } from '../engineCapsCache.js';
|
|
11
13
|
import { planReviewQuestionId, REOPEN_ID_TAIL, taskIdFromPlanReviewQuestionId } from './gateIdentity.js';
|
|
@@ -303,7 +305,10 @@ export function armPlanReviewApproval(result, sessionKey, opts) {
|
|
|
303
305
|
return false;
|
|
304
306
|
}
|
|
305
307
|
}
|
|
306
|
-
export function planReviewOutcomeTail(taskId, decision, effect) {
|
|
308
|
+
export function planReviewOutcomeTail(taskId, decision, effect, cause) {
|
|
309
|
+
if (effect === 'still_parked' && cause === 'stored_form_unsupported') {
|
|
310
|
+
return `Do not tell the user the plan was ${decision === 'approve' ? 'approved' : 'rejected'}, and do not ${decision} it again: deciding again will not take effect while it is stored in this form. Tell the user the way forward this outcome gives — cancel this task's run, then submit the work again as a new task.`;
|
|
311
|
+
}
|
|
307
312
|
switch (effect) {
|
|
308
313
|
case 'took_effect':
|
|
309
314
|
return decision === 'approve'
|
|
@@ -500,6 +505,7 @@ function planReviewClientsFor(resolved) {
|
|
|
500
505
|
async function decidePlanReviewInner(taskId, decision, permissionModeAfter, dispatchNo, releaseLatch, settle, opts, gateBefore) {
|
|
501
506
|
let outcome;
|
|
502
507
|
let effect = 'unconfirmed';
|
|
508
|
+
let stillParkedCause;
|
|
503
509
|
let stillParkedProven = false;
|
|
504
510
|
let statusSeen;
|
|
505
511
|
const wire = opts?.wire;
|
|
@@ -543,7 +549,7 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
543
549
|
}
|
|
544
550
|
catch (e1) {
|
|
545
551
|
const conflict = e1;
|
|
546
|
-
if (mode === 'default' && conflict?.status === 400 && conflict?.errorCode ===
|
|
552
|
+
if (mode === 'default' && conflict?.status === 400 && conflict?.errorCode === REQUEST_FIELD_CONFLICT) {
|
|
547
553
|
hostLog('debug', 'planReviewWire: permissionModeAfter "default" refused as not applicable — re-sending the approval without the key (same meaning)');
|
|
548
554
|
body = await sendOnce(undefined, () => client.assistant.planReview(taskId, reason !== undefined ? { decision, reason } : { decision }));
|
|
549
555
|
}
|
|
@@ -610,7 +616,7 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
610
616
|
const status = e?.status;
|
|
611
617
|
const refusedCode = e?.errorCode;
|
|
612
618
|
const noAnswer = typeof status === 'number' || typeof e !== 'object' ? null : noAnswerKindOf(e);
|
|
613
|
-
if (status === 400 && refusedCode ===
|
|
619
|
+
if (status === 400 && refusedCode === REQUEST_FIELD_CONFLICT && permissionModeAfter === 'acceptEdits') {
|
|
614
620
|
hostLog('debug', 'planReviewWire: approve + acceptEdits refused (request.field_conflict) — NOT re-sent with a different mode');
|
|
615
621
|
effect = 'still_parked';
|
|
616
622
|
stillParkedProven = true;
|
|
@@ -625,6 +631,14 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
625
631
|
`This ${decision} was not applied: the engine no longer has a pending plan review for this task — an earlier decision most likely already went through (or the review expired). ` +
|
|
626
632
|
'This is NOT a rejection of the plan and NOT a failure of the task; the answer just given simply had nothing left to act on.';
|
|
627
633
|
}
|
|
634
|
+
else if (checkpointUnsupportedVersionFromError(e) !== null) {
|
|
635
|
+
const cu = checkpointUnsupportedVersionFromError(e);
|
|
636
|
+
hostLog('debug', `planReviewWire: ${decision} → 409 checkpoint.unsupported_version (the server cannot continue this parked review in its stored form; still pending)`);
|
|
637
|
+
effect = 'still_parked';
|
|
638
|
+
stillParkedProven = true;
|
|
639
|
+
stillParkedCause = 'stored_form_unsupported';
|
|
640
|
+
outcome = `This ${decision} was not applied. ${checkpointUnsupportedVersionContent(cu, { parkKind: 'plan_review', runId: taskId })}`;
|
|
641
|
+
}
|
|
628
642
|
else if (typeof status === 'number' && corruptStoredRowWhere(e) !== undefined) {
|
|
629
643
|
const where = corruptStoredRowWhere(e);
|
|
630
644
|
hostLog('debug', `planReviewWire: ${decision} → ${String(status)} corrupt (stored resume context unreadable)`);
|
|
@@ -634,7 +648,7 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
634
648
|
else if (typeof status === 'number') {
|
|
635
649
|
const msg = e instanceof Error ? e.message : String(e);
|
|
636
650
|
hostLog('debug', `planReviewWire: ${decision} → ${status} ${msg.slice(0, 200)}`);
|
|
637
|
-
effect = status >= 400 && status < 500 ? 'not_applied' : 'unconfirmed';
|
|
651
|
+
effect = (status >= 400 && status < 500) || (typeof refusedCode === 'string' && isUnavailableFaceCode(refusedCode)) ? 'not_applied' : 'unconfirmed';
|
|
638
652
|
outcome = `${`The plan_review decision failed: HTTP ${status} ${msg}`.trim()}${decideFailureNote(e)}`;
|
|
639
653
|
}
|
|
640
654
|
else if (noAnswer === 'time-limit' || noAnswer === 'deadline') {
|
|
@@ -651,7 +665,7 @@ async function decidePlanReviewInner(taskId, decision, permissionModeAfter, disp
|
|
|
651
665
|
}
|
|
652
666
|
}
|
|
653
667
|
}
|
|
654
|
-
const tail = planReviewOutcomeTail(taskId, decision, effect);
|
|
668
|
+
const tail = planReviewOutcomeTail(taskId, decision, effect, stillParkedCause);
|
|
655
669
|
const prompt = `<plan-review-outcome>\n${outcome}\n${tail}\n</plan-review-outcome>`;
|
|
656
670
|
const onOutcome = opts?.onOutcome;
|
|
657
671
|
settle(effect === 'not_sent' || (effect === 'still_parked' && stillParkedProven));
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ApprovalsListEnvelope, type CrashConvergedRow } from './crashConverged.js';
|
|
2
|
+
export type ReplayedApprovalDisposition = {
|
|
3
|
+
readonly kind: 'live';
|
|
4
|
+
readonly basis: 'live_leg' | 'preamble' | 'pending_list';
|
|
5
|
+
} | {
|
|
6
|
+
readonly kind: 'decided';
|
|
7
|
+
readonly cause?: CrashConvergedRow['cause'];
|
|
8
|
+
} | {
|
|
9
|
+
readonly kind: 'unknown';
|
|
10
|
+
readonly why: 'unreadable_frame' | 'no_approval_id' | 'not_read' | 'list_unreadable' | 'list_not_reported' | 'list_incomplete';
|
|
11
|
+
};
|
|
12
|
+
export interface ReplayPendingBasis {
|
|
13
|
+
readonly preamble?: ReadonlySet<string>;
|
|
14
|
+
readonly approvals?: {
|
|
15
|
+
readonly read: 'ok';
|
|
16
|
+
readonly body: ApprovalsListEnvelope;
|
|
17
|
+
} | {
|
|
18
|
+
readonly read: 'failed';
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export declare function replayedApprovalDisposition(frame: object, basis: ReplayPendingBasis): ReplayedApprovalDisposition;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { hasLedgerPositionKey } from './gateLedger.js';
|
|
2
|
+
import { readLivePendingRows } from './livePendingAsk.js';
|
|
3
|
+
import { projectCrashConverged } from './crashConverged.js';
|
|
4
|
+
const live = (basis) => Object.freeze({ kind: 'live', basis });
|
|
5
|
+
const unknown = (why) => Object.freeze({ kind: 'unknown', why });
|
|
6
|
+
const decided = (cause) => Object.freeze(cause !== undefined ? { kind: 'decided', cause } : { kind: 'decided' });
|
|
7
|
+
function approvalIdOf(frame) {
|
|
8
|
+
const v = frame.approvalId;
|
|
9
|
+
return typeof v === 'string' && v !== '' ? v : undefined;
|
|
10
|
+
}
|
|
11
|
+
function crashConvergedCauseOf(body, approvalId) {
|
|
12
|
+
const projection = projectCrashConverged(body);
|
|
13
|
+
if (projection === undefined)
|
|
14
|
+
return undefined;
|
|
15
|
+
for (const row of [...projection.resumeSafe, ...projection.needsHuman]) {
|
|
16
|
+
if (row.approvalId === approvalId)
|
|
17
|
+
return row.cause;
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
export function replayedApprovalDisposition(frame, basis) {
|
|
22
|
+
let approvalId;
|
|
23
|
+
try {
|
|
24
|
+
if (typeof frame !== 'object' || frame === null)
|
|
25
|
+
return unknown('unreadable_frame');
|
|
26
|
+
if (!hasLedgerPositionKey(frame))
|
|
27
|
+
return live('live_leg');
|
|
28
|
+
approvalId = approvalIdOf(frame);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return unknown('unreadable_frame');
|
|
32
|
+
}
|
|
33
|
+
if (approvalId === undefined)
|
|
34
|
+
return unknown('no_approval_id');
|
|
35
|
+
let b;
|
|
36
|
+
let inPreamble = false;
|
|
37
|
+
try {
|
|
38
|
+
b = typeof basis === 'object' && basis !== null ? basis : undefined;
|
|
39
|
+
inPreamble = b?.preamble?.has(approvalId) === true;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
inPreamble = false;
|
|
43
|
+
}
|
|
44
|
+
if (inPreamble)
|
|
45
|
+
return live('preamble');
|
|
46
|
+
let approvals;
|
|
47
|
+
try {
|
|
48
|
+
approvals = b?.approvals;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return unknown('not_read');
|
|
52
|
+
}
|
|
53
|
+
if (approvals === undefined || approvals === null)
|
|
54
|
+
return unknown('not_read');
|
|
55
|
+
let body;
|
|
56
|
+
try {
|
|
57
|
+
if (approvals.read !== 'ok')
|
|
58
|
+
return unknown('list_unreadable');
|
|
59
|
+
body = approvals.body;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return unknown('list_unreadable');
|
|
63
|
+
}
|
|
64
|
+
let reading;
|
|
65
|
+
try {
|
|
66
|
+
reading = readLivePendingRows(body);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return unknown('list_unreadable');
|
|
70
|
+
}
|
|
71
|
+
if (reading.kind === 'present' && reading.rows.some((r) => r.approvalId === approvalId))
|
|
72
|
+
return live('pending_list');
|
|
73
|
+
let cause;
|
|
74
|
+
try {
|
|
75
|
+
cause = crashConvergedCauseOf(body, approvalId);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
cause = undefined;
|
|
79
|
+
}
|
|
80
|
+
if (cause !== undefined)
|
|
81
|
+
return decided(cause);
|
|
82
|
+
if (reading.kind === 'not_reported')
|
|
83
|
+
return unknown('list_not_reported');
|
|
84
|
+
if (reading.kind === 'malformed')
|
|
85
|
+
return unknown('list_unreadable');
|
|
86
|
+
if (reading.dropped > 0)
|
|
87
|
+
return unknown('list_incomplete');
|
|
88
|
+
return decided();
|
|
89
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { CONFIG_LEGACY_TOOL_NAME, RULES_LEGACY_TOOL_NAME } from '../engineErrorCodes.js';
|
|
2
2
|
import { rosterMountedNames } from './rosterMountedNames.js';
|
|
3
|
+
import { ruleCompileVerdict } from '../ruleCompileVerdict.js';
|
|
4
|
+
import { protocolSegmentOf } from '../protocolSegment.js';
|
|
3
5
|
import { PREFIXES_IN_PROSE, REFUSED_NAME_CLASSES, REFUSED_NAME_FIX, RULES_LEGACY_TOOL_NAME_WRITE_NOTE } from './refusedToolNameProse.js';
|
|
4
6
|
import { CORE_PROTOCOL_PREFIXES, CORE_RETIRED_TOOL_CURRENT_NAMES } from '../generated/toolNameTables.js';
|
|
5
7
|
const COVERING_NAMESPACE_PREFIXES = CORE_PROTOCOL_PREFIXES;
|
|
@@ -9,7 +11,7 @@ const WITHHELD_NOTICE = {
|
|
|
9
11
|
qualified_deny: 'Not written to this session’s rule record: that record holds whole tool names and command names only, and this rule has a part in parentheses it cannot carry — recording just the name could block more than this rule does.',
|
|
10
12
|
peer_wide: 'Not written to this session’s rule record: this rule names a server or agent peer without naming one of its tools, and an engine that matches that record by exact tool name would block none of that peer’s tools.',
|
|
11
13
|
wildcard: 'Not written to this session’s rule record: this rule uses a wildcard (*), and an engine that matches that record by exact tool name would block none of the tools it covers.',
|
|
12
|
-
not_a_tool_name: 'Not written to this session’s rule record: this entry is not exactly one tool name — it is empty, not text,
|
|
14
|
+
not_a_tool_name: 'Not written to this session’s rule record: this entry is not exactly one tool name — it is empty, not text, has spaces around the name, is not a single tool name in rule syntax (for example it contains a backslash or a parenthesis, or is longer than a rule can be), or spells its server or agent peer part in a way the engine never uses when naming tools (for example with a dot or a space), so no tool would ever match it.',
|
|
13
15
|
legacy_tool_name: `Not written to this session’s rule record: unless a tool by exactly this name is available, the engine refuses it — it is a retired tool name, or it contains "__" but does not start with ${PREFIXES_IN_PROSE} — and with it in that record every later run of this session would fail at startup until that entry is removed from the record. ${REFUSED_NAME_FIX}`,
|
|
14
16
|
not_in_roster: 'Not written to this session’s rule record: the tool roster supplied for this check has no tool by exactly this name, as a name or an alias, with letter case counted — it may be misspelled, or name a tool this deployment does not provide — so recorded as it is it would block nothing here.',
|
|
15
17
|
};
|
|
@@ -97,7 +99,23 @@ export function sessionPolicyDeliverable(behavior, rules, opts) {
|
|
|
97
99
|
withheld.push({ rule: '', why: 'not_a_tool_name' });
|
|
98
100
|
continue;
|
|
99
101
|
}
|
|
100
|
-
|
|
102
|
+
let why = withheldWhyOf(r);
|
|
103
|
+
if (why === undefined) {
|
|
104
|
+
const grammar = ruleCompileVerdict(r, 'deny');
|
|
105
|
+
if (grammar === 'unknown')
|
|
106
|
+
return unreadable();
|
|
107
|
+
if (grammar === 'refused')
|
|
108
|
+
why = 'not_a_tool_name';
|
|
109
|
+
}
|
|
110
|
+
if (why === undefined && COVERING_NAMESPACE_PREFIXES.some((p) => r.startsWith(p))) {
|
|
111
|
+
const segment = protocolSegmentOf(r);
|
|
112
|
+
if (segment.kind === 'unknown')
|
|
113
|
+
return unreadable();
|
|
114
|
+
if (segment.kind === 'unminted')
|
|
115
|
+
why = 'not_a_tool_name';
|
|
116
|
+
}
|
|
117
|
+
if (why === undefined && mounted !== undefined && !mounted.has(r))
|
|
118
|
+
why = 'not_in_roster';
|
|
101
119
|
if (why === undefined)
|
|
102
120
|
exact.push(r);
|
|
103
121
|
else {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { SessionPermissionRules, StoredSessionRules } from '@sema-agent/sdk';
|
|
2
2
|
import type { SessionPolicyReading } from '../sessionPolicyCapability.js';
|
|
3
3
|
import type { ToolRosterView } from '../toolRoster.js';
|
|
4
|
+
import { CORE_RULE_TOOL_NAME_VERDICTS } from '../generated/engineFactTables.js';
|
|
5
|
+
export type LegacyToolNameVerdict = Exclude<(typeof CORE_RULE_TOOL_NAME_VERDICTS)[number], 'current'>;
|
|
4
6
|
export interface SessionPolicyFacade {
|
|
5
7
|
getPolicy(sessionId: string, opts?: {
|
|
6
8
|
signal?: AbortSignal;
|
|
@@ -43,6 +45,8 @@ export type SessionPolicyFailure = {
|
|
|
43
45
|
message: string;
|
|
44
46
|
field?: SessionPolicyRemovedEntry['field'];
|
|
45
47
|
name?: string;
|
|
48
|
+
verdict?: LegacyToolNameVerdict;
|
|
49
|
+
currentName?: string;
|
|
46
50
|
} | {
|
|
47
51
|
kind: 'unauthorized';
|
|
48
52
|
message: string;
|
|
@@ -95,6 +99,8 @@ export type SessionPolicyTightenOutcome = {
|
|
|
95
99
|
message: string;
|
|
96
100
|
field?: SessionPolicyRemovedEntry['field'];
|
|
97
101
|
name?: string;
|
|
102
|
+
verdict?: LegacyToolNameVerdict;
|
|
103
|
+
currentName?: string;
|
|
98
104
|
alreadyInRecord?: boolean;
|
|
99
105
|
} | {
|
|
100
106
|
kind: 'unknown';
|
|
@@ -105,6 +111,11 @@ export declare function tightenSessionPolicy(facade: SessionPolicyFacade, req: S
|
|
|
105
111
|
signal?: AbortSignal;
|
|
106
112
|
}): Promise<SessionPolicyTightenOutcome>;
|
|
107
113
|
export declare function sessionPolicyTightenNotice(outcome: SessionPolicyTightenOutcome): string;
|
|
114
|
+
export declare function sessionPolicyReadNotice(outcome: Exclude<SessionPolicyReadOutcome, {
|
|
115
|
+
kind: 'present';
|
|
116
|
+
}> | {
|
|
117
|
+
kind: 'no_target';
|
|
118
|
+
}): string;
|
|
108
119
|
export interface SessionPolicyRemovedEntry {
|
|
109
120
|
readonly field: 'toolDeny' | 'toolAllow';
|
|
110
121
|
readonly rule: string;
|
|
@@ -143,6 +154,8 @@ export type SessionPolicyRemovalOutcome = {
|
|
|
143
154
|
message: string;
|
|
144
155
|
field?: SessionPolicyRemovedEntry['field'];
|
|
145
156
|
name?: string;
|
|
157
|
+
verdict?: LegacyToolNameVerdict;
|
|
158
|
+
currentName?: string;
|
|
146
159
|
};
|
|
147
160
|
export type SessionPolicyRosterRequiredWhy = 'absent' | 'unreadable';
|
|
148
161
|
export declare function removeRefusedSessionRules(facade: SessionPolicyFacade, sessionId: string, roster: ToolRosterView | undefined, opts?: {
|