@sema-agent/client-core 0.103.0 → 0.104.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 (45) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/README.md +18 -14
  3. package/dist/adapter/downstream/eventToSdkMessage.js +2 -0
  4. package/dist/adapter/downstream/terminalToSdkResult.d.ts +0 -1
  5. package/dist/adapter/downstream/terminalToSdkResult.js +3 -1
  6. package/dist/agentSession/engineAgentRegistryRead.js +3 -0
  7. package/dist/coreValuePorts.d.ts +3 -1
  8. package/dist/coreValuePorts.js +5 -0
  9. package/dist/deviceRebind.d.ts +17 -4
  10. package/dist/deviceRebind.js +43 -14
  11. package/dist/engineAgentAbsence.d.ts +1 -0
  12. package/dist/executionPlacementWords.d.ts +3 -0
  13. package/dist/executionPlacementWords.js +2 -0
  14. package/dist/generated/engineFactTables.d.ts +3 -2
  15. package/dist/generated/engineFactTables.js +17 -1
  16. package/dist/generated/engineNoticeTables.js +12 -0
  17. package/dist/hitl/automaticAnswerVerdict.js +47 -11
  18. package/dist/hitl/livePendingAsk.d.ts +2 -1
  19. package/dist/hitl/livePendingAsk.js +7 -1
  20. package/dist/hitl/sessionPolicyDeliverable.js +31 -5
  21. package/dist/hitl/toolApprovalWire.d.ts +5 -4
  22. package/dist/hitl/toolApprovalWire.js +215 -47
  23. package/dist/host.d.ts +0 -3
  24. package/dist/hostTurnSettings.js +3 -1
  25. package/dist/humanTurn.js +2 -2
  26. package/dist/index.d.ts +5 -2
  27. package/dist/index.js +1 -0
  28. package/dist/jsonFaithful.js +5 -3
  29. package/dist/mcpLiveness.d.ts +1 -1
  30. package/dist/mcpWireCaps.js +1 -1
  31. package/dist/printModelStatus.d.ts +1 -0
  32. package/dist/printModelStatus.js +6 -2
  33. package/dist/retryStatus.d.ts +8 -1
  34. package/dist/retryStatus.js +13 -4
  35. package/dist/ruleCompileVerdict.d.ts +1 -0
  36. package/dist/ruleCompileVerdict.js +3 -1
  37. package/dist/seatContract.d.ts +7 -7
  38. package/dist/seatContract.js +39 -10
  39. package/dist/sessionRowTag.d.ts +24 -0
  40. package/dist/sessionRowTag.js +77 -0
  41. package/dist/steering.d.ts +1 -0
  42. package/dist/steering.js +34 -26
  43. package/dist/systemReminderTag.js +14 -4
  44. package/docs/INTEGRATION-CLIENTS.md +346 -11
  45. package/package.json +5 -5
