@sema-agent/client-core 0.83.3 → 0.83.5
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 +141 -0
- package/README.md +19 -17
- package/dist/adapter/activeRunSelfHeal.d.ts +9 -1
- package/dist/adapter/activeRunSelfHeal.js +75 -16
- package/dist/compensations.js +1 -1
- package/dist/displayUntrusted.d.ts +13 -0
- package/dist/displayUntrusted.js +751 -125
- package/dist/hitl/armedGateRegistry.js +3 -5
- package/dist/hitl/gateIdentity.d.ts +1 -0
- package/dist/hitl/gateIdentity.js +8 -0
- package/dist/hitl/planReviewWire.d.ts +7 -0
- package/dist/hitl/planReviewWire.js +345 -76
- package/dist/hitl/toolApprovalWire.d.ts +2 -0
- package/dist/hitl/toolApprovalWire.js +23 -4
- package/dist/hooksWireCaps.js +78 -8
- package/dist/host.d.ts +2 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -2
- package/dist/pluginHooksWire.d.ts +9 -0
- package/dist/pluginHooksWire.js +119 -0
- package/dist/request/taskRequest.js +1 -1
- package/dist/seatContract.d.ts +18 -1
- package/dist/seatContract.js +20 -0
- package/dist/systemReminderTag.js +25 -4
- package/docs/INTEGRATION-CLIENTS.md +413 -68
- package/package.json +2 -2
package/dist/hooksWireCaps.js
CHANGED
|
@@ -2,7 +2,7 @@ import { engineCapNestedTrue, engineCapState } from './engineCapsCache.js';
|
|
|
2
2
|
import { displayUntrusted } from './displayUntrusted.js';
|
|
3
3
|
import { collapseLabel } from './fleetTaskDesc.js';
|
|
4
4
|
import { hostLog, hostSettings } from './host.js';
|
|
5
|
-
import { decidePluginHooks, filterSettingsHookGroups, occupancyOf, settingsHookDropText, } from './pluginHooksWire.js';
|
|
5
|
+
import { decidePluginHooks, dedupeSettingsHookGroups, filterSettingsHookGroups, occupancyOf, settingsHookDropText, } from './pluginHooksWire.js';
|
|
6
6
|
import { MAX_HOOK_NOTICE_TEXT_CHARS } from './notifications.js';
|
|
7
7
|
import { goalStopHookMatcher, __resetGoalStopHookForTests } from './goalStopHook.js';
|
|
8
8
|
export { GOAL_STOP_HOOK_WIRE_ENV, CC_STOP_SEMANTICS_MIN_SERVER, ccStopSemanticsFromVersion, engineCcStopSemantics, isGoalStopHookWireArmed, setWireSessionStopHook, getWireSessionStopHook, getWireSessionStopHookCcSemantics, buildGoalStopHookPrompt, } from './goalStopHook.js';
|
|
@@ -37,6 +37,40 @@ function nonManagedDisableAllHooks(settings) {
|
|
|
37
37
|
}
|
|
38
38
|
return false;
|
|
39
39
|
}
|
|
40
|
+
const HOOKS_STARTUP_MODES = ['safe', 'bare'];
|
|
41
|
+
const _startupModesPin = [true, true];
|
|
42
|
+
void _startupModesPin;
|
|
43
|
+
function describeStartupModeValue(v) {
|
|
44
|
+
if (v === null)
|
|
45
|
+
return 'null';
|
|
46
|
+
if (typeof v === 'string')
|
|
47
|
+
return JSON.stringify(v.length > 40 ? `${v.slice(0, 40)}…` : v);
|
|
48
|
+
return typeof v;
|
|
49
|
+
}
|
|
50
|
+
function startupModeWord(settings) {
|
|
51
|
+
let v;
|
|
52
|
+
try {
|
|
53
|
+
const member = settings.hooksStartupMode;
|
|
54
|
+
if (member === undefined)
|
|
55
|
+
return undefined;
|
|
56
|
+
if (typeof member !== 'function') {
|
|
57
|
+
hostLog('debug', `hooksWireCaps: hooksStartupMode is ${describeStartupModeValue(member)}, not a method — treated as neither safe nor bare mode (settings hooks not narrowed)`);
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
v = member.call(settings);
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
hostLog('debug', `hooksWireCaps: hooksStartupMode() threw — treated as neither safe nor bare mode (settings hooks not narrowed): ${String(err)}`);
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
if (v === undefined)
|
|
67
|
+
return undefined;
|
|
68
|
+
const word = HOOKS_STARTUP_MODES.find((w) => w === v);
|
|
69
|
+
if (word !== undefined)
|
|
70
|
+
return word;
|
|
71
|
+
hostLog('debug', `hooksWireCaps: hooksStartupMode() returned ${describeStartupModeValue(v)}, which is neither 'safe' nor 'bare' — treated as neither mode (settings hooks not narrowed)`);
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
40
74
|
function hooksOfSource(source) {
|
|
41
75
|
try {
|
|
42
76
|
const s = hostSettings()?.getSettingsForSource(source);
|
|
@@ -203,8 +237,27 @@ function buildHooksWirePlan(engine, shellEvents, withPlugins) {
|
|
|
203
237
|
hostLog('debug', 'hooksWireCaps: disableAllHooks (merged, non-managed) — only policySettings hooks projected to the engine');
|
|
204
238
|
}
|
|
205
239
|
const managedOnly = managedByPolicy || disabledByNonManaged;
|
|
206
|
-
const
|
|
240
|
+
const read = withPlugins ? readPluginHooks(settings, true) : undefined;
|
|
241
|
+
let oldStartupBit;
|
|
242
|
+
let pluginDataThrew = false;
|
|
243
|
+
if (read?.reading !== undefined) {
|
|
244
|
+
const reading = read.reading;
|
|
245
|
+
const bit = guardPluginTraversal(() => reading.suppressedByStartupMode === true);
|
|
246
|
+
if (bit === undefined)
|
|
247
|
+
pluginDataThrew = true;
|
|
248
|
+
else
|
|
249
|
+
oldStartupBit = bit;
|
|
250
|
+
}
|
|
251
|
+
const startupMode = startupModeWord(settings) ?? (oldStartupBit === true ? 'safe' : undefined);
|
|
252
|
+
if (startupMode === 'bare') {
|
|
253
|
+
hostLog('debug', 'hooksWireCaps: bare mode reported by the host — only policySettings hooks projected to the engine (same as safe mode)');
|
|
254
|
+
}
|
|
255
|
+
else if (startupMode === 'safe') {
|
|
256
|
+
hostLog('debug', 'hooksWireCaps: safe mode reported by the host — only policySettings hooks projected to the engine');
|
|
257
|
+
}
|
|
258
|
+
const sources = managedOnly || startupMode !== undefined ? ['policySettings'] : ALL_HOOK_SOURCES;
|
|
207
259
|
const merged = {};
|
|
260
|
+
const sourced = new Map();
|
|
208
261
|
for (const source of sources) {
|
|
209
262
|
const hooks = hooksOfSource(source);
|
|
210
263
|
if (!hooks)
|
|
@@ -218,8 +271,20 @@ function buildHooksWirePlan(engine, shellEvents, withPlugins) {
|
|
|
218
271
|
if (kept.groups.length === 0)
|
|
219
272
|
continue;
|
|
220
273
|
merged[event] = [...(merged[event] ?? []), ...kept.groups];
|
|
274
|
+
const tagged = sourced.get(event) ?? [];
|
|
275
|
+
for (const group of kept.groups)
|
|
276
|
+
tagged.push({ source, group });
|
|
277
|
+
sourced.set(event, tagged);
|
|
221
278
|
}
|
|
222
279
|
}
|
|
280
|
+
let settingsDeduped = 0;
|
|
281
|
+
for (const [event, groups] of sourced) {
|
|
282
|
+
const d = dedupeSettingsHookGroups(groups);
|
|
283
|
+
if (d.removed === 0)
|
|
284
|
+
continue;
|
|
285
|
+
merged[event] = d.groups;
|
|
286
|
+
settingsDeduped += d.removed;
|
|
287
|
+
}
|
|
223
288
|
const goalMatcher = goalStopHookMatcher(managedOnly);
|
|
224
289
|
if (goalMatcher !== null) {
|
|
225
290
|
merged['Stop'] = [...(merged['Stop'] ?? []), goalMatcher];
|
|
@@ -228,6 +293,10 @@ function buildHooksWirePlan(engine, shellEvents, withPlugins) {
|
|
|
228
293
|
hostLog('debug', `hooksWireCaps: ${settingsDropped.length} settings hook entr${settingsDropped.length === 1 ? 'y' : 'ies'} not sent to the engine ` +
|
|
229
294
|
`(${[...new Set(settingsDropped.map((d) => d.reason))].join(', ')}) — see hooksWirePlan().settingsDropped`);
|
|
230
295
|
}
|
|
296
|
+
if (settingsDeduped > 0) {
|
|
297
|
+
hostLog('debug', `hooksWireCaps: ${settingsDeduped} settings hook entr${settingsDeduped === 1 ? 'y appears' : 'ies appear'} more than once ` +
|
|
298
|
+
'(same event, matcher group and hook) — sent once, at the first position, with the fields of the highest-precedence source');
|
|
299
|
+
}
|
|
231
300
|
if (settingsStripped > 0) {
|
|
232
301
|
hostLog('debug', `hooksWireCaps: ${settingsStripped} settings hook entr${settingsStripped === 1 ? 'y' : 'ies'} carried a plugin key — ` +
|
|
233
302
|
'stripped before sending (plugin context is only minted for entries from the plugin reader)');
|
|
@@ -250,18 +319,19 @@ function buildHooksWirePlan(engine, shellEvents, withPlugins) {
|
|
|
250
319
|
}
|
|
251
320
|
return planOf('absent', wireOf());
|
|
252
321
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
322
|
+
if (read?.reading === undefined)
|
|
323
|
+
return planOf(read?.state ?? 'absent', wireOf());
|
|
324
|
+
if (pluginDataThrew)
|
|
325
|
+
return planOf('failed', wireOf());
|
|
256
326
|
const reading = read.reading;
|
|
257
327
|
const occupied = occupancyOf(merged);
|
|
258
328
|
const d = guardPluginTraversal(() => {
|
|
259
329
|
const plugins = reading.plugins;
|
|
260
|
-
const
|
|
261
|
-
const managedOnlyPlugins = !
|
|
330
|
+
const startup = startupMode !== undefined;
|
|
331
|
+
const managedOnlyPlugins = !startup && plugins.length > 0 && (policy?.allowManagedHooksOnly === true || mergedDisable());
|
|
262
332
|
return decidePluginHooks({
|
|
263
333
|
plugins,
|
|
264
|
-
exclusion:
|
|
334
|
+
exclusion: startup ? 'startup_mode' : undefined,
|
|
265
335
|
managedOnly: managedOnlyPlugins,
|
|
266
336
|
engine,
|
|
267
337
|
shellHookEvents: shellEvents,
|
package/dist/host.d.ts
CHANGED
|
@@ -7,7 +7,9 @@ export interface SettingsPort {
|
|
|
7
7
|
shouldSkipHookDueToTrust(hook?: unknown): boolean;
|
|
8
8
|
mergedDisableAllHooks?(): boolean | undefined;
|
|
9
9
|
enabledPluginHooks?(): PluginHooksReading;
|
|
10
|
+
hooksStartupMode?(): HooksStartupMode | undefined;
|
|
10
11
|
}
|
|
12
|
+
export type HooksStartupMode = 'safe' | 'bare';
|
|
11
13
|
export type PluginHookOptionValue = string | number | boolean;
|
|
12
14
|
export interface PluginHookSensitiveOption {
|
|
13
15
|
readonly sensitive: true;
|
package/dist/index.d.ts
CHANGED
|
@@ -57,8 +57,8 @@ export * from './modelCapabilityProbe.js';
|
|
|
57
57
|
export * from './classifierStatus.js';
|
|
58
58
|
export * from './engineToolLabelStore.js';
|
|
59
59
|
export * from './fleetTaskDesc.js';
|
|
60
|
-
export { displayUntrusted } from './displayUntrusted.js';
|
|
61
|
-
export type { DisplayUntrustedOptions, DisplayMark } from './displayUntrusted.js';
|
|
60
|
+
export { displayUntrusted, displayUntrustedMarks } from './displayUntrusted.js';
|
|
61
|
+
export type { DisplayUntrustedOptions, DisplayMark, DisplayMarkSpan, DisplayUntrustedMarked } from './displayUntrusted.js';
|
|
62
62
|
export type * from './types/engineState.js';
|
|
63
63
|
export * from './hostEnv.js';
|
|
64
64
|
export * from './unrefTimer.js';
|
|
@@ -66,6 +66,7 @@ export * from './controlRouter.js';
|
|
|
66
66
|
export * from './sseIdleTriage.js';
|
|
67
67
|
export * from './engineWireSdk.js';
|
|
68
68
|
export * from './classifierVerdictWire.js';
|
|
69
|
+
export { stripSystemReminderBlocks, unwrapSystemReminder } from './systemReminderTag.js';
|
|
69
70
|
export * from './engineErrorCodes.js';
|
|
70
71
|
export * from './agentsWireCaps.js';
|
|
71
72
|
export * from './attachmentsWireCaps.js';
|
|
@@ -154,7 +155,7 @@ export * from './hitl/editedRuleTextPrecheck.js';
|
|
|
154
155
|
export * from './coreValuePorts.js';
|
|
155
156
|
export * from './hitl/askGateWire.js';
|
|
156
157
|
export * from './hitl/planReviewWire.js';
|
|
157
|
-
export
|
|
158
|
+
export { PLAN_REVIEW_QUESTION_ID_PREFIX, REOPEN_ID_TAIL, approvalCallKey, askGateQuestionId, planReviewQuestionId, liveFrameCallKey, armedKeyFromQuestionId, } from './hitl/gateIdentity.js';
|
|
158
159
|
export * from './hitl/armedGateRegistry.js';
|
|
159
160
|
export * from './hitl/parkOwnership.js';
|
|
160
161
|
export * from './hitl/parkRowBirthWait.js';
|
package/dist/index.js
CHANGED
|
@@ -57,13 +57,14 @@ export * from './modelCapabilityProbe.js';
|
|
|
57
57
|
export * from './classifierStatus.js';
|
|
58
58
|
export * from './engineToolLabelStore.js';
|
|
59
59
|
export * from './fleetTaskDesc.js';
|
|
60
|
-
export { displayUntrusted } from './displayUntrusted.js';
|
|
60
|
+
export { displayUntrusted, displayUntrustedMarks } from './displayUntrusted.js';
|
|
61
61
|
export * from './hostEnv.js';
|
|
62
62
|
export * from './unrefTimer.js';
|
|
63
63
|
export * from './controlRouter.js';
|
|
64
64
|
export * from './sseIdleTriage.js';
|
|
65
65
|
export * from './engineWireSdk.js';
|
|
66
66
|
export * from './classifierVerdictWire.js';
|
|
67
|
+
export { stripSystemReminderBlocks, unwrapSystemReminder } from './systemReminderTag.js';
|
|
67
68
|
export * from './engineErrorCodes.js';
|
|
68
69
|
export * from './agentsWireCaps.js';
|
|
69
70
|
export * from './attachmentsWireCaps.js';
|
|
@@ -151,7 +152,7 @@ export * from './hitl/editedRuleTextPrecheck.js';
|
|
|
151
152
|
export * from './coreValuePorts.js';
|
|
152
153
|
export * from './hitl/askGateWire.js';
|
|
153
154
|
export * from './hitl/planReviewWire.js';
|
|
154
|
-
export
|
|
155
|
+
export { PLAN_REVIEW_QUESTION_ID_PREFIX, REOPEN_ID_TAIL, approvalCallKey, askGateQuestionId, planReviewQuestionId, liveFrameCallKey, armedKeyFromQuestionId, } from './hitl/gateIdentity.js';
|
|
155
156
|
export * from './hitl/armedGateRegistry.js';
|
|
156
157
|
export * from './hitl/parkOwnership.js';
|
|
157
158
|
export * from './hitl/parkRowBirthWait.js';
|
|
@@ -84,6 +84,15 @@ export interface SettingsHookFilterResult {
|
|
|
84
84
|
readonly drops: SettingsHookDrop[];
|
|
85
85
|
readonly stripped: number;
|
|
86
86
|
}
|
|
87
|
+
export interface SourcedSettingsHookGroup {
|
|
88
|
+
readonly source: HookSettingsSource;
|
|
89
|
+
readonly group: WireHookGroup;
|
|
90
|
+
}
|
|
91
|
+
export declare function dedupeSettingsHookGroups(groups: readonly SourcedSettingsHookGroup[]): SettingsHookDedupResult;
|
|
92
|
+
export interface SettingsHookDedupResult {
|
|
93
|
+
readonly groups: WireHookGroup[];
|
|
94
|
+
readonly removed: number;
|
|
95
|
+
}
|
|
87
96
|
export declare function pluginHookVerdictOf(plan: HooksWirePlan, pluginId: string, event: string, groupIndex: number, hookIndex: number): PluginHookVerdict | undefined;
|
|
88
97
|
export declare function pluginHookReasonText(reason: PluginHookReason): string;
|
|
89
98
|
export declare function settingsHookDropText(reason: SettingsHookDropReason): string;
|
package/dist/pluginHooksWire.js
CHANGED
|
@@ -305,6 +305,125 @@ export function filterSettingsHookGroups(source, event, groups, execFormCapable,
|
|
|
305
305
|
});
|
|
306
306
|
return { groups: out, drops, stripped };
|
|
307
307
|
}
|
|
308
|
+
function settingsHookEntryKey(entry) {
|
|
309
|
+
if (!isRecord(entry) || typeof entry.type !== 'string')
|
|
310
|
+
return undefined;
|
|
311
|
+
const cond = entry.if;
|
|
312
|
+
if (cond !== undefined && typeof cond !== 'string')
|
|
313
|
+
return undefined;
|
|
314
|
+
const ifKey = cond ?? '';
|
|
315
|
+
const str = (v) => typeof v === 'string';
|
|
316
|
+
const shell = entry.shell;
|
|
317
|
+
try {
|
|
318
|
+
switch (entry.type) {
|
|
319
|
+
case 'command':
|
|
320
|
+
if (!str(entry.command) || (shell !== undefined && !str(shell)))
|
|
321
|
+
return undefined;
|
|
322
|
+
return JSON.stringify(['command', shell ?? null, entry.command, entry.args ?? null, ifKey]);
|
|
323
|
+
case 'prompt':
|
|
324
|
+
case 'agent':
|
|
325
|
+
return str(entry.prompt) ? JSON.stringify([entry.type, entry.prompt, ifKey]) : undefined;
|
|
326
|
+
case 'http':
|
|
327
|
+
return str(entry.url) ? JSON.stringify(['http', entry.url, ifKey]) : undefined;
|
|
328
|
+
case 'mcp_tool':
|
|
329
|
+
return str(entry.server) && str(entry.tool)
|
|
330
|
+
? JSON.stringify(['mcp_tool', entry.server, entry.tool, entry.input ?? {}, ifKey])
|
|
331
|
+
: undefined;
|
|
332
|
+
case 'script': {
|
|
333
|
+
const script = entry.script;
|
|
334
|
+
const file = entry.file;
|
|
335
|
+
if ((script !== undefined && !str(script)) || (file !== undefined && !str(file)) || (script === undefined && file === undefined))
|
|
336
|
+
return undefined;
|
|
337
|
+
return JSON.stringify(['script', script ?? null, file ?? null, ifKey]);
|
|
338
|
+
}
|
|
339
|
+
default:
|
|
340
|
+
return undefined;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
catch {
|
|
344
|
+
return undefined;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function ccMergeRank(source) {
|
|
348
|
+
switch (source) {
|
|
349
|
+
case 'userSettings':
|
|
350
|
+
return 0;
|
|
351
|
+
case 'projectSettings':
|
|
352
|
+
return 1;
|
|
353
|
+
case 'localSettings':
|
|
354
|
+
return 2;
|
|
355
|
+
case 'flagSettings':
|
|
356
|
+
return 3;
|
|
357
|
+
case 'policySettings':
|
|
358
|
+
return 4;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
export function dedupeSettingsHookGroups(groups) {
|
|
362
|
+
const keysOf = [];
|
|
363
|
+
const winner = new Map();
|
|
364
|
+
for (const { source, group } of groups) {
|
|
365
|
+
if (!isRecord(group) || !Array.isArray(group.hooks)) {
|
|
366
|
+
keysOf.push([]);
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
let groupKey;
|
|
370
|
+
try {
|
|
371
|
+
const { hooks: _hooks, ...rest } = group;
|
|
372
|
+
void _hooks;
|
|
373
|
+
groupKey = JSON.stringify(rest);
|
|
374
|
+
}
|
|
375
|
+
catch {
|
|
376
|
+
groupKey = undefined;
|
|
377
|
+
}
|
|
378
|
+
const rank = ccMergeRank(source);
|
|
379
|
+
const keys = [];
|
|
380
|
+
for (const entry of group.hooks) {
|
|
381
|
+
const entryKey = groupKey === undefined ? undefined : settingsHookEntryKey(entry);
|
|
382
|
+
const key = entryKey === undefined ? undefined : `${groupKey}\u0000${entryKey}`;
|
|
383
|
+
keys.push(key);
|
|
384
|
+
if (key === undefined)
|
|
385
|
+
continue;
|
|
386
|
+
const prev = winner.get(key);
|
|
387
|
+
if (prev === undefined || rank >= prev.rank)
|
|
388
|
+
winner.set(key, { entry, rank });
|
|
389
|
+
}
|
|
390
|
+
keysOf.push(keys);
|
|
391
|
+
}
|
|
392
|
+
const placed = new Set();
|
|
393
|
+
const out = [];
|
|
394
|
+
let removed = 0;
|
|
395
|
+
groups.forEach(({ group }, gi) => {
|
|
396
|
+
if (!isRecord(group) || !Array.isArray(group.hooks)) {
|
|
397
|
+
out.push(group);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
const keys = keysOf[gi] ?? [];
|
|
401
|
+
const kept = [];
|
|
402
|
+
let changed = false;
|
|
403
|
+
group.hooks.forEach((entry, hi) => {
|
|
404
|
+
const key = keys[hi];
|
|
405
|
+
if (key === undefined) {
|
|
406
|
+
kept.push(entry);
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
if (placed.has(key)) {
|
|
410
|
+
removed++;
|
|
411
|
+
changed = true;
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
placed.add(key);
|
|
415
|
+
const win = winner.get(key)?.entry ?? entry;
|
|
416
|
+
if (win !== entry)
|
|
417
|
+
changed = true;
|
|
418
|
+
kept.push(win);
|
|
419
|
+
});
|
|
420
|
+
if (!changed)
|
|
421
|
+
out.push(group);
|
|
422
|
+
else if (kept.length > 0)
|
|
423
|
+
out.push({ ...group, hooks: kept });
|
|
424
|
+
});
|
|
425
|
+
return { groups: out, removed };
|
|
426
|
+
}
|
|
308
427
|
export function pluginHookVerdictOf(plan, pluginId, event, groupIndex, hookIndex) {
|
|
309
428
|
return plan.verdicts.find((v) => v.pluginId === pluginId && v.event === event && v.groupIndex === groupIndex && v.hookIndex === hookIndex);
|
|
310
429
|
}
|
|
@@ -19,7 +19,7 @@ export const REQUEST_FIELD_MATRIX = [
|
|
|
19
19
|
{ field: 'council', lanes: ['interactive', 'print'], live: true, why: 'code-review council 形开关(server 读 `=== true`;非 true 不 stamp=与缺席同义,发小的那个)', whyUtilityLane: '无座:场景键;side-channel 不走场景' },
|
|
20
20
|
{ field: 'debate', lanes: ['interactive', 'print'], live: true, why: 'council 的 debate 变体开关(server 读 `=== true`)', whyUtilityLane: '无座:场景键;side-channel 不走场景' },
|
|
21
21
|
{ field: 'rounds', lanes: ['interactive', 'print'], live: true, why: 'debate 轮数(server clamp [1,3],非有限数被 server 归 undefined——本层原样透传不预 clamp)', whyUtilityLane: '无座:场景键;side-channel 不走场景' },
|
|
22
|
-
{ field: 'lenses', lanes: ['interactive', 'print'], live: true, why: 'code-review
|
|
22
|
+
{ field: 'lenses', lanes: ['interactive', 'print'], live: true, why: 'code-review 镜头数(server 场景按数字读、非数字回 400;本层型面仍按旧形声明为字符串数组 —— 已知不一致,随该族退役处理)', whyUtilityLane: '无座:场景键;side-channel 不走场景' },
|
|
23
23
|
{ field: 'memoryCapture', lanes: ['interactive', 'print', 'utility'], live: true, why: '本会话永远不进长期记忆的**声明形**(server §12.4 body 键,单成员闭集 "off");每条车道都是提交面 ⇒ 都能声明。与 memoryWrite:false 不是一个轴(那是「本 run 只读平面」)。⚠️ parked 续跑腿结构性拿不到声明(决策/续跑端点从持久 body 重建 spec),那是引擎侧事实,不是本层的漏', whyUtilityLane: '有座:side-channel 提交是壳自己的一次性问答,**绝不该**进用户的长期记忆 —— 这一位正是那句声明,且它是收紧方向,带上去只会更严' },
|
|
24
24
|
{ field: 'memory', lanes: ['interactive', 'print', 'utility'], live: true, why: '这一次运行不挂记忆面的**请求级**开关(server §12.7 body 键,单成员闭集 "off");每条车道都是提交面 ⇒ 都能声明。与 memoryCapture(采集面)不是一个轴、可并存;与 memoryWrite:false(只读平面)也不是一个轴', whyUtilityLane: '有座:side-channel 提交不需要挂记忆面(不挂记忆段 / 索引 / 三只记忆工具),省 token 且零记忆写入;与 memoryCapture 不是一个轴,两条都带' },
|
|
25
25
|
{ field: 'approverPosture', lanes: ['interactive', 'print'], live: true, why: '宿主对这个 run 的**审批姿态声明**(「这个 run 的审批卡由我自己自动答」;单成员闭集 `"bypass"`)。🔴 它不授权任何东西、也不是一只门:引擎只把这句声明**渲**给模型(它自己的标记块,首个回合),没有任何裁决腿读它 —— 与 `permissionMode` 的 `bypassPermissions`(选门的形状)**不是一回事**,两键在请求体上各自原样过境、**互不派生**:宿主完全可以一边留着缺省权限模式、一边自己自动答这个 run 的审批卡。🔴 **声明它的唯一条件是「宿主确实自己自动答这个 run 的审批卡」,不许据此推导或改动 `permissionMode`** —— 拿这一位当理由去把门放宽,是把一句只渲给模型看的话读成了授权。宿主并不自动答卡却声明了它 ⇒ 那是对模型说了一句与实际不符的话,修法是**别声明**,不是去动门。加座的理由是**一个事实一处铸点**:同样一句话,要么由引擎在它自己的标记块里说,要么由壳把提醒形文本拼进本 turn 的输入 —— 后者在模型眼里是一段**没有引擎标记**的提醒形副本,按标记声明它该当**数据**处理并报出来。所以壳从此停拼,交这一位。两条用户车道都是提交面 ⇒ 都能声明。坏词由引擎响亮拒(单成员闭集,打错字不许被折成缺席),本层同律在构造期就拒(见 `refuseBadApproverPosture`)。⚠️ 上游的**提交体**与**引擎规格**两层都真读这一键,而装着的 sdk 型面尚未镜像它 ⇒ 本层的字面按提交体的形声明,不从 sdk 取型', whyUtilityLane: '无座:审批姿态是**用户**对他自己那条会话的声明,而 side-channel 是壳自己的提交 —— 把用户的 bypass 姿态带进一条他没参与的提交,等于替他重申一句他没说过的话;况且零工具面根本产不出审批卡' },
|
package/dist/seatContract.d.ts
CHANGED
|
@@ -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: {
|
package/dist/seatContract.js
CHANGED
|
@@ -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
|
|
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
|
|
5
|
-
|
|
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
|
-
|
|
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
|
}
|