@sema-agent/client-core 0.108.0 → 0.110.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 +294 -0
- package/README.md +77 -40
- package/dist/adapt/arms.js +41 -13
- package/dist/adapt/textStream.d.ts +2 -0
- package/dist/adapt/textStream.js +37 -16
- package/dist/adapter/activeRunSelfHeal.js +1 -1
- package/dist/adapter/downstream/eventToSdkMessage.js +11 -5
- package/dist/agentSession/engineAgentRegistryRead.js +2 -0
- package/dist/bashExitReading.d.ts +12 -0
- package/dist/bashExitReading.js +44 -0
- package/dist/checkpointRowVerdict.d.ts +1 -1
- package/dist/checkpointRowVerdict.js +4 -2
- package/dist/compensations.js +1 -1
- package/dist/declared/supportFloorDeclared.js +1 -1
- package/dist/displayUntrusted.js +132 -14
- package/dist/engineAgentAbsence.d.ts +2 -0
- package/dist/engineAgentAbsence.js +28 -0
- package/dist/engineErrorCodes.d.ts +7 -0
- package/dist/engineErrorCodes.js +7 -0
- package/dist/engineHealthFacts.d.ts +26 -0
- package/dist/engineHealthFacts.js +63 -4
- package/dist/engineInlineTaskStats.d.ts +4 -3
- package/dist/engineInlineTaskStats.js +18 -2
- package/dist/engineNoticeCodes.d.ts +138 -0
- package/dist/engineNoticeCodes.js +662 -55
- package/dist/engineRepoint.d.ts +140 -0
- package/dist/engineRepoint.js +285 -0
- package/dist/engineRowRef.js +2 -0
- package/dist/engineSessionMessages.d.ts +2 -1
- package/dist/engineSessionMessages.js +5 -0
- package/dist/errorSupplementRoles.js +5 -1
- package/dist/fleet/fleetProjection.js +3 -2
- package/dist/fleetTaskDesc.js +3 -3
- package/dist/generated/engineFactTables.d.ts +3 -0
- package/dist/generated/engineFactTables.js +18 -0
- package/dist/generated/engineNoticeTables.js +3 -0
- package/dist/generated/sdkFactTables.d.ts +1 -0
- package/dist/generated/sdkFactTables.js +4 -0
- package/dist/hitl/approvalAskers.d.ts +6 -0
- package/dist/hitl/approvalAskers.js +30 -0
- package/dist/hitl/approvalCardReasons.js +25 -4
- package/dist/hitl/approvalRequestFrameReading.d.ts +21 -0
- package/dist/hitl/approvalRequestFrameReading.js +90 -0
- package/dist/hitl/approvalResolution.d.ts +8 -1
- package/dist/hitl/approvalResolution.js +4 -0
- package/dist/hitl/approvalsFeed.js +1 -1
- package/dist/hitl/askGateWire.js +0 -2
- package/dist/hitl/askParkRowRouting.js +5 -2
- package/dist/hitl/automaticAnswerVerdict.d.ts +5 -2
- package/dist/hitl/automaticAnswerVerdict.js +18 -4
- package/dist/hitl/decisionConflict.d.ts +2 -1
- package/dist/hitl/decisionConflict.js +10 -4
- package/dist/hitl/decisionRefusal.js +1 -0
- package/dist/hitl/decisionSend.d.ts +3 -0
- package/dist/hitl/decisionSend.js +29 -11
- package/dist/hitl/editedApprovalAcceptance.d.ts +25 -0
- package/dist/hitl/editedApprovalAcceptance.js +26 -0
- package/dist/hitl/frameRouter.js +13 -19
- package/dist/hitl/gateLedger.d.ts +0 -4
- package/dist/hitl/gateLedger.js +0 -19
- package/dist/hitl/hitlBridge.d.ts +5 -0
- package/dist/hitl/hitlBridge.js +13 -9
- package/dist/hitl/hitlHostSurface.d.ts +1 -0
- package/dist/hitl/hitlHostSurface.js +7 -2
- package/dist/hitl/hostWithdrawal.d.ts +15 -0
- package/dist/hitl/hostWithdrawal.js +38 -0
- package/dist/hitl/livePendingAsk.js +17 -16
- package/dist/hitl/localAllowRule.js +2 -1
- package/dist/hitl/noAllowCause.d.ts +1 -1
- package/dist/hitl/noAllowCause.js +4 -2
- package/dist/hitl/parkExpiryJudgements.js +26 -1
- package/dist/hitl/parkOwnership.d.ts +2 -0
- package/dist/hitl/parkOwnership.js +48 -8
- package/dist/hitl/parkResolver.js +32 -16
- package/dist/hitl/parkedUnresumable.d.ts +13 -0
- package/dist/hitl/parkedUnresumable.js +80 -0
- package/dist/hitl/permissionRuleScope.d.ts +4 -0
- package/dist/hitl/permissionRuleScope.js +31 -0
- package/dist/hitl/persistedRulesWire.d.ts +2 -0
- package/dist/hitl/persistedRulesWire.js +13 -12
- package/dist/hitl/planReviewWire.js +14 -4
- package/dist/hitl/sessionAllowRoute.js +5 -4
- package/dist/hitl/sessionPolicyWire.js +6 -4
- package/dist/hitl/standingRows.d.ts +2 -5
- package/dist/hitl/standingRows.js +30 -15
- package/dist/hitl/toolApprovalWire.d.ts +42 -9
- package/dist/hitl/toolApprovalWire.js +267 -44
- package/dist/hooksWireCaps.js +35 -4
- package/dist/hostSettingsSource.js +2 -0
- package/dist/hostTurnSettings.d.ts +10 -4
- package/dist/hostTurnSettings.js +112 -5
- package/dist/i18n/messages.en.d.ts +251 -0
- package/dist/i18n/messages.en.js +254 -2
- package/dist/i18n/messages.zh.js +264 -2
- package/dist/index.d.ts +48 -12
- package/dist/index.js +29 -7
- package/dist/inheritEnvCapability.js +2 -0
- package/dist/inputAuthor.d.ts +16 -0
- package/dist/inputAuthor.js +44 -0
- package/dist/interruptRefusalNote.d.ts +2 -0
- package/dist/interruptRefusalNote.js +17 -0
- package/dist/liveModelCatalog.d.ts +2 -0
- package/dist/liveModelCatalog.js +21 -7
- package/dist/liveQuestionStore.d.ts +4 -1
- package/dist/liveQuestionStore.js +12 -3
- package/dist/localEngineJudge.d.ts +75 -0
- package/dist/localEngineJudge.js +242 -0
- package/dist/localEngineOccupancy.d.ts +88 -0
- package/dist/localEngineOccupancy.js +269 -0
- package/dist/localEngineSeatReading.d.ts +29 -0
- package/dist/localEngineSeatReading.js +294 -0
- package/dist/localEngineSpawn.d.ts +113 -0
- package/dist/localEngineSpawn.js +486 -0
- package/dist/localEngineStep.d.ts +286 -0
- package/dist/localEngineStep.js +1086 -0
- package/dist/localEngineStopLadder.d.ts +29 -0
- package/dist/localEngineStopLadder.js +139 -0
- package/dist/localEngineSwapCard.d.ts +43 -0
- package/dist/localEngineSwapCard.js +205 -0
- package/dist/localEngineTiming.d.ts +8 -0
- package/dist/localEngineTiming.js +8 -0
- package/dist/mcpEngineLeg.d.ts +3 -0
- package/dist/mcpEngineLeg.js +38 -1
- package/dist/mcpPanel.js +2 -0
- package/dist/mcpReconnect.d.ts +9 -1
- package/dist/mcpReconnect.js +132 -20
- package/dist/mcpWireCaps.js +2 -2
- package/dist/memoryEntriesWire.d.ts +1 -1
- package/dist/memoryEntriesWire.js +5 -0
- package/dist/modelCapabilityProbe.js +4 -3
- package/dist/modsFace.d.ts +8 -1
- package/dist/modsFace.js +27 -0
- package/dist/noticePresentation.d.ts +2 -0
- package/dist/noticePresentation.js +79 -1
- package/dist/notifications.d.ts +17 -6
- package/dist/notifications.js +67 -19
- package/dist/ownKey.d.ts +5 -0
- package/dist/ownKey.js +8 -1
- package/dist/peerFrames.d.ts +40 -2
- package/dist/peerFrames.js +158 -3
- package/dist/pluginHooksWire.d.ts +2 -1
- package/dist/pluginHooksWire.js +56 -1
- package/dist/replayBody.js +10 -2
- package/dist/request/taskRequest.d.ts +5 -0
- package/dist/request/taskRequest.js +138 -21
- package/dist/resumeRefusalCopy.d.ts +1 -3
- package/dist/resumeRefusalCopy.js +2 -1
- package/dist/retryStatus.d.ts +2 -2
- package/dist/retryStatus.js +2 -0
- package/dist/ruleReadings.js +1 -1
- package/dist/seam.d.ts +24 -2
- package/dist/seam.js +15 -2
- package/dist/seatContract.d.ts +125 -10
- package/dist/seatContract.js +373 -7
- package/dist/sessionBackground.js +4 -1
- package/dist/sessionBackgroundHandoff.js +2 -1
- package/dist/sessionEngine.d.ts +2 -0
- package/dist/sessionEngine.js +111 -0
- package/dist/sessionMemoryStatus.d.ts +2 -1
- package/dist/sessionSyncFileHistory.d.ts +4 -0
- package/dist/sessionSyncFileHistory.js +28 -0
- package/dist/sseIdleTriage.d.ts +13 -1
- package/dist/sseIdleTriage.js +24 -6
- package/dist/subagent/engineSubagentTail.js +6 -0
- package/dist/subagentContentStore.d.ts +12 -0
- package/dist/subagentContentStore.js +72 -11
- package/dist/terminalStatuses.d.ts +1 -0
- package/dist/terminalStatuses.js +4 -1
- package/dist/toolResult.js +18 -5
- package/dist/turnFailurePresentation.js +9 -2
- package/dist/typePins.d.ts +11 -0
- package/dist/usageAnalytics.d.ts +17 -0
- package/dist/usageAnalytics.js +44 -2
- package/dist/wireFailureShape.d.ts +22 -5
- package/dist/wireFailureShape.js +47 -26
- package/dist/wireRefusalCopy.d.ts +31 -1
- package/dist/wireRefusalCopy.js +167 -2
- package/docs/INTEGRATION-CLIENTS.md +3269 -21
- package/package.json +5 -5
package/dist/mcpReconnect.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { capForDisplay } from './fleetTaskDesc.js';
|
|
2
2
|
import { ownDataField } from './ownKey.js';
|
|
3
3
|
import { isSessionIdPathSegment } from './sessionIdSegment.js';
|
|
4
|
+
import { inSlotReadOf, inValueOf, slotReadOf } from './wireFailureShape.js';
|
|
4
5
|
import { readMcpAuthorization } from './mcpLiveness.js';
|
|
6
|
+
import { msg, renderMessage } from './i18n/translate.js';
|
|
5
7
|
export const MCP_RECONNECT_OUTCOMES = Object.freeze(['accepted', 'refused', 'unsupported']);
|
|
6
8
|
export const MCP_RECONNECT_TRANSACTION_NOTICE = 'Reconnecting closes the current connection before dialing again. On a healthy server this is a transaction, ' +
|
|
7
9
|
'not a refresh: a failed re-dial leaves the server down and withdraws its tools from the model until a later re-dial succeeds.';
|
|
8
|
-
export const MCP_RECONNECT_UNSETTLED_NOTICE = '
|
|
9
|
-
"Check the server's state before re-dialing it again — a second request that arrives after the dial has settled closes the connection and dials once more.";
|
|
10
|
+
export const MCP_RECONNECT_UNSETTLED_NOTICE = renderMessage(msg('session.mcpReconnect.unsettled'));
|
|
10
11
|
const UNSETTLED_CODE = 'state.mcp_reconnect_unsettled';
|
|
11
12
|
const _sdkClientIsAnInjection = [true];
|
|
12
13
|
void _sdkClientIsAnInjection;
|
|
@@ -82,8 +83,11 @@ export function projectMcpReconnectResult(body) {
|
|
|
82
83
|
added,
|
|
83
84
|
removed,
|
|
84
85
|
};
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
const names = inValueOf(body, 'toolNames');
|
|
87
|
+
if (names.state === 'unproven')
|
|
88
|
+
return undefined;
|
|
89
|
+
if (names.state === 'present' && names.value !== undefined) {
|
|
90
|
+
const toolNames = stringList(names.value);
|
|
87
91
|
if (toolNames === undefined)
|
|
88
92
|
return undefined;
|
|
89
93
|
view.toolNames = toolNames;
|
|
@@ -93,8 +97,11 @@ export function projectMcpReconnectResult(body) {
|
|
|
93
97
|
const status = projectStatus(body.status);
|
|
94
98
|
if (status !== undefined)
|
|
95
99
|
view.status = status;
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
const incomplete = inValueOf(body, 'listingIncomplete');
|
|
101
|
+
if (incomplete.state === 'unproven')
|
|
102
|
+
return undefined;
|
|
103
|
+
if (incomplete.state === 'present' && incomplete.value !== undefined) {
|
|
104
|
+
const li = projectListingIncomplete(incomplete.value);
|
|
98
105
|
if (li === undefined)
|
|
99
106
|
return undefined;
|
|
100
107
|
view.listingIncomplete = li;
|
|
@@ -116,7 +123,7 @@ function unsettledVerdict(carrier) {
|
|
|
116
123
|
}
|
|
117
124
|
function isUnsettledBody(raw) {
|
|
118
125
|
try {
|
|
119
|
-
return isRecord(raw) &&
|
|
126
|
+
return isRecord(raw) && inSlotReadOf(raw, 'outcome').state === 'absent' && raw.errorCode === UNSETTLED_CODE;
|
|
120
127
|
}
|
|
121
128
|
catch {
|
|
122
129
|
return false;
|
|
@@ -189,23 +196,128 @@ export function projectMcpReconnectCapability(caps) {
|
|
|
189
196
|
return undefined;
|
|
190
197
|
}
|
|
191
198
|
}
|
|
192
|
-
|
|
199
|
+
function reasonTailOf(reason, lang) {
|
|
200
|
+
return reason === undefined ? '' : renderMessage(msg('session.mcpReconnect.reasonTail', { reason }), lang);
|
|
201
|
+
}
|
|
202
|
+
function outcomeSentence(f, lang) {
|
|
203
|
+
switch (f.outcome) {
|
|
204
|
+
case 'accepted':
|
|
205
|
+
return renderMessage(msg(f.toolCount === 1 ? 'session.mcpReconnect.accepted.one' : 'session.mcpReconnect.accepted.other', { server: f.name, toolCount: f.toolCount, added: f.added, removed: f.removed }), lang);
|
|
206
|
+
case 'refused': {
|
|
207
|
+
const why = reasonTailOf(f.reason, lang);
|
|
208
|
+
if (f.roster === undefined)
|
|
209
|
+
return renderMessage(msg('session.mcpReconnect.refused.untouched', { server: f.name, why }), lang);
|
|
210
|
+
if (f.roster === 0)
|
|
211
|
+
return renderMessage(msg('session.mcpReconnect.refused.noTools', { server: f.name, why }), lang);
|
|
212
|
+
return renderMessage(msg(f.roster === 1 ? 'session.mcpReconnect.refused.listed.one' : 'session.mcpReconnect.refused.listed.other', { server: f.name, toolCount: f.roster, why }), lang);
|
|
213
|
+
}
|
|
214
|
+
case 'unsupported':
|
|
215
|
+
return renderMessage(msg('session.mcpReconnect.undeclared', { server: f.name, why: reasonTailOf(f.reason, lang) }), lang);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
export function mcpReconnectOutcomeDetail(view, lang) {
|
|
193
219
|
const name = capForDisplay(view.server, RECONNECT_WORD_MAX);
|
|
194
220
|
switch (view.outcome) {
|
|
195
221
|
case 'accepted':
|
|
196
|
-
return
|
|
222
|
+
return outcomeSentence({ outcome: 'accepted', name, toolCount: view.toolCount, added: view.added.length, removed: view.removed.length }, lang);
|
|
223
|
+
case 'refused':
|
|
224
|
+
return outcomeSentence({ outcome: 'refused', name, roster: view.toolNames?.length, reason: view.reason }, lang);
|
|
225
|
+
case 'unsupported':
|
|
226
|
+
return outcomeSentence({ outcome: 'unsupported', name, reason: view.reason }, lang);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function verdictDetailKey(kind) {
|
|
230
|
+
switch (kind) {
|
|
231
|
+
case 'not_running':
|
|
232
|
+
return 'session.mcpReconnect.notRunning';
|
|
233
|
+
case 'unsupported':
|
|
234
|
+
return 'session.mcpReconnect.unsupported';
|
|
235
|
+
case 'not_found':
|
|
236
|
+
return 'session.mcpReconnect.notFound';
|
|
237
|
+
case 'invalid':
|
|
238
|
+
return 'session.mcpReconnect.invalid';
|
|
239
|
+
case 'failed':
|
|
240
|
+
return 'session.mcpReconnect.failed';
|
|
241
|
+
case 'no_session':
|
|
242
|
+
return 'session.mcpReconnect.noSession';
|
|
243
|
+
case 'unavailable':
|
|
244
|
+
return 'session.mcpReconnect.unavailable';
|
|
245
|
+
case 'unsettled':
|
|
246
|
+
return 'session.mcpReconnect.unsettled';
|
|
247
|
+
default:
|
|
248
|
+
return undefined;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function fieldOf(o, key) {
|
|
252
|
+
const s = slotReadOf(o, key);
|
|
253
|
+
return s.presence === 'present' ? { kind: 'value', value: s.value } : s.presence === 'absent' ? { kind: 'absent' } : { kind: 'unreadable' };
|
|
254
|
+
}
|
|
255
|
+
function okOutcomeFactsOf(verdict, server) {
|
|
256
|
+
const slot = fieldOf(verdict, 'result');
|
|
257
|
+
if (slot.kind !== 'value')
|
|
258
|
+
return undefined;
|
|
259
|
+
const r = slot.value;
|
|
260
|
+
if (r === null || typeof r !== 'object' || Array.isArray(r))
|
|
261
|
+
return undefined;
|
|
262
|
+
const outcome = fieldOf(r, 'outcome');
|
|
263
|
+
const echoed = fieldOf(r, 'server');
|
|
264
|
+
if (outcome.kind !== 'value' || echoed.kind !== 'value' || echoed.value !== server)
|
|
265
|
+
return undefined;
|
|
266
|
+
const rs = fieldOf(r, 'reason');
|
|
267
|
+
if (rs.kind === 'unreadable' || (rs.kind === 'value' && typeof rs.value !== 'string'))
|
|
268
|
+
return undefined;
|
|
269
|
+
const reason = rs.kind === 'value' ? rs.value : undefined;
|
|
270
|
+
const name = capForDisplay(server, RECONNECT_WORD_MAX);
|
|
271
|
+
switch (outcome.value) {
|
|
272
|
+
case 'unsupported':
|
|
273
|
+
return { outcome: 'unsupported', name, reason };
|
|
274
|
+
case 'accepted': {
|
|
275
|
+
const toolCount = fieldOf(r, 'toolCount');
|
|
276
|
+
const added = fieldOf(r, 'added');
|
|
277
|
+
const removed = fieldOf(r, 'removed');
|
|
278
|
+
if (toolCount.kind !== 'value' || added.kind !== 'value' || removed.kind !== 'value')
|
|
279
|
+
return undefined;
|
|
280
|
+
const n = toolCount.value;
|
|
281
|
+
const addedList = stringList(added.value);
|
|
282
|
+
const removedList = stringList(removed.value);
|
|
283
|
+
if (typeof n !== 'number' || !Number.isInteger(n) || n < 0 || addedList === undefined || removedList === undefined)
|
|
284
|
+
return undefined;
|
|
285
|
+
return { outcome: 'accepted', name, toolCount: n, added: addedList.length, removed: removedList.length };
|
|
286
|
+
}
|
|
197
287
|
case 'refused': {
|
|
198
|
-
const
|
|
199
|
-
if (
|
|
200
|
-
return
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
288
|
+
const names = fieldOf(r, 'toolNames');
|
|
289
|
+
if (names.kind === 'unreadable')
|
|
290
|
+
return undefined;
|
|
291
|
+
if (names.kind === 'absent')
|
|
292
|
+
return { outcome: 'refused', name, roster: undefined, reason };
|
|
293
|
+
const roster = stringList(names.value);
|
|
294
|
+
if (roster === undefined)
|
|
295
|
+
return undefined;
|
|
296
|
+
return { outcome: 'refused', name, roster: roster.length, reason };
|
|
206
297
|
}
|
|
207
|
-
|
|
208
|
-
return
|
|
209
|
-
|
|
298
|
+
default:
|
|
299
|
+
return undefined;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
export function mcpReconnectVerdictDetail(verdict, server, lang) {
|
|
303
|
+
try {
|
|
304
|
+
if (verdict === null || typeof verdict !== 'object')
|
|
305
|
+
return undefined;
|
|
306
|
+
const kindSlot = slotReadOf(verdict, 'kind');
|
|
307
|
+
if (kindSlot.presence !== 'present' || typeof kindSlot.value !== 'string')
|
|
308
|
+
return undefined;
|
|
309
|
+
if (!nonEmpty(server))
|
|
310
|
+
return undefined;
|
|
311
|
+
if (kindSlot.value === 'ok') {
|
|
312
|
+
const facts = okOutcomeFactsOf(verdict, server);
|
|
313
|
+
return facts === undefined ? undefined : outcomeSentence(facts, lang);
|
|
314
|
+
}
|
|
315
|
+
const key = verdictDetailKey(kindSlot.value);
|
|
316
|
+
if (key === undefined)
|
|
317
|
+
return undefined;
|
|
318
|
+
return renderMessage(msg(key, { server: capForDisplay(server, RECONNECT_WORD_MAX) }), lang);
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
return undefined;
|
|
210
322
|
}
|
|
211
323
|
}
|
package/dist/mcpWireCaps.js
CHANGED
|
@@ -329,7 +329,7 @@ function dropNotice(name, reason, problem, opts, path, transportKind) {
|
|
|
329
329
|
: reason === 'elicitation_invalid'
|
|
330
330
|
? msg('config.mcp.elicitationInvalid', { name: quoted, where })
|
|
331
331
|
: reason === 'allow_tools_invalid'
|
|
332
|
-
? msg('config.mcp.allowToolsInvalid', { name: quoted, where, problem:
|
|
332
|
+
? msg('config.mcp.allowToolsInvalid', { name: quoted, where, problem: problem ?? '' })
|
|
333
333
|
: reason === 'identity_headers_both_set'
|
|
334
334
|
? msg('config.mcp.identityHeadersBothSet', { name: quoted, where })
|
|
335
335
|
: reason === 'transport_invalid'
|
|
@@ -448,6 +448,6 @@ export function mcpAssertionHeaderNotSentNotice(specs) {
|
|
|
448
448
|
const parts = distinct.slice(0, ASSERTION_NOTICE_NAMES_SHOWN).map((x) => `"${x}"`);
|
|
449
449
|
if (distinct.length > ASSERTION_NOTICE_NAMES_SHOWN)
|
|
450
450
|
parts.push(`+${distinct.length - ASSERTION_NOTICE_NAMES_SHOWN} more`);
|
|
451
|
-
return (`MCP servers that declare an assertion header (assertionHeader): ${parts.join(', ')} — server versions up to 7.
|
|
451
|
+
return (`MCP servers that declare an assertion header (assertionHeader): ${parts.join(', ')} — server versions up to 7.147.0 accept this setting but do not send the header, ` +
|
|
452
452
|
'and this client cannot tell whether the engine it uses sends it; a connector that requires the header will refuse requests from these servers');
|
|
453
453
|
}
|
|
@@ -61,7 +61,7 @@ export interface MemoryEraseConflictRow {
|
|
|
61
61
|
id: string;
|
|
62
62
|
reason: string;
|
|
63
63
|
}
|
|
64
|
-
declare const MEMORY_ERASE_CARRIER_CLASSES: readonly ["projection", "shadow", "indexLine", "quarantineCopy", "planArchive", "planWorkbook", "evidenceBinding", "backendDerived"];
|
|
64
|
+
declare const MEMORY_ERASE_CARRIER_CLASSES: readonly ["projection", "shadow", "indexLine", "quarantineCopy", "clearanceCustody", "planArchive", "planWorkbook", "evidenceBinding", "backendDerived"];
|
|
65
65
|
export type MemoryEraseCarrierClass = (typeof MEMORY_ERASE_CARRIER_CLASSES)[number];
|
|
66
66
|
export type MemoryEraseCarrierReading = {
|
|
67
67
|
removed: number;
|
|
@@ -174,6 +174,7 @@ const MEMORY_ERASE_CARRIER_CLASSES = Object.freeze([
|
|
|
174
174
|
'shadow',
|
|
175
175
|
'indexLine',
|
|
176
176
|
'quarantineCopy',
|
|
177
|
+
'clearanceCustody',
|
|
177
178
|
'planArchive',
|
|
178
179
|
'planWorkbook',
|
|
179
180
|
'evidenceBinding',
|
|
@@ -237,6 +238,10 @@ function eraseResidualsOf(o, lane) {
|
|
|
237
238
|
return null;
|
|
238
239
|
const carriers = {};
|
|
239
240
|
for (const cls of MEMORY_ERASE_CARRIER_CLASSES) {
|
|
241
|
+
if (!Object.hasOwn(rawCarriers, cls)) {
|
|
242
|
+
carriers[cls] = { unverified: true };
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
240
245
|
const answer = carrierAnswerOf(readThrownSlot(rawCarriers, cls, 'data'));
|
|
241
246
|
if (answer === null)
|
|
242
247
|
return null;
|
|
@@ -128,15 +128,16 @@ export async function probeModelCapability(entry, send) {
|
|
|
128
128
|
evidence: { baseline, attempts, lowEffortAccepted: !low.refused },
|
|
129
129
|
};
|
|
130
130
|
}
|
|
131
|
+
const _capabilityBitsPin = true;
|
|
132
|
+
void _capabilityBitsPin;
|
|
131
133
|
export function applyProbeToEntry(entry, result) {
|
|
132
134
|
if (result.verdict === 'inconclusive')
|
|
133
135
|
return entry;
|
|
134
136
|
const compatIn = typeof entry.compat === 'object' && entry.compat !== null && !Array.isArray(entry.compat)
|
|
135
137
|
? entry.compat
|
|
136
138
|
: undefined;
|
|
137
|
-
if (result.verdict === 'no_reasoning')
|
|
138
|
-
return
|
|
139
|
-
}
|
|
139
|
+
if (result.verdict === 'no_reasoning')
|
|
140
|
+
return entry;
|
|
140
141
|
if (result.verdict === 'disabled_via' && typeof result.thinkingFormat === 'string') {
|
|
141
142
|
return { ...entry, reasoning: true, compat: { ...compatIn, thinkingFormat: result.thinkingFormat } };
|
|
142
143
|
}
|
package/dist/modsFace.d.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
import type { WiringManifestModRefusedView } from './adapter/downstream/eventToSdkMessage.js';
|
|
1
|
+
import type { WiringManifestModRefusedView, WiringManifestModView } from './adapter/downstream/eventToSdkMessage.js';
|
|
2
|
+
import type { SessionModsView } from './seatContract.js';
|
|
3
|
+
import type { DeviceUnreadable } from './deviceRebind.js';
|
|
2
4
|
import type { EngineNoticeChromeEvent } from './seam.js';
|
|
3
5
|
import type { UiLanguage } from './env/uiLanguage.js';
|
|
6
|
+
import { type ApprovalRequestedBy } from './hitl/approvalAskers.js';
|
|
4
7
|
export declare const MOD_LOADER_VERBS: readonly ["list", "load", "unload", "status"];
|
|
5
8
|
export type ModLoaderVerb = (typeof MOD_LOADER_VERBS)[number];
|
|
6
9
|
export declare function readModRegistrationRefusedNotice(ev: EngineNoticeChromeEvent): WiringManifestModRefusedView | undefined;
|
|
10
|
+
export declare function sessionModsOf(manifest: {
|
|
11
|
+
readonly mods?: readonly WiringManifestModView[];
|
|
12
|
+
} | null | undefined): SessionModsView | DeviceUnreadable;
|
|
7
13
|
export declare function modCardSentence(input: object | null | undefined, lang?: UiLanguage): string | undefined;
|
|
14
|
+
export declare function approvalRequestedBySentence(by: ApprovalRequestedBy | null | undefined, lang?: UiLanguage): string | undefined;
|
|
8
15
|
export declare function modLoaderFailureSentence(name: string, error: unknown, lang?: UiLanguage): string;
|
package/dist/modsFace.js
CHANGED
|
@@ -4,6 +4,7 @@ import { displayUntrusted } from './displayUntrusted.js';
|
|
|
4
4
|
import { WIRE_FAILURE_UNRENDERABLE_TEXT, wireFailureTextsOf } from './wireFailureShape.js';
|
|
5
5
|
import { gateOutcomeOf, isDeniedGate, rewriteExtensionNamesOf, toolInputRewriteOf } from './gateOutcome.js';
|
|
6
6
|
import { guardedRead, readOwnDataNonEmptyString, readOwnDataValue, toolResultBlockOf } from './ownKey.js';
|
|
7
|
+
import { requestedByViewOf } from './hitl/approvalAskers.js';
|
|
7
8
|
const isCardRecordObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
8
9
|
export const MOD_LOADER_VERBS = Object.freeze(['list', 'load', 'unload', 'status']);
|
|
9
10
|
const modRowOf = (r) => modRowsOf([r])?.[0];
|
|
@@ -21,6 +22,18 @@ export function readModRegistrationRefusedNotice(ev) {
|
|
|
21
22
|
return undefined;
|
|
22
23
|
}
|
|
23
24
|
}
|
|
25
|
+
export function sessionModsOf(manifest) {
|
|
26
|
+
try {
|
|
27
|
+
if (!isCardRecordObject(manifest))
|
|
28
|
+
return sessionModsUnreadable();
|
|
29
|
+
const rows = modRowsOf(readOwnDataValue(manifest, 'mods'));
|
|
30
|
+
return rows === undefined ? sessionModsUnreadable() : Object.freeze({ rows: Object.freeze(rows.slice()) });
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return sessionModsUnreadable();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const sessionModsUnreadable = () => Object.freeze({ kind: 'unreadable', why: renderMessage(msg('config.mods.session.unreadable')) });
|
|
24
37
|
const MOD_NAME_SHOWN_MAX = 64;
|
|
25
38
|
const MOD_REASON_SHOWN_MAX = 160;
|
|
26
39
|
const shownName = (s) => displayUntrusted(s, { blanks: true, max: MOD_NAME_SHOWN_MAX });
|
|
@@ -54,6 +67,20 @@ export function modCardSentence(input, lang) {
|
|
|
54
67
|
return undefined;
|
|
55
68
|
}
|
|
56
69
|
}
|
|
70
|
+
export function approvalRequestedBySentence(by, lang) {
|
|
71
|
+
try {
|
|
72
|
+
const view = requestedByViewOf(by);
|
|
73
|
+
if (view === undefined)
|
|
74
|
+
return undefined;
|
|
75
|
+
const name = shownName(view.name);
|
|
76
|
+
if (name.trim() === '')
|
|
77
|
+
return undefined;
|
|
78
|
+
return renderMessage(msg('config.mods.request.by', { by: name }), lang);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
57
84
|
export function modLoaderFailureSentence(name, error, lang) {
|
|
58
85
|
const shown = guardedRead(() => (typeof name === 'string' ? shownName(name) : undefined)) ?? '';
|
|
59
86
|
const { reason } = wireFailureTextsOf(error);
|
|
@@ -6,7 +6,9 @@ export interface CutForDisplayResult {
|
|
|
6
6
|
readonly shown: string;
|
|
7
7
|
readonly truncated: boolean;
|
|
8
8
|
}
|
|
9
|
+
export declare function pairSafeCut(text: string, cut: number): number;
|
|
9
10
|
export declare function cutForDisplay(text: string, budget: number, opts?: CutForDisplayOptions): CutForDisplayResult;
|
|
11
|
+
export declare function elideMiddleForDisplay(text: string, budget: number): string;
|
|
10
12
|
export type NoticePresentationAudience = 'user' | 'operator' | 'unknown';
|
|
11
13
|
export type NoticeOutlet = 'present' | 'absent' | 'unknown';
|
|
12
14
|
export type NoticeOccurrence = {
|
|
@@ -69,6 +69,14 @@ function pickCut(src, limit, floor, boundary) {
|
|
|
69
69
|
return clause;
|
|
70
70
|
return word >= floor ? word : -1;
|
|
71
71
|
}
|
|
72
|
+
export function pairSafeCut(text, cut) {
|
|
73
|
+
if (cut <= 0 || cut >= text.length || text.charAt(cut) !== '\\' || text.charAt(cut - 1) !== '\\')
|
|
74
|
+
return cut;
|
|
75
|
+
let start = cut - 1;
|
|
76
|
+
while (start > 0 && text.charAt(start - 1) === '\\')
|
|
77
|
+
start--;
|
|
78
|
+
return (cut - start) % 2 === 1 ? cut - 1 : cut;
|
|
79
|
+
}
|
|
72
80
|
export function cutForDisplay(text, budget, opts) {
|
|
73
81
|
const src = typeof text === 'string' ? text : (guardedRead(() => String(text ?? '')) ?? '');
|
|
74
82
|
if (typeof budget !== 'number' || !Number.isFinite(budget))
|
|
@@ -83,9 +91,79 @@ export function cutForDisplay(text, budget, opts) {
|
|
|
83
91
|
const head = capDisplay(src, room, 'escape');
|
|
84
92
|
const floor = Math.ceil(head.length / CUT_KEEP_DIVISOR);
|
|
85
93
|
const at = pickCut(src, head.length, floor, boundary);
|
|
86
|
-
const kept = (at === -1 ? head :
|
|
94
|
+
const kept = src.slice(0, pairSafeCut(src, at === -1 ? head.length : at)).trimEnd();
|
|
87
95
|
return { shown: `${kept}${mark}`, truncated: true };
|
|
88
96
|
}
|
|
97
|
+
const ELISION_MARK = '…';
|
|
98
|
+
const ELISION_MIN_ROOM = 8;
|
|
99
|
+
const ELISION_SOFT_BACKOFF = 24;
|
|
100
|
+
const CLUSTER_EXTENDER = /^[\p{M}︀-️\u{1F3FB}-\u{1F3FF}\u{E0020}-\u{E007F}]/u;
|
|
101
|
+
const REGIONAL_INDICATOR = /^[\u{1F1E6}-\u{1F1FF}]$/u;
|
|
102
|
+
const RTL_LETTER = /^[\p{Script=Arabic}\p{Script=Hebrew}\p{Script=Syriac}\p{Script=Thaana}\p{Script=Nko}\p{Script=Samaritan}\p{Script=Mandaic}]$/u;
|
|
103
|
+
function codePointBefore(text, i) {
|
|
104
|
+
return text.slice(Math.max(0, i - 2), i).match(/[\s\S]$/u)?.[0] ?? '';
|
|
105
|
+
}
|
|
106
|
+
function codePointAfter(text, i) {
|
|
107
|
+
const cp = i < text.length ? text.codePointAt(i) : undefined;
|
|
108
|
+
return cp === undefined ? '' : String.fromCodePoint(cp);
|
|
109
|
+
}
|
|
110
|
+
function hardSafeBoundary(text, i) {
|
|
111
|
+
return i <= 0 || i >= text.length || (capDisplay(text, i, 'escape').length === i && pairSafeCut(text, i) === i);
|
|
112
|
+
}
|
|
113
|
+
function calmBoundary(text, i) {
|
|
114
|
+
const before = codePointBefore(text, i);
|
|
115
|
+
const after = codePointAfter(text, i);
|
|
116
|
+
if (before === '' || after === '')
|
|
117
|
+
return true;
|
|
118
|
+
if (before === '' || CLUSTER_EXTENDER.test(after))
|
|
119
|
+
return false;
|
|
120
|
+
if (RTL_LETTER.test(before) || RTL_LETTER.test(after))
|
|
121
|
+
return false;
|
|
122
|
+
if (REGIONAL_INDICATOR.test(before) && REGIONAL_INDICATOR.test(after)) {
|
|
123
|
+
let run = 0;
|
|
124
|
+
for (let j = i; j > 0;) {
|
|
125
|
+
const cp = codePointBefore(text, j);
|
|
126
|
+
if (!REGIONAL_INDICATOR.test(cp))
|
|
127
|
+
break;
|
|
128
|
+
run++;
|
|
129
|
+
j -= cp.length;
|
|
130
|
+
}
|
|
131
|
+
return run % 2 === 0;
|
|
132
|
+
}
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
function settleOnCalm(text, at, step) {
|
|
136
|
+
for (let k = 0; k <= ELISION_SOFT_BACKOFF; k++) {
|
|
137
|
+
const i = at + step * k;
|
|
138
|
+
if (i < 0 || i > text.length)
|
|
139
|
+
break;
|
|
140
|
+
if (hardSafeBoundary(text, i) && calmBoundary(text, i))
|
|
141
|
+
return i;
|
|
142
|
+
}
|
|
143
|
+
return at;
|
|
144
|
+
}
|
|
145
|
+
export function elideMiddleForDisplay(text, budget) {
|
|
146
|
+
const src = typeof text === 'string' ? text : (guardedRead(() => String(text ?? '')) ?? '');
|
|
147
|
+
if (typeof budget !== 'number' || !Number.isFinite(budget))
|
|
148
|
+
return src;
|
|
149
|
+
const limit = Math.max(0, Math.floor(budget));
|
|
150
|
+
if (src.length <= limit)
|
|
151
|
+
return src;
|
|
152
|
+
const room = limit - ELISION_MARK.length;
|
|
153
|
+
if (room < ELISION_MIN_ROOM)
|
|
154
|
+
return cutForDisplay(src, limit).shown;
|
|
155
|
+
const headRoom = Math.ceil(room / 2);
|
|
156
|
+
const tailRoom = room - headRoom;
|
|
157
|
+
let head = headRoom;
|
|
158
|
+
while (head > 0 && !hardSafeBoundary(src, head))
|
|
159
|
+
head--;
|
|
160
|
+
let tail = src.length - tailRoom;
|
|
161
|
+
while (tail < src.length && !hardSafeBoundary(src, tail))
|
|
162
|
+
tail++;
|
|
163
|
+
head = settleOnCalm(src, head, -1);
|
|
164
|
+
tail = settleOnCalm(src, tail, 1);
|
|
165
|
+
return `${src.slice(0, head).trimEnd()}${ELISION_MARK}${src.slice(tail)}`;
|
|
166
|
+
}
|
|
89
167
|
export const NOTICE_SEAT_CLASS = Object.freeze({
|
|
90
168
|
user: Object.freeze({ present: 'persistent+hint', unknown: 'persistent+hint', absent: 'persistent' }),
|
|
91
169
|
operator: Object.freeze({ present: 'transient', unknown: 'transient', absent: 'diagnostic' }),
|
package/dist/notifications.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export interface TaskNotificationFields {
|
|
|
9
9
|
outputFile?: string;
|
|
10
10
|
diagnostics?: string;
|
|
11
11
|
result?: string;
|
|
12
|
+
error?: string;
|
|
12
13
|
stoppedBy?: string;
|
|
13
14
|
partial?: boolean;
|
|
14
15
|
exitCode?: number;
|
|
@@ -93,11 +94,21 @@ export declare function noteWorkflowCompletionCardEnqueued(runId: string): void;
|
|
|
93
94
|
export declare function listNotifiedRuns(): string[];
|
|
94
95
|
export declare function listWorkflowCompletionCardsEnqueued(): string[];
|
|
95
96
|
export declare function isWorkflowCompletionCardEnqueued(runId: string): boolean;
|
|
96
|
-
type
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
export type WorkflowRunReader = (runId: string) => Promise<unknown>;
|
|
98
|
+
export type WorkflowRunTerminalReading = {
|
|
99
|
+
readonly state: 'running';
|
|
100
|
+
} | {
|
|
101
|
+
readonly state: 'awaiting approval';
|
|
102
|
+
} | {
|
|
103
|
+
readonly state: 'completed' | 'failed';
|
|
104
|
+
readonly result?: string;
|
|
105
|
+
readonly error?: string;
|
|
106
|
+
readonly pointer: 'absent';
|
|
107
|
+
} | {
|
|
108
|
+
readonly state: 'unknown';
|
|
109
|
+
readonly why: 'body_unreadable' | 'status_unreadable' | 'status_unrecognized';
|
|
110
|
+
};
|
|
111
|
+
export declare function workflowRunTerminalReadingOf(body: unknown): WorkflowRunTerminalReading;
|
|
101
112
|
export declare function outstandingWorkflowCount(): number;
|
|
102
113
|
export declare function outstandingDeliverableWorkflowCount(): number;
|
|
103
114
|
export declare function subscribeOutstandingWorkflows(listener: () => void): () => void;
|
|
@@ -111,7 +122,7 @@ export interface NotificationDropCounters {
|
|
|
111
122
|
bgWatchCollectedUnprobed: number;
|
|
112
123
|
}
|
|
113
124
|
export declare function notificationDropCounters(): NotificationDropCounters;
|
|
114
|
-
export declare function
|
|
125
|
+
export declare function installWorkflowRunReader(reader: WorkflowRunReader | null): void;
|
|
115
126
|
type BgTaskStatusProbe = (taskId: string) => Promise<{
|
|
116
127
|
terminal: boolean;
|
|
117
128
|
status: string;
|
package/dist/notifications.js
CHANGED
|
@@ -2,10 +2,11 @@ import { hostEnv } from './hostEnv.js';
|
|
|
2
2
|
import { unrefTimer } from './unrefTimer.js';
|
|
3
3
|
import { clearEnginePanelTaskResidentByWire, publishEngineAgentPanelEvent, } from './engineAgentPanelStore.js';
|
|
4
4
|
import { panelEndFact } from './panelReduce.js';
|
|
5
|
-
import { isCoreTaskNotificationTerminal, isCoreTaskNotificationTerminalNotSuccess } from './terminalStatuses.js';
|
|
5
|
+
import { isCoreTaskNotificationTerminal, isCoreTaskNotificationTerminalNotSuccess, isCoreWorkflowRunParkedEnd, isCoreWorkflowRunTerminal } from './terminalStatuses.js';
|
|
6
6
|
import { cycleSeqValue } from './cycleOrder.js';
|
|
7
7
|
import { CORE_ENGINE_AUTHORITY_ENVELOPE_TAGS } from './generated/engineFactTables.js';
|
|
8
8
|
import { guardedRead, ownSnapshot, ownSnapshotRecord, ownSnapshotTyped, readOwnDataValue, workflowCompletionExtrasOf } from './ownKey.js';
|
|
9
|
+
import { displayUntrusted } from './displayUntrusted.js';
|
|
9
10
|
function escapeXml(v) {
|
|
10
11
|
return v.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
11
12
|
}
|
|
@@ -102,6 +103,7 @@ export function freezeTaskNotificationFields(n) {
|
|
|
102
103
|
...(n.outputFile !== undefined ? { outputFile: n.outputFile } : {}),
|
|
103
104
|
...(n.diagnostics !== undefined ? { diagnostics: n.diagnostics } : {}),
|
|
104
105
|
...(n.result !== undefined ? { result: n.result } : {}),
|
|
106
|
+
...(n.error !== undefined ? { error: n.error } : {}),
|
|
105
107
|
...(n.stoppedBy !== undefined ? { stoppedBy: n.stoppedBy } : {}),
|
|
106
108
|
...(n.partial !== undefined ? { partial: n.partial } : {}),
|
|
107
109
|
...(n.exitCode !== undefined ? { exitCode: n.exitCode } : {}),
|
|
@@ -128,7 +130,7 @@ export function taskNotificationOf(carrier) {
|
|
|
128
130
|
if (typeof taskId !== 'string' || taskId.length === 0 || typeof status !== 'string' || typeof summary !== 'string')
|
|
129
131
|
return undefined;
|
|
130
132
|
const out = { taskId, status, summary };
|
|
131
|
-
for (const k of ['taskType', 'toolUseId', 'outputFile', 'diagnostics', 'result', 'stoppedBy', 'externalSource']) {
|
|
133
|
+
for (const k of ['taskType', 'toolUseId', 'outputFile', 'diagnostics', 'result', 'error', 'stoppedBy', 'externalSource']) {
|
|
132
134
|
const v = slot(k);
|
|
133
135
|
if (typeof v === 'string')
|
|
134
136
|
out[k] = v;
|
|
@@ -478,6 +480,50 @@ const cardEnqueuedRunIds = new Set();
|
|
|
478
480
|
export function isWorkflowCompletionCardEnqueued(runId) {
|
|
479
481
|
return cardEnqueuedRunIds.has(runId);
|
|
480
482
|
}
|
|
483
|
+
export function workflowRunTerminalReadingOf(body) {
|
|
484
|
+
const snap = probeAnswerSnapshot(body);
|
|
485
|
+
if (snap === null)
|
|
486
|
+
return Object.freeze({ state: 'unknown', why: 'body_unreadable' });
|
|
487
|
+
const status = guardedRead(() => readOwnDataValue(snap, 'status'));
|
|
488
|
+
if (typeof status !== 'string')
|
|
489
|
+
return Object.freeze({ state: 'unknown', why: 'status_unreadable' });
|
|
490
|
+
if (status === 'running')
|
|
491
|
+
return Object.freeze({ state: 'running' });
|
|
492
|
+
if (!isCoreWorkflowRunTerminal(status))
|
|
493
|
+
return Object.freeze({ state: 'unknown', why: 'status_unrecognized' });
|
|
494
|
+
if (isCoreWorkflowRunParkedEnd(status, guardedRead(() => readOwnDataValue(snap, 'errorCode'))))
|
|
495
|
+
return Object.freeze({ state: 'awaiting approval' });
|
|
496
|
+
const result = guardedRead(() => readOwnDataValue(snap, 'result'));
|
|
497
|
+
const error = status === 'failed' ? guardedRead(() => readOwnDataValue(snap, 'error')) : undefined;
|
|
498
|
+
const shownError = typeof error === 'string' && error.length > 0 ? shownRunError(error) : '';
|
|
499
|
+
return Object.freeze({
|
|
500
|
+
state: status,
|
|
501
|
+
...(typeof result === 'string' && result.length > 0 ? { result } : {}),
|
|
502
|
+
...(shownError.length > 0 ? { error: shownError } : {}),
|
|
503
|
+
pointer: 'absent',
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
const WORKFLOW_RUN_ERROR_MAX = 200;
|
|
507
|
+
function shownRunError(raw) {
|
|
508
|
+
const opts = { foldLines: false, escapeBackslashes: 'auto' };
|
|
509
|
+
const whole = displayUntrusted(raw, opts);
|
|
510
|
+
return whole.length <= WORKFLOW_RUN_ERROR_MAX ? whole : `${displayUntrusted(raw, { ...opts, max: WORKFLOW_RUN_ERROR_MAX - 1 })}…`;
|
|
511
|
+
}
|
|
512
|
+
function workflowPollDecisionOf(delivered, reading) {
|
|
513
|
+
if (delivered)
|
|
514
|
+
return { act: 'stop', why: 'delivered' };
|
|
515
|
+
if (reading.state === 'running')
|
|
516
|
+
return { act: 'wait', why: 'running' };
|
|
517
|
+
if (reading.state === 'awaiting approval')
|
|
518
|
+
return { act: 'wait', why: 'awaiting approval' };
|
|
519
|
+
if (reading.state === 'unknown')
|
|
520
|
+
return { act: 'wait', why: 'unknown' };
|
|
521
|
+
return { act: 'emit', status: reading.state, ...(reading.result !== undefined ? { result: reading.result } : {}), ...(reading.error !== undefined ? { error: reading.error } : {}) };
|
|
522
|
+
}
|
|
523
|
+
function workflowPollCompletionSummary(status, error) {
|
|
524
|
+
const head = `workflow ${status} (read from its run record; no handle to the full record is available)`;
|
|
525
|
+
return error === undefined ? head : `${head} — failure reason from the run record: ${error}`;
|
|
526
|
+
}
|
|
481
527
|
const outstandingRuns = new Map();
|
|
482
528
|
const outstandingListeners = new Set();
|
|
483
529
|
function notifyOutstanding() {
|
|
@@ -493,7 +539,7 @@ export function outstandingWorkflowCount() {
|
|
|
493
539
|
return outstandingRuns.size;
|
|
494
540
|
}
|
|
495
541
|
export function outstandingDeliverableWorkflowCount() {
|
|
496
|
-
return
|
|
542
|
+
return workflowRunReader ? outstandingRuns.size : 0;
|
|
497
543
|
}
|
|
498
544
|
export function subscribeOutstandingWorkflows(listener) {
|
|
499
545
|
outstandingListeners.add(listener);
|
|
@@ -531,12 +577,12 @@ export function notificationDropCounters() {
|
|
|
531
577
|
bgWatchCollectedUnprobed,
|
|
532
578
|
};
|
|
533
579
|
}
|
|
534
|
-
let
|
|
580
|
+
let workflowRunReader = null;
|
|
535
581
|
let watchTimer = null;
|
|
536
582
|
const WATCH_INTERVAL_MS = 5000;
|
|
537
583
|
const WATCH_TTL_MS = 2 * 60 * 60 * 1000;
|
|
538
|
-
export function
|
|
539
|
-
|
|
584
|
+
export function installWorkflowRunReader(reader) {
|
|
585
|
+
workflowRunReader = reader;
|
|
540
586
|
}
|
|
541
587
|
const BG_FIRST_SEQ = 1;
|
|
542
588
|
function normalizeBgSeq(seq) {
|
|
@@ -762,26 +808,25 @@ async function tickWatchInner(now) {
|
|
|
762
808
|
bgProbeInFlight.delete(key);
|
|
763
809
|
}
|
|
764
810
|
}
|
|
765
|
-
const
|
|
811
|
+
const reader = workflowRunReader;
|
|
766
812
|
for (const runId of [...outstandingRuns.keys()]) {
|
|
767
813
|
if (notifiedRunIds.has(runId)) {
|
|
768
814
|
if (outstandingRuns.delete(runId))
|
|
769
815
|
notifyOutstanding();
|
|
770
816
|
continue;
|
|
771
817
|
}
|
|
772
|
-
if (!
|
|
818
|
+
if (!reader)
|
|
773
819
|
continue;
|
|
774
820
|
runProbeInFlight.set(runId, now);
|
|
775
821
|
try {
|
|
776
|
-
const
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
});
|
|
822
|
+
const reading = workflowRunTerminalReadingOf(await withProbeDeadline(reader(runId), probeTimeoutMs()));
|
|
823
|
+
const decision = workflowPollDecisionOf(notifiedRunIds.has(runId), reading);
|
|
824
|
+
if (decision.act === 'wait')
|
|
825
|
+
continue;
|
|
826
|
+
if (outstandingRuns.delete(runId))
|
|
827
|
+
notifyOutstanding();
|
|
828
|
+
if (decision.act === 'emit') {
|
|
829
|
+
enqueueWorkflowCompletion({ runId, status: decision.status, summary: workflowPollCompletionSummary(decision.status, decision.error) }, { ...(decision.result !== undefined ? { result: decision.result } : {}), ...(decision.error !== undefined ? { error: decision.error } : {}) });
|
|
785
830
|
}
|
|
786
831
|
}
|
|
787
832
|
catch {
|
|
@@ -845,6 +890,9 @@ export function enqueueEngineWorkflowNotification(raw) {
|
|
|
845
890
|
traceNotif('workflow-notification dropped:payload-unreadable');
|
|
846
891
|
return;
|
|
847
892
|
}
|
|
893
|
+
enqueueWorkflowCompletion(c, completionIdOnly(workflowCompletionExtrasOf(c)));
|
|
894
|
+
}
|
|
895
|
+
function enqueueWorkflowCompletion(c, body) {
|
|
848
896
|
if (notifiedRunIds.has(c.runId)) {
|
|
849
897
|
workflowDedupDropped++;
|
|
850
898
|
traceNotif(`workflow notification suppressed (run already notified) runId=${c.runId} status=${c.status}`);
|
|
@@ -852,7 +900,7 @@ export function enqueueEngineWorkflowNotification(raw) {
|
|
|
852
900
|
}
|
|
853
901
|
markRunNotified(c.runId);
|
|
854
902
|
cardEnqueuedRunIds.add(c.runId);
|
|
855
|
-
port()?.enqueuePendingNotification(taskNotificationQueueCommand({ taskId: c.runId, taskType: 'workflow', status: c.status, summary: c.summary, ...
|
|
903
|
+
port()?.enqueuePendingNotification(taskNotificationQueueCommand({ taskId: c.runId, taskType: 'workflow', status: c.status, summary: c.summary, ...body }));
|
|
856
904
|
}
|
|
857
905
|
export function _resetEngineTaskNotificationForTest() {
|
|
858
906
|
notifiedRunIds.clear();
|
|
@@ -867,7 +915,7 @@ export function _resetEngineTaskNotificationForTest() {
|
|
|
867
915
|
bgProbeInFlight.clear();
|
|
868
916
|
runProbeInFlight.clear();
|
|
869
917
|
tickGeneration = 0;
|
|
870
|
-
|
|
918
|
+
workflowRunReader = null;
|
|
871
919
|
bgStatusProbe = null;
|
|
872
920
|
tickStartedAtMs = null;
|
|
873
921
|
abandonedCount = 0;
|