@sema-agent/client-core 0.85.2 → 0.87.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 +263 -0
- package/README.md +25 -16
- package/dist/adapt/arms.js +14 -1
- package/dist/adapter/downstream/terminalToSdkResult.d.ts +3 -3
- package/dist/adapter/downstream/terminalToSdkResult.js +4 -1
- package/dist/adapter/runStream.js +75 -2
- package/dist/adapter/types.d.ts +3 -0
- package/dist/agentSession/backgroundView.js +7 -1
- package/dist/agentSession/contract.d.ts +1 -1
- package/dist/centerWiringCapability.d.ts +38 -0
- package/dist/centerWiringCapability.js +202 -0
- package/dist/decideFailureNote.js +3 -0
- package/dist/decideReceipt.d.ts +1 -1
- package/dist/decideReceipt.js +1 -1
- package/dist/detachWire.d.ts +6 -1
- package/dist/detachWire.js +22 -1
- package/dist/engineCapReader.d.ts +8 -0
- package/dist/engineCapReader.js +15 -0
- package/dist/engineErrorCodes.d.ts +3 -0
- package/dist/engineErrorCodes.js +8 -0
- package/dist/engineNoticeCodes.d.ts +62 -0
- package/dist/engineNoticeCodes.js +247 -29
- package/dist/executionLaneCapability.d.ts +6 -0
- package/dist/executionLaneCapability.js +55 -2
- package/dist/fleet/fleetProjection.js +6 -8
- package/dist/fleet/workflowSizeWarning.js +2 -5
- package/dist/gateOutcome.d.ts +13 -0
- package/dist/gateOutcome.js +48 -1
- package/dist/gateVocabulary.d.ts +6 -3
- package/dist/gateVocabulary.js +7 -15
- package/dist/generated/engineFactTables.d.ts +10 -0
- package/dist/generated/engineFactTables.js +113 -0
- package/dist/generated/engineNoticeTables.js +14 -0
- package/dist/hitl/crashConverged.d.ts +1 -0
- package/dist/hitl/crashConverged.js +17 -2
- package/dist/hitl/hitlBridge.d.ts +4 -0
- package/dist/hitl/hitlBridge.js +6 -0
- package/dist/hitl/parkResolver.js +17 -12
- package/dist/hitl/refusedToolNameProse.d.ts +4 -0
- package/dist/hitl/refusedToolNameProse.js +7 -0
- package/dist/hitl/sessionPolicyDeliverable.js +9 -5
- package/dist/hitl/sessionPolicyWire.d.ts +16 -0
- package/dist/hitl/sessionPolicyWire.js +49 -1
- package/dist/hitl/suspendedReopen.d.ts +7 -0
- package/dist/hitl/suspendedReopen.js +38 -7
- package/dist/hitl/toolApprovalWire.js +4 -5
- package/dist/index.d.ts +6 -1
- package/dist/index.js +6 -1
- package/dist/inheritEnvWire.d.ts +22 -0
- package/dist/inheritEnvWire.js +84 -0
- package/dist/peerFrames.js +2 -10
- package/dist/printInitToolFace.d.ts +6 -0
- package/dist/printInitToolFace.js +115 -2
- package/dist/readCredentialRefusal.d.ts +2 -0
- package/dist/readCredentialRefusal.js +7 -0
- package/dist/request/taskRequest.d.ts +4 -5
- package/dist/request/taskRequest.js +33 -23
- package/dist/resumeRefusalCopy.d.ts +2 -7
- package/dist/resumeRefusalCopy.js +2 -24
- package/dist/rewindArchiveCapability.js +5 -0
- package/dist/runTerminal.js +2 -6
- package/dist/seam.d.ts +11 -1
- package/dist/seam.js +7 -0
- package/dist/sqlEngineCapability.js +15 -2
- package/dist/storePostureCapability.d.ts +25 -0
- package/dist/storePostureCapability.js +132 -0
- package/dist/toolHistoryMismatch.d.ts +63 -0
- package/dist/toolHistoryMismatch.js +425 -0
- package/dist/toolResult.js +2 -47
- package/dist/wireErrorTriage.d.ts +1 -0
- package/dist/wireErrorTriage.js +3 -0
- package/dist/wireFailureShape.d.ts +3 -0
- package/dist/wireFailureShape.js +44 -0
- package/dist/wireRefusalCopy.d.ts +39 -1
- package/dist/wireRefusalCopy.js +350 -2
- package/dist/workflowClient.d.ts +7 -2
- package/dist/workflowClient.js +15 -7
- package/docs/INTEGRATION-CLIENTS.md +942 -16
- package/package.json +2 -2
package/dist/gateOutcome.js
CHANGED
|
@@ -50,6 +50,39 @@ function readSettlement(v) {
|
|
|
50
50
|
...(note !== undefined ? { note } : {}),
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
|
+
function nonEmptyStringList(v) {
|
|
54
|
+
if (!Array.isArray(v) || v.length === 0)
|
|
55
|
+
return undefined;
|
|
56
|
+
const out = [];
|
|
57
|
+
for (const x of v) {
|
|
58
|
+
const t = str(x);
|
|
59
|
+
if (t === undefined)
|
|
60
|
+
return undefined;
|
|
61
|
+
out.push(t);
|
|
62
|
+
}
|
|
63
|
+
return Object.freeze(out);
|
|
64
|
+
}
|
|
65
|
+
function readModeRelease(raw) {
|
|
66
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
|
|
67
|
+
return undefined;
|
|
68
|
+
const r = raw;
|
|
69
|
+
const mode = str(r.mode);
|
|
70
|
+
const askClass = str(r.askClass);
|
|
71
|
+
const classes = nonEmptyStringList(r.classes);
|
|
72
|
+
if (mode === undefined || askClass === undefined || classes === undefined)
|
|
73
|
+
return undefined;
|
|
74
|
+
const origin = str(r.origin);
|
|
75
|
+
const mandate = str(r.mandate);
|
|
76
|
+
const reason = str(r.reason);
|
|
77
|
+
return {
|
|
78
|
+
mode,
|
|
79
|
+
askClass,
|
|
80
|
+
classes,
|
|
81
|
+
...(origin !== undefined ? { origin } : {}),
|
|
82
|
+
...(mandate !== undefined ? { mandate } : {}),
|
|
83
|
+
...(reason !== undefined ? { reason } : {}),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
53
86
|
function readClassifierRound(raw) {
|
|
54
87
|
if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
|
|
55
88
|
return undefined;
|
|
@@ -85,18 +118,29 @@ export function gateOutcomeOf(frame) {
|
|
|
85
118
|
let disposition;
|
|
86
119
|
const classifier = readClassifierRound(dr.classifier);
|
|
87
120
|
if (dk === 'allowed') {
|
|
88
|
-
|
|
121
|
+
const released = readModeRelease(dr.released);
|
|
122
|
+
disposition = {
|
|
123
|
+
kind: 'allowed',
|
|
124
|
+
...(classifier !== undefined ? { classifier } : {}),
|
|
125
|
+
...(released !== undefined ? { released } : {}),
|
|
126
|
+
};
|
|
89
127
|
}
|
|
90
128
|
else if (dk === 'denied') {
|
|
91
129
|
const deniedBy = str(dr.deniedBy);
|
|
92
130
|
if (deniedBy === undefined)
|
|
93
131
|
return undefined;
|
|
94
132
|
const cause = str(dr.cause);
|
|
133
|
+
const mode = str(dr.mode);
|
|
134
|
+
const askClass = str(dr.askClass);
|
|
135
|
+
const classes = nonEmptyStringList(dr.classes);
|
|
95
136
|
disposition = {
|
|
96
137
|
kind: 'denied',
|
|
97
138
|
deniedBy,
|
|
98
139
|
...(cause !== undefined ? { cause } : {}),
|
|
99
140
|
...(classifier !== undefined ? { classifier } : {}),
|
|
141
|
+
...(mode !== undefined ? { mode } : {}),
|
|
142
|
+
...(askClass !== undefined ? { askClass } : {}),
|
|
143
|
+
...(classes !== undefined ? { classes } : {}),
|
|
100
144
|
};
|
|
101
145
|
}
|
|
102
146
|
else {
|
|
@@ -115,6 +159,9 @@ export function isGateParkedToolEnd(frame) {
|
|
|
115
159
|
return false;
|
|
116
160
|
return frame.errorCode === GATE_PARKED_ERROR_CODE;
|
|
117
161
|
}
|
|
162
|
+
export function gateModeRelease(g) {
|
|
163
|
+
return g?.disposition.kind === 'allowed' ? g.disposition.released : undefined;
|
|
164
|
+
}
|
|
118
165
|
export function isDeniedGate(g) {
|
|
119
166
|
return g?.disposition.kind === 'denied';
|
|
120
167
|
}
|
package/dist/gateVocabulary.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { CORE_DENIED_BY_VALUES } from './generated/engineFactTables.js';
|
|
2
2
|
import type { APPROVAL_MANDATE_WORDS } from './hitl/toolApprovalWire.js';
|
|
3
3
|
export declare const CC_TOOL_DENIAL_KINDS: readonly ["user-rejected", "permission-rule", "automode-blocked", "automode-unavailable", "automode-parsing-error", "interrupted", "cancelled"];
|
|
4
4
|
export type CcToolDenialKind = (typeof CC_TOOL_DENIAL_KINDS)[number];
|
|
5
5
|
export declare function isCcToolDenialKind(v: unknown): v is CcToolDenialKind;
|
|
6
6
|
export declare function isCcToolDenialKindADenial(v: unknown): boolean;
|
|
7
7
|
export declare function ccToolDenialKindForSettledBy(settledBy: unknown): CcToolDenialKind | undefined;
|
|
8
|
-
|
|
9
|
-
export
|
|
8
|
+
declare const RETIRED_DENIED_BY_WORDS: readonly ["plan_mode"];
|
|
9
|
+
export type GateDeniedByWord = (typeof CORE_DENIED_BY_VALUES)[number] | (typeof RETIRED_DENIED_BY_WORDS)[number];
|
|
10
|
+
export declare const GATE_DENIED_BY_WORDS: readonly GateDeniedByWord[];
|
|
11
|
+
export declare function isGateDeniedByWord(v: unknown): v is GateDeniedByWord;
|
|
10
12
|
export declare function gateDeniedByDetail(deniedBy: unknown): string;
|
|
11
13
|
export declare function askOriginDetail(origin: unknown, message?: unknown): string;
|
|
12
14
|
export declare const RULE_STORE_UNREADABLE_KINDS: readonly ["store", "call"];
|
|
@@ -17,3 +19,4 @@ export declare function mandatedApprovalDetail(): string;
|
|
|
17
19
|
export type ApprovalMandateWord = (typeof APPROVAL_MANDATE_WORDS)[number];
|
|
18
20
|
export declare function isApprovalMandateWord(v: unknown): v is ApprovalMandateWord;
|
|
19
21
|
export declare function approvalMandateDetail(word: ApprovalMandateWord): string;
|
|
22
|
+
export {};
|
package/dist/gateVocabulary.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CORE_DENIED_BY_VALUES } from './generated/engineFactTables.js';
|
|
1
2
|
export const CC_TOOL_DENIAL_KINDS = Object.freeze([
|
|
2
3
|
'user-rejected',
|
|
3
4
|
'permission-rule',
|
|
@@ -20,18 +21,10 @@ export function ccToolDenialKindForSettledBy(settledBy) {
|
|
|
20
21
|
return 'permission-rule';
|
|
21
22
|
return undefined;
|
|
22
23
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
'persisted_rule',
|
|
28
|
-
'classifier',
|
|
29
|
-
'plan_mode',
|
|
30
|
-
'compliance',
|
|
31
|
-
'write_protection',
|
|
32
|
-
'ask_resolution',
|
|
33
|
-
'read_boundary',
|
|
34
|
-
]);
|
|
24
|
+
const RETIRED_DENIED_BY_WORDS = Object.freeze(['plan_mode']);
|
|
25
|
+
export const GATE_DENIED_BY_WORDS = Object.freeze([...CORE_DENIED_BY_VALUES, ...RETIRED_DENIED_BY_WORDS]);
|
|
26
|
+
const _deniedByWordsPin = true;
|
|
27
|
+
void _deniedByWordsPin;
|
|
35
28
|
const DENIED_BY_SENTENCES = Object.freeze({
|
|
36
29
|
policy: "denied by this deployment's permission policy",
|
|
37
30
|
hook: 'denied by a PreToolUse hook',
|
|
@@ -43,6 +36,7 @@ const DENIED_BY_SENTENCES = Object.freeze({
|
|
|
43
36
|
write_protection: 'denied because the write landed on a write-protected path',
|
|
44
37
|
ask_resolution: 'denied when the approval was resolved',
|
|
45
38
|
read_boundary: 'denied by the read boundary',
|
|
39
|
+
mode: "denied by this session's permission mode",
|
|
46
40
|
});
|
|
47
41
|
export function isGateDeniedByWord(v) {
|
|
48
42
|
return typeof v === 'string' && GATE_DENIED_BY_WORDS.includes(v);
|
|
@@ -63,7 +57,7 @@ const ASK_ORIGIN_SENTENCES = Object.freeze({
|
|
|
63
57
|
hook: 'a PreToolUse hook asked about this call',
|
|
64
58
|
ask_rule: 'a persisted ask rule matches this call',
|
|
65
59
|
denial_limit_fallback: 'the auto-mode classifier hit its denial limit and handed this call back to you',
|
|
66
|
-
shell_gate_tighten: '
|
|
60
|
+
shell_gate_tighten: 'the coarse shell check rates this command above the tier it lets through, so it asks',
|
|
67
61
|
safety_tighten: 'the gate tightened on this call’s own facts (network egress, irreversibility, or a protected write)',
|
|
68
62
|
policy: 'this deployment’s permission policy asks about this call',
|
|
69
63
|
});
|
|
@@ -82,8 +76,6 @@ export function askOriginDetail(origin, message) {
|
|
|
82
76
|
const word = typeof origin === 'string' && origin.length > 0 ? origin : '(none)';
|
|
83
77
|
return `this call asks; its origin ${word} is a word newer than this client, so answer it as usual`;
|
|
84
78
|
}
|
|
85
|
-
const _deniedByWordsPin = GATE_DENIED_BY_WORDS;
|
|
86
|
-
void _deniedByWordsPin;
|
|
87
79
|
export const RULE_STORE_UNREADABLE_KINDS = Object.freeze(['store', 'call']);
|
|
88
80
|
const RULE_STORE_UNREADABLE_SET = new Set(RULE_STORE_UNREADABLE_KINDS);
|
|
89
81
|
export function isRuleStoreUnreadableKind(v) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const CORE_ASK_UNRESOLVABLE_CAUSES: readonly string[];
|
|
2
|
+
export declare const CORE_DURABLE_MANDATE_SOURCES: readonly string[];
|
|
3
|
+
export declare const CORE_TERMINAL_CAUSE_KINDS: readonly ["completed", "failed", "blocked", "paused"];
|
|
4
|
+
export declare const CORE_RESERVED_AGENT_NAMES: readonly string[];
|
|
5
|
+
export declare const CORE_WORKFLOW_SIZE_GUIDELINE_AGENT_CAPS: Readonly<Record<"small" | "medium" | "large", number>>;
|
|
6
|
+
export declare const CORE_ENGINE_AUTHORITY_ENVELOPE_TAGS: readonly string[];
|
|
7
|
+
export declare const CORE_DENIED_BY_VALUES: readonly ["policy", "hook", "org", "persisted_rule", "classifier", "compliance", "write_protection", "ask_resolution", "read_boundary", "mode"];
|
|
8
|
+
export declare const CORE_PERMISSION_MODE_FOLD_REASONS: readonly string[];
|
|
9
|
+
export declare const CORE_ENGINE_CARD_TYPES: readonly string[];
|
|
10
|
+
export declare const CORE_TOOL_DECLARATION_CODES: readonly string[];
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
export const CORE_ASK_UNRESOLVABLE_CAUSES = Object.freeze([
|
|
2
|
+
"approver_unavailable",
|
|
3
|
+
"no_approver_wired",
|
|
4
|
+
"mandate_unreconstructible",
|
|
5
|
+
]);
|
|
6
|
+
export const CORE_DURABLE_MANDATE_SOURCES = Object.freeze([
|
|
7
|
+
"force_durable_gate",
|
|
8
|
+
"durable_approval",
|
|
9
|
+
]);
|
|
10
|
+
export const CORE_TERMINAL_CAUSE_KINDS = Object.freeze([
|
|
11
|
+
"completed",
|
|
12
|
+
"failed",
|
|
13
|
+
"blocked",
|
|
14
|
+
"paused",
|
|
15
|
+
]);
|
|
16
|
+
export const CORE_RESERVED_AGENT_NAMES = Object.freeze([
|
|
17
|
+
"StructuredOutput",
|
|
18
|
+
"ToolSearch",
|
|
19
|
+
"ReadToolResult",
|
|
20
|
+
"ReportBlocked",
|
|
21
|
+
"Agent",
|
|
22
|
+
]);
|
|
23
|
+
export const CORE_WORKFLOW_SIZE_GUIDELINE_AGENT_CAPS = Object.freeze({
|
|
24
|
+
"small": 5,
|
|
25
|
+
"medium": 15,
|
|
26
|
+
"large": 50,
|
|
27
|
+
});
|
|
28
|
+
export const CORE_ENGINE_AUTHORITY_ENVELOPE_TAGS = Object.freeze([
|
|
29
|
+
"system-reminder",
|
|
30
|
+
"task-notification",
|
|
31
|
+
"new-diagnostics",
|
|
32
|
+
"user_memory",
|
|
33
|
+
"scope",
|
|
34
|
+
"skills",
|
|
35
|
+
"total_tokens",
|
|
36
|
+
"instruction-files",
|
|
37
|
+
]);
|
|
38
|
+
export const CORE_DENIED_BY_VALUES = Object.freeze([
|
|
39
|
+
"policy",
|
|
40
|
+
"hook",
|
|
41
|
+
"org",
|
|
42
|
+
"persisted_rule",
|
|
43
|
+
"classifier",
|
|
44
|
+
"compliance",
|
|
45
|
+
"write_protection",
|
|
46
|
+
"ask_resolution",
|
|
47
|
+
"read_boundary",
|
|
48
|
+
"mode",
|
|
49
|
+
]);
|
|
50
|
+
export const CORE_PERMISSION_MODE_FOLD_REASONS = Object.freeze([
|
|
51
|
+
"operator_deny",
|
|
52
|
+
"face_absent",
|
|
53
|
+
"inherited_ceiling",
|
|
54
|
+
]);
|
|
55
|
+
export const CORE_ENGINE_CARD_TYPES = Object.freeze([
|
|
56
|
+
"mcp",
|
|
57
|
+
"a2a",
|
|
58
|
+
"path_not_in_root",
|
|
59
|
+
"read_path_denied",
|
|
60
|
+
"readonly_out_of_root",
|
|
61
|
+
"read_unestablished",
|
|
62
|
+
"text",
|
|
63
|
+
"notebook",
|
|
64
|
+
"file_unchanged",
|
|
65
|
+
"image",
|
|
66
|
+
"document",
|
|
67
|
+
"edit",
|
|
68
|
+
"create",
|
|
69
|
+
"update",
|
|
70
|
+
"notebook-edit",
|
|
71
|
+
"bash",
|
|
72
|
+
"bash_invalid_timeout",
|
|
73
|
+
"grep",
|
|
74
|
+
"glob",
|
|
75
|
+
"repo-map",
|
|
76
|
+
"artifact",
|
|
77
|
+
"worktree",
|
|
78
|
+
"task-output",
|
|
79
|
+
"task-stop",
|
|
80
|
+
"monitor-start",
|
|
81
|
+
"send-message",
|
|
82
|
+
"agent-transcript",
|
|
83
|
+
"list-agents",
|
|
84
|
+
"read-inbox",
|
|
85
|
+
"workflow-run",
|
|
86
|
+
"report-findings",
|
|
87
|
+
"tool-search",
|
|
88
|
+
"ask-question",
|
|
89
|
+
"exit-plan-mode",
|
|
90
|
+
"enter-plan-mode",
|
|
91
|
+
"cron-create",
|
|
92
|
+
"cron-delete",
|
|
93
|
+
"cron-list",
|
|
94
|
+
"schedule-wakeup",
|
|
95
|
+
"web-fetch",
|
|
96
|
+
"web-search",
|
|
97
|
+
"todo",
|
|
98
|
+
"task",
|
|
99
|
+
"task-list",
|
|
100
|
+
"agent",
|
|
101
|
+
"fork",
|
|
102
|
+
]);
|
|
103
|
+
export const CORE_TOOL_DECLARATION_CODES = Object.freeze([
|
|
104
|
+
"config.tool_mount_denied",
|
|
105
|
+
"config.tool_roster_bound",
|
|
106
|
+
"config.tool_name_too_long",
|
|
107
|
+
"config.tool_contract_too_long",
|
|
108
|
+
"config.tool_schema_bound",
|
|
109
|
+
"config.tool_face_invalid",
|
|
110
|
+
"config.tool_name_invalid",
|
|
111
|
+
"config.egress_requires_write_effect",
|
|
112
|
+
"config.reserved_tool_name",
|
|
113
|
+
]);
|
|
@@ -24,6 +24,11 @@ export const CORE_ENGINE_NOTICE_CODES = Object.freeze([
|
|
|
24
24
|
"config.peer_admission_out_of_range",
|
|
25
25
|
"config.peer_lane_unmounted",
|
|
26
26
|
"config.secret_env_scrubbed",
|
|
27
|
+
"config.permission_mode_unavailable",
|
|
28
|
+
"permission.ask_released_by_mode",
|
|
29
|
+
"permission.read_deny_unapplied",
|
|
30
|
+
"permission.rule_store_unavailable",
|
|
31
|
+
"resume.legacy_mode_seed_migrated",
|
|
27
32
|
"peer.inbound_disposition",
|
|
28
33
|
"peer.held_settled",
|
|
29
34
|
"peer.idle_subscription",
|
|
@@ -31,6 +36,7 @@ export const CORE_ENGINE_NOTICE_CODES = Object.freeze([
|
|
|
31
36
|
"classifier.fallback",
|
|
32
37
|
"classifier.role_defaulted",
|
|
33
38
|
"classifier.contract_broken",
|
|
39
|
+
"classifier.unavailable",
|
|
34
40
|
"checkpoint.execution_outcome_unrecorded",
|
|
35
41
|
"delegation.transcript_integrity",
|
|
36
42
|
"delegation.ask_unresolvable",
|
|
@@ -58,6 +64,7 @@ export const CORE_ENGINE_NOTICE_CODES = Object.freeze([
|
|
|
58
64
|
"route.classifier_unroutable",
|
|
59
65
|
"route.base_url_changed_key_unchanged",
|
|
60
66
|
"route.response_model_mismatch",
|
|
67
|
+
"route.tool_call_id_folded",
|
|
61
68
|
"task.user_steer_undrained",
|
|
62
69
|
"task.user_followup_undrained",
|
|
63
70
|
"steering.parked_input_blocked",
|
|
@@ -99,6 +106,11 @@ export const CORE_NOTICE_AUDIENCE = Object.freeze({
|
|
|
99
106
|
"config.peer_admission_out_of_range": "operator",
|
|
100
107
|
"config.peer_lane_unmounted": "operator",
|
|
101
108
|
"config.secret_env_scrubbed": "operator",
|
|
109
|
+
"config.permission_mode_unavailable": "user",
|
|
110
|
+
"permission.ask_released_by_mode": "user",
|
|
111
|
+
"permission.read_deny_unapplied": "user",
|
|
112
|
+
"permission.rule_store_unavailable": "user",
|
|
113
|
+
"resume.legacy_mode_seed_migrated": "operator",
|
|
102
114
|
"peer.inbound_disposition": "user",
|
|
103
115
|
"peer.held_settled": "user",
|
|
104
116
|
"peer.idle_subscription": "user",
|
|
@@ -106,6 +118,7 @@ export const CORE_NOTICE_AUDIENCE = Object.freeze({
|
|
|
106
118
|
"classifier.fallback": "operator",
|
|
107
119
|
"classifier.role_defaulted": "operator",
|
|
108
120
|
"classifier.contract_broken": "operator",
|
|
121
|
+
"classifier.unavailable": "user",
|
|
109
122
|
"checkpoint.execution_outcome_unrecorded": "operator",
|
|
110
123
|
"delegation.transcript_integrity": "operator",
|
|
111
124
|
"delegation.ask_unresolvable": "user",
|
|
@@ -133,6 +146,7 @@ export const CORE_NOTICE_AUDIENCE = Object.freeze({
|
|
|
133
146
|
"route.classifier_unroutable": "operator",
|
|
134
147
|
"route.base_url_changed_key_unchanged": "operator",
|
|
135
148
|
"route.response_model_mismatch": "operator",
|
|
149
|
+
"route.tool_call_id_folded": "operator",
|
|
136
150
|
"task.user_steer_undrained": "user",
|
|
137
151
|
"task.user_followup_undrained": "user",
|
|
138
152
|
"steering.parked_input_blocked": "user",
|
|
@@ -14,6 +14,7 @@ export interface CrashConvergedRow {
|
|
|
14
14
|
readonly decidedAtMs?: number;
|
|
15
15
|
readonly convergedAtMs: number;
|
|
16
16
|
readonly resumeSafe: boolean;
|
|
17
|
+
readonly needsHumanReason?: 'approved_then_interrupted' | 'pending_unsafe' | 'unstable_row' | 'contradictory';
|
|
17
18
|
}
|
|
18
19
|
export interface ApprovalsListEnvelope {
|
|
19
20
|
pending: PendingCheckpoint[];
|
|
@@ -71,6 +71,15 @@ function readCrashConvergedRow(v) {
|
|
|
71
71
|
return null;
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
+
function needsHumanReasonOf(reading) {
|
|
75
|
+
if (reading.unstable)
|
|
76
|
+
return 'unstable_row';
|
|
77
|
+
if (reading.orphanState === 'pending' ? reading.hasApprovalEvidence : reading.resumeSafe === true)
|
|
78
|
+
return 'contradictory';
|
|
79
|
+
if (reading.orphanState === 'decided')
|
|
80
|
+
return 'approved_then_interrupted';
|
|
81
|
+
return 'pending_unsafe';
|
|
82
|
+
}
|
|
74
83
|
export function projectCrashConverged(env) {
|
|
75
84
|
const resumeSafe = [];
|
|
76
85
|
const needsHuman = [];
|
|
@@ -96,10 +105,16 @@ export function projectCrashConverged(env) {
|
|
|
96
105
|
}
|
|
97
106
|
const row = reading.row;
|
|
98
107
|
const consistentlyUnexecuted = reading.orphanState === 'pending' && !reading.hasApprovalEvidence;
|
|
99
|
-
if (!reading.unstable && consistentlyUnexecuted && reading.resumeSafe === true)
|
|
108
|
+
if (!reading.unstable && consistentlyUnexecuted && reading.resumeSafe === true) {
|
|
109
|
+
Reflect.deleteProperty(row, 'needsHumanReason');
|
|
100
110
|
resumeSafe.push(row);
|
|
101
|
-
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
Object.defineProperty(row, 'needsHumanReason', {
|
|
114
|
+
value: needsHumanReasonOf(reading), enumerable: true, writable: true, configurable: true,
|
|
115
|
+
});
|
|
102
116
|
needsHuman.push(row);
|
|
117
|
+
}
|
|
103
118
|
}
|
|
104
119
|
}
|
|
105
120
|
catch {
|
|
@@ -47,6 +47,10 @@ export type PlanReviewModeAfter = (typeof PLAN_REVIEW_MODE_AFTER_WORDS)[number];
|
|
|
47
47
|
export declare function isPlanReviewModeAfter(v: string | undefined): v is PlanReviewModeAfter;
|
|
48
48
|
export type HitlSafetyCode = 'binding_mismatch' | 'no_pending' | 'wrong_gate' | 'bad_plan_edit' | 'bad_plan_mode' | 'empty_answer';
|
|
49
49
|
export type HitlFailureStage = 'fetch' | 'input' | 'card' | 'decide' | 'orchestration';
|
|
50
|
+
export declare const NO_APPROVAL_CARD_PORT_REASON = "no approval card port installed (print/non-REPL mode, or host forgot installApprovalCardPort)";
|
|
51
|
+
export declare const APPROVAL_CARD_PORT_KEY_MISMATCH_REASON = "no approval card port installed for the default session key, but a non-default sessionKey has one \u2014 likely a keyed-install/default-read mismatch (installApprovalCardPortFor vs the zero-arg default-key read), not a missing install";
|
|
52
|
+
export declare const NO_QUESTION_OVERLAY_REASON = "no question overlay mounted (print/non-REPL mode)";
|
|
53
|
+
export declare function isNoDecisionSurfaceReason(reason: string): boolean;
|
|
50
54
|
export interface HitlDecideDenyOutcome {
|
|
51
55
|
decision: 'deny';
|
|
52
56
|
reason?: string | undefined;
|
package/dist/hitl/hitlBridge.js
CHANGED
|
@@ -25,6 +25,12 @@ export const PLAN_REVIEW_MODE_AFTER_WORDS = Object.freeze(['default', 'acceptEdi
|
|
|
25
25
|
export function isPlanReviewModeAfter(v) {
|
|
26
26
|
return v !== undefined && PLAN_REVIEW_MODE_AFTER_WORDS.includes(v);
|
|
27
27
|
}
|
|
28
|
+
export const NO_APPROVAL_CARD_PORT_REASON = 'no approval card port installed (print/non-REPL mode, or host forgot installApprovalCardPort)';
|
|
29
|
+
export const APPROVAL_CARD_PORT_KEY_MISMATCH_REASON = 'no approval card port installed for the default session key, but a non-default sessionKey has one — likely a keyed-install/default-read mismatch (installApprovalCardPortFor vs the zero-arg default-key read), not a missing install';
|
|
30
|
+
export const NO_QUESTION_OVERLAY_REASON = 'no question overlay mounted (print/non-REPL mode)';
|
|
31
|
+
export function isNoDecisionSurfaceReason(reason) {
|
|
32
|
+
return reason === NO_APPROVAL_CARD_PORT_REASON || reason === APPROVAL_CARD_PORT_KEY_MISMATCH_REASON || reason === NO_QUESTION_OVERLAY_REASON;
|
|
33
|
+
}
|
|
28
34
|
export class HitlSafetyError extends Error {
|
|
29
35
|
code;
|
|
30
36
|
constructor(message, code) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { putOwnKey } from '../ownKey.js';
|
|
2
|
-
import { DecideTransportRetryExhaustedError, HitlBridge, HitlSafetyError, findPendingForTask, readDecideCurrentPending, readWireErrorCode, } from './hitlBridge.js';
|
|
2
|
+
import { DecideTransportRetryExhaustedError, HitlBridge, HitlSafetyError, NO_QUESTION_OVERLAY_REASON, findPendingForTask, isNoDecisionSurfaceReason, readDecideCurrentPending, readWireErrorCode, } from './hitlBridge.js';
|
|
3
3
|
import { PARKED_RESUME_STARTUP_FAILED, RESUME_POLICY_FOLD_CODE, isDecideParkMovedCode } from '../engineErrorCodes.js';
|
|
4
4
|
import { publishQuestionFrame, registerLocalQuestionResponder, hasQuestionOverlay, } from '../liveQuestionStore.js';
|
|
5
5
|
import { hostLog } from '../host.js';
|
|
@@ -75,7 +75,7 @@ export function toAnsweredOutput(questions, answer) {
|
|
|
75
75
|
}
|
|
76
76
|
async function surfaceGateAndDecide(deps, taskId, askArgsByCall, signal, parkGatedCallId, onPresented) {
|
|
77
77
|
if (!hasQuestionOverlay()) {
|
|
78
|
-
return { kind: 'failed', stage: 'card', reason:
|
|
78
|
+
return { kind: 'failed', stage: 'card', reason: NO_QUESTION_OVERLAY_REASON };
|
|
79
79
|
}
|
|
80
80
|
const found = await findPendingForTask(deps.client, taskId, isAskTool, signal ? { signal } : undefined, parkGatedCallId, (row) => isAskTool(typeof row.toolName === 'string' ? row.toolName : undefined));
|
|
81
81
|
if (!found.ok)
|
|
@@ -301,7 +301,8 @@ export async function resolvePark(park, ctx) {
|
|
|
301
301
|
: outcome.kind === 'retracted'
|
|
302
302
|
? 'the approval card was retracted by the host without a decision (nothing was sent; the run stays parked and remains decidable elsewhere)'
|
|
303
303
|
: undefined;
|
|
304
|
-
|
|
304
|
+
const noDecisionSurface = outcome.kind === 'failed' && outcome.stage === 'card' && isNoDecisionSurfaceReason(outcome.reason);
|
|
305
|
+
return { kind: 'failsoft', events: failsoftEvents(park, ctx, outcome.kind === 'aborted', failedReason, undefined, noDecisionSurface) };
|
|
305
306
|
}
|
|
306
307
|
led.dropHeldForDecidedPark(outcome.gatedCallId, park.gatedCallId);
|
|
307
308
|
if (outcome.gatedCallId) {
|
|
@@ -324,7 +325,15 @@ export async function resolvePark(park, ctx) {
|
|
|
324
325
|
...(handoffTaskId !== undefined ? { handoffTaskId } : {}),
|
|
325
326
|
};
|
|
326
327
|
}
|
|
327
|
-
function
|
|
328
|
+
function failsoftReasonSentence(park, failedReason, terminal) {
|
|
329
|
+
if (terminal !== undefined)
|
|
330
|
+
return terminal.errorMessage;
|
|
331
|
+
if (failedReason === undefined)
|
|
332
|
+
return undefined;
|
|
333
|
+
return (`${park.gate === 'fs' ? 'Tool approval' : 'AskUserQuestion'} gate could not be answered: ${failedReason}` +
|
|
334
|
+
' — the run is still parked on this approval: decide it on the card when it is shown again, or cancel the run.');
|
|
335
|
+
}
|
|
336
|
+
function failsoftEvents(park, ctx, gateAbortedByUser, failedReason, terminal, noDecisionSurface = false) {
|
|
328
337
|
const events = [
|
|
329
338
|
...flushHeldWithInterruptRewrite(ctx.led, {
|
|
330
339
|
terminal: park.pendingDone,
|
|
@@ -332,19 +341,15 @@ function failsoftEvents(park, ctx, gateAbortedByUser, failedReason, terminal) {
|
|
|
332
341
|
gateAbortedByUser,
|
|
333
342
|
}),
|
|
334
343
|
];
|
|
344
|
+
const reason = failsoftReasonSentence(park, failedReason, terminal);
|
|
335
345
|
if (park.pendingDone) {
|
|
336
|
-
events.push(park.pendingDone);
|
|
346
|
+
events.push(reason === undefined || noDecisionSurface ? park.pendingDone : { ...park.pendingDone, _sema_park_failed_reason: reason });
|
|
337
347
|
}
|
|
338
348
|
else if (terminal !== undefined) {
|
|
339
349
|
events.push({ type: 'failed', ...terminal });
|
|
340
350
|
}
|
|
341
|
-
else if (
|
|
342
|
-
events.push({
|
|
343
|
-
type: 'failed',
|
|
344
|
-
errorCode: 'hitl_unanswered',
|
|
345
|
-
errorMessage: `${park.gate === 'fs' ? 'Tool approval' : 'AskUserQuestion'} gate could not be answered: ${failedReason}` +
|
|
346
|
-
' — the run is still parked on this approval: decide it on the card when it is shown again, or cancel the run.',
|
|
347
|
-
});
|
|
351
|
+
else if (reason !== undefined) {
|
|
352
|
+
events.push({ type: 'failed', errorCode: 'hitl_unanswered', errorMessage: reason });
|
|
348
353
|
}
|
|
349
354
|
return events;
|
|
350
355
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { CORE_PROTOCOL_PREFIXES } from '../generated/toolNameTables.js';
|
|
2
|
+
export const PREFIXES_IN_PROSE = CORE_PROTOCOL_PREFIXES.map((prefix) => `"${prefix}"`).join(' or ');
|
|
3
|
+
export const REFUSED_NAME_CLASSES = `a retired tool name, or a name that contains "__" but does not start with ${PREFIXES_IN_PROSE}`;
|
|
4
|
+
export const REFUSED_NAME_FIX = `If the tool still exists, use its current name; a server or agent-peer tool name starts with ${PREFIXES_IN_PROSE}.`;
|
|
5
|
+
export const RULES_LEGACY_TOOL_NAME_WRITE_NOTE = `The engine refused to write these session rules, and the stored record was not changed at all: they name a tool the engine refuses — ${REFUSED_NAME_CLASSES} (the engine's own error text names the entry). ` +
|
|
6
|
+
`${REFUSED_NAME_FIX} Then write the rules again. ` +
|
|
7
|
+
'If that entry was already in this session’s rule record, it has to be removed from the record before the record can be written again.';
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { CONFIG_LEGACY_TOOL_NAME } from '../engineErrorCodes.js';
|
|
1
|
+
import { CONFIG_LEGACY_TOOL_NAME, RULES_LEGACY_TOOL_NAME } from '../engineErrorCodes.js';
|
|
2
2
|
import { rosterMountedNames } from './rosterMountedNames.js';
|
|
3
|
+
import { PREFIXES_IN_PROSE, REFUSED_NAME_CLASSES, REFUSED_NAME_FIX, RULES_LEGACY_TOOL_NAME_WRITE_NOTE } from './refusedToolNameProse.js';
|
|
3
4
|
import { CORE_PROTOCOL_PREFIXES, CORE_RETIRED_TOOL_CURRENT_NAMES } from '../generated/toolNameTables.js';
|
|
4
5
|
const COVERING_NAMESPACE_PREFIXES = CORE_PROTOCOL_PREFIXES;
|
|
5
6
|
const ENGINE_RETIRED_TOOL_NAMES = CORE_RETIRED_TOOL_CURRENT_NAMES;
|
|
6
|
-
const PREFIXES_IN_PROSE = COVERING_NAMESPACE_PREFIXES.map((prefix) => `"${prefix}"`).join(' or ');
|
|
7
7
|
const WITHHELD_NOTICE = {
|
|
8
8
|
ask_no_bucket: 'Not written to this session’s rule record: that record only holds rules that block outright, and this rule asks before running instead — recording it as a block would change what it means.',
|
|
9
9
|
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
10
|
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
11
|
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
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, or has spaces around the name.',
|
|
13
|
-
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.
|
|
13
|
+
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
14
|
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
15
|
};
|
|
16
16
|
export const SESSION_POLICY_WITHHELD_WHY = Object.freeze(Object.keys(WITHHELD_NOTICE));
|
|
@@ -114,8 +114,12 @@ export function sessionPolicyWithheldNotice(why) {
|
|
|
114
114
|
? WITHHELD_NOTICE[why]
|
|
115
115
|
: 'Not written to this session’s rule record.';
|
|
116
116
|
}
|
|
117
|
-
const LEGACY_TOOL_NAME_FAILURE_NOTE = `This run did not start: a tool rule applied to it names a tool the engine refuses —
|
|
117
|
+
const LEGACY_TOOL_NAME_FAILURE_NOTE = `This run did not start: a tool rule applied to it names a tool the engine refuses — ${REFUSED_NAME_CLASSES} (the engine's own error text names the entry). ` +
|
|
118
118
|
'Every later run fails the same way until that entry is removed from wherever it is set: this session’s rule record, the settings sent with the run, a parent session’s rule record, or the deployment’s own tool rules.';
|
|
119
119
|
export function legacyToolNameFailureNoteOf(code) {
|
|
120
|
-
|
|
120
|
+
if (code === CONFIG_LEGACY_TOOL_NAME)
|
|
121
|
+
return LEGACY_TOOL_NAME_FAILURE_NOTE;
|
|
122
|
+
if (code === RULES_LEGACY_TOOL_NAME)
|
|
123
|
+
return RULES_LEGACY_TOOL_NAME_WRITE_NOTE;
|
|
124
|
+
return undefined;
|
|
121
125
|
}
|
|
@@ -38,6 +38,11 @@ export type SessionPolicyFailure = {
|
|
|
38
38
|
} | {
|
|
39
39
|
kind: 'request-rejected';
|
|
40
40
|
message: string;
|
|
41
|
+
} | {
|
|
42
|
+
kind: 'legacy-tool-name';
|
|
43
|
+
message: string;
|
|
44
|
+
field?: SessionPolicyRemovedEntry['field'];
|
|
45
|
+
name?: string;
|
|
41
46
|
} | {
|
|
42
47
|
kind: 'unauthorized';
|
|
43
48
|
message: string;
|
|
@@ -85,6 +90,12 @@ export type SessionPolicyTightenOutcome = {
|
|
|
85
90
|
} | {
|
|
86
91
|
kind: 'capability_absent';
|
|
87
92
|
why: 'not_reported' | 'absent';
|
|
93
|
+
} | {
|
|
94
|
+
kind: 'legacy_tool_name';
|
|
95
|
+
message: string;
|
|
96
|
+
field?: SessionPolicyRemovedEntry['field'];
|
|
97
|
+
name?: string;
|
|
98
|
+
alreadyInRecord?: boolean;
|
|
88
99
|
} | {
|
|
89
100
|
kind: 'unknown';
|
|
90
101
|
why: SessionPolicyTightenUnknownWhy;
|
|
@@ -127,6 +138,11 @@ export type SessionPolicyRemovalOutcome = {
|
|
|
127
138
|
kind: 'write_unconfirmed';
|
|
128
139
|
why: Extract<SessionPolicyTightenUnknownWhy, 'write_indeterminate' | 'write_receipt_unreadable' | 'write_receipt_mismatch'>;
|
|
129
140
|
message?: string;
|
|
141
|
+
} | {
|
|
142
|
+
kind: 'legacy_tool_name';
|
|
143
|
+
message: string;
|
|
144
|
+
field?: SessionPolicyRemovedEntry['field'];
|
|
145
|
+
name?: string;
|
|
130
146
|
};
|
|
131
147
|
export type SessionPolicyRosterRequiredWhy = 'absent' | 'unreadable';
|
|
132
148
|
export declare function removeRefusedSessionRules(facade: SessionPolicyFacade, sessionId: string, roster: ToolRosterView | undefined, opts?: {
|