@sema-agent/client-core 0.111.0 → 0.112.1

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.
Files changed (115) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/README.md +16 -7
  3. package/dist/agentSession/engineSessionBackgroundStop.js +2 -2
  4. package/dist/checkpointRowVerdict.d.ts +3 -0
  5. package/dist/checkpointRowVerdict.js +14 -1
  6. package/dist/consoleCaPin.d.ts +136 -0
  7. package/dist/consoleCaPin.js +827 -0
  8. package/dist/decideReceipt.d.ts +2 -1
  9. package/dist/deviceRebind.js +3 -3
  10. package/dist/effectiveFacts.d.ts +9 -0
  11. package/dist/effectiveFacts.js +33 -0
  12. package/dist/engineAddress.d.ts +1 -0
  13. package/dist/engineAddress.js +4 -1
  14. package/dist/engineCredentialRelease.d.ts +54 -0
  15. package/dist/engineCredentialRelease.js +146 -0
  16. package/dist/engineErrorCodes.d.ts +1 -0
  17. package/dist/engineErrorCodes.js +1 -0
  18. package/dist/engineHealthFacts.d.ts +0 -11
  19. package/dist/engineHealthFacts.js +0 -21
  20. package/dist/engineHealthProbeVerdict.d.ts +1 -0
  21. package/dist/engineHealthProbeVerdict.js +21 -13
  22. package/dist/engineUnavailable.d.ts +3 -2
  23. package/dist/engineUnavailable.js +16 -12
  24. package/dist/engineUrl.js +4 -2
  25. package/dist/gateFailureCodes.js +2 -2
  26. package/dist/generated/sdkFactTables.d.ts +1 -0
  27. package/dist/generated/sdkFactTables.js +13 -0
  28. package/dist/generated/settingsSchemaFns.d.ts +1 -0
  29. package/dist/generated/settingsSchemaFns.js +3 -0
  30. package/dist/generated/settingsSchemaHooksClassifier.js +38 -9
  31. package/dist/generated/settingsSchemaTables.d.ts +52 -0
  32. package/dist/generated/settingsSchemaTables.js +18 -0
  33. package/dist/generated/settingsSchemaTypes.d.ts +23 -13
  34. package/dist/generated/settingsSchemaUrlShape.d.ts +1 -0
  35. package/dist/generated/settingsSchemaUrlShape.js +17 -0
  36. package/dist/hitl/decisionRefusal.d.ts +4 -0
  37. package/dist/hitl/decisionRefusal.js +12 -2
  38. package/dist/hitl/hitlBridge.d.ts +1 -0
  39. package/dist/hitl/hitlBridge.js +17 -4
  40. package/dist/hitl/localAllowRedemption.d.ts +9 -6
  41. package/dist/hitl/localAllowRedemption.js +20 -0
  42. package/dist/hitl/parkOwnership.js +2 -1
  43. package/dist/hitl/parkResolver.js +7 -0
  44. package/dist/hitl/planReviewCard.d.ts +3 -2
  45. package/dist/hitl/planReviewCard.js +2 -0
  46. package/dist/hitl/planReviewWire.d.ts +4 -2
  47. package/dist/hitl/planReviewWire.js +134 -97
  48. package/dist/hitl/screenDecisionCards.js +3 -2
  49. package/dist/hitl/sessionPolicyWire.js +2 -2
  50. package/dist/hitl/standingRows.d.ts +4 -2
  51. package/dist/hitl/standingRows.js +10 -3
  52. package/dist/hooksReaderGenerations.js +1 -1
  53. package/dist/i18n/messages.en.d.ts +165 -0
  54. package/dist/i18n/messages.en.js +166 -1
  55. package/dist/i18n/messages.zh.js +177 -1
  56. package/dist/index.d.ts +30 -5
  57. package/dist/index.js +15 -2
  58. package/dist/liveModelCatalog.d.ts +4 -0
  59. package/dist/liveModelCatalog.js +10 -1
  60. package/dist/localConfig/documents.d.ts +61 -0
  61. package/dist/localConfig/documents.js +51 -0
  62. package/dist/localConfig/kernel.d.ts +149 -0
  63. package/dist/localConfig/kernel.js +597 -0
  64. package/dist/localConfig/mcp/edit.d.ts +161 -0
  65. package/dist/localConfig/mcp/edit.js +846 -0
  66. package/dist/localConfig/mcp/servers.d.ts +165 -0
  67. package/dist/localConfig/mcp/servers.js +776 -0
  68. package/dist/localConfig/mcp/views.d.ts +170 -0
  69. package/dist/localConfig/mcp/views.js +429 -0
  70. package/dist/localConfig/modelPool.d.ts +262 -0
  71. package/dist/localConfig/modelPool.js +1489 -0
  72. package/dist/localConfig/modelPoolWords.d.ts +13 -0
  73. package/dist/localConfig/modelPoolWords.js +20 -0
  74. package/dist/localConfig/receiptShape.d.ts +7 -0
  75. package/dist/localConfig/receiptShape.js +135 -0
  76. package/dist/localConfig/settingsWrite.d.ts +127 -0
  77. package/dist/localConfig/settingsWrite.js +237 -0
  78. package/dist/localConfig/skills.d.ts +124 -0
  79. package/dist/localConfig/skills.js +397 -0
  80. package/dist/localConfig/skillsWords.d.ts +10 -0
  81. package/dist/localConfig/skillsWords.js +5 -0
  82. package/dist/mcpProbeWire.js +2 -1
  83. package/dist/mcpWireCaps.js +1 -1
  84. package/dist/memoryVerbsWire.js +3 -2
  85. package/dist/model/onboardJudgement.d.ts +14 -4
  86. package/dist/model/onboardJudgement.js +91 -139
  87. package/dist/modelConfigGapNotice.d.ts +20 -0
  88. package/dist/modelConfigGapNotice.js +102 -0
  89. package/dist/modsKernelIdentity.js +2 -47
  90. package/dist/notifications.js +14 -12
  91. package/dist/ownKey.d.ts +1 -0
  92. package/dist/ownKey.js +40 -8
  93. package/dist/permissionsDeclaration.d.ts +2 -1
  94. package/dist/permissionsDeclaration.js +31 -17
  95. package/dist/planReviewOutcomeCause.d.ts +2 -0
  96. package/dist/planReviewOutcomeCause.js +12 -0
  97. package/dist/promptBlockedCopy.js +2 -2
  98. package/dist/resumeRefusalCopy.d.ts +3 -1
  99. package/dist/resumeRefusalCopy.js +12 -4
  100. package/dist/runLedgerSnapshot.js +3 -3
  101. package/dist/runPermissionModeSwitch.js +2 -3
  102. package/dist/seatContract.d.ts +154 -4
  103. package/dist/seatContract.js +228 -2
  104. package/dist/sessionIdSegment.d.ts +1 -0
  105. package/dist/sessionIdSegment.js +4 -1
  106. package/dist/sessionMemoryErase.js +2 -1
  107. package/dist/sha256Bytes.d.ts +1 -0
  108. package/dist/sha256Bytes.js +46 -0
  109. package/dist/skillOverrideChain.d.ts +83 -0
  110. package/dist/skillOverrideChain.js +378 -0
  111. package/dist/skillsWireCaps.d.ts +3 -0
  112. package/dist/skillsWireCaps.js +16 -1
  113. package/dist/turnFailurePresentation.js +4 -10
  114. package/docs/INTEGRATION-CLIENTS.md +680 -14
  115. package/package.json +3 -3
