@sema-agent/client-core 0.103.0 → 0.104.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +76 -0
- package/README.md +16 -13
- package/dist/adapter/downstream/eventToSdkMessage.js +2 -0
- package/dist/adapter/downstream/terminalToSdkResult.d.ts +0 -1
- package/dist/adapter/downstream/terminalToSdkResult.js +3 -1
- package/dist/agentSession/engineAgentRegistryRead.js +3 -0
- package/dist/coreValuePorts.d.ts +3 -1
- package/dist/coreValuePorts.js +5 -0
- package/dist/deviceRebind.d.ts +17 -4
- package/dist/deviceRebind.js +43 -14
- package/dist/engineAgentAbsence.d.ts +1 -0
- package/dist/executionPlacementWords.d.ts +3 -0
- package/dist/executionPlacementWords.js +2 -0
- package/dist/generated/engineFactTables.d.ts +3 -2
- package/dist/generated/engineFactTables.js +17 -1
- package/dist/generated/engineNoticeTables.js +12 -0
- package/dist/hitl/automaticAnswerVerdict.js +47 -11
- package/dist/hitl/sessionPolicyDeliverable.js +31 -5
- package/dist/hitl/toolApprovalWire.d.ts +3 -2
- package/dist/hitl/toolApprovalWire.js +162 -15
- package/dist/host.d.ts +0 -3
- package/dist/hostTurnSettings.js +3 -1
- package/dist/humanTurn.js +2 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +1 -0
- package/dist/jsonFaithful.js +5 -3
- package/dist/mcpLiveness.d.ts +1 -1
- package/dist/mcpWireCaps.js +1 -1
- package/dist/printModelStatus.d.ts +1 -0
- package/dist/printModelStatus.js +6 -2
- package/dist/retryStatus.d.ts +8 -1
- package/dist/retryStatus.js +13 -4
- package/dist/ruleCompileVerdict.d.ts +1 -0
- package/dist/ruleCompileVerdict.js +3 -1
- package/dist/seatContract.d.ts +7 -7
- package/dist/seatContract.js +39 -10
- package/dist/sessionRowTag.d.ts +24 -0
- package/dist/sessionRowTag.js +77 -0
- package/dist/systemReminderTag.js +14 -4
- package/docs/INTEGRATION-CLIENTS.md +252 -11
- package/package.json +5 -5
package/dist/seatContract.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
+
}
|
|
@@ -39,11 +39,21 @@ export function stripSystemReminderBlocks(text) {
|
|
|
39
39
|
}
|
|
40
40
|
return pos === 0 ? text : out + text.slice(pos);
|
|
41
41
|
}
|
|
42
|
-
const
|
|
43
|
-
const
|
|
42
|
+
const ENGINE_DEFUSAL_ENTITY = /&(lt|gt);/g;
|
|
43
|
+
const ENVELOPE_TAG_TOKEN = /<[^A-Za-z0-9>]{0,12}(?:system-reminder|task-notification)(?:[^>]{0,200}>)?/gi;
|
|
44
|
+
const CUT_TAG_HEAD = /<[^A-Za-z0-9>]{0,12}([A-Za-z-]{1,16})…/g;
|
|
45
|
+
const ENVELOPE_TAG_HEAD_BRACKET = /<(?=[^A-Za-z0-9>]{0,12}(?:system-reminder|task-notification|([A-Za-z-]{1,16})…))/gi;
|
|
46
|
+
const ENVELOPE_TAG_NAMES = Object.freeze(['system-reminder', 'task-notification']);
|
|
47
|
+
function isCutTagName(head) {
|
|
48
|
+
if (head === undefined || head === '')
|
|
49
|
+
return false;
|
|
50
|
+
const h = head.toLowerCase();
|
|
51
|
+
return ENVELOPE_TAG_NAMES.some((name) => name.length > h.length && name.startsWith(h));
|
|
52
|
+
}
|
|
44
53
|
export function stripSystemReminderTagTokens(text) {
|
|
45
54
|
if (typeof text !== 'string')
|
|
46
55
|
return text;
|
|
47
|
-
const
|
|
48
|
-
|
|
56
|
+
const restored = text.replace(ENGINE_DEFUSAL_ENTITY, (_entity, name) => (name === 'lt' ? '<' : '>'));
|
|
57
|
+
const once = restored.replace(ENVELOPE_TAG_TOKEN, ' ').replace(CUT_TAG_HEAD, (head, name) => (isCutTagName(name) ? ' …' : head));
|
|
58
|
+
return once.replace(ENVELOPE_TAG_HEAD_BRACKET, (bracket, cutName) => (cutName === undefined || isCutTagName(cutName) ? ' ' : bracket));
|
|
49
59
|
}
|