@sema-agent/client-core 0.96.0 → 0.97.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.
- package/CHANGELOG.md +110 -0
- package/README.md +29 -22
- package/dist/adapter/activeRunSelfHeal.d.ts +0 -2
- package/dist/adapter/activeRunSelfHeal.js +7 -7
- package/dist/adapter/downstream/terminalToSdkResult.js +6 -2
- package/dist/agentSession/backgroundView.js +2 -15
- package/dist/agentsWireCaps.d.ts +3 -1
- package/dist/agentsWireCaps.js +67 -39
- package/dist/approvalDecidedByCapability.d.ts +2 -0
- package/dist/approvalDecidedByCapability.js +0 -1
- package/dist/approvalsStreamLiveCapability.d.ts +2 -0
- package/dist/approvalsStreamLiveCapability.js +3 -2
- package/dist/attachmentSeatCapability.d.ts +2 -0
- package/dist/attachmentSeatCapability.js +0 -1
- package/dist/callerCwdCapability.d.ts +2 -0
- package/dist/callerCwdCapability.js +0 -1
- package/dist/centerWiringCapability.d.ts +2 -0
- package/dist/centerWiringCapability.js +4 -3
- package/dist/cloudConfigWireCaps.d.ts +2 -2
- package/dist/cloudConfigWireCaps.js +192 -56
- package/dist/declared/supportFloorDeclared.js +3 -3
- package/dist/deviceExecutorManagementCapability.d.ts +2 -0
- package/dist/deviceExecutorManagementCapability.js +3 -2
- package/dist/displayUntrusted.d.ts +3 -0
- package/dist/displayUntrusted.js +15 -0
- package/dist/engineCapReader.d.ts +8 -2
- package/dist/engineCapReader.js +15 -7
- package/dist/engineCapsCache.d.ts +11 -3
- package/dist/engineCapsCache.js +54 -23
- package/dist/engineErrorCodes.d.ts +3 -0
- package/dist/engineErrorCodes.js +9 -1
- package/dist/engineNoticeCodes.js +2 -3
- package/dist/engineToolDirectory.d.ts +27 -0
- package/dist/engineToolDirectory.js +178 -0
- package/dist/executionLaneCapability.d.ts +2 -0
- package/dist/executionLaneCapability.js +3 -2
- package/dist/fileHistoryCaptureCapability.d.ts +2 -0
- package/dist/fileHistoryCaptureCapability.js +34 -8
- package/dist/fileHistoryLegNotice.js +4 -0
- package/dist/fleet/fleetLedger.js +129 -105
- package/dist/gateOutcome.js +2 -4
- package/dist/gateVocabulary.d.ts +1 -6
- package/dist/gateVocabulary.js +2 -5
- package/dist/generated/sdkFactTables.d.ts +2 -0
- package/dist/generated/sdkFactTables.js +7 -0
- package/dist/generated/toolNameTables.d.ts +0 -1
- package/dist/generated/toolNameTables.js +0 -42
- package/dist/hitl/approvalDecisionNoteAudit.js +2 -2
- package/dist/hitl/approvalEndingCause.js +2 -7
- package/dist/hitl/approvalHolds.d.ts +16 -1
- package/dist/hitl/approvalHolds.js +52 -12
- package/dist/hitl/askGateWire.js +1 -0
- package/dist/hitl/automaticAnswerVerdict.js +5 -7
- package/dist/hitl/decisionConflict.js +4 -3
- package/dist/hitl/decisionRefusal.js +19 -12
- package/dist/hitl/frameRouter.d.ts +3 -0
- package/dist/hitl/frameRouter.js +52 -25
- package/dist/hitl/gateLedger.d.ts +0 -5
- package/dist/hitl/gateLedger.js +0 -21
- package/dist/hitl/hitlBridge.js +18 -4
- package/dist/hitl/parkResolver.js +7 -3
- package/dist/hitl/replayedApproval.d.ts +2 -3
- package/dist/hitl/replayedApproval.js +7 -16
- package/dist/hitl/sessionPolicyDeliverable.d.ts +0 -1
- package/dist/hitl/sessionPolicyDeliverable.js +15 -17
- package/dist/hitl/sessionPolicyWire.d.ts +2 -0
- package/dist/hitl/sessionPolicyWire.js +15 -3
- package/dist/hitl/standingRows.d.ts +1 -0
- package/dist/hitl/standingRows.js +38 -17
- package/dist/hitl/suspendedReopen.js +2 -1
- package/dist/hitl/toolApprovalWire.d.ts +8 -8
- package/dist/hitl/toolApprovalWire.js +50 -24
- package/dist/index.d.ts +8 -4
- package/dist/index.js +5 -3
- package/dist/liveModelCatalog.d.ts +13 -0
- package/dist/liveModelCatalog.js +56 -0
- package/dist/mcpProbeCapability.d.ts +2 -0
- package/dist/mcpProbeCapability.js +3 -2
- package/dist/mcpWireCaps.d.ts +1 -0
- package/dist/mcpWireCaps.js +4 -1
- package/dist/memoryComplianceCapability.d.ts +2 -0
- package/dist/memoryComplianceCapability.js +3 -2
- package/dist/memoryOriginCapability.d.ts +2 -0
- package/dist/memoryOriginCapability.js +3 -2
- package/dist/modelIdentityResolvability.js +2 -2
- package/dist/noticePresentation.js +2 -2
- package/dist/outputStyleSeatCapability.d.ts +2 -0
- package/dist/outputStyleSeatCapability.js +0 -1
- package/dist/peerLaneCapability.d.ts +2 -0
- package/dist/peerLaneCapability.js +3 -2
- package/dist/permissionModesCapability.d.ts +2 -0
- package/dist/permissionModesCapability.js +0 -1
- package/dist/permissionRulesWriteCapability.d.ts +2 -0
- package/dist/permissionRulesWriteCapability.js +3 -2
- package/dist/permissionsAllowGrantCapability.d.ts +2 -0
- package/dist/permissionsAllowGrantCapability.js +4 -2
- package/dist/permissionsDeclaration.js +19 -21
- package/dist/request/taskRequest.d.ts +1 -4
- package/dist/request/taskRequest.js +74 -19
- package/dist/resumeRefusalCopy.js +4 -4
- package/dist/rewindArchiveCapability.js +5 -0
- package/dist/rewindArchiveReading.d.ts +2 -0
- package/dist/rewindArchiveReading.js +0 -1
- package/dist/runTerminal.d.ts +33 -4
- package/dist/runTerminal.js +170 -15
- package/dist/sdkWireTransit.d.ts +2 -1
- package/dist/sdkWireTransit.js +1 -0
- package/dist/sealedKeyCapability.d.ts +2 -0
- package/dist/sealedKeyCapability.js +0 -1
- package/dist/seatContract.d.ts +7 -4
- package/dist/seatContract.js +34 -20
- package/dist/sessionPolicyCapability.d.ts +2 -0
- package/dist/sessionPolicyCapability.js +3 -2
- package/dist/sessionReadRedactionCapability.d.ts +2 -0
- package/dist/sessionReadRedactionCapability.js +0 -1
- package/dist/sessionSyncFileHistoryCapability.d.ts +2 -0
- package/dist/sessionSyncFileHistoryCapability.js +3 -2
- package/dist/skillToolListsCapability.d.ts +2 -0
- package/dist/skillToolListsCapability.js +3 -2
- package/dist/sqlEngineCapability.d.ts +2 -0
- package/dist/sqlEngineCapability.js +4 -3
- package/dist/steerPriorityCapability.d.ts +2 -0
- package/dist/steerPriorityCapability.js +3 -2
- package/dist/storePostureCapability.d.ts +2 -0
- package/dist/storePostureCapability.js +4 -3
- package/dist/toolHistoryBadTurn.js +3 -2
- package/dist/toolResult.d.ts +11 -0
- package/dist/toolResult.js +53 -0
- package/dist/userInvokedSkillsCapability.d.ts +2 -0
- package/dist/userInvokedSkillsCapability.js +3 -2
- package/dist/webSearchBackendCapability.d.ts +2 -0
- package/dist/webSearchBackendCapability.js +3 -2
- package/dist/wireErrorTriage.js +58 -34
- package/dist/wireFailureShape.d.ts +1 -1
- package/dist/wireFailureShape.js +76 -25
- package/dist/workflowsJournalCapability.d.ts +2 -0
- package/dist/workflowsJournalCapability.js +3 -2
- package/dist/writeProtectionCapability.d.ts +2 -0
- package/dist/writeProtectionCapability.js +3 -2
- package/docs/INTEGRATION-CLIENTS.md +585 -14
- package/package.json +4 -4
- package/dist/engineRefusedToolName.d.ts +0 -1
- package/dist/engineRefusedToolName.js +0 -8
- package/dist/engineWordRenames.d.ts +0 -8
- package/dist/engineWordRenames.js +0 -13
package/dist/engineCapsCache.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { settleWithinDeadline } from './abortableSleep.js';
|
|
2
2
|
import { ownDataField } from './ownKey.js';
|
|
3
|
+
const NOT_LANDED = Object.freeze({ kind: 'not_landed' });
|
|
3
4
|
const capsByBase = new Map();
|
|
4
5
|
const inFlight = new Set();
|
|
5
6
|
const settleByBase = new Map();
|
|
@@ -13,11 +14,16 @@ export function engineCapsGeneration(baseUrl) {
|
|
|
13
14
|
return capsGeneration(baseUrl);
|
|
14
15
|
}
|
|
15
16
|
export function engineCapsLandingOf(baseUrl) {
|
|
16
|
-
|
|
17
|
+
if (typeof baseUrl !== 'string' || baseUrl === '')
|
|
18
|
+
return NOT_LANDED;
|
|
19
|
+
return capsByBase.get(baseUrl) ?? NOT_LANDED;
|
|
17
20
|
}
|
|
18
21
|
export function kickEngineCapsProbe(baseUrl, probe) {
|
|
22
|
+
startEngineCapsProbe(baseUrl, probe);
|
|
23
|
+
}
|
|
24
|
+
function startEngineCapsProbe(baseUrl, probe) {
|
|
19
25
|
if (!baseUrl || capsByBase.has(baseUrl) || inFlight.has(baseUrl))
|
|
20
|
-
return;
|
|
26
|
+
return false;
|
|
21
27
|
probeByBase.set(baseUrl, probe);
|
|
22
28
|
const gen = capsGeneration(baseUrl);
|
|
23
29
|
inFlight.add(baseUrl);
|
|
@@ -35,8 +41,7 @@ export function kickEngineCapsProbe(baseUrl, probe) {
|
|
|
35
41
|
const caps = await probe();
|
|
36
42
|
if (gen !== capsGeneration(baseUrl))
|
|
37
43
|
return;
|
|
38
|
-
|
|
39
|
-
landCapsSnapshot(baseUrl, gen, caps);
|
|
44
|
+
landCapsSnapshot(baseUrl, gen, caps);
|
|
40
45
|
}
|
|
41
46
|
catch {
|
|
42
47
|
}
|
|
@@ -49,6 +54,7 @@ export function kickEngineCapsProbe(baseUrl, probe) {
|
|
|
49
54
|
markSettled();
|
|
50
55
|
}
|
|
51
56
|
})();
|
|
57
|
+
return true;
|
|
52
58
|
}
|
|
53
59
|
const MAX_WITHIN_MS = 2_147_483_647;
|
|
54
60
|
function waitBudgetOf(opts) {
|
|
@@ -118,9 +124,7 @@ export async function engineCapsSettled(baseUrl, opts) {
|
|
|
118
124
|
}
|
|
119
125
|
}
|
|
120
126
|
export function engineCapState(baseUrl, key) {
|
|
121
|
-
|
|
122
|
-
return 'unprobed';
|
|
123
|
-
return capStateIn(capsByBase.get(baseUrl)?.caps, key);
|
|
127
|
+
return stateOfSlot(slotInLanding(engineCapsLandingOf(baseUrl), key));
|
|
124
128
|
}
|
|
125
129
|
const SNAPSHOT_MAX_DEPTH = 64;
|
|
126
130
|
const SNAPSHOT_MAX_NODES = 100_000;
|
|
@@ -156,10 +160,16 @@ function throwingAccessor() {
|
|
|
156
160
|
};
|
|
157
161
|
}
|
|
158
162
|
function landCapsSnapshot(baseUrl, gen, caps) {
|
|
163
|
+
if (!isPlainCapsBody(caps))
|
|
164
|
+
return 'not_a_record';
|
|
159
165
|
const snapshot = snapshotCapsBody(caps);
|
|
160
166
|
if (gen !== capsGeneration(baseUrl))
|
|
161
|
-
return;
|
|
162
|
-
|
|
167
|
+
return 'superseded';
|
|
168
|
+
const record = snapshot === UNREADABLE_NODE
|
|
169
|
+
? Object.freeze({ kind: 'unreadable' })
|
|
170
|
+
: Object.freeze({ kind: 'landed', caps: snapshot });
|
|
171
|
+
capsByBase.set(baseUrl, record);
|
|
172
|
+
return 'landed';
|
|
163
173
|
}
|
|
164
174
|
function snapshotCapsBody(body) {
|
|
165
175
|
if (body === null || typeof body !== 'object')
|
|
@@ -237,8 +247,20 @@ function capSlotIn(caps, key) {
|
|
|
237
247
|
return SLOT_UNREADABLE;
|
|
238
248
|
}
|
|
239
249
|
}
|
|
250
|
+
function slotInLanding(landing, key) {
|
|
251
|
+
switch (landing.kind) {
|
|
252
|
+
case 'not_landed':
|
|
253
|
+
return SLOT_UNOBSERVED;
|
|
254
|
+
case 'unreadable':
|
|
255
|
+
return SLOT_UNREADABLE;
|
|
256
|
+
case 'landed':
|
|
257
|
+
return capSlotIn(landing.caps, key);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
240
260
|
function capStateIn(caps, key) {
|
|
241
|
-
|
|
261
|
+
return stateOfSlot(capSlotIn(caps, key));
|
|
262
|
+
}
|
|
263
|
+
function stateOfSlot(slot) {
|
|
242
264
|
switch (slot.kind) {
|
|
243
265
|
case 'unobserved':
|
|
244
266
|
return 'unprobed';
|
|
@@ -276,7 +298,8 @@ export async function engineCapStateVia(baseUrl, key, probe, opts) {
|
|
|
276
298
|
try {
|
|
277
299
|
if (signal?.aborted)
|
|
278
300
|
return 'unprobed';
|
|
279
|
-
|
|
301
|
+
const body = await probe(signal);
|
|
302
|
+
return isPlainCapsBody(body) ? capStateIn(body, key) : 'unprobed';
|
|
280
303
|
}
|
|
281
304
|
catch {
|
|
282
305
|
return 'unprobed';
|
|
@@ -297,9 +320,7 @@ export function engineCapNestedTrue(baseUrl, parentKey, key) {
|
|
|
297
320
|
return state.kind === 'value' && state.value === true;
|
|
298
321
|
}
|
|
299
322
|
export function engineCapNestedValue(baseUrl, parentKey, key) {
|
|
300
|
-
|
|
301
|
-
return { kind: 'unobserved' };
|
|
302
|
-
const parent = capSlotIn(capsByBase.get(baseUrl)?.caps, parentKey);
|
|
323
|
+
const parent = slotInLanding(engineCapsLandingOf(baseUrl), parentKey);
|
|
303
324
|
if (parent.kind !== 'present')
|
|
304
325
|
return valueStateOfSlot(parent);
|
|
305
326
|
const group = parent.value;
|
|
@@ -308,9 +329,7 @@ export function engineCapNestedValue(baseUrl, parentKey, key) {
|
|
|
308
329
|
return valueStateOfSlot(capSlotIn(group, key));
|
|
309
330
|
}
|
|
310
331
|
export function engineCapValue(baseUrl, key) {
|
|
311
|
-
|
|
312
|
-
return { kind: 'unobserved' };
|
|
313
|
-
return valueStateOfSlot(capSlotIn(capsByBase.get(baseUrl)?.caps, key));
|
|
332
|
+
return valueStateOfSlot(slotInLanding(engineCapsLandingOf(baseUrl), key));
|
|
314
333
|
}
|
|
315
334
|
function valueStateOfSlot(slot) {
|
|
316
335
|
switch (slot.kind) {
|
|
@@ -325,9 +344,7 @@ function valueStateOfSlot(slot) {
|
|
|
325
344
|
}
|
|
326
345
|
}
|
|
327
346
|
export function engineCapString(baseUrl, key) {
|
|
328
|
-
|
|
329
|
-
return undefined;
|
|
330
|
-
const slot = capSlotIn(capsByBase.get(baseUrl)?.caps, key);
|
|
347
|
+
const slot = slotInLanding(engineCapsLandingOf(baseUrl), key);
|
|
331
348
|
return slot.kind === 'present' && typeof slot.value === 'string' && slot.value.length > 0 ? slot.value : undefined;
|
|
332
349
|
}
|
|
333
350
|
export function invalidateEngineCaps(baseUrl, probe) {
|
|
@@ -348,6 +365,19 @@ export function invalidateEngineCaps(baseUrl, probe) {
|
|
|
348
365
|
if (probe !== undefined)
|
|
349
366
|
kickEngineCapsProbe(baseUrl, probe);
|
|
350
367
|
}
|
|
368
|
+
export function rotateEngineCapsProbe(baseUrl, probe) {
|
|
369
|
+
try {
|
|
370
|
+
if (typeof baseUrl !== 'string' || baseUrl === '' || typeof probe !== 'function')
|
|
371
|
+
return 'ignored';
|
|
372
|
+
if (startEngineCapsProbe(baseUrl, probe))
|
|
373
|
+
return 'started';
|
|
374
|
+
probeByBase.set(baseUrl, probe);
|
|
375
|
+
return 'registered';
|
|
376
|
+
}
|
|
377
|
+
catch {
|
|
378
|
+
return 'ignored';
|
|
379
|
+
}
|
|
380
|
+
}
|
|
351
381
|
export async function refreshEngineCapsOnce(baseUrl, opts) {
|
|
352
382
|
try {
|
|
353
383
|
if (typeof baseUrl !== 'string' || baseUrl === '')
|
|
@@ -370,10 +400,9 @@ export async function refreshEngineCapsOnce(baseUrl, opts) {
|
|
|
370
400
|
void (async () => {
|
|
371
401
|
try {
|
|
372
402
|
const caps = await probe();
|
|
373
|
-
if (gen !== capsGeneration(baseUrl) || !capsByBase.has(baseUrl)
|
|
403
|
+
if (gen !== capsGeneration(baseUrl) || !capsByBase.has(baseUrl))
|
|
374
404
|
return false;
|
|
375
|
-
landCapsSnapshot(baseUrl, gen, caps);
|
|
376
|
-
return gen === capsGeneration(baseUrl);
|
|
405
|
+
return landCapsSnapshot(baseUrl, gen, caps) === 'landed';
|
|
377
406
|
}
|
|
378
407
|
catch {
|
|
379
408
|
return false;
|
|
@@ -393,6 +422,8 @@ function isPlainCapsBody(caps) {
|
|
|
393
422
|
try {
|
|
394
423
|
if (typeof caps !== 'object' || caps === null)
|
|
395
424
|
return false;
|
|
425
|
+
if (Array.isArray(caps))
|
|
426
|
+
return false;
|
|
396
427
|
const proto = Object.getPrototypeOf(caps);
|
|
397
428
|
if (proto !== null && Object.getPrototypeOf(proto) !== null)
|
|
398
429
|
return false;
|
|
@@ -35,6 +35,9 @@ export declare const CAPABILITY_SELF_ORCHESTRATION_REQUIRED = "capability.self_o
|
|
|
35
35
|
export declare const CAPABILITY_IMAGE_INDEX_REQUIRED = "capability.image_index_required";
|
|
36
36
|
export declare const GATE_PARKED_ERROR_CODE = "gate.parked";
|
|
37
37
|
export declare const ACTIVE_RUN_BUSY_ERROR_CODE = "conflict.session_active_run";
|
|
38
|
+
export declare const CONFLICT_IDEMPOTENCY_PAYLOAD_MISMATCH = "conflict.idempotency_payload_mismatch";
|
|
39
|
+
export declare function isIdempotencyPayloadMismatchCode(code: string | null | undefined): boolean;
|
|
40
|
+
export declare function idempotencyPayloadMismatchContent(): string;
|
|
38
41
|
export interface BusyClassification {
|
|
39
42
|
readonly busy: boolean;
|
|
40
43
|
readonly handle: string | null;
|
package/dist/engineErrorCodes.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { frozenSet } from './frozenSet.js';
|
|
2
|
+
import { ownDataField } from './ownKey.js';
|
|
2
3
|
import { SDK_DENY_REASON_ROUTING_FAILURE, SDK_RUN_DEATH_VERDICT_CODES, SDK_STATUS_DERIVED_COARSE_CODES } from './generated/sdkFactTables.js';
|
|
3
4
|
export const LIMITS_MAX_TOKENS_EXCEEDED = 'limits.max_tokens_exceeded';
|
|
4
5
|
export const LIMITS_MAX_COST_EXCEEDED = 'limits.max_cost_exceeded';
|
|
@@ -65,6 +66,13 @@ export const CAPABILITY_SELF_ORCHESTRATION_REQUIRED = 'capability.self_orchestra
|
|
|
65
66
|
export const CAPABILITY_IMAGE_INDEX_REQUIRED = 'capability.image_index_required';
|
|
66
67
|
export const GATE_PARKED_ERROR_CODE = 'gate.parked';
|
|
67
68
|
export const ACTIVE_RUN_BUSY_ERROR_CODE = 'conflict.session_active_run';
|
|
69
|
+
export const CONFLICT_IDEMPOTENCY_PAYLOAD_MISMATCH = 'conflict.idempotency_payload_mismatch';
|
|
70
|
+
export function isIdempotencyPayloadMismatchCode(code) {
|
|
71
|
+
return code === CONFLICT_IDEMPOTENCY_PAYLOAD_MISMATCH;
|
|
72
|
+
}
|
|
73
|
+
export function idempotencyPayloadMismatchContent() {
|
|
74
|
+
return 'This request reused an idempotency key that is already bound to a different request body, so the engine refused it and nothing ran · To retry, send exactly the same request again; to send a different request, use a new idempotency key';
|
|
75
|
+
}
|
|
68
76
|
export function classifyActiveRunBusy(input) {
|
|
69
77
|
const handle = typeof input.activeTaskId === 'string' && input.activeTaskId.length > 0 ? input.activeTaskId : null;
|
|
70
78
|
if (input.errorCode === ACTIVE_RUN_BUSY_ERROR_CODE)
|
|
@@ -139,7 +147,7 @@ export function refusalSaysStillPending(err) {
|
|
|
139
147
|
}
|
|
140
148
|
export function refusalRetriableReading(err) {
|
|
141
149
|
try {
|
|
142
|
-
return
|
|
150
|
+
return ownDataField(err, 'retriable') === true;
|
|
143
151
|
}
|
|
144
152
|
catch {
|
|
145
153
|
return undefined;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { AUTO_MODE_UNAVAILABLE_CAUSES } from './autoModeUnavailable.js';
|
|
2
2
|
import { displayUntrusted } from './displayUntrusted.js';
|
|
3
|
-
import { currentEngineWord } from './engineWordRenames.js';
|
|
4
3
|
import { CORE_ENGINE_NOTICE_CODES, CORE_MCP_INJECTION_DROP_REASONS, CORE_NOTICE_AUDIENCE, CORE_USER_INPUT_UNDRAINED_FAMILIES, } from './generated/engineNoticeTables.js';
|
|
5
4
|
import { CORE_ASK_UNRESOLVABLE_CAUSES, CORE_DURABLE_MANDATE_SOURCES, CORE_PERMISSION_MODE_FOLD_REASONS, } from './generated/engineFactTables.js';
|
|
6
5
|
export const ENGINE_NOTICE_CODES = CORE_ENGINE_NOTICE_CODES;
|
|
@@ -9,13 +8,13 @@ function audienceRow(table, code) {
|
|
|
9
8
|
return Object.hasOwn(table, code) ? table[code] : undefined;
|
|
10
9
|
}
|
|
11
10
|
export function engineNoticeInCatalog(code) {
|
|
12
|
-
return typeof code === 'string' &&
|
|
11
|
+
return typeof code === 'string' && CATALOG.has(code);
|
|
13
12
|
}
|
|
14
13
|
export const ENGINE_NOTICE_AUDIENCE = CORE_NOTICE_AUDIENCE;
|
|
15
14
|
export function noticeAudienceOf(code) {
|
|
16
15
|
if (typeof code !== 'string')
|
|
17
16
|
return 'operator';
|
|
18
|
-
return audienceRow(ENGINE_NOTICE_AUDIENCE, code) ??
|
|
17
|
+
return audienceRow(ENGINE_NOTICE_AUDIENCE, code) ?? 'operator';
|
|
19
18
|
}
|
|
20
19
|
export const MCP_INJECTION_DROP_REASONS = CORE_MCP_INJECTION_DROP_REASONS;
|
|
21
20
|
const DROP_REASONS = new Set(MCP_INJECTION_DROP_REASONS);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface EngineToolDirectorySource {
|
|
2
|
+
readonly requestMcpServers?: readonly {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
}[] | null;
|
|
5
|
+
readonly deploymentMcpServers?: readonly {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
}[] | null;
|
|
8
|
+
}
|
|
9
|
+
export type EngineToolDirectory = {
|
|
10
|
+
readonly state: 'not_landed';
|
|
11
|
+
} | {
|
|
12
|
+
readonly state: 'unreadable';
|
|
13
|
+
} | {
|
|
14
|
+
readonly state: 'directory';
|
|
15
|
+
readonly mcpServerPrefixes: ReadonlySet<string>;
|
|
16
|
+
};
|
|
17
|
+
export declare function engineToolDirectoryOf(source: EngineToolDirectorySource | null | undefined): EngineToolDirectory;
|
|
18
|
+
export type McpEntryReading = 'outside' | 'unchecked' | 'unparsable' | 'answers' | 'unanswered';
|
|
19
|
+
export declare function mcpEntryReadingOf(toolName: string, directory: EngineToolDirectory | undefined): McpEntryReading;
|
|
20
|
+
export declare function deniedEntryToolName(entry: string): string | undefined;
|
|
21
|
+
export interface ExcludeToolsAgainstDirectory {
|
|
22
|
+
readonly send: string[];
|
|
23
|
+
readonly omitted: string[];
|
|
24
|
+
readonly verdict: 'directory_absent' | 'filtered';
|
|
25
|
+
readonly disclosure?: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function excludeToolsAgainstDirectory(exclude: readonly string[], directory: EngineToolDirectory | undefined): ExcludeToolsAgainstDirectory;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { frozenSet } from './frozenSet.js';
|
|
2
|
+
import { ownDataField } from './ownKey.js';
|
|
3
|
+
import { hasCoreValuePort, mcpNamespace, mintNamespacePrefix, protocolOf } from './coreValuePorts.js';
|
|
4
|
+
import { CORE_PROTOCOL_PREFIXES } from './generated/toolNameTables.js';
|
|
5
|
+
const notLanded = () => Object.freeze({ state: 'not_landed' });
|
|
6
|
+
const unreadable = () => Object.freeze({ state: 'unreadable' });
|
|
7
|
+
function readHalf(half) {
|
|
8
|
+
if (half === undefined || half === null)
|
|
9
|
+
return 'absent';
|
|
10
|
+
if (!Array.isArray(half))
|
|
11
|
+
return 'unreadable';
|
|
12
|
+
const names = [];
|
|
13
|
+
const n = ownDataField(half, 'length');
|
|
14
|
+
if (typeof n !== 'number' || !Number.isSafeInteger(n) || n < 0)
|
|
15
|
+
return 'unreadable';
|
|
16
|
+
for (let i = 0; i < n; i++) {
|
|
17
|
+
if (!Object.hasOwn(half, i))
|
|
18
|
+
return 'unreadable';
|
|
19
|
+
const row = ownDataField(half, String(i));
|
|
20
|
+
if (row === null || typeof row !== 'object')
|
|
21
|
+
return 'unreadable';
|
|
22
|
+
const name = ownDataField(row, 'name');
|
|
23
|
+
if (typeof name !== 'string' || name.length === 0)
|
|
24
|
+
return 'unreadable';
|
|
25
|
+
names.push(name);
|
|
26
|
+
}
|
|
27
|
+
return names;
|
|
28
|
+
}
|
|
29
|
+
export function engineToolDirectoryOf(source) {
|
|
30
|
+
if (source === undefined || source === null)
|
|
31
|
+
return notLanded();
|
|
32
|
+
try {
|
|
33
|
+
if (typeof source !== 'object')
|
|
34
|
+
return unreadable();
|
|
35
|
+
const request = readHalf(ownDataField(source, 'requestMcpServers'));
|
|
36
|
+
const deployment = readHalf(ownDataField(source, 'deploymentMcpServers'));
|
|
37
|
+
if (request === 'unreadable' || deployment === 'unreadable')
|
|
38
|
+
return unreadable();
|
|
39
|
+
if (request === 'absent' || deployment === 'absent')
|
|
40
|
+
return notLanded();
|
|
41
|
+
const ns = namingPorts();
|
|
42
|
+
if (ns === undefined)
|
|
43
|
+
return unreadable();
|
|
44
|
+
const prefixes = [];
|
|
45
|
+
for (const name of [...request, ...deployment]) {
|
|
46
|
+
const prefix = mintNamespacePrefix(ns, name);
|
|
47
|
+
if (typeof prefix !== 'string' || prefix.length === 0)
|
|
48
|
+
return unreadable();
|
|
49
|
+
prefixes.push(prefix);
|
|
50
|
+
}
|
|
51
|
+
return Object.freeze({ state: 'directory', mcpServerPrefixes: frozenSet(prefixes) });
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return unreadable();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function mcpEntryReadingOf(toolName, directory) {
|
|
58
|
+
let ns;
|
|
59
|
+
try {
|
|
60
|
+
const mcp = namingPorts();
|
|
61
|
+
if (mcp === undefined)
|
|
62
|
+
return CORE_PROTOCOL_PREFIXES.some((p) => toolName.startsWith(p)) ? 'unchecked' : 'outside';
|
|
63
|
+
ns = protocolOf(toolName);
|
|
64
|
+
if (ns === undefined || ns.id !== mcp.id)
|
|
65
|
+
return 'outside';
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return CORE_PROTOCOL_PREFIXES.some((p) => toolName.startsWith(p)) ? 'unchecked' : 'outside';
|
|
69
|
+
}
|
|
70
|
+
const prefixes = prefixesInHand(directory);
|
|
71
|
+
if (prefixes === undefined)
|
|
72
|
+
return 'unchecked';
|
|
73
|
+
let prefix;
|
|
74
|
+
try {
|
|
75
|
+
const peer = peerSegmentOf(ns, toolName);
|
|
76
|
+
if (peer === undefined || peer.includes('*'))
|
|
77
|
+
return 'unparsable';
|
|
78
|
+
prefix = mintNamespacePrefix(ns, peer);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return 'unparsable';
|
|
82
|
+
}
|
|
83
|
+
if (typeof prefix !== 'string' || prefix.length === 0)
|
|
84
|
+
return 'unchecked';
|
|
85
|
+
try {
|
|
86
|
+
return prefixes.has(prefix) ? 'answers' : 'unanswered';
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return 'unchecked';
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function prefixesInHand(directory) {
|
|
93
|
+
try {
|
|
94
|
+
if (directory === undefined || directory === null || typeof directory !== 'object' || directory.state !== 'directory')
|
|
95
|
+
return undefined;
|
|
96
|
+
const prefixes = directory.mcpServerPrefixes;
|
|
97
|
+
if (prefixes === null || typeof prefixes !== 'object' || typeof prefixes.has !== 'function')
|
|
98
|
+
return undefined;
|
|
99
|
+
return prefixes;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function namingPorts() {
|
|
106
|
+
if (!hasCoreValuePort('protocolOf') || !hasCoreValuePort('mintNamespacePrefix'))
|
|
107
|
+
return undefined;
|
|
108
|
+
return mcpNamespace();
|
|
109
|
+
}
|
|
110
|
+
function peerSegmentOf(ns, toolName) {
|
|
111
|
+
const read = (n) => {
|
|
112
|
+
const parsed = ns.parse(n);
|
|
113
|
+
if (parsed === null || typeof parsed !== 'object')
|
|
114
|
+
return undefined;
|
|
115
|
+
const peer = ownDataField(parsed, 'peer');
|
|
116
|
+
return typeof peer === 'string' && peer.length > 0 ? peer : undefined;
|
|
117
|
+
};
|
|
118
|
+
const direct = read(toolName);
|
|
119
|
+
if (direct !== undefined)
|
|
120
|
+
return direct;
|
|
121
|
+
if (!toolName.startsWith(ns.prefix))
|
|
122
|
+
return undefined;
|
|
123
|
+
const rest = toolName.slice(ns.prefix.length);
|
|
124
|
+
if (rest.length === 0)
|
|
125
|
+
return undefined;
|
|
126
|
+
return read(ns.makeName(rest, 'x')) === rest ? rest : undefined;
|
|
127
|
+
}
|
|
128
|
+
function unescapedIndexOf(s, ch, last) {
|
|
129
|
+
for (let k = 0; k < s.length; k++) {
|
|
130
|
+
const i = last ? s.length - 1 - k : k;
|
|
131
|
+
if (s[i] !== ch)
|
|
132
|
+
continue;
|
|
133
|
+
let backslashes = 0;
|
|
134
|
+
for (let j = i - 1; j >= 0 && s[j] === '\\'; j--)
|
|
135
|
+
backslashes++;
|
|
136
|
+
if (backslashes % 2 === 0)
|
|
137
|
+
return i;
|
|
138
|
+
}
|
|
139
|
+
return -1;
|
|
140
|
+
}
|
|
141
|
+
export function deniedEntryToolName(entry) {
|
|
142
|
+
if (entry !== entry.trim())
|
|
143
|
+
return undefined;
|
|
144
|
+
const open = unescapedIndexOf(entry, '(', false);
|
|
145
|
+
if (open === -1)
|
|
146
|
+
return entry;
|
|
147
|
+
const close = unescapedIndexOf(entry, ')', true);
|
|
148
|
+
if (close === -1 || close <= open || close !== entry.length - 1)
|
|
149
|
+
return entry;
|
|
150
|
+
const name = entry.substring(0, open);
|
|
151
|
+
return name.length === 0 ? entry : name.trim();
|
|
152
|
+
}
|
|
153
|
+
export function excludeToolsAgainstDirectory(exclude, directory) {
|
|
154
|
+
if (!Array.isArray(exclude))
|
|
155
|
+
throw new TypeError('excludeToolsAgainstDirectory: exclude must be an array of tool names');
|
|
156
|
+
const verdict = prefixesInHand(directory) !== undefined ? 'filtered' : 'directory_absent';
|
|
157
|
+
const send = [];
|
|
158
|
+
const omitted = [];
|
|
159
|
+
for (const entry of exclude) {
|
|
160
|
+
if (verdict === 'filtered' && typeof entry === 'string') {
|
|
161
|
+
const name = deniedEntryToolName(entry);
|
|
162
|
+
if (name !== undefined && mcpEntryReadingOf(name, directory) === 'unanswered') {
|
|
163
|
+
omitted.push(entry);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
send.push(entry);
|
|
168
|
+
}
|
|
169
|
+
if (omitted.length === 0)
|
|
170
|
+
return { send, omitted, verdict };
|
|
171
|
+
const one = omitted.length === 1;
|
|
172
|
+
return {
|
|
173
|
+
send,
|
|
174
|
+
omitted,
|
|
175
|
+
verdict,
|
|
176
|
+
disclosure: `excludeTools: ${omitted.map((e) => JSON.stringify(e)).join(', ')} ${one ? 'names' : 'name'} no tool this deployment answers to (no MCP server of that name is declared for this session) — omitted. Excluding a tool that cannot be mounted changes nothing; the engine refuses a request that names one.`,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { capForDisplay } from './fleetTaskDesc.js';
|
|
2
2
|
import { engineWireTarget } from './engineWireTarget.js';
|
|
3
|
-
import { createEngineCapReader } from './engineCapReader.js';
|
|
3
|
+
import { createEngineCapReader, unreadableReadingDetail } from './engineCapReader.js';
|
|
4
4
|
import { ownDataField } from './ownKey.js';
|
|
5
5
|
export function projectExecutionLaneCapability(caps) {
|
|
6
6
|
try {
|
|
@@ -55,7 +55,6 @@ export function projectExecutionLaneWiring(wiring) {
|
|
|
55
55
|
const reader = createEngineCapReader({
|
|
56
56
|
name: 'executionLane',
|
|
57
57
|
project: projectExecutionLaneCapability,
|
|
58
|
-
makeUnobserved: () => ({ kind: 'unobserved' }),
|
|
59
58
|
});
|
|
60
59
|
export function noteEngineCapsForExecutionLane(baseUrl, caps, opts) {
|
|
61
60
|
reader.note(baseUrl, caps, opts);
|
|
@@ -98,6 +97,8 @@ export function noOsSandboxContent() {
|
|
|
98
97
|
const LANE_DETAIL_MAX = 40;
|
|
99
98
|
export function executionLaneDoctorDetail(reading) {
|
|
100
99
|
switch (reading.kind) {
|
|
100
|
+
case 'unreadable':
|
|
101
|
+
return unreadableReadingDetail('execution lane');
|
|
101
102
|
case 'unobserved':
|
|
102
103
|
return ('execution lane not observed — the engine reports it on /v1/capabilities; ' +
|
|
103
104
|
'this process has no usable capabilities reading cached for it (none received, the last one was unreadable, or the last one was discarded when the engine connection was reset; this process cannot tell which)');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { capForDisplay } from './fleetTaskDesc.js';
|
|
2
2
|
import { engineWireTarget } from './engineWireTarget.js';
|
|
3
|
-
import { createEngineCapReader } from './engineCapReader.js';
|
|
3
|
+
import { createEngineCapReader, unreadableReadingDetail } from './engineCapReader.js';
|
|
4
4
|
import { refreshEngineCapsOnce } from './engineCapsCache.js';
|
|
5
5
|
import { ownDataField } from './ownKey.js';
|
|
6
6
|
import { FILE_HISTORY_DISABLED } from './request/taskRequest.js';
|
|
@@ -22,7 +22,6 @@ export function projectFileHistoryCaptureCapability(caps) {
|
|
|
22
22
|
const reader = createEngineCapReader({
|
|
23
23
|
name: 'fileHistoryCapture',
|
|
24
24
|
project: projectFileHistoryCaptureCapability,
|
|
25
|
-
makeUnobserved: () => ({ kind: 'unobserved' }),
|
|
26
25
|
});
|
|
27
26
|
export function noteEngineCapsForFileHistoryCapture(baseUrl, caps, opts) {
|
|
28
27
|
reader.note(baseUrl, caps, opts);
|
|
@@ -64,11 +63,36 @@ export function fileHistoryTightenVerdict(reading, captureOffByUser) {
|
|
|
64
63
|
return 'already_off';
|
|
65
64
|
return 'unjudged';
|
|
66
65
|
}
|
|
67
|
-
const
|
|
68
|
-
'stop recording file history for a session
|
|
69
|
-
'regardless and let file history be recorded this time, turn file checkpointing back on in your settings.';
|
|
66
|
+
const FILE_HISTORY_REFUSAL_NOT_YET = "Nothing was sent: file checkpointing is turned off in your settings, but this engine's capabilities have not been read " +
|
|
67
|
+
'yet, so whether it can stop recording file history for a session is unknown and sending now could record it anyway. ' +
|
|
68
|
+
'Try again in a moment; to send regardless and let file history be recorded this time, turn file checkpointing back on in your settings.';
|
|
69
|
+
const FILE_HISTORY_REFUSAL_NOT_SAID = 'Nothing was sent: file checkpointing is turned off in your settings, but this engine did not report whether it can ' +
|
|
70
|
+
'stop recording file history for a session, so sending now could record it anyway. Trying again will not change that ' +
|
|
71
|
+
'while this engine reports the same; to send and let file history be recorded this time, turn file checkpointing back on in your settings.';
|
|
72
|
+
const FILE_HISTORY_REFUSAL_UNREADABLE = "Nothing was sent: file checkpointing is turned off in your settings, but this engine's report on whether it can stop " +
|
|
73
|
+
'recording file history for a session could not be read by this client, so sending now could record it anyway. Trying ' +
|
|
74
|
+
'again will not change that while this engine reports the same; to send and let file history be recorded this time, ' +
|
|
75
|
+
'turn file checkpointing back on in your settings.';
|
|
76
|
+
function unjudgedRefusalOf(reading) {
|
|
77
|
+
let kind;
|
|
78
|
+
let word;
|
|
79
|
+
try {
|
|
80
|
+
kind = typeof reading === 'object' && reading !== null ? reading.kind : undefined;
|
|
81
|
+
word = kind === 'present' ? reading.word : undefined;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return FILE_HISTORY_REFUSAL_NOT_YET;
|
|
85
|
+
}
|
|
86
|
+
if (kind === 'unreadable')
|
|
87
|
+
return FILE_HISTORY_REFUSAL_UNREADABLE;
|
|
88
|
+
if (kind === 'not_reported')
|
|
89
|
+
return FILE_HISTORY_REFUSAL_NOT_SAID;
|
|
90
|
+
if (kind === 'present' && typeof word === 'string' && word.length > 0)
|
|
91
|
+
return FILE_HISTORY_REFUSAL_NOT_SAID;
|
|
92
|
+
return FILE_HISTORY_REFUSAL_NOT_YET;
|
|
93
|
+
}
|
|
70
94
|
export function fileHistoryUnjudgedRefusal(reading, captureOffByUser) {
|
|
71
|
-
return fileHistoryTightenVerdict(reading, captureOffByUser) === 'unjudged' ?
|
|
95
|
+
return fileHistoryTightenVerdict(reading, captureOffByUser) === 'unjudged' ? unjudgedRefusalOf(reading) : undefined;
|
|
72
96
|
}
|
|
73
97
|
const FILE_HISTORY_REFRESH_WITHIN_MS = 3000;
|
|
74
98
|
export async function judgeFileHistoryTighten(captureOffByUser, opts) {
|
|
@@ -76,7 +100,7 @@ export async function judgeFileHistoryTighten(captureOffByUser, opts) {
|
|
|
76
100
|
const failClosed = () => {
|
|
77
101
|
const reading = { kind: 'unobserved' };
|
|
78
102
|
return closed
|
|
79
|
-
? { verdict: 'unjudged', reading, refreshed: false, refusal:
|
|
103
|
+
? { verdict: 'unjudged', reading, refreshed: false, refusal: FILE_HISTORY_REFUSAL_NOT_YET }
|
|
80
104
|
: { verdict: 'not_requested', reading, refreshed: false, refusal: undefined };
|
|
81
105
|
};
|
|
82
106
|
try {
|
|
@@ -115,7 +139,7 @@ export async function judgeFileHistoryTighten(captureOffByUser, opts) {
|
|
|
115
139
|
verdict: v2,
|
|
116
140
|
reading: second,
|
|
117
141
|
refreshed: outcome !== 'no_probe',
|
|
118
|
-
refusal: v2 === 'unjudged' ?
|
|
142
|
+
refusal: v2 === 'unjudged' ? unjudgedRefusalOf(second) : undefined,
|
|
119
143
|
};
|
|
120
144
|
}
|
|
121
145
|
catch {
|
|
@@ -137,6 +161,8 @@ export function fileHistoryCaptureDoctorDetail(reading) {
|
|
|
137
161
|
return unobserved;
|
|
138
162
|
}
|
|
139
163
|
switch (kind) {
|
|
164
|
+
case 'unreadable':
|
|
165
|
+
return unreadableReadingDetail('file history capture');
|
|
140
166
|
case 'not_reported':
|
|
141
167
|
return 'file history capture not reported by this engine; this does not say whether file history is captured here';
|
|
142
168
|
case 'present':
|
|
@@ -89,6 +89,8 @@ function canonReading(r) {
|
|
|
89
89
|
const kind = r.kind;
|
|
90
90
|
if (kind === 'not_reported')
|
|
91
91
|
return 'not_reported';
|
|
92
|
+
if (kind === 'unreadable')
|
|
93
|
+
return 'unreadable';
|
|
92
94
|
if (kind === 'present') {
|
|
93
95
|
const word = r.word;
|
|
94
96
|
return typeof word === 'string' && word.length > 0 ? `present:${word}` : 'unobserved';
|
|
@@ -102,6 +104,8 @@ function canonReading(r) {
|
|
|
102
104
|
function readingOfCanon(c) {
|
|
103
105
|
if (c === 'not_reported')
|
|
104
106
|
return { kind: 'not_reported' };
|
|
107
|
+
if (c === 'unreadable')
|
|
108
|
+
return { kind: 'unreadable' };
|
|
105
109
|
if (c.startsWith('present:'))
|
|
106
110
|
return { kind: 'present', word: c.slice('present:'.length) };
|
|
107
111
|
return { kind: 'unobserved' };
|