@@ -1,13 +1,27 @@
1
1
  export const SETTINGS_CLASSIFY_HOOKS_CONFIG_CLOSURE = (HOOK_EVENTS) => {
2
2
  const HOOK_GUARD_EVENTS = ["PreToolUse", "PermissionRequest"];
3
3
  const HOOK_SHELLS = ["bash", "powershell"];
4
- const HOOK_ENTRY_COMMON_FIELDS = { if: "string?", timeout: "seconds?", statusMessage: "string?", once: "boolean?" };
5
- const HOOK_ENTRY_FIELDS = {
6
- command: { command: "text", shell: "shell?", async: "boolean?", asyncRewake: "boolean?", ...HOOK_ENTRY_COMMON_FIELDS },
7
- prompt: { prompt: "text", model: "string?", ...HOOK_ENTRY_COMMON_FIELDS },
8
- agent: { prompt: "text", model: "string?", ...HOOK_ENTRY_COMMON_FIELDS },
9
- http: { url: "text", headers: "stringRecord?", allowedEnvVars: "stringList?", ...HOOK_ENTRY_COMMON_FIELDS },
10
- mcp_tool: { server: "string", tool: "string", input: "record?", ...HOOK_ENTRY_COMMON_FIELDS },
4
+ const HOOK_ON_FAILURE_ACCEPTED = { continue: true, block: true };
5
+ const HOOK_ENTRY_FIELDS_READ = {
6
+ command: {
7
+ command: "text",
8
+ args: "argv?",
9
+ shell: "shell?",
10
+ async: "boolean?",
11
+ asyncRewake: "boolean?",
12
+ onFailure: "onFailure?",
13
+ if: "string?",
14
+ timeout: "seconds?",
15
+ statusMessage: "string?",
16
+ once: "boolean?",
17
+ rewakeMessage: "nonEmpty?",
18
+ rewakeSummary: "nonEmpty?",
19
+ cloud: "any?",
20
+ },
21
+ prompt: { prompt: "text", model: "string?", if: "string?", timeout: "seconds?", statusMessage: "string?", once: "boolean?", continueOnBlock: "boolean?" },
22
+ agent: { prompt: "text", model: "string?", if: "string?", timeout: "seconds?", statusMessage: "string?", once: "boolean?" },
23
+ http: { url: "text", headers: "stringRecord?", allowedEnvVars: "stringList?", onFailure: "onFailure?", if: "string?", timeout: "seconds?", statusMessage: "string?", once: "boolean?", cloud: "any?" },
24
+ mcp_tool: { server: "string", tool: "string", input: "record?", if: "string?", timeout: "seconds?", statusMessage: "string?", once: "boolean?" },
11
25
  };
12
26
  function hookShapeOf(v) {
13
27
  try {
@@ -146,6 +160,8 @@ export const SETTINGS_CLASSIFY_HOOKS_CONFIG_CLOSURE = (HOOK_EVENTS) => {
146
160
  switch (base) {
147
161
  case "text":
148
162
  return typeof value === "string" && value.length > 0 && !value.includes("\0");
163
+ case "nonEmpty":
164
+ return typeof value === "string" && value.length > 0;
149
165
  case "string":
150
166
  return typeof value === "string";
151
167
  case "shell":
@@ -163,6 +179,19 @@ export const SETTINGS_CLASSIFY_HOOKS_CONFIG_CLOSURE = (HOOK_EVENTS) => {
163
179
  if (typeof value[i] !== "string")
164
180
  return false;
165
181
  return true;
182
+ case "argv":
183
+ if (!hookIsArray(value))
184
+ return false;
185
+ for (let i = 0; i < value.length; i++) {
186
+ const arg = value[i];
187
+ if (typeof arg !== "string" || arg.includes("\0"))
188
+ return false;
189
+ }
190
+ return true;
191
+ case "onFailure":
192
+ return typeof value === "string" && Object.hasOwn(HOOK_ON_FAILURE_ACCEPTED, value);
193
+ case "any":
194
+ return true;
166
195
  case "record":
167
196
  return hookIsRecord(value);
168
197
  default: {
@@ -175,9 +204,9 @@ export const SETTINGS_CLASSIFY_HOOKS_CONFIG_CLOSURE = (HOOK_EVENTS) => {
175
204
  if (!hookIsRecord(entry))
176
205
  return {};
177
206
  const type = Object.hasOwn(entry, "type") ? entry.type : undefined;
178
- if (typeof type !== "string" || !Object.hasOwn(HOOK_ENTRY_FIELDS, type))
207
+ if (typeof type !== "string" || !Object.hasOwn(HOOK_ENTRY_FIELDS_READ, type))
179
208
  return { field: "type" };
180
- const fields = HOOK_ENTRY_FIELDS[type];
209
+ const fields = HOOK_ENTRY_FIELDS_READ[type];
181
210
  const out = { type };
182
211
  for (const field of Object.keys(fields)) {
183
212
  const kind = fields[field];
@@ -1,6 +1,7 @@
1
1
  export declare const SETTINGS_ENGINE_OWNED_HOOK_EVENTS: readonly ["PreToolUse", "PostToolUse", "PostToolUseFailure", "PostToolBatch", "PreCompact", "PostCompact", "Stop", "StopFailure", "UserPromptSubmit"];
2
2
  export declare const SETTINGS_SECRET_LITERAL_RE: RegExp;
3
3
  export declare const SETTINGS_PRIVATE_KEY_PEM_HEADER: RegExp;
4
+ export declare const SETTINGS_SHA256_DIGEST_RE: RegExp;
4
5
  export declare const SETTINGS_DEVICE_CODE_TTL_SECONDS = 900;
5
6
  export declare const SETTINGS_DEVICE_POLL_INTERVAL_SECONDS = 5;
6
7
  export declare const SETTINGS_TIER_GROUP_NAME_RE: RegExp;
@@ -19,3 +20,54 @@ export declare const SETTINGS_WEB_ADMIN_TOKEN_ENV: Readonly<{
19
20
  readonly tokenFile: "SEMA_WEB_ADMIN_TOKEN_FILE";
20
21
  }>;
21
22
  export declare const SETTINGS_NO_EXECUTION_TARGET_LANES: readonly ["none"];
23
+ export declare const SETTINGS_ROOT_DOCUMENT_UNREADABLE_REASONS: readonly ["bad_json", "bad_shape", "io"];
24
+ export declare const SETTINGS_UNIT_PLACES: Readonly<{
25
+ permissions: Readonly<{
26
+ path: readonly ["permissions"];
27
+ category: "permissions";
28
+ }>;
29
+ permissionMode: Readonly<{
30
+ path: readonly ["permissions", "defaultMode"];
31
+ category: "permissionMode";
32
+ reading: "permissionMode";
33
+ }>;
34
+ additionalDirectories: Readonly<{
35
+ path: readonly ["permissions", "additionalDirectories"];
36
+ category: "sandboxNetwork";
37
+ reading: "stringList";
38
+ }>;
39
+ sandbox: Readonly<{
40
+ path: readonly ["sandbox"];
41
+ category: "sandboxNetwork";
42
+ }>;
43
+ disableBypassPermissionsMode: Readonly<{
44
+ path: readonly ["permissions", "disableBypassPermissionsMode"];
45
+ category: "killSwitches";
46
+ reading: "killSwitch";
47
+ }>;
48
+ disableAutoMode: Readonly<{
49
+ path: readonly ["permissions", "disableAutoMode"];
50
+ category: "killSwitches";
51
+ reading: "killSwitch";
52
+ }>;
53
+ hooks: Readonly<{
54
+ path: readonly ["hooks"];
55
+ category: "hooks";
56
+ reading: "hooks";
57
+ }>;
58
+ env: Readonly<{
59
+ path: readonly ["env"];
60
+ category: "env";
61
+ reading: "envMap";
62
+ }>;
63
+ model: Readonly<{
64
+ path: readonly ["model"];
65
+ category: "model";
66
+ reading: "nonEmptyString";
67
+ }>;
68
+ outputStyle: Readonly<{
69
+ path: readonly ["outputStyle"];
70
+ category: "ui";
71
+ reading: "nonEmptyString";
72
+ }>;
73
+ }>;
@@ -11,6 +11,7 @@ export const SETTINGS_ENGINE_OWNED_HOOK_EVENTS = Object.freeze([
11
11
  ]);
12
12
  export const SETTINGS_SECRET_LITERAL_RE = Object.freeze(/^(sk-|gh[opus]_|github_pat_|xox[baprse]-|xapp-|bearer\s|basic\s|eyJ[A-Za-z0-9_-]{10,}\.|AKIA[0-9A-Z]{12,}|AIza[0-9A-Za-z_-]{20,}|-----BEGIN[A-Z0-9 ]*PRIVATE KEY)/i);
13
13
  export const SETTINGS_PRIVATE_KEY_PEM_HEADER = Object.freeze(/-----BEGIN[A-Z0-9 ]*PRIVATE KEY/i);
14
+ export const SETTINGS_SHA256_DIGEST_RE = Object.freeze(/^sha256:[0-9a-f]{64}$/);
14
15
  export const SETTINGS_DEVICE_CODE_TTL_SECONDS = 900;
15
16
  export const SETTINGS_DEVICE_POLL_INTERVAL_SECONDS = 5;
16
17
  export const SETTINGS_TIER_GROUP_NAME_RE = Object.freeze(/^[a-z0-9][a-z0-9._-]*$/i);
@@ -74,3 +75,20 @@ export const SETTINGS_WEB_ADMIN_TOKEN_ENV = Object.freeze({ token: "SEMA_WEB_ADM
74
75
  export const SETTINGS_NO_EXECUTION_TARGET_LANES = Object.freeze([
75
76
  "none",
76
77
  ]);
78
+ export const SETTINGS_ROOT_DOCUMENT_UNREADABLE_REASONS = Object.freeze([
79
+ "bad_json",
80
+ "bad_shape",
81
+ "io",
82
+ ]);
83
+ export const SETTINGS_UNIT_PLACES = Object.freeze({
84
+ "permissions": Object.freeze({ path: Object.freeze(["permissions"]), category: "permissions" }),
85
+ "permissionMode": Object.freeze({ path: Object.freeze(["permissions", "defaultMode"]), category: "permissionMode", reading: "permissionMode" }),
86
+ "additionalDirectories": Object.freeze({ path: Object.freeze(["permissions", "additionalDirectories"]), category: "sandboxNetwork", reading: "stringList" }),
87
+ "sandbox": Object.freeze({ path: Object.freeze(["sandbox"]), category: "sandboxNetwork" }),
88
+ "disableBypassPermissionsMode": Object.freeze({ path: Object.freeze(["permissions", "disableBypassPermissionsMode"]), category: "killSwitches", reading: "killSwitch" }),
89
+ "disableAutoMode": Object.freeze({ path: Object.freeze(["permissions", "disableAutoMode"]), category: "killSwitches", reading: "killSwitch" }),
90
+ "hooks": Object.freeze({ path: Object.freeze(["hooks"]), category: "hooks", reading: "hooks" }),
91
+ "env": Object.freeze({ path: Object.freeze(["env"]), category: "env", reading: "envMap" }),
92
+ "model": Object.freeze({ path: Object.freeze(["model"]), category: "model", reading: "nonEmptyString" }),
93
+ "outputStyle": Object.freeze({ path: Object.freeze(["outputStyle"]), category: "ui", reading: "nonEmptyString" }),
94
+ });
@@ -3,35 +3,44 @@ declare const HOOK_EVENTS: readonly ["PreToolUse", "PostToolUse", "PostToolUseFa
3
3
  type HookEventWord = (typeof HOOK_EVENTS)[number];
4
4
  declare const HOOK_ENTRY_TYPES: readonly ["command", "prompt", "agent", "http", "mcp_tool"];
5
5
  export type HookEntryType = (typeof HOOK_ENTRY_TYPES)[number];
6
+ declare const HOOK_ON_FAILURE_WORDS: readonly ["continue", "block"];
7
+ export type HookOnFailure = (typeof HOOK_ON_FAILURE_WORDS)[number];
6
8
  declare const HOOK_SHELLS: readonly ["bash", "powershell"];
7
- declare const HOOK_ENTRY_FIELDS: {
8
- readonly command: {
9
+ declare const HOOK_ENTRY_FIELDS: Readonly<{
10
+ command: Readonly<{
11
+ readonly rewakeMessage: "nonEmpty?";
12
+ readonly rewakeSummary: "nonEmpty?";
13
+ readonly cloud: "any?";
9
14
  readonly if: "string?";
10
15
  readonly timeout: "seconds?";
11
16
  readonly statusMessage: "string?";
12
17
  readonly once: "boolean?";
13
18
  readonly command: "text";
19
+ readonly args: "argv?";
14
20
  readonly shell: "shell?";
15
21
  readonly async: "boolean?";
16
22
  readonly asyncRewake: "boolean?";
17
- };
18
- readonly prompt: {
23
+ readonly onFailure: "onFailure?";
24
+ }>;
25
+ prompt: Readonly<{
26
+ readonly continueOnBlock: "boolean?";
19
27
  readonly if: "string?";
20
28
  readonly timeout: "seconds?";
21
29
  readonly statusMessage: "string?";
22
30
  readonly once: "boolean?";
23
31
  readonly prompt: "text";
24
32
  readonly model: "string?";
25
- };
26
- readonly agent: {
33
+ }>;
34
+ agent: Readonly<{
27
35
  readonly if: "string?";
28
36
  readonly timeout: "seconds?";
29
37
  readonly statusMessage: "string?";
30
38
  readonly once: "boolean?";
31
39
  readonly prompt: "text";
32
40
  readonly model: "string?";
33
- };
34
- readonly http: {
41
+ }>;
42
+ http: Readonly<{
43
+ readonly cloud: "any?";
35
44
  readonly if: "string?";
36
45
  readonly timeout: "seconds?";
37
46
  readonly statusMessage: "string?";
@@ -39,8 +48,9 @@ declare const HOOK_ENTRY_FIELDS: {
39
48
  readonly url: "text";
40
49
  readonly headers: "stringRecord?";
41
50
  readonly allowedEnvVars: "stringList?";
42
- };
43
- readonly mcp_tool: {
51
+ readonly onFailure: "onFailure?";
52
+ }>;
53
+ mcp_tool: Readonly<{
44
54
  readonly if: "string?";
45
55
  readonly timeout: "seconds?";
46
56
  readonly statusMessage: "string?";
@@ -48,9 +58,9 @@ declare const HOOK_ENTRY_FIELDS: {
48
58
  readonly server: "string";
49
59
  readonly tool: "string";
50
60
  readonly input: "record?";
51
- };
52
- };
53
- type HookFieldBaseValue<B> = B extends "text" | "string" ? string : B extends "shell" ? (typeof HOOK_SHELLS)[number] : B extends "boolean" ? boolean : B extends "seconds" ? number : B extends "stringRecord" ? Record<string, string> : B extends "stringList" ? string[] : B extends "record" ? Record<string, unknown> : never;
61
+ }>;
62
+ }>;
63
+ type HookFieldBaseValue<B> = B extends "text" | "nonEmpty" | "string" ? string : B extends "shell" ? (typeof HOOK_SHELLS)[number] : B extends "onFailure" ? HookOnFailure : B extends "any" ? unknown : B extends "boolean" ? boolean : B extends "seconds" ? number : B extends "stringRecord" ? Record<string, string> : B extends "stringList" | "argv" ? string[] : B extends "record" ? Record<string, unknown> : never;
54
64
  type HookFieldValue<K> = K extends `${infer B}?` ? HookFieldBaseValue<B> : HookFieldBaseValue<K>;
55
65
  type Flat<O> = {
56
66
  [K in keyof O]: O[K];
@@ -0,0 +1 @@
1
+ export declare const SETTINGS_URL_SHAPE_CLOSURE: (isHierarchicalUrl: (raw: string) => boolean) => any;
@@ -0,0 +1,17 @@
1
+ export const SETTINGS_URL_SHAPE_CLOSURE = (isHierarchicalUrl) => {
2
+ const HTTP_URL_PARTS = ["userinfo", "query", "fragment"];
3
+ function httpUrlFlaws(raw, forbid) {
4
+ if (!isHierarchicalUrl(raw))
5
+ return ["not-http-url"];
6
+ const u = new URL(raw);
7
+ const hashAt = u.href.indexOf("#");
8
+ const present = {
9
+ userinfo: u.username !== "" || u.password !== "",
10
+ query: (hashAt < 0 ? u.href : u.href.slice(0, hashAt)).includes("?"),
11
+ fragment: hashAt >= 0,
12
+ };
13
+ const shape = u.protocol === "http:" || u.protocol === "https:" ? [] : ["not-http-url"];
14
+ return [...shape, ...HTTP_URL_PARTS.filter((p) => forbid.includes(p) && present[p])];
15
+ }
16
+ return { httpUrlFlaws };
17
+ };
@@ -5,7 +5,9 @@ import { type ProtocolErrorName, type ProtocolErrorNameReading } from '../wireFa
5
5
  import type { DenyAttributionRefusalDetail } from '../wireRefusalCopy.js';
6
6
  import { type DeliveredReading } from './attributionLadder.js';
7
7
  import { type AnswerCapture, type UnansweredCapture } from '../engineWireFor.js';
8
+ import { type DecisionRefusalWayOut } from '../checkpointRowVerdict.js';
8
9
  export declare const ASK_DECIDED_CODE = "conflict.ask_decided";
10
+ export type { DecisionRefusalWayOut } from '../checkpointRowVerdict.js';
9
11
  export type DecisionRefusalStanding = 'settled-elsewhere' | 'ambiguous' | 'refused-pending';
10
12
  export type { DecisionRefusalConflict } from './decisionConflict.js';
11
13
  export { refusalCarrierOf, type RefusalCarrier } from './hitlBridge.js';
@@ -57,6 +59,7 @@ export interface DecisionRefusalReading {
57
59
  readonly conflict?: DecisionRefusalConflict;
58
60
  readonly unrecoverable?: true;
59
61
  readonly acceptableForm?: {} | null;
62
+ readonly wayOut?: DecisionRefusalWayOut;
60
63
  }
61
64
  export declare function mintDecisionRefusalReading(err: {} | null | undefined, context?: RefusalRouteContext, attributionCarried?: boolean, delivered?: DeliveredReading, carried?: RefusalCarrier): DecisionRefusalReading;
62
65
  export declare function readDecisionRefusalReading(err: {} | null | undefined, context?: RefusalRouteContext): DecisionRefusalReading;
@@ -67,6 +70,7 @@ export interface DecisionRefusalClassified {
67
70
  readonly transient?: true;
68
71
  readonly conflict?: DecisionRefusalConflict;
69
72
  readonly unrecoverable?: true;
73
+ readonly wayOut?: DecisionRefusalWayOut;
70
74
  }
71
75
  export declare function classifyDecisionRefusal(err: unknown, context?: RefusalRouteContext): DecisionRefusalClassified | undefined;
72
76
  export declare function classifyDeliveredDecisionRefusal(err: {} | null | undefined, delivered: DeliveredReading): DecisionRefusalClassified | undefined;
@@ -1,4 +1,4 @@
1
- import { ASK_GONE, ASK_GONE_PARKED, RESUME_CONSTRAINT_REJECTED, RESUME_PREFLIGHT_REJECTED, isDecideAlreadyResolvedCode, isDecideParkMovedCode, isDecideRowUnrevivableCode, refusalRetriableReading, refusalSaysStillPending } from '../engineErrorCodes.js';
1
+ import { ASK_GONE, ASK_GONE_PARKED, CORRUPT_ROW_ERROR_CODE, RESUME_CONSTRAINT_REJECTED, RESUME_PREFLIGHT_REJECTED, isDecideAlreadyResolvedCode, isDecideParkMovedCode, isDecideRowUnrevivableCode, refusalRetriableReading, refusalSaysStillPending } from '../engineErrorCodes.js';
2
2
  import { isUiLanguage } from '../env/uiLanguage.js';
3
3
  import { msg, renderMessage } from '../i18n/translate.js';
4
4
  import { notPendingCauseReading, refusalCauseWordOf } from './approvalEndingCause.js';
@@ -10,6 +10,7 @@ import { guardedRead, ownDataField, readOwnDataValue } from '../ownKey.js';
10
10
  import { PROTOCOL_ERROR_NAME, THROWN_SLOT_UNREADABLE, isDisplayableWireCode, isSlotCarrier, ownFoldedCodeBitsOf, protocolErrorNameReading, readErrorPresent, readErrorString, readRefusalField, readThrownSlot } from '../wireFailureShape.js';
11
11
  import { attributionRefusalOf, carrierReadOf } from './attributionLadder.js';
12
12
  import { CODE_UNREADABLE, ownReadingSlot } from '../engineWireFor.js';
13
+ import { isDecisionRefusalWayOut } from '../checkpointRowVerdict.js';
13
14
  export const ASK_DECIDED_CODE = 'conflict.ask_decided';
14
15
  const TRANSIENT_STATUSES = new Set([408, 425, 429, 502, 503, 504]);
15
16
  export { refusalCarrierOf } from './hitlBridge.js';
@@ -32,7 +33,7 @@ const ROUTE_WORDS = { approval_decide: true, live_respond: true, ask_decide: tru
32
33
  export const REFUSAL_ROUTES = Object.freeze(Object.keys(ROUTE_WORDS));
33
34
  const PRIOR_REFUSAL_EVIDENCE = Object.freeze([
34
35
  Object.freeze({
35
- errorCode: 'corrupt',
36
+ errorCode: CORRUPT_ROW_ERROR_CODE,
36
37
  routes: Object.freeze(['plan_review', 'approval_decide', 'live_respond']),
37
38
  evidence: 'server resume-legs.ts:1019 / :2752 / :2910 (resume context read, before the claim CAS at :1666 / :1457); plan_review resumePlanReview reads context before settling',
38
39
  }),
@@ -223,10 +224,13 @@ export function mintDecisionRefusalReading(err, context, attributionCarried, del
223
224
  const conflict = inRange && status === 409 && obj !== undefined ? isolated(() => decisionConflictOf(obj, captured, pn)) : undefined;
224
225
  const unrecoverable = inRange && status !== undefined && Number.isInteger(status) && status >= 400 && status <= 599 && isDecideRowUnrevivableCode(errorCode);
225
226
  const acceptableForm = errorCode === RESUME_CONSTRAINT_REJECTED && obj !== undefined ? isolated(() => readErrorPresent(obj, 'acceptableForm')) : undefined;
227
+ const wayOutRaw = inRange && status === 409 && obj !== undefined ? isolated(() => readErrorString(obj, 'wayOut')) : undefined;
228
+ const wayOut = isDecisionRefusalWayOut(wayOutRaw) ? wayOutRaw : undefined;
226
229
  const transient = inRange &&
227
230
  conflict !== 'grant_changed' &&
228
231
  conflict !== 'row_integrity' &&
229
232
  !unrecoverable &&
233
+ wayOut === undefined &&
230
234
  (TRANSIENT_STATUSES.has(status) || retriable || (status === 404 && obj !== undefined && isolated(() => notPendingCauseReading(obj)) === 'other_replica'));
231
235
  return Object.freeze({
232
236
  ...(status !== undefined ? { status } : {}),
@@ -246,6 +250,7 @@ export function mintDecisionRefusalReading(err, context, attributionCarried, del
246
250
  ...(conflict !== undefined ? { conflict } : {}),
247
251
  ...(unrecoverable ? { unrecoverable: true } : {}),
248
252
  ...(acceptableForm !== undefined ? { acceptableForm } : {}),
253
+ ...(wayOut !== undefined ? { wayOut } : {}),
249
254
  });
250
255
  }
251
256
  export function readDecisionRefusalReading(err, context) {
@@ -264,6 +269,7 @@ function classifiedOf(reading) {
264
269
  const transient = ownDataField(reading, 'transient') === true;
265
270
  const conflict = ownDataField(reading, 'conflict');
266
271
  const unrecoverable = ownDataField(reading, 'unrecoverable') === true;
272
+ const wayOut = ownDataField(reading, 'wayOut');
267
273
  return Object.freeze({
268
274
  standing,
269
275
  status,
@@ -271,6 +277,7 @@ function classifiedOf(reading) {
271
277
  ...(transient ? { transient: true } : {}),
272
278
  ...(typeof conflict === 'string' ? { conflict: conflict } : {}),
273
279
  ...(unrecoverable ? { unrecoverable: true } : {}),
280
+ ...(isDecisionRefusalWayOut(wayOut) ? { wayOut } : {}),
274
281
  });
275
282
  }
276
283
  catch {
@@ -312,6 +319,9 @@ export function decisionRefusalClosesOut(refusal, pending) {
312
319
  const conflict = readThrownSlot(refusal, 'conflict', 'data');
313
320
  if (conflict === 'grant_changed' || conflict === THROWN_SLOT_UNREADABLE)
314
321
  return false;
322
+ const wayOut = readThrownSlot(refusal, 'wayOut', 'data');
323
+ if (wayOut === 'newer_worker' || wayOut === THROWN_SLOT_UNREADABLE)
324
+ return false;
315
325
  return standing === 'ambiguous' || standing === 'refused-pending';
316
326
  }
317
327
  export function decisionStatusTag(status, errorCode) {
@@ -41,6 +41,7 @@ export interface PlanReviewOutcome {
41
41
  editedPlan?: string;
42
42
  reason?: string;
43
43
  permissionModeAfter?: PlanReviewModeAfter;
44
+ checkpointId?: string;
44
45
  }
45
46
  export declare const PLAN_REVIEW_MODE_AFTER_WORDS: readonly ["default", "acceptEdits"];
46
47
  export type PlanReviewModeAfter = (typeof PLAN_REVIEW_MODE_AFTER_WORDS)[number];
@@ -13,6 +13,7 @@ import { closeDecisionOp, decisionLedgerKeysOf, openDecisionOp } from './decisio
13
13
  import { askParkBlockGeneration, noteAskParkDecideSettled } from './askParkAutoReopenBlock.js';
14
14
  import { checkpointQueueRowSortOf, QUEUE_ROW_FAMILY_UNREADABLE_GATE } from './approvalQueueRow.js';
15
15
  import { checkpointDeadlineStateOf, checkpointRowBarOf, checkpointRowBarSentence, unboundWayOutContent } from '../checkpointRowVerdict.js';
16
+ import { DATA_FACE_LANGUAGE, msg, renderMessage } from '../i18n/translate.js';
16
17
  export const DEFAULT_DENY_REASON = 'The user rejected this tool use';
17
18
  export const MAX_DENY_REASON_CHARS = 4096;
18
19
  export function denyReasonForWire(reason, tag) {
@@ -419,32 +420,44 @@ export class HitlBridge {
419
420
  return this.decideRaw(pending.sessionId, decision, opts);
420
421
  }
421
422
  async reviewPlan(outcome, opts) {
423
+ const decisionRaw = outcome.decision;
424
+ const decision = decisionRaw === 'approve' || decisionRaw === 'edit' || decisionRaw === 'reject' ? decisionRaw : undefined;
425
+ if (decision === undefined) {
426
+ throw new HitlSafetyError('plan-review decision must be "approve", "edit" or "reject"', 'bad_plan_edit');
427
+ }
422
428
  const kind = this.active?.gate.kind;
423
429
  if (kind && kind !== 'plan_review' && kind !== 'needs_review') {
424
430
  throw new HitlSafetyError(`cannot plan-review a "${kind}" gate — a plan decision must never resolve a tool approval`, 'wrong_gate');
425
431
  }
426
- if (outcome.decision === 'edit') {
432
+ if (decision === 'edit') {
427
433
  if (!outcome.editedPlan || outcome.editedPlan.length === 0) {
428
434
  throw new HitlSafetyError('plan-review "edit" requires a non-empty editedPlan', 'bad_plan_edit');
429
435
  }
430
436
  }
431
437
  else if (outcome.editedPlan !== undefined) {
432
- throw new HitlSafetyError(`plan-review "${outcome.decision}" must NOT carry editedPlan`, 'bad_plan_edit');
438
+ throw new HitlSafetyError(`plan-review "${decision}" must NOT carry editedPlan`, 'bad_plan_edit');
433
439
  }
434
440
  const modeAfter = outcome.permissionModeAfter;
435
441
  if (modeAfter !== undefined && !isPlanReviewModeAfter(modeAfter)) {
436
442
  throw new HitlSafetyError('plan-review permissionModeAfter must be "default" or "acceptEdits"', 'bad_plan_mode');
437
443
  }
438
- const req = { decision: outcome.decision };
444
+ const gateRaw = outcome.checkpointId;
445
+ const gate = typeof gateRaw === 'string' && gateRaw !== '' ? gateRaw : undefined;
446
+ if (gate === undefined && (decision === 'approve' || decision === 'edit')) {
447
+ throw new HitlSafetyError(renderMessage(msg('approval.planReview.bridge.gateIdMissing'), DATA_FACE_LANGUAGE), 'wrong_gate');
448
+ }
449
+ const req = { decision };
439
450
  if (modeAfter !== undefined && isPlanReviewModeAfter(modeAfter))
440
451
  req.permissionModeAfter = modeAfter;
441
- if (outcome.decision === 'edit' && outcome.editedPlan !== undefined)
452
+ if (decision === 'edit' && outcome.editedPlan !== undefined)
442
453
  req.editedPlan = outcome.editedPlan;
443
454
  if (outcome.reason !== undefined) {
444
455
  const r = denyReasonForWire(outcome.reason, `plan-review ${this.taskId}`);
445
456
  if (r !== undefined)
446
457
  req.reason = r;
447
458
  }
459
+ if (gate !== undefined)
460
+ req.checkpointId = gate;
448
461
  return this.client.assistant.planReview(this.taskId, req, opts);
449
462
  }
450
463
  async resumeResourceLimit(opts) {
@@ -21,18 +21,21 @@ export interface LocalAllowOfferedFacts {
21
21
  readonly scopeRoot: string | undefined;
22
22
  readonly road: EngineCapState;
23
23
  }
24
- export type LocalAllowRedeemabilityInput = ({
25
- readonly subject: 'offered';
26
- } & LocalAllowOfferedFacts) | {
27
- readonly subject: 'declared';
24
+ export interface LocalAllowDeclaredFacts {
28
25
  readonly source: HostSettingsLayer;
29
26
  readonly workspace: WorkspaceTrustState | 'unreadable';
30
27
  readonly managedRulesOnly: boolean | 'unreadable';
31
- readonly rule: string;
32
28
  readonly scopeRoot: string | undefined;
33
29
  readonly store: ListAllPersistedRulesOutcome;
34
- };
30
+ }
31
+ export type LocalAllowRedeemabilityInput = ({
32
+ readonly subject: 'offered';
33
+ } & LocalAllowOfferedFacts) | ({
34
+ readonly subject: 'declared';
35
+ readonly rule: string;
36
+ } & LocalAllowDeclaredFacts);
35
37
  export declare function localAllowRedeemability(input: LocalAllowRedeemabilityInput): LocalAllowRedeemability;
38
+ export declare function localAllowLandingRedeemability(rule: {} | null | undefined, landing: {} | null | undefined): LocalAllowRedeemability;
36
39
  export declare const LOCAL_ALLOW_REDEMPTION_REFUSALS: readonly ["managed_only", "no_scope_root", "rule_shape", "request_refused", "preview_mismatch", "engine_refused", "ticket_dead"];
37
40
  export type LocalAllowRedemptionRefusal = (typeof LOCAL_ALLOW_REDEMPTION_REFUSALS)[number];
38
41
  export declare const LOCAL_ALLOW_REDEMPTION_UNAVAILABLE_CAUSES: readonly ["road_closed", "road_unknown", "policy_unreadable", "input_unreadable", "prepare_failed"];
@@ -99,6 +99,26 @@ export function localAllowRedeemability(input) {
99
99
  return unknownBecause('input_unreadable');
100
100
  }
101
101
  }
102
+ export function localAllowLandingRedeemability(rule, landing) {
103
+ try {
104
+ if (typeof rule !== 'string' || rule.trim() === '')
105
+ return unknownBecause('input_unreadable');
106
+ if (typeof landing !== 'object' || landing === null)
107
+ return unknownBecause('input_unreadable');
108
+ return localAllowRedeemability({
109
+ subject: 'declared',
110
+ rule,
111
+ source: readOwnDataValue(landing, 'source'),
112
+ workspace: readOwnDataValue(landing, 'workspace'),
113
+ managedRulesOnly: readOwnDataValue(landing, 'managedRulesOnly'),
114
+ scopeRoot: readOwnDataValue(landing, 'scopeRoot'),
115
+ store: readOwnDataValue(landing, 'store'),
116
+ });
117
+ }
118
+ catch {
119
+ return unknownBecause('input_unreadable');
120
+ }
121
+ }
102
122
  export const LOCAL_ALLOW_REDEMPTION_REFUSALS = Object.freeze([
103
123
  'managed_only',
104
124
  'no_scope_root',
@@ -4,6 +4,7 @@ import { hostSessionFor } from '../host.js';
4
4
  import { DEFAULT_SESSION_KEY } from '../sessionSlot.js';
5
5
  import { pendingRowIsOfSession } from './parkRowDelegation.js';
6
6
  import { slotReadOf } from '../wireFailureShape.js';
7
+ import { safetyStopNamed } from './hitlBridge.js';
7
8
  export function pendingRowIsOwnedByThisSession(row, deps) {
8
9
  return ownershipVerdictOf(row, deps) === 'owned';
9
10
  }
@@ -70,5 +71,5 @@ export function askQuestionsFromPending(pending) {
70
71
  return usableAskQuestions(input.questions);
71
72
  }
72
73
  export function isHitlSafetyErrorLike(e) {
73
- return typeof e === 'object' && e !== null && e.name === 'HitlSafetyError';
74
+ return typeof e === 'object' && e !== null && safetyStopNamed(e);
74
75
  }
@@ -17,6 +17,7 @@ import { decideFailureNote } from '../decideFailureNote.js';
17
17
  import { wireFailureTextsOf } from '../wireFailureShape.js';
18
18
  import { msg, renderMessage } from '../i18n/translate.js';
19
19
  import { refusedParkFromError } from '../resumeRefusalCopy.js';
20
+ import { decisionRefusalWayOutContent } from '../checkpointRowVerdict.js';
20
21
  export const MAX_GATE_HOPS = 2;
21
22
  export function nextHopBudget(prev, round) {
22
23
  const total = (prev?.total ?? 0) + 1;
@@ -332,6 +333,10 @@ function failsoftWayOutOf(outcome) {
332
333
  return 'speak';
333
334
  if (outcome.kind === 'not_sent' && (outcome.cause === 'row_unresumable' || (outcome.cause === 'safety_stop' && outcome.safetyCode === 'binding_missing')))
334
335
  return 'reason';
336
+ if (outcome.kind === 'unsettled' && outcome.cause === 'engine_refused' && outcome.refusal?.wayOut === 'newer_worker')
337
+ return 'newer_worker';
338
+ if (outcome.kind === 'unsettled' && outcome.cause === 'engine_refused' && outcome.refusal?.wayOut === 'cancel_run')
339
+ return 'cancel';
335
340
  if (outcome.kind === 'unsettled' && outcome.cause === 'engine_refused' && (refusedParkFromError(outcome.refusal) !== null || outcome.refusal?.conflict === 'row_integrity'))
336
341
  return 'cancel';
337
342
  if (outcome.kind === 'unsettled' && outcome.cause === 'engine_refused')
@@ -348,6 +353,8 @@ function failsoftReasonSentence(park, failedReason, terminal, wayOut) {
348
353
  return renderMessage(msg('approval.park.stillParked', { gate, reason: failedReason }));
349
354
  if (wayOut === 'cancel')
350
355
  return renderMessage(msg('approval.park.refusedNotResumable', { gate, reason: failedReason }));
356
+ if (wayOut === 'newer_worker')
357
+ return renderMessage(msg('approval.park.newerWorker', { gate, reason: failedReason, wayOut: decisionRefusalWayOutContent('newer_worker') ?? '' }));
351
358
  return renderMessage(msg(wayOut === 'speak' ? 'approval.park.resume.speak' : 'approval.park.resume.card', { gate, reason: failedReason }));
352
359
  }
353
360
  function failsoftEvents(park, ctx, gateAbortedByUser, failedReason, terminal, noDecisionSurface, wayOut) {
@@ -1,13 +1,14 @@
1
1
  import { type PlanReviewCardFacts, type PlanReviewFactsTaken } from '../planReviewCardFacts.js';
2
2
  import { type MessageRef } from '../i18n/translate.js';
3
+ import type { PlanReviewOutcomeCause } from '../planReviewOutcomeCause.js';
3
4
  import type { MessageKey } from '../i18n/messages.en.js';
4
5
  import type { UiLanguage } from '../env/uiLanguage.js';
5
6
  export declare function planReviewCardNoteRef(facts: PlanReviewCardFacts): MessageRef | undefined;
6
7
  export declare function planReviewUnreadableNoteRef(): MessageRef;
7
8
  export declare function planReviewCardNote(facts: PlanReviewCardFacts, lang?: UiLanguage): string | undefined;
8
9
  export declare function planReviewAskAgainKey(facts: PlanReviewCardFacts | undefined): MessageKey;
9
- export type PlanReviewDecisionReason = 'card_missing' | 'card_withholds' | 'gate_moved' | 'gate_untrusted';
10
- export declare function planReviewDecisionReasonRef(reason: PlanReviewDecisionReason, card?: PlanReviewCardFacts): MessageRef;
10
+ export type PlanReviewDecisionReason = Exclude<PlanReviewOutcomeCause, 'stored_form_unsupported'>;
11
+ export declare function planReviewDecisionReasonRef(reason: PlanReviewOutcomeCause, card?: PlanReviewCardFacts): MessageRef | undefined;
11
12
  export interface PlanReviewLedgerSource {
12
13
  readonly runRow: (taskId: string, signal: AbortSignal) => Promise<object>;
13
14
  readonly frames: (taskId: string, signal: AbortSignal) => AsyncIterable<object>;
@@ -48,6 +48,8 @@ export function planReviewAskAgainKey(facts) {
48
48
  }
49
49
  export function planReviewDecisionReasonRef(reason, card) {
50
50
  switch (reason) {
51
+ case 'stored_form_unsupported':
52
+ return undefined;
51
53
  case 'card_missing':
52
54
  return msg('approval.planReview.decision.cardMissing');
53
55
  case 'card_withholds': {
@@ -3,6 +3,8 @@ import type { EngineWireInjection } from '../engineWireSdk.js';
3
3
  import { type PlanReviewModeAfter } from './hitlBridge.js';
4
4
  import type { AgentClient } from '@sema-agent/sdk';
5
5
  import { isPlanReviewPark, type PlanReviewCardFacts } from '../planReviewCardFacts.js';
6
+ import { type PlanReviewOutcomeCause } from '../planReviewOutcomeCause.js';
7
+ export type { PlanReviewOutcomeCause } from '../planReviewOutcomeCause.js';
6
8
  import type { UiLanguage } from '../env/uiLanguage.js';
7
9
  import type { ReopenCardVerdict } from '../adapter/activeRunSelfHeal.js';
8
10
  export declare const PLAN_REVIEW_APPROVE_LABEL = "Yes, approve and run the plan";
@@ -80,10 +82,10 @@ export interface PlanReviewOutcomeMeta {
80
82
  dispatchNo: number;
81
83
  decision: 'approve' | 'reject' | 'ask_again';
82
84
  effect: PlanReviewDecisionEffect;
83
- cause?: 'stored_form_unsupported';
85
+ cause?: PlanReviewOutcomeCause;
84
86
  questionId?: string;
85
87
  }
86
- export declare function planReviewOutcomeTail(taskId: string, decision: 'approve' | 'reject', effect: PlanReviewDecisionEffect, cause?: 'stored_form_unsupported'): string;
88
+ export declare function planReviewOutcomeTail(taskId: string, decision: 'approve' | 'reject', effect: PlanReviewDecisionEffect, cause?: PlanReviewOutcomeCause): string;
87
89
  export type PlanReviewWireClient = {
88
90
  assistant: Pick<AgentClient['assistant'], 'planReview'>;
89
91
  runs: Pick<AgentClient['runs'], 'get'> & Partial<Pick<AgentClient['runs'], 'events'>>;