package/dist/index.d.ts CHANGED
@@ -361,13 +361,16 @@ export { BACKGROUND_HANDOFF_ABORT_REASON, handoffInteractiveRun, handedOffRunsOf
361
361
  export type { BackgroundHandoffRequest, BackgroundHandoffOutcome, BackgroundRun, BackgroundRunSettlement, HandedOffRunRow, HandedOffRunsReading, } from './sessionBackgroundHandoff.js';
362
362
  export { listDevices, readSessionBinding, rebindSession, reconcileRebindOutcome, rebindProvesRequestNotApplied } from './deviceRebind.js';
363
363
  export { issueEnrollToken, enrollDevice, revokeDevice, deviceIdentityMismatchOf, deviceIdentityMismatchLine, deviceRegistrationOutcomeLine } from './deviceRebind.js';
364
- export type { DeviceSummary, DeviceListResult, DeviceBinding, DeviceSessionBinding, DeviceRebindRequest, DeviceRevokeRequest, DeviceRebindResult, DeviceEnrollToken, DeviceEnrollRequest, DeviceEnrollResult, DeviceRevokeResult, } from '@sema-agent/sdk';
365
- export type { DeviceListVerb, DeviceSessionBindingVerb, DeviceRebindVerb, DeviceRosterRow, DeviceBindingReading, DeviceRefused, DeviceLaneAbsent, DeviceTransport, DeviceUnreadable, DeviceUnarmed, DeviceListOutcome, DeviceSessionBindingOutcome, DeviceRebindInput, DeviceRebindUnconfirmed, DeviceRebindOutcome, DeviceRebindReconcileVerdict, DeviceIssueEnrollTokenVerb, DeviceEnrollVerb, DeviceRevokeVerb, DeviceEnrollOptions, DeviceRateLimited, DeviceEnrollTokenOutcome, DeviceEnrollOutcome, DeviceRevokeOutcome, DeviceRegistrationVerb, DeviceIdentityMismatch, } from './deviceRebind.js';
364
+ export type { DeviceSummary, DeviceListResult, DeviceRebindRequest, DeviceRevokeRequest, DeviceRebindResult, DeviceEnrollToken, DeviceEnrollRequest, DeviceEnrollResult, DeviceRevokeResult, } from '@sema-agent/sdk';
365
+ export type { ExecutionPlacementRole } from './executionPlacementWords.js';
366
+ export type { DeviceListVerb, SessionExecutionPlacementVerb, DeviceFaceUnsupported, DeviceRebindVerb, DeviceRosterRow, DeviceBindingReading, DeviceRefused, DeviceLaneAbsent, DeviceTransport, DeviceUnreadable, DeviceUnarmed, DeviceListOutcome, DeviceSessionBindingOutcome, DeviceRebindInput, DeviceRebindUnconfirmed, DeviceRebindOutcome, DeviceRebindReconcileVerdict, DeviceIssueEnrollTokenVerb, DeviceEnrollVerb, DeviceRevokeVerb, DeviceEnrollOptions, DeviceRateLimited, DeviceEnrollTokenOutcome, DeviceEnrollOutcome, DeviceRevokeOutcome, DeviceRegistrationVerb, DeviceIdentityMismatch, } from './deviceRebind.js';
366
367
  export { resolveEngineEnvSourcing, engineEnvView } from './engineEnvSourcing.js';
367
368
  export type { EngineEnvView, EngineEnvGroupDecl, EngineEnvSourcingInput, EngineEnvTouchedKey, EngineEnvGroupReading, EngineEnvKeyReason, EngineEnvSourcingVerdict, } from './engineEnvSourcing.js';
368
369
  export * from './compensations.js';
369
370
  export * from './request/printNotification.js';
370
371
  export * from './seatContract.js';
372
+ export { sessionRowTagOf, sessionRowPatchRefusalOf } from './sessionRowTag.js';
373
+ export type { SessionRowCurrentReading, SessionRowPatchRefusal } from './sessionRowTag.js';
371
374
  export { mcpToolTogglesToExcludeTools } from './mcpToolToggles.js';
372
375
  export type { McpToolToggleKey, McpMountedServers } from './mcpToolToggles.js';
373
376
  export { readThinkingLatch, writeThinkingLatch, thinkingLatchView, thinkingLatchSentence, thinkingLatchRowWordOf, readSeatThinkingLatch } from './thinkingLatch.js';
package/dist/index.js CHANGED
@@ -299,6 +299,7 @@ export { resolveEngineEnvSourcing, engineEnvView } from './engineEnvSourcing.js'
299
299
  export * from './compensations.js';
300
300
  export * from './request/printNotification.js';
301
301
  export * from './seatContract.js';
302
+ export { sessionRowTagOf, sessionRowPatchRefusalOf } from './sessionRowTag.js';
302
303
  export { mcpToolTogglesToExcludeTools } from './mcpToolToggles.js';
303
304
  export { readThinkingLatch, writeThinkingLatch, thinkingLatchView, thinkingLatchSentence, thinkingLatchRowWordOf, readSeatThinkingLatch } from './thinkingLatch.js';
304
305
  export { devicesFaceOf, devicesWritesAllowed, devicesFaceDetail } from './devicesFace.js';
@@ -19,14 +19,16 @@ export const isPlainRecord = (v) => {
19
19
  return true;
20
20
  if (typeof proto !== 'object')
21
21
  return false;
22
- if (!Object.prototype.hasOwnProperty.call(proto, 'constructor'))
22
+ const ctorDesc = Object.getOwnPropertyDescriptor(proto, 'constructor');
23
+ if (ctorDesc === undefined || !Object.hasOwn(ctorDesc, 'value'))
23
24
  return false;
24
- const ctor = proto.constructor;
25
+ const ctor = ctorDesc.value;
25
26
  if (typeof ctor !== 'function')
26
27
  return false;
27
28
  if (Function.prototype.toString.call(ctor) !== Function.prototype.toString.call(Object))
28
29
  return false;
29
- return ctor.prototype === proto;
30
+ const protoDesc = Object.getOwnPropertyDescriptor(ctor, 'prototype');
31
+ return protoDesc !== undefined && Object.hasOwn(protoDesc, 'value') && protoDesc.value === proto;
30
32
  };
31
33
  export const materializeJsonFaithful = (v, path, seen = new Set(), opts = {}) => {
32
34
  const guard = opts.guard;
@@ -29,7 +29,7 @@ export interface McpEngineLegHealth {
29
29
  }
30
30
  export declare function mcpLivenessRollupOf(rows: readonly McpLivenessBearingRow[] | undefined): McpEngineLegHealth;
31
31
  export declare function mcpEngineLegHealthDetail(health: McpEngineLegHealth | undefined): string;
32
- export declare const MCP_FAILURE_KINDS: readonly ["connect_refused", "connection_failed", "connection_closed", "unauthorized", "http_status", "not_mcp_response", "spawn_failed", "timeout", "protocol", "invalid_config", "credential_unavailable", "unknown"];
32
+ export declare const MCP_FAILURE_KINDS: readonly ["connect_refused", "connection_failed", "connection_closed", "unauthorized", "http_status", "not_mcp_response", "spawn_failed", "timeout", "protocol", "invalid_config", "credential_unavailable", "host_refused", "unknown"];
33
33
  export declare const MCP_AUTHORIZATION_REASONS: readonly ["rejected", "scope", "none"];
34
34
  export declare const MCP_FAILURE_UNAUTHORIZED: Extract<(typeof CORE_MCP_FAILURE_KINDS)[number], 'unauthorized'>;
35
35
  export interface McpAuthorizationView {
@@ -437,6 +437,6 @@ export function mcpAssertionHeaderNotSentNotice(specs) {
437
437
  const parts = distinct.slice(0, ASSERTION_NOTICE_NAMES_SHOWN).map((x) => `"${x}"`);
438
438
  if (distinct.length > ASSERTION_NOTICE_NAMES_SHOWN)
439
439
  parts.push(`+${distinct.length - ASSERTION_NOTICE_NAMES_SHOWN} more`);
440
- return (`MCP servers that declare an assertion header (assertionHeader): ${parts.join(', ')} — server versions up to 7.134.0 accept this setting but do not send the header, ` +
440
+ return (`MCP servers that declare an assertion header (assertionHeader): ${parts.join(', ')} — server versions up to 7.139.0 accept this setting but do not send the header, ` +
441
441
  'and this client cannot tell whether the engine it uses sends it; a connector that requires the header will refuse requests from these servers');
442
442
  }
@@ -20,6 +20,7 @@ export interface PrintModelStatusPayload {
20
20
  readonly elapsed_ms?: number;
21
21
  readonly timeout_ms?: number;
22
22
  readonly model?: string;
23
+ readonly seat?: string;
23
24
  readonly phase: RetryStatusNotice['phase'];
24
25
  readonly recourse?: NonNullable<RetryStatusNotice['recourse']>;
25
26
  readonly message: string;
@@ -41,6 +41,7 @@ function modelStatusPayload(status, ctx) {
41
41
  ...(elapsed !== undefined ? { elapsed_ms: elapsed } : {}),
42
42
  ...(ceiling !== undefined ? { timeout_ms: ceiling } : {}),
43
43
  ...(model !== undefined ? { model } : {}),
44
+ ...(typeof status.seat === 'string' ? { seat: status.seat } : {}),
44
45
  phase: notice.phase,
45
46
  ...(notice.recourse !== undefined ? { recourse: notice.recourse } : {}),
46
47
  message: notice.line,
@@ -49,6 +50,8 @@ function modelStatusPayload(status, ctx) {
49
50
  function apiRetryKeyOf(status) {
50
51
  const terminal = 'terminal' in status && status.terminal === true;
51
52
  const ok = (v) => typeof v === 'number' && Number.isInteger(v) && v >= 0;
53
+ if (status.seat !== undefined)
54
+ return null;
52
55
  if (status.kind === 'waiting' || terminal || !ok(status.attempt) || !ok(status.maxRetries))
53
56
  return null;
54
57
  return `${String(status.attempt)}/${String(status.maxRetries)}/${String(status.errClass ?? '')}/${String(status.errorStatus ?? '')}`;
@@ -126,12 +129,13 @@ export function createPrintModelStatusTracker() {
126
129
  clear();
127
130
  return null;
128
131
  }
129
- const model = readOwnDataNonEmptyString(ctx, 'model');
132
+ const model = status.seat === undefined ? readOwnDataNonEmptyString(ctx, 'model') : undefined;
130
133
  const key = apiRetryKeyOf(status);
131
134
  const refresh = key !== null && key === lastApiRetryKey;
132
135
  const safeCtx = { sessionId, nowMs, mintUuid, ...(model !== undefined ? { model } : {}) };
133
136
  const frame = statusFrameOf(status, safeCtx, mintUuid(), refresh);
134
- lastApiRetryKey = key;
137
+ if (status.seat === undefined)
138
+ lastApiRetryKey = key;
135
139
  last = { status, model };
136
140
  return frame;
137
141
  }
@@ -13,6 +13,7 @@ export type RetryStatus = {
13
13
  timeoutMs?: number;
14
14
  sleptMs?: number;
15
15
  waitSegment?: BrainStatusWaitSegment | (string & {});
16
+ seat?: BrainStatusSeat | (string & {});
16
17
  detail?: string;
17
18
  } | {
18
19
  kind: 'error';
@@ -30,6 +31,7 @@ export type RetryStatus = {
30
31
  timeoutMs?: number;
31
32
  sleptMs?: number;
32
33
  waitSegment?: BrainStatusWaitSegment | (string & {});
34
+ seat?: BrainStatusSeat | (string & {});
33
35
  } | {
34
36
  kind: 'waiting';
35
37
  deadline: number;
@@ -44,6 +46,7 @@ export type RetryStatus = {
44
46
  timeoutMs?: number;
45
47
  sleptMs?: number;
46
48
  waitSegment?: BrainStatusWaitSegment | (string & {});
49
+ seat?: BrainStatusSeat | (string & {});
47
50
  terminal?: never;
48
51
  error: RetryStatusErrorPayload;
49
52
  };
@@ -77,10 +80,13 @@ export interface BrainStatusPayload {
77
80
  providerConditionSource?: string;
78
81
  sleptMs?: number;
79
82
  waitSegment?: BrainStatusWaitSegment | (string & {});
83
+ seat?: BrainStatusSeat | (string & {});
80
84
  }
81
85
  export declare const BRAIN_STATUS_WAIT_SEGMENTS: readonly ["response_headers", "first_token"];
82
86
  export type BrainStatusWaitSegment = (typeof BRAIN_STATUS_WAIT_SEGMENTS)[number];
83
- export declare const BRAIN_STATUS_PAYLOAD_KEYS: readonly ["phase", "detail", "retryInSec", "retryInMs", "attempt", "maxRetries", "errClass", "retryAtMs", "errorStatus", "elapsedMs", "timeoutMs", "providerCondition", "providerConditionSource", "sleptMs", "waitSegment"];
87
+ export declare const BRAIN_STATUS_SEATS: readonly ["classifier"];
88
+ type BrainStatusSeat = (typeof BRAIN_STATUS_SEATS)[number];
89
+ export declare const BRAIN_STATUS_PAYLOAD_KEYS: readonly ["phase", "detail", "retryInSec", "retryInMs", "attempt", "maxRetries", "errClass", "retryAtMs", "errorStatus", "elapsedMs", "timeoutMs", "providerCondition", "providerConditionSource", "sleptMs", "waitSegment", "seat"];
84
90
  export declare function mapBrainStatusToRetry(p: BrainStatusPayload, nowMs: number): RetryStatus | null;
85
91
  export interface RetryStatusArmLike {
86
92
  readonly phase?: string;
@@ -98,6 +104,7 @@ export interface RetryStatusArmLike {
98
104
  readonly providerConditionSource?: string;
99
105
  readonly sleptMs?: number;
100
106
  readonly waitSegment?: string;
107
+ readonly seat?: string;
101
108
  readonly parentToolCallId?: string;
102
109
  }
103
110
  export declare function retryStatusFromArm(arm: RetryStatusArmLike | null | undefined, nowMs: number): RetryStatus | null | undefined;
@@ -14,6 +14,9 @@ export const BRAIN_STATUS_PHASES = [
14
14
  export const BRAIN_STATUS_WAIT_SEGMENTS = Object.freeze(['response_headers', 'first_token']);
15
15
  void true;
16
16
  void true;
17
+ export const BRAIN_STATUS_SEATS = Object.freeze(['classifier']);
18
+ void true;
19
+ void true;
17
20
  export const BRAIN_STATUS_PAYLOAD_KEYS = [
18
21
  'phase',
19
22
  'detail',
@@ -30,6 +33,7 @@ export const BRAIN_STATUS_PAYLOAD_KEYS = [
30
33
  'providerConditionSource',
31
34
  'sleptMs',
32
35
  'waitSegment',
36
+ 'seat',
33
37
  ];
34
38
  const _brainStatusKeyPin = [true, true];
35
39
  void _brainStatusKeyPin;
@@ -61,9 +65,11 @@ export function mapBrainStatusToRetry(p, nowMs) {
61
65
  };
62
66
  const sleptMs = saneMs(p.sleptMs);
63
67
  const waitSegment = p.waitSegment;
68
+ const seat = p.seat;
64
69
  const waitFacts = {
65
70
  ...(sleptMs !== undefined ? { sleptMs } : {}),
66
71
  ...(typeof waitSegment === 'string' && waitSegment.length > 0 ? { waitSegment } : {}),
72
+ ...(typeof seat === 'string' && seat.length > 0 ? { seat } : {}),
67
73
  };
68
74
  const stalledShown = typeof p.detail === 'string' && p.detail.length > 0 ? displayUntrusted(p.detail) : '';
69
75
  const stalledDetail = stalledShown.length > 0 ? { detail: stalledShown } : {};
@@ -112,8 +118,8 @@ export function retryStatusFromArm(arm, nowMs) {
112
118
  const v = str(key);
113
119
  return v !== undefined && v.length > 0 ? v : undefined;
114
120
  };
115
- const [detail, retryInSec, retryInMs, attempt, maxRetries, errClass, retryAtMs, errorStatus, elapsedMs, timeoutMs, providerCondition, providerConditionSource, sleptMs, waitSegment] = [
116
- str('detail'), num('retryInSec'), num('retryInMs'), num('attempt'), num('maxRetries'), str('errClass'), num('retryAtMs'), num('errorStatus'), num('elapsedMs'), num('timeoutMs'), word('providerCondition'), word('providerConditionSource'), num('sleptMs'), word('waitSegment'),
121
+ const [detail, retryInSec, retryInMs, attempt, maxRetries, errClass, retryAtMs, errorStatus, elapsedMs, timeoutMs, providerCondition, providerConditionSource, sleptMs, waitSegment, seat] = [
122
+ str('detail'), num('retryInSec'), num('retryInMs'), num('attempt'), num('maxRetries'), str('errClass'), num('retryAtMs'), num('errorStatus'), num('elapsedMs'), num('timeoutMs'), word('providerCondition'), word('providerConditionSource'), num('sleptMs'), word('waitSegment'), word('seat'),
117
123
  ];
118
124
  const phase = str('phase');
119
125
  if (phase === undefined)
@@ -134,6 +140,7 @@ export function retryStatusFromArm(arm, nowMs) {
134
140
  ...(providerConditionSource !== undefined ? { providerConditionSource } : {}),
135
141
  ...(sleptMs !== undefined ? { sleptMs } : {}),
136
142
  ...(waitSegment !== undefined ? { waitSegment } : {}),
143
+ ...(seat !== undefined ? { seat } : {}),
137
144
  }, nowMs);
138
145
  }
139
146
  const WAIT_SWITCH_FRACTION = 0.5;
@@ -218,7 +225,8 @@ function sealParts(p) {
218
225
  }
219
226
  export function retryStatusParts(status, ctx) {
220
227
  const modelName = washedText(noticeText(ctx, 'modelName'))?.trim();
221
- const model = modelName === undefined || modelName === '' ? undefined : modelName;
228
+ const otherSeat = status !== null && noticeText(status, 'seat') !== undefined;
229
+ const model = otherSeat || modelName === undefined || modelName === '' ? undefined : modelName;
222
230
  if (status === null)
223
231
  return sealParts({ phase: 'cleared' });
224
232
  const armWord = noticeText(status, 'kind');
@@ -243,7 +251,7 @@ export function retryStatusParts(status, ctx) {
243
251
  const condition = reading === undefined ? undefined : `${reading.clause.charAt(0).toUpperCase()}${reading.clause.slice(1)}`;
244
252
  const serviceSide = http !== undefined && isServiceSideStatus(http);
245
253
  const knownArm = armWord === 'stalled' || armWord === 'error';
246
- const recourse = !knownArm
254
+ const recourseOfArm = !knownArm
247
255
  ? undefined
248
256
  : reading !== undefined
249
257
  ? conditionRecourseOf(conditionWord)
@@ -252,6 +260,7 @@ export function retryStatusParts(status, ctx) {
252
260
  : !terminal
253
261
  ? 'wait'
254
262
  : undefined;
263
+ const recourse = otherSeat && recourseOfArm === 'switch_model' ? (terminal ? undefined : 'wait') : recourseOfArm;
255
264
  const deadlineRaw = knownArm && !terminal && noticeNumber(status, 'retryAtMs') !== undefined ? noticeNumber(status, 'deadline') : undefined;
256
265
  const retryAtMs = deadlineRaw !== undefined && deadlineRaw > 0 ? deadlineRaw : undefined;
257
266
  return sealParts({
@@ -17,6 +17,7 @@ export declare function ruleCompileVerdictUnder(rule: string, behavior: 'deny' |
17
17
  export type DenyEntryInertReason = 'grammar' | 'content_unsupported' | 'wildcard_all' | 'namespace_wildcard';
18
18
  export type DenyEntryReading = {
19
19
  readonly kind: 'row';
20
+ readonly covering?: true;
20
21
  } | {
21
22
  readonly kind: 'inert';
22
23
  readonly reason: DenyEntryInertReason;
@@ -58,7 +58,9 @@ export function denyEntryReading(rule) {
58
58
  const kind = readOwnDataValue(reading, 'kind');
59
59
  if (kind === 'row') {
60
60
  const compiled = readOwnDataValue(reading, 'compiled');
61
- return typeof compiled === 'object' && compiled !== null ? { kind: 'row' } : { kind: 'unknown' };
61
+ if (typeof compiled !== 'object' || compiled === null)
62
+ return { kind: 'unknown' };
63
+ return readOwnDataValue(compiled, 'kind') === 'covering' ? { kind: 'row', covering: true } : { kind: 'row' };
62
64
  }
63
65
  if (kind !== 'inert')
64
66
  return { kind: 'unknown' };
@@ -14,6 +14,7 @@ import type { DeviceEnrollTokenOutcome, DeviceListOutcome, DeviceRebindOutcome,
14
14
  import type { McpPanelView } from "./mcpPanel.js";
15
15
  import type { McpToolToggleKey } from "./mcpToolToggles.js";
16
16
  import { type ThinkingLatchEvent, type ThinkingLatchRowWord } from "./thinkingLatchWords.js";
17
+ import type { SessionRowPatchFields } from "./sessionRowTag.js";
17
18
  export interface SeatValidationFailure {
18
19
  reason: string;
19
20
  field?: string;
@@ -48,6 +49,7 @@ export interface LocalSessionRecord {
48
49
  }>;
49
50
  parked?: boolean;
50
51
  pinned?: boolean;
52
+ rowTag?: string;
51
53
  thinkingLatch?: ThinkingLatchRowWord;
52
54
  }
53
55
  export declare const TOOL_PERMISSION_REQUEST_ID_DOMAINS: readonly ["live:", "durable:", "question:", "elicit:", "plan:"];
@@ -301,12 +303,10 @@ export declare const ATTACHMENT_UPLOAD_REQUEST_KEYS: readonly ["name", "mimeType
301
303
  export interface SessionScopedRequest {
302
304
  sessionId: string;
303
305
  }
304
- export interface SessionRowPatchRequest {
306
+ export interface SessionRowPatchRequest extends SessionRowPatchFields {
305
307
  sessionId: string;
306
- title?: string | null;
307
- pinned?: boolean;
308
308
  }
309
- export declare const SESSION_ROW_PATCH_REQUEST_KEYS: readonly ["sessionId", "title", "pinned"];
309
+ export declare const SESSION_ROW_PATCH_REQUEST_KEYS: readonly ["sessionId", "title", "pinned", "expectedRowTag"];
310
310
  export interface SetThinkingLatchRequest {
311
311
  readonly sessionId: string;
312
312
  readonly event: ThinkingLatchEvent;
@@ -688,7 +688,7 @@ export declare const SEAT_STORE_PROPS: readonly ["sessionsStateStore", "focusedS
688
688
  export declare const SEAT_EVENT_METHOD_NAMES: readonly ["onEvent", "onToolPermissionRequest"];
689
689
  export declare const LOCAL_SESSION_EVENT_KEYS: readonly ["type", "sessionId", "message", "error", "errorCategory", "code", "session", "userMessageUuid", "requestId", "outcome", "parked", "chrome", "_sema_turnFailure", "subagentContent"];
690
690
  export declare const TOOL_PERMISSION_REQUEST_KEYS: readonly ["requestId", "sessionId", "toolName", "toolCallId", "input", "message", "toctouBinding", "createdAtMs", "governanceForced", "delegation", "mandated", "mandate", "origin", "ruleOffersAbsence", "askClass", "classes", "sessionRow"];
691
- export declare const LOCAL_SESSION_RECORD_KEYS: readonly ["sessionId", "engineSessionId", "model", "title", "cwd", "isRunning", "createdAt", "lastActivityAt", "queuedMessages", "parked", "pinned", "thinkingLatch"];
691
+ export declare const LOCAL_SESSION_RECORD_KEYS: readonly ["sessionId", "engineSessionId", "model", "title", "cwd", "isRunning", "createdAt", "lastActivityAt", "queuedMessages", "parked", "pinned", "rowTag", "thinkingLatch"];
692
692
  export declare const SEND_MESSAGE_REQUEST_KEYS: readonly ["sessionId", "text", "model", "userMessageUuid", "permissionMode", "attachmentIds", "reasoningEffort", "retainBackgroundProcesses", "forwardSubagentEvents", "retainSubagentSessions", "settings", "scenario", "rewind"];
693
693
  export declare const SEAT_SEND_MESSAGE_KEY_ORIGINS: {
694
694
  readonly sessionId: {
@@ -738,7 +738,7 @@ export declare const SEAT_VALIDATOR_KEY_COVERAGE: {
738
738
  readonly LocalSessionRecord: {
739
739
  readonly guard: "isLocalSessionRecord";
740
740
  readonly check: (v: unknown) => SeatValidation;
741
- readonly validated: readonly ["sessionId", "engineSessionId", "model", "title", "cwd", "isRunning", "createdAt", "lastActivityAt", "queuedMessages", "parked", "pinned", "thinkingLatch"];
741
+ readonly validated: readonly ["sessionId", "engineSessionId", "model", "title", "cwd", "isRunning", "createdAt", "lastActivityAt", "queuedMessages", "parked", "pinned", "rowTag", "thinkingLatch"];
742
742
  readonly unchecked: readonly [];
743
743
  };
744
744
  readonly ToolPermissionRequest: {
@@ -815,7 +815,7 @@ export declare const SEAT_VALIDATOR_KEY_COVERAGE: {
815
815
  readonly SessionRowPatchRequest: {
816
816
  readonly guard: "isSessionRowPatchRequest";
817
817
  readonly check: (v: unknown) => SeatValidation;
818
- readonly validated: readonly ["sessionId", "title", "pinned"];
818
+ readonly validated: readonly ["sessionId", "title", "pinned", "expectedRowTag"];
819
819
  readonly unchecked: readonly [];
820
820
  };
821
821
  readonly SetThinkingLatchRequest: {
@@ -6,6 +6,7 @@ import { PROBE_VERDICTS } from "./modelCapabilityProbe.js";
6
6
  import { guardedRead, ownDataField, ownDataRefusalKeyOf, ownSendableField, readOwnDataValue } from "./ownKey.js";
7
7
  import { MCP_LIVENESS_STATES } from "./mcpLiveness.js";
8
8
  import { THINKING_LATCH_EVENTS, THINKING_LATCH_ROW_WORDS } from "./thinkingLatchWords.js";
9
+ import { EXECUTION_PLACEMENT_ROLES } from "./executionPlacementWords.js";
9
10
  const bad = (reason, field) => field === undefined ? { reason } : { reason, field };
10
11
  const nested = (prefix, verdict) => {
11
12
  const path = verdict.field === undefined
@@ -122,7 +123,22 @@ function shapeVerdict(spec, v, path, disc) {
122
123
  return true;
123
124
  }
124
125
  const shapeRule = (label, spec) => ({ label, test: (v, path) => shapeVerdict(spec, v, path) });
125
- const armsRule = (label, disc, arms) => ({
126
+ function keyGroupsVerdict(groups, v, path) {
127
+ let seen;
128
+ for (const g of groups) {
129
+ const present = g.filter((k) => !isAbsent(own(v, k)));
130
+ if (present.length === 0)
131
+ continue;
132
+ const missing = g.find((k) => !present.includes(k));
133
+ if (missing !== undefined)
134
+ return bad("missing key", pathAt(path, missing));
135
+ if (seen !== undefined)
136
+ return bad(`bad value (${seen.join("+")} | ${g.join("+")})`, pathAt(path, g[0]));
137
+ seen = g;
138
+ }
139
+ return true;
140
+ }
141
+ const armsRule = (label, disc, arms, groups = {}) => ({
126
142
  label,
127
143
  test: (v, path) => {
128
144
  if (!isPlainRecord(v))
@@ -130,7 +146,10 @@ const armsRule = (label, disc, arms) => ({
130
146
  const d = own(v, disc);
131
147
  if (typeof d !== "string" || !Object.hasOwn(arms, d))
132
148
  return bad("unknown arm", pathAt(path, disc));
133
- return shapeVerdict(arms[d], v, path, disc);
149
+ const verdict = shapeVerdict(arms[d], v, path, disc);
150
+ if (verdict !== true || !Object.hasOwn(groups, d))
151
+ return verdict;
152
+ return keyGroupsVerdict(groups[d], v, path);
134
153
  },
135
154
  });
136
155
  const isArraySlotKey = (k, n) => k === "length" || (/^(0|[1-9][0-9]*)$/.test(k) && Number(k) < n);
@@ -559,7 +578,7 @@ export const SEAT_TURN_SETTINGS_ORIGINS = Object.freeze({
559
578
  });
560
579
  export const SEAT_REWIND_INTENT_KEYS = ["mode", "targetMessageUuid"];
561
580
  export const ATTACHMENT_UPLOAD_REQUEST_KEYS = ["name", "mimeType", "bytes"];
562
- export const SESSION_ROW_PATCH_REQUEST_KEYS = Object.freeze(["sessionId", "title", "pinned"]);
581
+ export const SESSION_ROW_PATCH_REQUEST_KEYS = Object.freeze(["sessionId", "title", "pinned", "expectedRowTag"]);
563
582
  const SET_THINKING_LATCH_REQUEST_KEYS = Object.freeze(["sessionId", "event"]);
564
583
  export const SESSION_SEARCH_RESULT_KEYS = ["sessionId", "title", "matches"];
565
584
  export const GET_TRANSCRIPT_RESULT_KEYS = ["sessionId", "messages"];
@@ -582,7 +601,6 @@ const DEVICE_ROSTER_ROW = shapeRule("device row", {
582
601
  const DEVICE_BINDING = shapeRule("device binding", {
583
602
  deviceId: reqF(R_NONEMPTY),
584
603
  rev: reqF(R_INT0),
585
- boundAt: optF(R_STR),
586
604
  });
587
605
  const checkDeviceListOutcome = seatShapeCheck(armsRule("listDevices outcome", "kind", {
588
606
  ok: { devices: reqF(arrayRule(DEVICE_ROSTER_ROW)), dropped: reqF(R_INT0) },
@@ -594,23 +612,24 @@ const checkDeviceListOutcome = seatShapeCheck(armsRule("listDevices outcome", "k
594
612
  }));
595
613
  const checkDeviceSessionBindingOutcome = seatShapeCheck(armsRule("readSessionBinding outcome", "kind", {
596
614
  ok: { rootSessionId: reqF(R_NONEMPTY), boundDevice: reqF(DEVICE_BINDING) },
615
+ unbound: { role: reqF(oneOfRule("placement role", EXECUTION_PLACEMENT_ROLES)) },
597
616
  "not-found": {},
617
+ unsupported: { reason: reqF(oneOfRule("unsupported reason", ["route", "capability"])) },
598
618
  unreadable: DEVICE_UNREADABLE_SPEC,
599
- "lane-absent": DEVICE_LANE_ABSENT_SPEC,
600
619
  refused: DEVICE_REFUSED_SPEC,
601
620
  transport: DEVICE_TRANSPORT_SPEC,
602
621
  unarmed: {},
603
622
  }));
604
623
  const checkDeviceRebindOutcome = seatShapeCheck(armsRule("rebindSession outcome", "kind", {
605
624
  ok: { deviceId: reqF(R_NONEMPTY), rev: reqF(R_INT0), workspaceCarryover: optF(oneOfRule("carryover", ["none"])) },
606
- "active-run": { activeTaskId: optF(R_STR) },
625
+ "active-run": { activeTaskId: optF(R_STR), children: optF(arrayRule(R_STR)), retirePath: optF(R_NONEMPTY) },
607
626
  "rev-conflict": { currentRev: optF(R_INT0) },
608
627
  "not-found": {},
609
628
  unconfirmed: DEVICE_UNCONFIRMED_SPEC,
610
629
  "lane-absent": DEVICE_LANE_ABSENT_SPEC,
611
630
  refused: DEVICE_REFUSED_SPEC,
612
631
  unarmed: {},
613
- }));
632
+ }, { "active-run": [["activeTaskId"], ["children", "retirePath"]] }));
614
633
  const ENROLL_TOKEN_ALLOW = new Set(["token"]);
615
634
  const checkDeviceEnrollTokenOutcome = seatShapeCheck(armsRule("issueEnrollToken outcome", "kind", {
616
635
  ok: { token: reqF(R_NONEMPTY), expiresAt: optF(R_STR), ttlSec: optF(R_INT1) },
@@ -983,6 +1002,10 @@ const checkLocalSessionRecord = seatCheck((s) => {
983
1002
  if (!isAbsent(pinned) && typeof pinned !== "boolean") {
984
1003
  return bad("pinned must be a boolean when present (absent = not pinned or unknown)", "pinned");
985
1004
  }
1005
+ const rowTag = own(s, "rowTag");
1006
+ if (!isAbsent(rowTag) && (typeof rowTag !== "string" || rowTag.length === 0)) {
1007
+ return bad("rowTag must be a non-empty string when present (absent = this row cannot be written conditionally)", "rowTag");
1008
+ }
986
1009
  const latch = own(s, "thinkingLatch");
987
1010
  if (!isAbsent(latch)) {
988
1011
  const verdict = R_THINKING_LATCH_ROW.test(latch, "thinkingLatch");
@@ -1079,6 +1102,7 @@ const RETRY_COMMON_SPEC = {
1079
1102
  timeoutMs: optF(R_NUM),
1080
1103
  sleptMs: optF(R_NUM0),
1081
1104
  waitSegment: optF(R_NONEMPTY),
1105
+ seat: optF(R_NONEMPTY),
1082
1106
  };
1083
1107
  const RETRY_STATUS = armsRule("retry status", "kind", {
1084
1108
  stalled: { ...RETRY_COMMON_SPEC, detail: optF(R_STR) },
@@ -1451,8 +1475,12 @@ export const checkSessionRowPatchRequest = seatCheck((v) => {
1451
1475
  }
1452
1476
  if (!isAbsent(pinned) && typeof pinned !== "boolean")
1453
1477
  return bad("pinned must be a boolean when present", "pinned");
1478
+ const expectedRowTag = body.expectedRowTag;
1479
+ if (!isAbsent(expectedRowTag) && (typeof expectedRowTag !== "string" || expectedRowTag.length === 0)) {
1480
+ return bad("expectedRowTag must be the non-empty rowTag read from the same session row when present", "expectedRowTag");
1481
+ }
1454
1482
  if (isAbsent(title) && isAbsent(pinned))
1455
- return bad("nothing to change: carry at least one of title / pinned");
1483
+ return bad("nothing to change: carry at least one of title / pinned (expectedRowTag is a precondition, not a change)");
1456
1484
  return true;
1457
1485
  });
1458
1486
  export function isSessionRowPatchRequest(v) {
@@ -1744,6 +1772,7 @@ export const LOCAL_SESSION_RECORD_KEYS = [
1744
1772
  "queuedMessages",
1745
1773
  "parked",
1746
1774
  "pinned",
1775
+ "rowTag",
1747
1776
  "thinkingLatch",
1748
1777
  ];
1749
1778
  export const SEND_MESSAGE_REQUEST_KEYS = [
@@ -1789,7 +1818,7 @@ export const SEAT_VALIDATOR_KEY_COVERAGE = {
1789
1818
  LocalSessionRecord: {
1790
1819
  guard: "isLocalSessionRecord",
1791
1820
  check: checkLocalSessionRecord,
1792
- validated: ["sessionId", "engineSessionId", "model", "title", "cwd", "isRunning", "createdAt", "lastActivityAt", "queuedMessages", "parked", "pinned", "thinkingLatch"],
1821
+ validated: ["sessionId", "engineSessionId", "model", "title", "cwd", "isRunning", "createdAt", "lastActivityAt", "queuedMessages", "parked", "pinned", "rowTag", "thinkingLatch"],
1793
1822
  unchecked: [],
1794
1823
  },
1795
1824
  ToolPermissionRequest: {
@@ -1910,7 +1939,7 @@ export const SEAT_VALIDATOR_KEY_COVERAGE = {
1910
1939
  SessionRowPatchRequest: {
1911
1940
  guard: "isSessionRowPatchRequest",
1912
1941
  check: checkSessionRowPatchRequest,
1913
- validated: ["sessionId", "title", "pinned"],
1942
+ validated: ["sessionId", "title", "pinned", "expectedRowTag"],
1914
1943
  unchecked: [],
1915
1944
  },
1916
1945
  SetThinkingLatchRequest: {
@@ -0,0 +1,24 @@
1
+ export interface SessionRowPatchFields {
2
+ title?: string | null;
3
+ pinned?: boolean;
4
+ expectedRowTag?: string;
5
+ }
6
+ export interface SessionRowCurrentReading {
7
+ readonly rowTag?: string;
8
+ readonly title?: string;
9
+ readonly pinned?: boolean;
10
+ }
11
+ export type SessionRowPatchRefusal = {
12
+ readonly kind: 'already-applied';
13
+ readonly current: SessionRowCurrentReading;
14
+ } | {
15
+ readonly kind: 'row-changed';
16
+ readonly current?: SessionRowCurrentReading;
17
+ } | {
18
+ readonly kind: 'mirror';
19
+ readonly forkPath?: string;
20
+ } | {
21
+ readonly kind: 'tag-unsupported';
22
+ };
23
+ export declare function sessionRowTagOf(row: object | null | undefined): string | undefined;
24
+ export declare function sessionRowPatchRefusalOf(error: {} | null | undefined, sent: Readonly<SessionRowPatchFields> | null | undefined): SessionRowPatchRefusal | null;
@@ -0,0 +1,77 @@
1
+ import { guardedRead, readOwnDataValue } from './ownKey.js';
2
+ import { isSlotCarrier, readErrorRecord, readErrorString, readThrownSlot, slotReadOf } from './wireFailureShape.js';
3
+ const ROW_CHANGED = 'conflict.session_row_changed';
4
+ const SESSION_MIRROR = 'conflict.session_mirror';
5
+ const BODY_SHAPE = 'request.body_shape';
6
+ function ownData(v, key) {
7
+ if (v === null || typeof v !== 'object')
8
+ return undefined;
9
+ return guardedRead(() => readOwnDataValue(v, key));
10
+ }
11
+ export function sessionRowTagOf(row) {
12
+ const v = ownData(row, 'rowTag');
13
+ return typeof v === 'string' && v.length > 0 ? v : undefined;
14
+ }
15
+ function currentRowOf(e) {
16
+ const row = guardedRead(() => readErrorRecord(e, 'currentRow'));
17
+ if (row === undefined)
18
+ return undefined;
19
+ const rowTag = sessionRowTagOf(row);
20
+ const title = ownData(row, 'title');
21
+ const pinned = ownData(row, 'pinned');
22
+ return Object.freeze({
23
+ ...(rowTag !== undefined ? { rowTag } : {}),
24
+ ...(typeof title === 'string' ? { title } : {}),
25
+ ...(typeof pinned === 'boolean' ? { pinned } : {}),
26
+ });
27
+ }
28
+ function pendingSlotOf(sent, key) {
29
+ if (sent === null || typeof sent !== 'object')
30
+ return { state: 'absent' };
31
+ const read = slotReadOf(sent, key);
32
+ if (read.presence === 'absent')
33
+ return { state: 'absent' };
34
+ if (read.presence !== 'present')
35
+ return { state: 'unreadable' };
36
+ return read.value === undefined ? { state: 'absent' } : { state: 'value', value: read.value };
37
+ }
38
+ function alreadyApplied(sent, current) {
39
+ const title = pendingSlotOf(sent, 'title');
40
+ const pinned = pendingSlotOf(sent, 'pinned');
41
+ if (title.state === 'unreadable' || pinned.state === 'unreadable')
42
+ return false;
43
+ let compared = 0;
44
+ if (title.state === 'value') {
45
+ if (typeof title.value !== 'string' || current.title !== title.value)
46
+ return false;
47
+ compared++;
48
+ }
49
+ if (pinned.state === 'value') {
50
+ if (typeof pinned.value !== 'boolean' || current.pinned !== pinned.value)
51
+ return false;
52
+ compared++;
53
+ }
54
+ return compared > 0;
55
+ }
56
+ export function sessionRowPatchRefusalOf(error, sent) {
57
+ if (!isSlotCarrier(error))
58
+ return null;
59
+ const status = readThrownSlot(error, 'status', 'data');
60
+ const code = readThrownSlot(error, 'errorCode', 'data');
61
+ if (status === 409 && code === ROW_CHANGED) {
62
+ const current = currentRowOf(error);
63
+ if (current !== undefined && alreadyApplied(sent, current))
64
+ return { kind: 'already-applied', current };
65
+ return { kind: 'row-changed', ...(current !== undefined ? { current } : {}) };
66
+ }
67
+ if (status === 409 && code === SESSION_MIRROR) {
68
+ const forkPath = guardedRead(() => readErrorString(error, 'forkPath'));
69
+ return { kind: 'mirror', ...(typeof forkPath === 'string' && forkPath.length > 0 ? { forkPath } : {}) };
70
+ }
71
+ if (status === 400 && code === BODY_SHAPE) {
72
+ const tag = ownData(sent, 'expectedRowTag');
73
+ if (typeof tag === 'string' && tag.length > 0)
74
+ return { kind: 'tag-unsupported' };
75
+ }
76
+ return null;
77
+ }
@@ -11,6 +11,7 @@ export interface EngineTaskStatusAttachment {
11
11
  status: 'pending' | 'running' | 'completed' | 'failed' | 'killed' | 'parked';
12
12
  description: string;
13
13
  deltaSummary: null;
14
+ read?: string;
14
15
  }
15
16
  export type SteeringRenderable = SteeringInjectedAttachment | EngineTaskStatusAttachment;
16
17
  export declare function steeringLabel(source: string): string;