@sema-agent/client-core 0.83.2 → 0.83.4

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.
@@ -19,11 +19,12 @@ function laneState(lanes, lane) {
19
19
  if (hit === undefined)
20
20
  return 'not_reported';
21
21
  const v = hit.v;
22
- const _catalogPin = true;
23
- void _catalogPin;
24
- if (v === 'complete')
25
- return lane === 'modelCatalogFile' ? 'unusable' : v;
26
- return v === 'partial' || v === 'absent' || v === 'unreadable' ? v : 'unusable';
22
+ return v === 'complete' || v === 'partial' || v === 'absent' || v === 'unreadable' ? v : 'unusable';
23
+ }
24
+ function catalogDefaultEvidence(catalog, accepts) {
25
+ const _evidencePin = true;
26
+ void _evidencePin;
27
+ return catalog === 'complete' && accepts === true ? { catalog, engineAccepts: accepts } : undefined;
27
28
  }
28
29
  function analyse(reading) {
29
30
  try {
@@ -40,16 +41,36 @@ function analyse(reading) {
40
41
  return malformed();
41
42
  const lanesHit = own(reading, 'lanes')?.v;
42
43
  const order = laneOrder();
43
- if (!isPlainObject(lanesHit))
44
- return { verdict: 'unknown', why: 'lanes', unreadable: [], unreported: [...order], unusable: [] };
44
+ if (!isPlainObject(lanesHit)) {
45
+ return { verdict: 'unknown', why: 'lanes', unreadable: [], unreported: [...order], unusable: [], catalogUnconfirmed: null, catalogRefused: false };
46
+ }
45
47
  const partial = [];
46
48
  const unreadable = [];
47
49
  const unreported = [];
48
50
  const unusable = [];
51
+ let catalogRefused = false;
52
+ let catalogUnconfirmed = null;
49
53
  for (const lane of order) {
50
54
  const s = laneState(lanesHit, lane);
51
- if (s === 'complete')
55
+ if (lane === 'modelCatalogFile') {
56
+ if (s === 'complete') {
57
+ const bit = own(reading, 'engineAcceptsCatalogDefault');
58
+ const evidence = catalogDefaultEvidence(s, bit?.v);
59
+ if (evidence !== undefined)
60
+ return { verdict: 'resolvable', why: 'catalog_default', evidence };
61
+ if (bit !== undefined && bit.v === false) {
62
+ partial.push(lane);
63
+ catalogRefused = true;
64
+ }
65
+ else {
66
+ catalogUnconfirmed = bit === undefined || bit.v === undefined ? 'not_reported' : 'unusable';
67
+ }
68
+ continue;
69
+ }
70
+ }
71
+ else if (s === 'complete') {
52
72
  return { verdict: 'resolvable', why: 'lane', lane };
73
+ }
53
74
  if (s === 'partial')
54
75
  partial.push(lane);
55
76
  else if (s === 'unreadable')
@@ -59,10 +80,10 @@ function analyse(reading) {
59
80
  else if (s === 'unusable')
60
81
  unusable.push(lane);
61
82
  }
62
- if (unreadable.length > 0 || unreported.length > 0 || unusable.length > 0) {
63
- return { verdict: 'unknown', why: 'lanes', unreadable, unreported, unusable };
83
+ if (unreadable.length > 0 || unreported.length > 0 || unusable.length > 0 || catalogUnconfirmed !== null) {
84
+ return { verdict: 'unknown', why: 'lanes', unreadable, unreported, unusable, catalogUnconfirmed, catalogRefused };
64
85
  }
65
- return { verdict: 'not_resolvable', partial };
86
+ return { verdict: 'not_resolvable', partial, catalogRefused };
66
87
  }
67
88
  catch {
68
89
  return malformed();
@@ -97,13 +118,25 @@ export function modelIdentityDetail(reading) {
97
118
  if (a.why === 'engine_answered') {
98
119
  return 'the engine is up and answering, and it does not start without a model name, so it has one — whether its gateway accepts that name is a separate check';
99
120
  }
121
+ if (a.why === 'catalog_default') {
122
+ return `${laneLabel('modelCatalogFile')} supplies a model name as its default, and the local engine accepts a catalog default in place of a startup model name, so it can start with it — whether its gateway accepts that name is a separate check`;
123
+ }
100
124
  return `${laneLabel(a.lane)} supplies a model name, so the local engine can start with it — whether its gateway accepts that name is a separate check`;
101
- case 'not_resolvable':
102
- if (a.partial.length > 0) {
103
- const labels = joinPhrases(a.partial.map(laneLabel));
104
- return `no model name is configured for the local engine — ${labels} ${a.partial.length > 1 ? 'carry' : 'carries'} part of a model configuration (such as a gateway address or a credential) but no model name, and the engine does not start without one`;
125
+ case 'not_resolvable': {
126
+ const others = a.catalogRefused ? a.partial.filter((l) => l !== 'modelCatalogFile') : a.partial;
127
+ const partialClause = others.length > 0
128
+ ? `${joinPhrases(others.map(laneLabel))} ${others.length > 1 ? 'carry' : 'carries'} part of a model configuration (such as a gateway address or a credential) but no model name`
129
+ : '';
130
+ if (a.catalogRefused) {
131
+ return (`no model name is configured for the local engine to start with — ${laneLabel('modelCatalogFile')} names a default model, but the local engine is reported not to accept a catalog default in place of a startup model name` +
132
+ (partialClause !== '' ? `, and ${partialClause}` : '') +
133
+ ', and the engine does not start without one');
134
+ }
135
+ if (others.length > 0) {
136
+ return `no model name is configured for the local engine — ${partialClause}, and the engine does not start without one`;
105
137
  }
106
138
  return 'no model name is configured for the local engine — none of the places this client reads supplies one, and the engine does not start without it';
139
+ }
107
140
  case 'unknown':
108
141
  if (a.why === 'remote') {
109
142
  return a.answer === 'unreachable'
@@ -116,6 +149,15 @@ export function modelIdentityDetail(reading) {
116
149
  ...a.unreported.map((l) => `${laneLabel(l)} was not reported`),
117
150
  ...a.unusable.map((l) => `${laneLabel(l)} gave no usable reading`),
118
151
  ];
152
+ if (a.catalogUnconfirmed === 'not_reported') {
153
+ parts.push(`${laneLabel('modelCatalogFile')} names a default model, but whether the local engine accepts a catalog default in place of a startup model name was not reported`);
154
+ }
155
+ else if (a.catalogUnconfirmed === 'unusable') {
156
+ parts.push(`${laneLabel('modelCatalogFile')} names a default model, but the report of whether the local engine accepts a catalog default in place of a startup model name gave no usable reading`);
157
+ }
158
+ else if (a.catalogRefused) {
159
+ parts.push(`${laneLabel('modelCatalogFile')} names a default model, but the local engine is reported not to accept a catalog default in place of a startup model name`);
160
+ }
119
161
  return `whether the local engine has a model name is unknown — ${joinPhrases(parts)}, and no other place this client reads provides one`;
120
162
  }
121
163
  return "this reading of the engine's model configuration could not be interpreted, so whether the engine has a model name is unknown";
@@ -10,7 +10,7 @@ export declare const PLUGIN_HOOK_EXCLUSION_REASONS: readonly ["workspace_untrust
10
10
  export type PluginHookExclusionReason = (typeof PLUGIN_HOOK_EXCLUSION_REASONS)[number];
11
11
  export declare const SETTINGS_HOOK_DROP_REASONS: readonly ["exec_form_unsupported", "mcp_tool_unsupported"];
12
12
  export type SettingsHookDropReason = (typeof SETTINGS_HOOK_DROP_REASONS)[number];
13
- export type HookSettingsSource = 'policySettings' | 'userSettings' | 'projectSettings' | 'localSettings';
13
+ export type HookSettingsSource = 'policySettings' | 'userSettings' | 'projectSettings' | 'localSettings' | 'flagSettings';
14
14
  export interface PluginHookKey {
15
15
  readonly pluginId: string;
16
16
  readonly pluginName: string;
@@ -332,6 +332,7 @@ const SOURCE_TEXT = {
332
332
  userSettings: 'User settings',
333
333
  projectSettings: 'Project settings',
334
334
  localSettings: 'Local settings',
335
+ flagSettings: 'Flag settings',
335
336
  };
336
337
  export function pluginHookReasonText(reason) {
337
338
  return REASON_TEXT[reason];
@@ -2,6 +2,7 @@ import type { ReasoningEffort } from "./effortWire.js";
2
2
  import type { LaneProof } from "./seam.js";
3
3
  import type { QuestionAnswer } from "./liveQuestionStore.js";
4
4
  import type { ToolApprovalDelegation } from "./hitl/toolApprovalWire.js";
5
+ import type { ApprovalMandateWord } from "./gateVocabulary.js";
5
6
  export interface SeatValidationFailure {
6
7
  reason: string;
7
8
  field?: string;
@@ -55,6 +56,10 @@ export interface ToolPermissionRequest {
55
56
  createdAtMs?: number;
56
57
  governanceForced?: true;
57
58
  delegation?: ToolApprovalDelegation;
59
+ mandated?: true;
60
+ mandate?: ApprovalMandateWord;
61
+ origin?: string;
62
+ ruleOffersAbsence?: string;
58
63
  }
59
64
  declare const SEAT_EVENT_TYPE_TUPLE: readonly ["start", "close", "session_updated", "message", "stream_event", "chrome_event", "error", "tool_permission_resolved"];
60
65
  export type SeatEventType = (typeof SEAT_EVENT_TYPE_TUPLE)[number];
@@ -255,7 +260,7 @@ export declare const SEAT_METHOD_NAMES: readonly ["start", "sendMessage", "inter
255
260
  export declare const SEAT_STORE_PROPS: readonly ["sessionsStateStore", "focusedSessionStateStore", "settingsStateStore"];
256
261
  export declare const SEAT_EVENT_METHOD_NAMES: readonly ["onEvent", "onToolPermissionRequest"];
257
262
  export declare const LOCAL_SESSION_EVENT_KEYS: readonly ["type", "sessionId", "message", "messages", "error", "errorCategory", "code", "request", "data", "sources", "permissionMode", "session", "userMessageUuid", "hasPreClearSession", "requestId", "outcome", "chrome"];
258
- export declare const TOOL_PERMISSION_REQUEST_KEYS: readonly ["requestId", "sessionId", "toolName", "toolCallId", "input", "message", "toctouBinding", "createdAtMs", "governanceForced", "delegation"];
263
+ export declare const TOOL_PERMISSION_REQUEST_KEYS: readonly ["requestId", "sessionId", "toolName", "toolCallId", "input", "message", "toctouBinding", "createdAtMs", "governanceForced", "delegation", "mandated", "mandate", "origin", "ruleOffersAbsence"];
259
264
  export declare const LOCAL_SESSION_RECORD_KEYS: readonly ["sessionId", "engineSessionId", "model", "title", "isRunning", "createdAt", "lastActivityAt", "queuedMessages", "parked"];
260
265
  export declare const SEND_MESSAGE_REQUEST_KEYS: readonly ["sessionId", "text", "model", "userMessageUuid", "permissionMode", "attachmentIds", "reasoningEffort", "retainBackgroundProcesses", "forwardSubagentEvents", "retainSubagentSessions", "settings", "scenario"];
261
266
  export declare const SEAT_SEND_MESSAGE_KEY_ORIGINS: {
@@ -318,6 +323,18 @@ export declare const SEAT_VALIDATOR_KEY_COVERAGE: {
318
323
  }, {
319
324
  readonly key: "delegation";
320
325
  readonly why: "展示增强位,权威结构门在 hitl wire 卡口(结构读+仅子代 ask 在场,坏形降缺席);座位门同款理由不设第二判官。消费端坏形降缺席,绝不拒行。";
326
+ }, {
327
+ readonly key: "mandated";
328
+ readonly why: "展示增强位(CC-203),权威窄读在卡口与过境口 toolPermissionRequestAskBits(严格 true 才上座);座位门再裁一次会为一个徽标键拒掉整条 request、把安全 ask 丢给引擎 TTL deny。消费端经 approvalIsMandated 读,其余一律当缺席。";
329
+ }, {
330
+ readonly key: "mandate";
331
+ readonly why: "展示增强位(CC-203),权威窄读在卡口与过境口(闭集六词精确字节,表外不上座);座位门不设第二判官,理由同 mandated。消费端经 readApprovalMandate 读,表外值读成缺席。";
332
+ }, {
333
+ readonly key: "origin";
334
+ readonly why: "喂姿态谓词 askSurvivesPosture 的安全位(CC-203),按开集读(非空串原文字节、词表不校);权威窄读在卡口与过境口 toolPermissionRequestAskBits,座位门不设第二判官(为一个位拒掉整条 request = 把安全 ask 丢给引擎 TTL deny)。坏形的两种失效方向:非串 / 空串读成缺席 ⇒ 宿主报了没命中时姿态谓词可答可以放(放宽方向:真出身若是必问词就丢了);表外串读成不认识的词 ⇒ 答 unknown,宿主退回自己的规则(不放宽)。不经过境口自铸座位请求的宿主要自己按同一把窄读。";
335
+ }, {
336
+ readonly key: "ruleOffersAbsence";
337
+ readonly why: "喂强制判据口 approvalIsMandated 的位(CC-203;寄存行那条腿的强制只以缺席词 mandated 表达),按开集读(非空串原文字节、词表不校);权威窄读在卡口与过境口,座位门不设第二判官,理由同 mandated。坏形读成缺席 ⇒ 这一路不判强制(放宽方向:寄存行上的强制就丢了;另两路 mandated / mandate 照判);表外串只是不等于 mandated。不经过境口自铸座位请求的宿主要自己按同一把窄读。";
321
338
  }];
322
339
  };
323
340
  readonly LocalSessionEvent: {
@@ -489,6 +489,10 @@ export const TOOL_PERMISSION_REQUEST_KEYS = [
489
489
  "createdAtMs",
490
490
  "governanceForced",
491
491
  "delegation",
492
+ "mandated",
493
+ "mandate",
494
+ "origin",
495
+ "ruleOffersAbsence",
492
496
  ];
493
497
  export const LOCAL_SESSION_RECORD_KEYS = [
494
498
  "sessionId",
@@ -562,6 +566,22 @@ export const SEAT_VALIDATOR_KEY_COVERAGE = {
562
566
  key: "delegation",
563
567
  why: "展示增强位,权威结构门在 hitl wire 卡口(结构读+仅子代 ask 在场,坏形降缺席);座位门同款理由不设第二判官。消费端坏形降缺席,绝不拒行。",
564
568
  },
569
+ {
570
+ key: "mandated",
571
+ why: "展示增强位(CC-203),权威窄读在卡口与过境口 toolPermissionRequestAskBits(严格 true 才上座);座位门再裁一次会为一个徽标键拒掉整条 request、把安全 ask 丢给引擎 TTL deny。消费端经 approvalIsMandated 读,其余一律当缺席。",
572
+ },
573
+ {
574
+ key: "mandate",
575
+ why: "展示增强位(CC-203),权威窄读在卡口与过境口(闭集六词精确字节,表外不上座);座位门不设第二判官,理由同 mandated。消费端经 readApprovalMandate 读,表外值读成缺席。",
576
+ },
577
+ {
578
+ key: "origin",
579
+ why: "喂姿态谓词 askSurvivesPosture 的安全位(CC-203),按开集读(非空串原文字节、词表不校);权威窄读在卡口与过境口 toolPermissionRequestAskBits,座位门不设第二判官(为一个位拒掉整条 request = 把安全 ask 丢给引擎 TTL deny)。坏形的两种失效方向:非串 / 空串读成缺席 ⇒ 宿主报了没命中时姿态谓词可答可以放(放宽方向:真出身若是必问词就丢了);表外串读成不认识的词 ⇒ 答 unknown,宿主退回自己的规则(不放宽)。不经过境口自铸座位请求的宿主要自己按同一把窄读。",
580
+ },
581
+ {
582
+ key: "ruleOffersAbsence",
583
+ why: "喂强制判据口 approvalIsMandated 的位(CC-203;寄存行那条腿的强制只以缺席词 mandated 表达),按开集读(非空串原文字节、词表不校);权威窄读在卡口与过境口,座位门不设第二判官,理由同 mandated。坏形读成缺席 ⇒ 这一路不判强制(放宽方向:寄存行上的强制就丢了;另两路 mandated / mandate 照判);表外串只是不等于 mandated。不经过境口自铸座位请求的宿主要自己按同一把窄读。",
584
+ },
565
585
  ],
566
586
  },
567
587
  LocalSessionEvent: {
@@ -1,9 +1,30 @@
1
1
  const OPEN_TAG_SOURCE = '<system-reminder(?: mark="[A-Za-z0-9_-]{22}")?>';
2
- const WRAPPED_RE = new RegExp(`^\\s*${OPEN_TAG_SOURCE}\\s*\\n?([\\s\\S]*?)\\n?\\s*<\\/system-reminder>\\s*$`);
2
+ const CLOSE_TAG = '</system-reminder>';
3
+ const OPEN_TAG_AT_START_RE = new RegExp(`^${OPEN_TAG_SOURCE}`);
3
4
  export function unwrapSystemReminder(text) {
4
- const m = WRAPPED_RE.exec(text);
5
- return m ? m[1] : text;
5
+ const head = text.trimStart();
6
+ const open = OPEN_TAG_AT_START_RE.exec(head);
7
+ if (open === null)
8
+ return text;
9
+ const whole = head.trimEnd();
10
+ if (!whole.endsWith(CLOSE_TAG) || whole.length - CLOSE_TAG.length < open[0].length)
11
+ return text;
12
+ return whole.slice(open[0].length, whole.length - CLOSE_TAG.length).trim();
6
13
  }
7
14
  export function stripSystemReminderBlocks(text) {
8
- return text.replace(new RegExp(`${OPEN_TAG_SOURCE}[\\s\\S]*?<\\/system-reminder>`, 'g'), '');
15
+ const openRe = new RegExp(OPEN_TAG_SOURCE, 'g');
16
+ let out = '';
17
+ let pos = 0;
18
+ for (;;) {
19
+ openRe.lastIndex = pos;
20
+ const open = openRe.exec(text);
21
+ if (open === null)
22
+ break;
23
+ const close = text.indexOf(CLOSE_TAG, open.index + open[0].length);
24
+ if (close === -1)
25
+ break;
26
+ out += text.slice(pos, open.index);
27
+ pos = close + CLOSE_TAG.length;
28
+ }
29
+ return pos === 0 ? text : out + text.slice(pos);
9
30
  }