@sema-agent/client-core 0.110.0 → 0.111.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 +169 -0
- package/README.md +50 -18
- package/dist/abortableSleep.d.ts +7 -0
- package/dist/abortableSleep.js +37 -0
- package/dist/adapt/arms.js +25 -4
- package/dist/adapt/toolCards.d.ts +3 -1
- package/dist/adapt/toolCards.js +4 -2
- package/dist/adapter/activeRunSelfHeal.d.ts +39 -0
- package/dist/adapter/activeRunSelfHeal.js +171 -3
- package/dist/adapter/downstream/eventToSdkMessage.js +10 -10
- package/dist/adapter/downstream/terminalToSdkResult.d.ts +2 -0
- package/dist/adapter/downstream/terminalToSdkResult.js +11 -0
- package/dist/adapter/runStream.d.ts +5 -0
- package/dist/adapter/runStream.js +15 -3
- package/dist/attachmentRoute.d.ts +20 -0
- package/dist/attachmentRoute.js +39 -0
- package/dist/attachmentsWireCaps.js +1 -1
- package/dist/builtinOutputStyles.d.ts +12 -0
- package/dist/builtinOutputStyles.js +147 -0
- package/dist/compactionStatus.d.ts +9 -0
- package/dist/compactionStatus.js +29 -5
- package/dist/controlRouter.js +2 -1
- package/dist/coreValuePorts.d.ts +39 -1
- package/dist/coreValuePorts.js +10 -0
- package/dist/displayUntrusted.d.ts +1 -0
- package/dist/displayUntrusted.js +6 -0
- package/dist/engineHealthFacts.d.ts +11 -0
- package/dist/engineHealthFacts.js +21 -0
- package/dist/engineHealthProbeVerdict.d.ts +23 -0
- package/dist/engineHealthProbeVerdict.js +115 -0
- package/dist/engineNoticeCodes.js +5 -7
- package/dist/engineRepoint.d.ts +12 -5
- package/dist/engineRepoint.js +44 -19
- package/dist/engineRowRef.d.ts +3 -0
- package/dist/engineRowRef.js +11 -1
- package/dist/engineSwapCrossing.d.ts +57 -0
- package/dist/engineSwapCrossing.js +297 -0
- package/dist/engineUnavailable.d.ts +44 -0
- package/dist/engineUnavailable.js +492 -0
- package/dist/eventLeg.js +3 -3
- package/dist/generated/sdkFactTables.d.ts +1 -1
- package/dist/generated/sdkFactTables.js +1 -0
- package/dist/generated/settingsSchemaTables.d.ts +4 -0
- package/dist/generated/settingsSchemaTables.js +1 -0
- package/dist/hitl/approvalDelivery.d.ts +1 -0
- package/dist/hitl/approvalDelivery.js +1 -1
- package/dist/hitl/approvalResolution.d.ts +1 -1
- package/dist/hitl/approvalResolution.js +2 -1
- package/dist/hitl/askGateWire.js +5 -6
- package/dist/hitl/decisionRefusal.d.ts +2 -0
- package/dist/hitl/decisionRefusal.js +7 -8
- package/dist/hitl/decisionRefusalCarrier.d.ts +3 -0
- package/dist/hitl/decisionRefusalCarrier.js +348 -0
- package/dist/hitl/decisionSend.d.ts +7 -0
- package/dist/hitl/decisionSend.js +34 -12
- package/dist/hitl/editedApprovalAcceptance.js +1 -1
- package/dist/hitl/elicitationRefusal.d.ts +4 -0
- package/dist/hitl/elicitationRefusal.js +23 -0
- package/dist/hitl/frameRouter.d.ts +2 -2
- package/dist/hitl/frameRouter.js +8 -15
- package/dist/hitl/gateIdentity.d.ts +5 -0
- package/dist/hitl/gateIdentity.js +23 -0
- package/dist/hitl/hitlBridge.d.ts +6 -0
- package/dist/hitl/hitlBridge.js +10 -4
- package/dist/hitl/hitlHostSurface.d.ts +2 -1
- package/dist/hitl/hitlHostSurface.js +7 -2
- package/dist/hitl/localAllowRedemption.d.ts +67 -0
- package/dist/hitl/localAllowRedemption.js +256 -0
- package/dist/hitl/parkResolver.js +7 -4
- package/dist/hitl/persistedRulesWire.d.ts +16 -0
- package/dist/hitl/persistedRulesWire.js +70 -0
- package/dist/hitl/planReviewWire.d.ts +3 -0
- package/dist/hitl/planReviewWire.js +23 -6
- package/dist/hitl/policyDenyCause.d.ts +1 -1
- package/dist/hitl/policyDenyCause.js +1 -1
- package/dist/hitl/resumeRunningCard.d.ts +1 -0
- package/dist/hitl/resumeRunningCard.js +1 -0
- package/dist/hitl/screenCardCarriers.d.ts +15 -0
- package/dist/hitl/screenCardCarriers.js +41 -0
- package/dist/hitl/screenDecisionCards.d.ts +98 -0
- package/dist/hitl/screenDecisionCards.js +362 -0
- package/dist/hitl/standingRows.d.ts +3 -1
- package/dist/hitl/standingRows.js +18 -0
- package/dist/hitl/toolApprovalWire.d.ts +30 -8
- package/dist/hitl/toolApprovalWire.js +264 -121
- package/dist/hooksWireCaps.js +3 -3
- package/dist/hostSettingNoticeArm.d.ts +3 -0
- package/dist/hostSettingNoticeArm.js +12 -0
- package/dist/hostTurnSettings.d.ts +3 -0
- package/dist/hostTurnSettings.js +38 -5
- package/dist/humanTurnRead.js +11 -1
- package/dist/i18n/messages.en.d.ts +129 -0
- package/dist/i18n/messages.en.js +135 -7
- package/dist/i18n/messages.zh.js +142 -6
- package/dist/i18n/translate.d.ts +3 -0
- package/dist/i18n/translate.js +78 -10
- package/dist/i18n/zh.d.ts +1 -0
- package/dist/i18n/zh.js +1 -0
- package/dist/index.d.ts +43 -17
- package/dist/index.js +30 -12
- package/dist/interactiveHalt.js +2 -1
- package/dist/interactiveInterrupt.js +20 -2
- package/dist/liveModelCatalog.js +2 -2
- package/dist/localEngineSeatReading.d.ts +2 -2
- package/dist/localEngineSeatReading.js +5 -3
- package/dist/localEngineSpawn.d.ts +0 -7
- package/dist/localEngineSpawn.js +0 -92
- package/dist/localEngineStep.d.ts +2 -2
- package/dist/localEngineStep.js +2 -0
- package/dist/mcpReconnect.js +3 -3
- package/dist/mcpWireCaps.js +1 -1
- package/dist/model/engineModelWords.d.ts +3 -0
- package/dist/model/engineModelWords.js +13 -0
- package/dist/model/subagentModelWord.d.ts +21 -2
- package/dist/model/subagentModelWord.js +100 -29
- package/dist/modelCapabilityProbe.js +3 -40
- package/dist/notifications.d.ts +19 -0
- package/dist/notifications.js +114 -12
- package/dist/ownKey.d.ts +6 -0
- package/dist/ownKey.js +31 -0
- package/dist/pendingUserRow.d.ts +4 -0
- package/dist/pendingUserRow.js +16 -0
- package/dist/permissionsDeclaration.d.ts +1 -0
- package/dist/permissionsDeclaration.js +11 -1
- package/dist/planReviewCardFacts.d.ts +3 -0
- package/dist/planReviewCardFacts.js +52 -2
- package/dist/pluginHooksWire.d.ts +9 -1
- package/dist/pluginHooksWire.js +40 -6
- package/dist/printToolResultFrame.js +4 -2
- package/dist/promptBlockedCopy.js +11 -6
- package/dist/promptSuggestionBatch.d.ts +7 -0
- package/dist/promptSuggestionBatch.js +9 -0
- package/dist/promptSuggestionsTail.d.ts +39 -0
- package/dist/promptSuggestionsTail.js +116 -0
- package/dist/request/taskRequest.js +10 -6
- package/dist/requestCoordinate.d.ts +1 -0
- package/dist/requestCoordinate.js +13 -0
- package/dist/rewindArchiveReading.js +4 -4
- package/dist/runCancelContext.d.ts +2 -1
- package/dist/runCancelContext.js +1 -0
- package/dist/runPermissionModeSwitch.js +2 -2
- package/dist/scenarioRefusal.js +11 -13
- package/dist/scenarioSkills.js +11 -6
- package/dist/seam.d.ts +16 -2
- package/dist/seam.js +10 -0
- package/dist/seatContract.d.ts +109 -9
- package/dist/seatContract.js +218 -15
- package/dist/sessionCostReading.d.ts +70 -0
- package/dist/sessionCostReading.js +150 -0
- package/dist/sessionHooksRoster.d.ts +4 -0
- package/dist/sessionHooksRoster.js +158 -0
- package/dist/sessionMemoryStatus.js +2 -2
- package/dist/sessionReadRedaction.d.ts +7 -0
- package/dist/sessionReadRedaction.js +26 -0
- package/dist/sessionSyncFileHistory.js +8 -9
- package/dist/settingsHooksSalvage.js +2 -1
- package/dist/sseIdleTriage.js +2 -1
- package/dist/storeDurabilityWord.d.ts +15 -0
- package/dist/storeDurabilityWord.js +18 -0
- package/dist/storePostureCapability.d.ts +7 -3
- package/dist/storePostureCapability.js +69 -39
- package/dist/subagent/engineSubagentResume.js +2 -2
- package/dist/subagent/engineSubagentTail.js +2 -1
- package/dist/subagentContentStore.d.ts +11 -0
- package/dist/subagentContentStore.js +28 -2
- package/dist/toolResult.d.ts +1 -0
- package/dist/toolResult.js +2 -0
- package/dist/userAttachmentEnvelope.d.ts +2 -1
- package/dist/userAttachmentEnvelope.js +10 -0
- package/dist/wireErrorTriage.js +2 -1
- package/dist/wireFailureShape.d.ts +4 -0
- package/dist/wireFailureShape.js +141 -19
- package/dist/wireRefusalCopy.d.ts +6 -5
- package/dist/wireRefusalCopy.js +49 -92
- package/docs/INTEGRATION-CLIENTS.md +2118 -17
- package/package.json +6 -2
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { AUTO_MODE_UNAVAILABLE_CAUSES } from './autoModeUnavailable.js';
|
|
2
|
-
import { displayUntrusted } from './displayUntrusted.js';
|
|
2
|
+
import { displayUntrusted, displayUntrustedCapped } from './displayUntrusted.js';
|
|
3
3
|
import { CORE_ENGINE_NOTICE_CODES, CORE_MCP_INJECTION_DROP_REASONS, CORE_NOTICE_AUDIENCE, CORE_USER_INPUT_UNDRAINED_FAMILIES, } from './generated/engineNoticeTables.js';
|
|
4
4
|
import { CORE_APPROVAL_REMEDY_KINDS, CORE_ASK_UNRESOLVABLE_CAUSES, CORE_DISCARDED_DELIVERY_PATHS, CORE_DURABLE_MANDATE_SOURCES, CORE_PERMISSION_MODE_FOLD_REASONS, } from './generated/engineFactTables.js';
|
|
5
|
-
import { msg, renderMessage } from './i18n/translate.js';
|
|
5
|
+
import { msg, renderLanguageOf, renderMessage } from './i18n/translate.js';
|
|
6
6
|
import { ownDataField, readOwnDataValue } from './ownKey.js';
|
|
7
7
|
import { inValueOf, slotReadOf } from './wireFailureShape.js';
|
|
8
8
|
export const ENGINE_NOTICE_CODES = CORE_ENGINE_NOTICE_CODES;
|
|
@@ -196,7 +196,6 @@ function readAskUnresolvableOf(notice) {
|
|
|
196
196
|
};
|
|
197
197
|
}
|
|
198
198
|
export const DURABLE_MANDATE_SOURCES = CORE_DURABLE_MANDATE_SOURCES;
|
|
199
|
-
const DURABLE_MANDATE_SOURCE_SET = new Set(DURABLE_MANDATE_SOURCES);
|
|
200
199
|
export function readDurableGateUnavailable(notice) {
|
|
201
200
|
if (typeof notice !== 'object' || notice === null || Array.isArray(notice))
|
|
202
201
|
return undefined;
|
|
@@ -213,7 +212,7 @@ export function readDurableGateUnavailable(notice) {
|
|
|
213
212
|
const causeIn = o.cause;
|
|
214
213
|
const sessionId = typeof sessionIdIn === 'string' && sessionIdIn.length > 0 ? sessionIdIn : undefined;
|
|
215
214
|
const runId = typeof runIdIn === 'string' && runIdIn.length > 0 ? runIdIn : undefined;
|
|
216
|
-
const source = typeof sourceIn === 'string' &&
|
|
215
|
+
const source = typeof sourceIn === 'string' && sourceIn.length > 0 ? sourceIn : undefined;
|
|
217
216
|
const cause = typeof causeIn === 'string' && causeIn.length > 0 ? causeIn : undefined;
|
|
218
217
|
if (sessionId === undefined || runId === undefined || source === undefined || cause === undefined) {
|
|
219
218
|
return undefined;
|
|
@@ -1417,15 +1416,14 @@ function shownName(v) {
|
|
|
1417
1416
|
function shownText(v, max) {
|
|
1418
1417
|
if (v === undefined)
|
|
1419
1418
|
return undefined;
|
|
1420
|
-
const
|
|
1421
|
-
const s = whole.length <= max ? whole : `${displayUntrusted(v, { max: max - 1 })}…`;
|
|
1419
|
+
const s = displayUntrustedCapped(v, max);
|
|
1422
1420
|
return s.length > 0 ? s : undefined;
|
|
1423
1421
|
}
|
|
1424
1422
|
function wordLookup(table, word) {
|
|
1425
1423
|
return Object.hasOwn(table, word) ? table[word] : undefined;
|
|
1426
1424
|
}
|
|
1427
1425
|
function joinSentences(parts, lang) {
|
|
1428
|
-
return parts.filter((p) => p !== undefined && p.length > 0).join(lang === 'zh' ? '' : ' ');
|
|
1426
|
+
return parts.filter((p) => p !== undefined && p.length > 0).join(renderLanguageOf(lang) === 'zh' ? '' : ' ');
|
|
1429
1427
|
}
|
|
1430
1428
|
export function memorySessionPollutedSentence(facts, lang) {
|
|
1431
1429
|
try {
|
package/dist/engineRepoint.d.ts
CHANGED
|
@@ -15,7 +15,12 @@ export declare function deviceHelloReadingOf(frame: {} | null | undefined): Devi
|
|
|
15
15
|
export interface EngineRepointInput {
|
|
16
16
|
readonly from: string;
|
|
17
17
|
readonly to: string;
|
|
18
|
-
readonly
|
|
18
|
+
readonly continuity?: EngineRepointContinuity;
|
|
19
|
+
}
|
|
20
|
+
export interface EngineRepointContinuity {
|
|
21
|
+
readonly deviceId: string;
|
|
22
|
+
readonly from: string;
|
|
23
|
+
readonly to: string;
|
|
19
24
|
}
|
|
20
25
|
export interface EngineRepointHandshakeTarget {
|
|
21
26
|
readonly address: string;
|
|
@@ -26,8 +31,7 @@ export type EngineRepointHandshakePort = (target: EngineRepointHandshakeTarget,
|
|
|
26
31
|
}) => Promise<{} | null | undefined> | {} | null | undefined;
|
|
27
32
|
export interface EngineRepointRoster {
|
|
28
33
|
readonly credential: 'operator' | 'non-operator';
|
|
29
|
-
readonly address: string;
|
|
30
|
-
readonly devices: DeviceListVerb | null | undefined;
|
|
34
|
+
readonly devices: (address: string) => DeviceListVerb | null | undefined;
|
|
31
35
|
}
|
|
32
36
|
export type EngineRepointRecordReading = {
|
|
33
37
|
readonly kind: 'absent';
|
|
@@ -75,7 +79,7 @@ export type EngineRepointRosterProof = {
|
|
|
75
79
|
readonly why: 'not-listed' | 'lane-absent';
|
|
76
80
|
} | {
|
|
77
81
|
readonly state: 'unreadable';
|
|
78
|
-
readonly why: 'operator-credential' | 'credential-unknown' | '
|
|
82
|
+
readonly why: 'operator-credential' | 'credential-unknown' | 'list-incomplete' | 'refused' | 'transport' | 'unreadable' | 'unarmed';
|
|
79
83
|
};
|
|
80
84
|
export interface EngineRepointProofs {
|
|
81
85
|
readonly handshake: EngineRepointHandshakeProof;
|
|
@@ -94,11 +98,12 @@ export interface EngineRepointSessionTally {
|
|
|
94
98
|
readonly unreadable: number;
|
|
95
99
|
readonly storeFailed: number;
|
|
96
100
|
}
|
|
97
|
-
export type EngineRepointRefusal = 'address-invalid' | 'same-address' | 'record-absent' | 'record-not-active' | 'record-unreadable' | 'target-occupied' | 'unarmed';
|
|
101
|
+
export type EngineRepointRefusal = 'address-invalid' | 'same-address' | 'record-absent' | 'record-not-active' | 'record-unreadable' | 'target-occupied' | 'roster-unbound' | 'unarmed';
|
|
98
102
|
export type EngineRepointResult = EngineRepointDone | EngineRepointPartial | EngineRepointUnverified | EngineRepointUndetermined | EngineRepointStoreFailed | EngineRepointRefused;
|
|
99
103
|
export interface EngineRepointDone {
|
|
100
104
|
readonly kind: 'repointed';
|
|
101
105
|
readonly deviceId: string;
|
|
106
|
+
readonly continuity: EngineRepointContinuity;
|
|
102
107
|
readonly record: 'moved' | 'already-there';
|
|
103
108
|
readonly sessions: EngineRepointSessionTally;
|
|
104
109
|
readonly proofs: EngineRepointProofs;
|
|
@@ -107,6 +112,7 @@ export interface EngineRepointPartial {
|
|
|
107
112
|
readonly kind: 'partial';
|
|
108
113
|
readonly stage: 'sessions' | 'sessions-list';
|
|
109
114
|
readonly deviceId: string;
|
|
115
|
+
readonly continuity: EngineRepointContinuity;
|
|
110
116
|
readonly record: 'moved' | 'already-there';
|
|
111
117
|
readonly sessions: EngineRepointSessionTally;
|
|
112
118
|
readonly proofs: EngineRepointProofs;
|
|
@@ -128,6 +134,7 @@ export interface EngineRepointStoreFailed {
|
|
|
128
134
|
readonly kind: 'store-failed';
|
|
129
135
|
readonly stage: 'record';
|
|
130
136
|
readonly deviceId: string;
|
|
137
|
+
readonly continuity: EngineRepointContinuity;
|
|
131
138
|
}
|
|
132
139
|
export interface EngineRepointRefused {
|
|
133
140
|
readonly kind: 'refused';
|
package/dist/engineRepoint.js
CHANGED
|
@@ -58,18 +58,27 @@ async function handshakeProofOf(port, target, signal) {
|
|
|
58
58
|
return freeze({ state: 'unreadable', why: r.code });
|
|
59
59
|
}
|
|
60
60
|
const RO_PROVEN = freeze({ state: 'proven' });
|
|
61
|
-
|
|
61
|
+
function rosterSliceOf(roster, target) {
|
|
62
62
|
if (!isObject(roster))
|
|
63
|
-
return freeze({ state: 'unreadable', why: 'unarmed' });
|
|
63
|
+
return { kind: 'proof', proof: freeze({ state: 'unreadable', why: 'unarmed' }) };
|
|
64
64
|
const credential = dataOf(roster, 'credential');
|
|
65
65
|
if (credential === 'operator')
|
|
66
|
-
return freeze({ state: 'unreadable', why: 'operator-credential' });
|
|
66
|
+
return { kind: 'proof', proof: freeze({ state: 'unreadable', why: 'operator-credential' }) };
|
|
67
67
|
if (credential !== 'non-operator')
|
|
68
|
-
return freeze({ state: 'unreadable', why: 'credential-unknown' });
|
|
69
|
-
const
|
|
70
|
-
if (
|
|
71
|
-
return
|
|
72
|
-
|
|
68
|
+
return { kind: 'proof', proof: freeze({ state: 'unreadable', why: 'credential-unknown' }) };
|
|
69
|
+
const factory = dataOf(roster, 'devices');
|
|
70
|
+
if (typeof factory !== 'function')
|
|
71
|
+
return { kind: 'unbound' };
|
|
72
|
+
let slice;
|
|
73
|
+
try {
|
|
74
|
+
slice = factory(target);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return { kind: 'proof', proof: freeze({ state: 'unreadable', why: 'unreadable' }) };
|
|
78
|
+
}
|
|
79
|
+
return slice === null || slice === undefined ? { kind: 'unbound' } : { kind: 'slice', devices: slice };
|
|
80
|
+
}
|
|
81
|
+
async function rosterProofOf(devices, deviceId, signal) {
|
|
73
82
|
const out = await listDevices(devices, signal !== undefined ? { signal } : undefined);
|
|
74
83
|
switch (out.kind) {
|
|
75
84
|
case 'ok':
|
|
@@ -123,7 +132,17 @@ async function recordFactOf(read, port, address) {
|
|
|
123
132
|
const deviceId = dataOf(raw, 'deviceId');
|
|
124
133
|
return typeof deviceId === 'string' && deviceId !== '' ? freeze({ k: 'active', deviceId }) : UNREADABLE_RECORD;
|
|
125
134
|
}
|
|
126
|
-
function
|
|
135
|
+
function continuityReadingOf(raw) {
|
|
136
|
+
if (!isObject(raw))
|
|
137
|
+
return undefined;
|
|
138
|
+
const deviceId = dataOf(raw, 'deviceId');
|
|
139
|
+
const f = addressOf(dataOf(raw, 'from'), false);
|
|
140
|
+
const t = addressOf(dataOf(raw, 'to'), true);
|
|
141
|
+
if (typeof deviceId !== 'string' || deviceId === '' || f === undefined || t === undefined)
|
|
142
|
+
return undefined;
|
|
143
|
+
return freeze({ deviceId, fromKey: engineNamespaceKeyFor(f), toKey: engineNamespaceKeyFor(t) });
|
|
144
|
+
}
|
|
145
|
+
function planOf(from, to, evidence, pair) {
|
|
127
146
|
if (from.k === 'unreadable' || to.k === 'unreadable')
|
|
128
147
|
return { kind: 'refused', reason: 'record-unreadable' };
|
|
129
148
|
if (from.k === 'other')
|
|
@@ -135,8 +154,10 @@ function planOf(from, to, expectDeviceId) {
|
|
|
135
154
|
return { kind: 'move', deviceId: from.deviceId };
|
|
136
155
|
return { kind: 'refused', reason: 'target-occupied' };
|
|
137
156
|
}
|
|
138
|
-
if (to.k === 'active')
|
|
139
|
-
|
|
157
|
+
if (to.k === 'active') {
|
|
158
|
+
const bound = evidence !== undefined && to.deviceId === evidence.deviceId && evidence.fromKey === pair.fromKey && evidence.toKey === pair.toKey;
|
|
159
|
+
return bound ? { kind: 'finish', deviceId: to.deviceId } : { kind: 'refused', reason: 'record-absent' };
|
|
160
|
+
}
|
|
140
161
|
if (to.k === 'other')
|
|
141
162
|
return { kind: 'refused', reason: 'record-not-active', recordStatus: to.status };
|
|
142
163
|
return { kind: 'refused', reason: 'record-absent' };
|
|
@@ -188,14 +209,17 @@ export async function repointEngine(input, ports) {
|
|
|
188
209
|
const roster = dataOf(ports, 'roster');
|
|
189
210
|
const signalRaw = dataOf(ports, 'signal');
|
|
190
211
|
const signal = isObject(signalRaw) ? signalRaw : undefined;
|
|
191
|
-
const
|
|
192
|
-
const expectDeviceId = typeof expectRaw === 'string' && expectRaw !== '' ? expectRaw : undefined;
|
|
212
|
+
const evidence = continuityReadingOf(dataOf(input, 'continuity'));
|
|
193
213
|
const [fromFact, toFact] = await Promise.all([recordFactOf(read, recordPort, from), recordFactOf(read, recordPort, to)]);
|
|
194
|
-
const plan = planOf(fromFact, toFact,
|
|
214
|
+
const plan = planOf(fromFact, toFact, evidence, { fromKey: engineNamespaceKeyFor(from), toKey: engineNamespaceKeyFor(to) });
|
|
195
215
|
if (plan.kind === 'refused')
|
|
196
216
|
return refusedOf(plan.reason, plan.recordStatus);
|
|
197
217
|
const deviceId = plan.deviceId;
|
|
198
|
-
const
|
|
218
|
+
const continuity = freeze({ deviceId, from, to });
|
|
219
|
+
const slice = rosterSliceOf(roster, to);
|
|
220
|
+
if (slice.kind === 'unbound')
|
|
221
|
+
return refusedOf('roster-unbound');
|
|
222
|
+
const [hs, ro] = await Promise.all([handshakeProofOf(handshake, freeze({ address: to, deviceId }), signal), slice.kind === 'proof' ? slice.proof : rosterProofOf(slice.devices, deviceId, signal)]);
|
|
199
223
|
const proofs = freeze({ handshake: hs, roster: ro });
|
|
200
224
|
const verdict = engineRepointVerdictOf(proofs);
|
|
201
225
|
if (verdict !== 'apply') {
|
|
@@ -217,7 +241,7 @@ export async function repointEngine(input, ports) {
|
|
|
217
241
|
moved = afterFrom.k === 'absent' && afterTo.k === 'active' && afterTo.deviceId === deviceId;
|
|
218
242
|
}
|
|
219
243
|
if (!moved)
|
|
220
|
-
return freeze({ kind: 'store-failed', stage: 'record', deviceId });
|
|
244
|
+
return freeze({ kind: 'store-failed', stage: 'record', deviceId, continuity });
|
|
221
245
|
}
|
|
222
246
|
const record = plan.kind === 'move' ? 'moved' : 'already-there';
|
|
223
247
|
const counts = { moved: 0, unaffected: 0, conflict: 0, unreadable: 0, storeFailed: 0 };
|
|
@@ -229,7 +253,7 @@ export async function repointEngine(input, ports) {
|
|
|
229
253
|
stores = undefined;
|
|
230
254
|
}
|
|
231
255
|
if (stores === undefined)
|
|
232
|
-
return freeze({ kind: 'partial', stage: 'sessions-list', deviceId, record, sessions: tallyOf(counts), proofs });
|
|
256
|
+
return freeze({ kind: 'partial', stage: 'sessions-list', deviceId, continuity, record, sessions: tallyOf(counts), proofs });
|
|
233
257
|
for (const store of stores) {
|
|
234
258
|
const r = repointSessionEngine(store, from, to);
|
|
235
259
|
if (r === 'moved')
|
|
@@ -245,8 +269,8 @@ export async function repointEngine(input, ports) {
|
|
|
245
269
|
}
|
|
246
270
|
const sessions = tallyOf(counts);
|
|
247
271
|
return counts.conflict + counts.unreadable + counts.storeFailed > 0
|
|
248
|
-
? freeze({ kind: 'partial', stage: 'sessions', deviceId, record, sessions, proofs })
|
|
249
|
-
: freeze({ kind: 'repointed', deviceId, record, sessions, proofs });
|
|
272
|
+
? freeze({ kind: 'partial', stage: 'sessions', deviceId, continuity, record, sessions, proofs })
|
|
273
|
+
: freeze({ kind: 'repointed', deviceId, continuity, record, sessions, proofs });
|
|
250
274
|
}
|
|
251
275
|
catch {
|
|
252
276
|
return refusedOf('unarmed');
|
|
@@ -259,6 +283,7 @@ const REFUSAL_KEYS = {
|
|
|
259
283
|
'record-not-active': 'session.devices.repoint.refused.recordNotActive',
|
|
260
284
|
'record-unreadable': 'session.devices.repoint.refused.recordUnreadable',
|
|
261
285
|
'target-occupied': 'session.devices.repoint.refused.targetOccupied',
|
|
286
|
+
'roster-unbound': 'session.devices.repoint.refused.rosterUnbound',
|
|
262
287
|
unarmed: 'session.devices.repoint.refused.unarmed',
|
|
263
288
|
};
|
|
264
289
|
export function engineRepointMessageOf(result) {
|
package/dist/engineRowRef.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AgentEvent } from '@sema-agent/sdk';
|
|
2
|
+
import { type GateOutcomeView } from './gateOutcome.js';
|
|
2
3
|
export interface EngineRowRef {
|
|
3
4
|
readonly eventId?: string;
|
|
4
5
|
readonly taskId?: string;
|
|
@@ -22,7 +23,9 @@ export declare function terminalRunIdOf(ev: Extract<AgentEvent, {
|
|
|
22
23
|
} | {
|
|
23
24
|
type: 'failed';
|
|
24
25
|
}>): string | undefined;
|
|
26
|
+
export declare const SEMA_REFUSAL_NOTED_KEY = "_sema_refusal_noted";
|
|
25
27
|
export declare const TRANSCRIPT_ROW_SIDECAR_KEYS: readonly string[];
|
|
28
|
+
export declare function refusalNotedOf(denialKind: string | undefined, gate: GateOutcomeView | undefined): boolean;
|
|
26
29
|
export declare function parkHeldCallIdOf(carrier: {} | null | undefined): string | undefined;
|
|
27
30
|
export declare function parkedToolEndRoleOf(frame: {} | null | undefined): 'held' | 'collateral' | undefined;
|
|
28
31
|
export declare function isUnsettledHeldToolEnd(frame: {} | null | undefined): boolean;
|
package/dist/engineRowRef.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { guardedRead, readFrameStringKey, readOwnDataNonEmptyString, readOwnDataValue } from './ownKey.js';
|
|
2
2
|
import { GATE_PARKED_ERROR_CODE } from './engineErrorCodes.js';
|
|
3
|
-
import { isCcToolDenialKind } from './gateVocabulary.js';
|
|
3
|
+
import { ccToolDenialKindForSettledBy, isCcToolDenialKind } from './gateVocabulary.js';
|
|
4
|
+
import { gateDeniedBy } from './gateOutcome.js';
|
|
4
5
|
import { doneResultView } from './replayBody.js';
|
|
5
6
|
import { TRANSCRIPT_ID_NAMESPACE, uuidV5 } from './uuidV5.js';
|
|
6
7
|
const ENGINE_REF_KEY = '_sema_engine_ref';
|
|
@@ -77,6 +78,7 @@ export function terminalRunIdOf(ev) {
|
|
|
77
78
|
const v = doneResultView(ev.result).body?.runId;
|
|
78
79
|
return typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
79
80
|
}
|
|
81
|
+
export const SEMA_REFUSAL_NOTED_KEY = '_sema_refusal_noted';
|
|
80
82
|
export const TRANSCRIPT_ROW_SIDECAR_KEYS = Object.freeze([
|
|
81
83
|
'_sema_segment_id',
|
|
82
84
|
'_sema_engine_ref',
|
|
@@ -93,6 +95,7 @@ export const TRANSCRIPT_ROW_SIDECAR_KEYS = Object.freeze([
|
|
|
93
95
|
'_sema_interrupted_never_started',
|
|
94
96
|
'_sema_approval_window_expired',
|
|
95
97
|
'_sema_denial_limit_window_expired',
|
|
98
|
+
SEMA_REFUSAL_NOTED_KEY,
|
|
96
99
|
'_sema_api_error_status',
|
|
97
100
|
'_sema_api_error_code',
|
|
98
101
|
'_sema_provider_condition',
|
|
@@ -105,6 +108,13 @@ export const TRANSCRIPT_ROW_SIDECAR_KEYS = Object.freeze([
|
|
|
105
108
|
'_sema_run_origin',
|
|
106
109
|
'_sema_run_origin_unreadable',
|
|
107
110
|
]);
|
|
111
|
+
export function refusalNotedOf(denialKind, gate) {
|
|
112
|
+
if (denialKind !== ccToolDenialKindForSettledBy('human'))
|
|
113
|
+
return false;
|
|
114
|
+
if (gateDeniedBy(gate) !== 'ask_resolution')
|
|
115
|
+
return false;
|
|
116
|
+
return gate?.settlement?.kind === 'human_refused' && gate.settlement.note !== undefined;
|
|
117
|
+
}
|
|
108
118
|
export function parkHeldCallIdOf(carrier) {
|
|
109
119
|
return readFrameStringKey(carrier, 'toolCallId');
|
|
110
120
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ApprovalsFeedEmission } from './hitl/approvalsFeed.js';
|
|
2
|
+
import type { LocalEngineUpgradePath, LocalEngineVerdict } from './localEngineJudge.js';
|
|
3
|
+
import type { RequestLane } from './request/taskRequest.js';
|
|
4
|
+
import type { UiLanguage } from './env/uiLanguage.js';
|
|
5
|
+
export declare const ENGINE_DRAIN_CLASSES: readonly ["parked_tool_approvals"];
|
|
6
|
+
export type EngineDrainClass = (typeof ENGINE_DRAIN_CLASSES)[number];
|
|
7
|
+
export interface EngineDrainSession {
|
|
8
|
+
readonly sessionId: string;
|
|
9
|
+
readonly count: number;
|
|
10
|
+
}
|
|
11
|
+
export type EngineDrainReading = {
|
|
12
|
+
readonly class: EngineDrainClass;
|
|
13
|
+
readonly state: 'count';
|
|
14
|
+
readonly count: number;
|
|
15
|
+
readonly sessions: readonly EngineDrainSession[];
|
|
16
|
+
} | {
|
|
17
|
+
readonly class: EngineDrainClass;
|
|
18
|
+
readonly state: 'failed';
|
|
19
|
+
} | {
|
|
20
|
+
readonly class: EngineDrainClass;
|
|
21
|
+
readonly state: 'unsupported';
|
|
22
|
+
};
|
|
23
|
+
export type EngineSwapCrossing = 'newer' | 'same' | 'older' | 'none' | 'unknown';
|
|
24
|
+
export interface EngineSwapCrossingFacts {
|
|
25
|
+
readonly engine: LocalEngineVerdict;
|
|
26
|
+
readonly drain?: readonly EngineDrainReading[];
|
|
27
|
+
readonly informed?: string;
|
|
28
|
+
}
|
|
29
|
+
export type EngineSwapCrossingVerdict = {
|
|
30
|
+
readonly kind: 'clear';
|
|
31
|
+
readonly crossing: 'none' | 'same' | 'older' | 'unknown';
|
|
32
|
+
} | {
|
|
33
|
+
readonly kind: 'hold';
|
|
34
|
+
readonly reason: 'newer_engine';
|
|
35
|
+
readonly crossing: 'newer';
|
|
36
|
+
readonly running: string;
|
|
37
|
+
readonly own: string;
|
|
38
|
+
readonly source: 'running' | 'high-water';
|
|
39
|
+
} | {
|
|
40
|
+
readonly kind: 'hold';
|
|
41
|
+
readonly reason: 'drain_unconfirmed';
|
|
42
|
+
readonly crossing: 'older';
|
|
43
|
+
readonly classes: readonly EngineDrainReading[];
|
|
44
|
+
readonly fingerprint: string;
|
|
45
|
+
} | {
|
|
46
|
+
readonly kind: 'unknown';
|
|
47
|
+
readonly crossing: 'unknown';
|
|
48
|
+
readonly classes: readonly EngineDrainReading[];
|
|
49
|
+
readonly fingerprint: string;
|
|
50
|
+
} | {
|
|
51
|
+
readonly kind: 'swap_and_tell';
|
|
52
|
+
readonly crossing: 'older' | 'unknown';
|
|
53
|
+
readonly classes: readonly EngineDrainReading[];
|
|
54
|
+
};
|
|
55
|
+
export declare function engineSwapCrossingVerdict(facts: EngineSwapCrossingFacts): EngineSwapCrossingVerdict;
|
|
56
|
+
export declare function parkedToolApprovalsBySession(emission: ApprovalsFeedEmission | null | undefined): EngineDrainReading;
|
|
57
|
+
export declare function engineSwapCrossingText(verdict: EngineSwapCrossingVerdict, lane: Exclude<RequestLane, 'utility'>, upgrade: LocalEngineUpgradePath | undefined, lang?: UiLanguage): string | undefined;
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { checkpointQueueRowSortOf } from './hitl/approvalQueueRow.js';
|
|
2
|
+
import { durableDecideLegOf } from './adapter/activeRunSelfHeal.js';
|
|
3
|
+
import { checkpointRowVerdictOf } from './checkpointRowVerdict.js';
|
|
4
|
+
import { compareReleaseVersions } from './supportFloor.js';
|
|
5
|
+
import { engineIdentityOf } from './engineIdentity.js';
|
|
6
|
+
import { slotReadOf } from './wireFailureShape.js';
|
|
7
|
+
import { msg, renderMessage } from './i18n/translate.js';
|
|
8
|
+
export const ENGINE_DRAIN_CLASSES = Object.freeze(['parked_tool_approvals']);
|
|
9
|
+
function own(o, key) {
|
|
10
|
+
if (typeof o !== 'object' || o === null)
|
|
11
|
+
return undefined;
|
|
12
|
+
const r = slotReadOf(o, key);
|
|
13
|
+
return r.presence === 'present' ? r.value : undefined;
|
|
14
|
+
}
|
|
15
|
+
function isArray(v) {
|
|
16
|
+
try {
|
|
17
|
+
return Array.isArray(v);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function isObject(v) {
|
|
24
|
+
return typeof v === 'object' && v !== null && !isArray(v);
|
|
25
|
+
}
|
|
26
|
+
function isCount(v) {
|
|
27
|
+
return typeof v === 'number' && Number.isSafeInteger(v) && v >= 0;
|
|
28
|
+
}
|
|
29
|
+
function nonEmpty(v) {
|
|
30
|
+
return typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
31
|
+
}
|
|
32
|
+
function elementsOf(v) {
|
|
33
|
+
if (!isArray(v))
|
|
34
|
+
return undefined;
|
|
35
|
+
const len = own(v, 'length');
|
|
36
|
+
if (!isCount(len))
|
|
37
|
+
return undefined;
|
|
38
|
+
const out = [];
|
|
39
|
+
for (let i = 0; i < len; i++) {
|
|
40
|
+
const r = slotReadOf(v, String(i));
|
|
41
|
+
if (r.presence !== 'present')
|
|
42
|
+
return undefined;
|
|
43
|
+
out.push(r.value);
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
function failedOf(c) {
|
|
48
|
+
return Object.freeze({ class: c, state: 'failed' });
|
|
49
|
+
}
|
|
50
|
+
function crossingOf(v) {
|
|
51
|
+
const kind = own(v, 'kind');
|
|
52
|
+
if (kind === 'none')
|
|
53
|
+
return { crossing: 'none' };
|
|
54
|
+
if (kind === 'attach')
|
|
55
|
+
return isObject(own(v, 'engine')) ? { crossing: 'same' } : { crossing: 'unknown' };
|
|
56
|
+
if (kind === 'ahead' || kind === 'behind') {
|
|
57
|
+
const running = own(v, 'running');
|
|
58
|
+
const mine = own(v, 'own');
|
|
59
|
+
if (typeof running !== 'string' || typeof mine !== 'string')
|
|
60
|
+
return { crossing: 'unknown' };
|
|
61
|
+
const cmp = compareReleaseVersions(running, mine);
|
|
62
|
+
if (kind === 'ahead')
|
|
63
|
+
return cmp === -1 && isObject(own(v, 'engine')) ? { crossing: 'older' } : { crossing: 'unknown' };
|
|
64
|
+
const source = own(v, 'source');
|
|
65
|
+
if (cmp !== 1 || (source !== 'running' && source !== 'high-water'))
|
|
66
|
+
return { crossing: 'unknown' };
|
|
67
|
+
return { crossing: 'newer', running, own: mine, source };
|
|
68
|
+
}
|
|
69
|
+
return { crossing: 'unknown' };
|
|
70
|
+
}
|
|
71
|
+
function targetOf(v) {
|
|
72
|
+
const engine = own(v, 'engine');
|
|
73
|
+
const anchored = own(v, 'anchored');
|
|
74
|
+
const src = isObject(engine) ? own(engine, 'identity') : isObject(anchored) ? anchored : v;
|
|
75
|
+
const id = engineIdentityOf({ pid: own(src, 'pid'), instanceId: own(src, 'instanceId'), startedAt: own(src, 'startedAt') });
|
|
76
|
+
return { ...(id.pid !== undefined ? { pid: id.pid } : {}), ...(id.instanceId !== undefined ? { instanceId: id.instanceId } : {}), ...(id.startedAt !== undefined ? { startedAt: id.startedAt } : {}) };
|
|
77
|
+
}
|
|
78
|
+
function drainEntryOf(e) {
|
|
79
|
+
if (!isObject(e))
|
|
80
|
+
return undefined;
|
|
81
|
+
const c = own(e, 'class');
|
|
82
|
+
if (!ENGINE_DRAIN_CLASSES.includes(c))
|
|
83
|
+
return undefined;
|
|
84
|
+
const cls = c;
|
|
85
|
+
const state = own(e, 'state');
|
|
86
|
+
if (state === 'failed')
|
|
87
|
+
return failedOf(cls);
|
|
88
|
+
if (state === 'unsupported')
|
|
89
|
+
return Object.freeze({ class: cls, state: 'unsupported' });
|
|
90
|
+
if (state !== 'count')
|
|
91
|
+
return undefined;
|
|
92
|
+
const count = own(e, 'count');
|
|
93
|
+
const rows = elementsOf(own(e, 'sessions'));
|
|
94
|
+
if (!isCount(count) || rows === undefined)
|
|
95
|
+
return undefined;
|
|
96
|
+
const sessions = [];
|
|
97
|
+
const seen = new Set();
|
|
98
|
+
let sum = 0;
|
|
99
|
+
for (const s of rows) {
|
|
100
|
+
const sessionId = nonEmpty(own(s, 'sessionId'));
|
|
101
|
+
const n = own(s, 'count');
|
|
102
|
+
if (sessionId === undefined || !isCount(n) || n === 0 || seen.has(sessionId))
|
|
103
|
+
return undefined;
|
|
104
|
+
seen.add(sessionId);
|
|
105
|
+
sum += n;
|
|
106
|
+
sessions.push(Object.freeze({ sessionId, count: n }));
|
|
107
|
+
}
|
|
108
|
+
if (sum !== count)
|
|
109
|
+
return undefined;
|
|
110
|
+
sessions.sort((a, b) => (a.sessionId < b.sessionId ? -1 : a.sessionId > b.sessionId ? 1 : 0));
|
|
111
|
+
return Object.freeze({ class: cls, state: 'count', count, sessions: Object.freeze(sessions) });
|
|
112
|
+
}
|
|
113
|
+
function drainOf(raw) {
|
|
114
|
+
const entries = elementsOf(raw);
|
|
115
|
+
const byClass = new Map();
|
|
116
|
+
let bad = entries === undefined;
|
|
117
|
+
for (const e of entries ?? []) {
|
|
118
|
+
const r = drainEntryOf(e);
|
|
119
|
+
if (r === undefined || byClass.has(r.class)) {
|
|
120
|
+
bad = true;
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
byClass.set(r.class, r);
|
|
124
|
+
}
|
|
125
|
+
return Object.freeze(ENGINE_DRAIN_CLASSES.map(c => (bad ? undefined : byClass.get(c)) ?? failedOf(c)));
|
|
126
|
+
}
|
|
127
|
+
function fingerprintOf(crossing, v, drain) {
|
|
128
|
+
const running = own(v, 'running');
|
|
129
|
+
const mine = own(v, 'own');
|
|
130
|
+
return JSON.stringify([
|
|
131
|
+
'engine-swap-crossing/1',
|
|
132
|
+
crossing,
|
|
133
|
+
targetOf(v),
|
|
134
|
+
typeof running === 'string' ? running : null,
|
|
135
|
+
typeof mine === 'string' ? mine : null,
|
|
136
|
+
drain.map(r => (r.state === 'count' ? [r.class, r.state, r.count, r.sessions.map(s => [s.sessionId, s.count])] : [r.class, r.state])),
|
|
137
|
+
]);
|
|
138
|
+
}
|
|
139
|
+
function judge(facts) {
|
|
140
|
+
const engine = own(facts, 'engine');
|
|
141
|
+
const d = crossingOf(engine);
|
|
142
|
+
if (d.crossing === 'newer') {
|
|
143
|
+
return Object.freeze({ kind: 'hold', reason: 'newer_engine', crossing: 'newer', running: d.running, own: d.own, source: d.source });
|
|
144
|
+
}
|
|
145
|
+
if (d.crossing === 'none' || d.crossing === 'same')
|
|
146
|
+
return Object.freeze({ kind: 'clear', crossing: d.crossing });
|
|
147
|
+
const drain = drainOf(own(facts, 'drain'));
|
|
148
|
+
const classes = Object.freeze(drain.filter(r => !(r.state === 'count' && r.count === 0)));
|
|
149
|
+
const holding = drain.some(r => r.state === 'failed' || (r.state === 'count' && r.count > 0));
|
|
150
|
+
const fingerprint = fingerprintOf(d.crossing, engine, drain);
|
|
151
|
+
const informedRaw = own(facts, 'informed');
|
|
152
|
+
const informed = typeof informedRaw === 'string' && informedRaw === fingerprint;
|
|
153
|
+
if (d.crossing === 'older') {
|
|
154
|
+
if (!holding)
|
|
155
|
+
return Object.freeze({ kind: 'clear', crossing: 'older' });
|
|
156
|
+
if (informed)
|
|
157
|
+
return Object.freeze({ kind: 'swap_and_tell', crossing: 'older', classes });
|
|
158
|
+
return Object.freeze({ kind: 'hold', reason: 'drain_unconfirmed', crossing: 'older', classes, fingerprint });
|
|
159
|
+
}
|
|
160
|
+
if (informed) {
|
|
161
|
+
return holding
|
|
162
|
+
? Object.freeze({ kind: 'swap_and_tell', crossing: 'unknown', classes })
|
|
163
|
+
: Object.freeze({ kind: 'clear', crossing: 'unknown' });
|
|
164
|
+
}
|
|
165
|
+
return Object.freeze({ kind: 'unknown', crossing: 'unknown', classes, fingerprint });
|
|
166
|
+
}
|
|
167
|
+
const UNKNOWN_ALL_FAILED = () => {
|
|
168
|
+
const drain = Object.freeze(ENGINE_DRAIN_CLASSES.map(failedOf));
|
|
169
|
+
return Object.freeze({ kind: 'unknown', crossing: 'unknown', classes: drain, fingerprint: fingerprintOf('unknown', undefined, drain) });
|
|
170
|
+
};
|
|
171
|
+
export function engineSwapCrossingVerdict(facts) {
|
|
172
|
+
try {
|
|
173
|
+
return judge(facts);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return UNKNOWN_ALL_FAILED();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const PARKED = 'parked_tool_approvals';
|
|
180
|
+
function parkedOf(emission) {
|
|
181
|
+
if (!isObject(emission) || own(emission, 'kind') !== 'snapshot')
|
|
182
|
+
return failedOf(PARKED);
|
|
183
|
+
const droppedSlot = slotReadOf(emission, 'pendingDropped');
|
|
184
|
+
if (droppedSlot.presence === 'unreadable')
|
|
185
|
+
return failedOf(PARKED);
|
|
186
|
+
if (droppedSlot.presence === 'present' && droppedSlot.value !== undefined && !(isCount(droppedSlot.value) && droppedSlot.value === 0))
|
|
187
|
+
return failedOf(PARKED);
|
|
188
|
+
const rows = elementsOf(own(emission, 'pending'));
|
|
189
|
+
if (rows === undefined)
|
|
190
|
+
return failedOf(PARKED);
|
|
191
|
+
const tally = new Map();
|
|
192
|
+
for (const row of rows) {
|
|
193
|
+
if (!isObject(row))
|
|
194
|
+
return failedOf(PARKED);
|
|
195
|
+
const sort = checkpointQueueRowSortOf(row);
|
|
196
|
+
if (sort === 'excluded')
|
|
197
|
+
continue;
|
|
198
|
+
if (sort === 'unreadable')
|
|
199
|
+
return failedOf(PARKED);
|
|
200
|
+
const gate = slotReadOf(row, 'gateKind');
|
|
201
|
+
const leg = gate.presence === 'unreadable' ? 'unreadable' : durableDecideLegOf({ gateKind: typeof gate.value === 'string' ? gate.value : null });
|
|
202
|
+
if (leg === 'plan_review' || leg === 'other_gate')
|
|
203
|
+
continue;
|
|
204
|
+
const verdict = checkpointRowVerdictOf(row);
|
|
205
|
+
if (verdict !== undefined && verdict.kind !== 'ok')
|
|
206
|
+
continue;
|
|
207
|
+
if (leg !== 'approval')
|
|
208
|
+
return failedOf(PARKED);
|
|
209
|
+
const sessionId = nonEmpty(own(row, 'sessionId'));
|
|
210
|
+
if (sessionId === undefined)
|
|
211
|
+
return failedOf(PARKED);
|
|
212
|
+
tally.set(sessionId, (tally.get(sessionId) ?? 0) + 1);
|
|
213
|
+
}
|
|
214
|
+
const sessions = [...tally.entries()]
|
|
215
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
216
|
+
.map(([sessionId, count]) => Object.freeze({ sessionId, count }));
|
|
217
|
+
const count = sessions.reduce((n, s) => n + s.count, 0);
|
|
218
|
+
return Object.freeze({ class: PARKED, state: 'count', count, sessions: Object.freeze(sessions) });
|
|
219
|
+
}
|
|
220
|
+
export function parkedToolApprovalsBySession(emission) {
|
|
221
|
+
try {
|
|
222
|
+
return parkedOf(emission);
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
return failedOf(PARKED);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function say(key, lang, params) {
|
|
229
|
+
return renderMessage(msg(key, params), lang);
|
|
230
|
+
}
|
|
231
|
+
function classLine(r, lang) {
|
|
232
|
+
const state = own(r, 'state');
|
|
233
|
+
if (state === 'failed')
|
|
234
|
+
return say('localEngine.swapGuard.parked.failed', lang);
|
|
235
|
+
if (state === 'unsupported')
|
|
236
|
+
return say('localEngine.swapGuard.parked.unsupported', lang);
|
|
237
|
+
const count = own(r, 'count');
|
|
238
|
+
if (state !== 'count' || !isCount(count) || count === 0)
|
|
239
|
+
return undefined;
|
|
240
|
+
if (count === 1)
|
|
241
|
+
return say('localEngine.swapGuard.parked.one', lang);
|
|
242
|
+
const sessions = elementsOf(own(r, 'sessions'));
|
|
243
|
+
if (sessions !== undefined && sessions.length > 1)
|
|
244
|
+
return say('localEngine.swapGuard.parked.manySessions', lang, { count, sessions: sessions.length });
|
|
245
|
+
return say('localEngine.swapGuard.parked.oneSession', lang, { count });
|
|
246
|
+
}
|
|
247
|
+
function toldLine(r, lang) {
|
|
248
|
+
const state = own(r, 'state');
|
|
249
|
+
if (state === 'failed' || state === 'unsupported')
|
|
250
|
+
return say('localEngine.swapGuard.told.unknown', lang);
|
|
251
|
+
const count = own(r, 'count');
|
|
252
|
+
if (state !== 'count' || !isCount(count) || count === 0)
|
|
253
|
+
return undefined;
|
|
254
|
+
return count === 1 ? say('localEngine.swapGuard.told.one', lang) : say('localEngine.swapGuard.told.other', lang, { count });
|
|
255
|
+
}
|
|
256
|
+
function classesOf(v) {
|
|
257
|
+
return elementsOf(own(v, 'classes')) ?? [];
|
|
258
|
+
}
|
|
259
|
+
function joined(parts, lang) {
|
|
260
|
+
const out = parts.filter((p) => typeof p === 'string' && p.length > 0);
|
|
261
|
+
return out.length > 0 ? out.join(lang === 'zh' ? '' : ' ') : undefined;
|
|
262
|
+
}
|
|
263
|
+
export function engineSwapCrossingText(verdict, lane, upgrade, lang) {
|
|
264
|
+
try {
|
|
265
|
+
const kind = own(verdict, 'kind');
|
|
266
|
+
const reason = own(verdict, 'reason');
|
|
267
|
+
if (kind === 'hold' && reason === 'newer_engine') {
|
|
268
|
+
const running = own(verdict, 'running');
|
|
269
|
+
const mine = own(verdict, 'own');
|
|
270
|
+
const source = own(verdict, 'source');
|
|
271
|
+
if (typeof running !== 'string' || typeof mine !== 'string' || compareReleaseVersions(running, mine) !== 1)
|
|
272
|
+
return undefined;
|
|
273
|
+
if (source !== 'running' && source !== 'high-water')
|
|
274
|
+
return undefined;
|
|
275
|
+
const head = say(source === 'high-water' ? 'localEngine.swapGuard.newer.highWater' : 'localEngine.swapGuard.newer', lang, { running, own: mine });
|
|
276
|
+
const way = upgrade === 'cli-update'
|
|
277
|
+
? say('localEngine.swapGuard.newer.cliUpdate', lang)
|
|
278
|
+
: upgrade === 'app-update'
|
|
279
|
+
? say('localEngine.swapGuard.newer.appUpdate', lang)
|
|
280
|
+
: undefined;
|
|
281
|
+
return joined([head, way], lang);
|
|
282
|
+
}
|
|
283
|
+
const wayOut = () => say(lane === 'interactive' ? 'localEngine.swapGuard.wayOut.interactive' : 'localEngine.swapGuard.wayOut.print', lang);
|
|
284
|
+
if (kind === 'hold' && reason === 'drain_unconfirmed') {
|
|
285
|
+
return joined([say('localEngine.swapGuard.hold', lang), ...classesOf(verdict).map(r => classLine(r, lang)), wayOut()], lang);
|
|
286
|
+
}
|
|
287
|
+
if (kind === 'unknown') {
|
|
288
|
+
return joined([say('localEngine.swapGuard.unknown', lang), ...classesOf(verdict).map(r => classLine(r, lang)), wayOut()], lang);
|
|
289
|
+
}
|
|
290
|
+
if (kind === 'swap_and_tell')
|
|
291
|
+
return joined(classesOf(verdict).map(r => toldLine(r, lang)), lang);
|
|
292
|
+
return undefined;
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
return undefined;
|
|
296
|
+
}
|
|
297
|
+
}
|