@sema-agent/client-core 0.107.0 → 0.109.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 +239 -0
- package/README.md +54 -36
- package/dist/adapt/arms.js +23 -8
- package/dist/adapt/textStream.d.ts +2 -0
- package/dist/adapt/textStream.js +37 -16
- package/dist/adapter/activeRunSelfHeal.d.ts +2 -0
- package/dist/adapter/activeRunSelfHeal.js +21 -4
- package/dist/adapter/downstream/eventToSdkMessage.js +2 -1
- 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 +6 -1
- package/dist/checkpointRowVerdict.js +30 -2
- package/dist/cloudConfigWireCaps.d.ts +10 -0
- package/dist/cloudConfigWireCaps.js +33 -0
- package/dist/compensations.js +1 -1
- package/dist/coreValuePorts.d.ts +17 -1
- package/dist/coreValuePorts.js +5 -0
- package/dist/decideReceipt.js +2 -1
- package/dist/engineAgentAbsence.d.ts +2 -0
- package/dist/engineAgentAbsence.js +28 -0
- package/dist/engineErrorCodes.d.ts +9 -0
- package/dist/engineErrorCodes.js +9 -0
- package/dist/engineHealthFacts.d.ts +17 -0
- package/dist/engineHealthFacts.js +42 -3
- package/dist/engineNoticeCodes.d.ts +147 -1
- package/dist/engineNoticeCodes.js +694 -57
- package/dist/engineTargetSeat.js +1 -0
- package/dist/errorSupplementRoles.js +6 -1
- package/dist/eventLeg.js +1 -1
- package/dist/executionLaneWords.js +2 -1
- package/dist/fleetTaskDesc.js +3 -3
- package/dist/gateOutcome.d.ts +10 -1
- package/dist/gateOutcome.js +48 -1
- package/dist/generated/engineFactTables.d.ts +2 -0
- package/dist/generated/engineFactTables.js +11 -0
- package/dist/generated/engineNoticeTables.js +6 -0
- package/dist/generated/settingsSchemaFns.d.ts +4 -2
- package/dist/generated/settingsSchemaFns.js +9 -3
- package/dist/generated/settingsSchemaMcpRequestEntry.d.ts +1 -1
- package/dist/generated/settingsSchemaMcpRequestEntry.js +9 -18
- package/dist/generated/settingsSchemaReservedMemberName.d.ts +1 -0
- package/dist/generated/settingsSchemaReservedMemberName.js +7 -0
- package/dist/generated/settingsSchemaTables.d.ts +2 -0
- package/dist/generated/settingsSchemaTables.js +10 -0
- package/dist/generated/settingsSchemaWireName.d.ts +1 -0
- package/dist/generated/settingsSchemaWireName.js +8 -0
- package/dist/headlessReconnectWire.js +12 -7
- package/dist/hitl/approvalAskers.d.ts +13 -0
- package/dist/hitl/approvalAskers.js +79 -0
- package/dist/hitl/approvalCardReasons.js +22 -4
- package/dist/hitl/approvalRequestFrameReading.d.ts +14 -0
- package/dist/hitl/approvalRequestFrameReading.js +79 -0
- package/dist/hitl/approvalResolution.d.ts +3 -0
- package/dist/hitl/approvalResolution.js +22 -4
- package/dist/hitl/approvalsFeed.js +4 -13
- package/dist/hitl/askParkRowRouting.js +10 -3
- package/dist/hitl/automaticAnswerVerdict.d.ts +5 -2
- package/dist/hitl/automaticAnswerVerdict.js +16 -2
- package/dist/hitl/decisionConflict.d.ts +2 -1
- package/dist/hitl/decisionConflict.js +4 -1
- package/dist/hitl/decisionRefusal.d.ts +1 -0
- package/dist/hitl/decisionRefusal.js +5 -2
- package/dist/hitl/decisionSend.js +2 -6
- package/dist/hitl/frameRouter.d.ts +3 -2
- package/dist/hitl/frameRouter.js +37 -7
- package/dist/hitl/gateLedger.d.ts +2 -0
- package/dist/hitl/gateLedger.js +3 -1
- package/dist/hitl/hitlBridge.d.ts +4 -0
- package/dist/hitl/hitlBridge.js +11 -8
- 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 +39 -30
- package/dist/hitl/parkRowBirthWait.d.ts +2 -0
- package/dist/hitl/parkRowBirthWait.js +8 -1
- 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 +7 -1
- package/dist/hitl/persistedRulesWire.js +25 -8
- package/dist/hitl/planReviewWire.js +12 -16
- package/dist/hitl/policyDenyCause.d.ts +3 -0
- package/dist/hitl/policyDenyCause.js +16 -0
- package/dist/hitl/sessionAllowRoute.js +5 -4
- package/dist/hitl/sessionPolicyDeliverable.js +1 -1
- package/dist/hitl/standingRows.d.ts +1 -5
- package/dist/hitl/standingRows.js +15 -14
- package/dist/hitl/toolApprovalWire.d.ts +16 -4
- package/dist/hitl/toolApprovalWire.js +57 -17
- package/dist/hooksReaderGenerations.d.ts +1 -0
- package/dist/hooksReaderGenerations.js +12 -0
- package/dist/hooksWireCaps.js +12 -7
- package/dist/hostSettingsSource.js +2 -0
- package/dist/i18n/messages.en.d.ts +206 -1
- package/dist/i18n/messages.en.js +209 -4
- package/dist/i18n/messages.zh.js +214 -4
- package/dist/index.d.ts +35 -8
- package/dist/index.js +20 -5
- package/dist/liveModelCatalog.d.ts +4 -2
- package/dist/liveModelCatalog.js +164 -26
- 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/mcpReconnect.js +12 -5
- package/dist/mcpWireCaps.js +2 -2
- package/dist/memoryEntriesWire.d.ts +1 -1
- package/dist/memoryEntriesWire.js +5 -0
- package/dist/model/catalogDefault.d.ts +25 -0
- package/dist/model/catalogDefault.js +44 -0
- package/dist/noticePresentation.d.ts +2 -0
- package/dist/noticePresentation.js +79 -1
- package/dist/ownKey.d.ts +10 -0
- package/dist/ownKey.js +15 -1
- package/dist/permissionWireCaps.d.ts +1 -0
- package/dist/permissionWireCaps.js +11 -0
- package/dist/pluginHooksWire.d.ts +5 -3
- package/dist/pluginHooksWire.js +116 -7
- package/dist/queuedDelivery.d.ts +1 -0
- package/dist/queuedDelivery.js +12 -0
- package/dist/resultFrameKeys.js +4 -2
- package/dist/resumeRefusalCopy.js +15 -8
- package/dist/ruleReadings.js +1 -1
- package/dist/runPermissionModeSwitch.d.ts +2 -1
- package/dist/runPermissionModeSwitch.js +16 -6
- package/dist/seam.d.ts +13 -2
- package/dist/seam.js +8 -1
- package/dist/seatContract.d.ts +103 -12
- package/dist/seatContract.js +344 -14
- package/dist/sessionBackground.js +2 -0
- package/dist/sessionEngine.d.ts +2 -1
- package/dist/sessionEngine.js +5 -0
- package/dist/settingsHooksSalvage.d.ts +5 -0
- package/dist/settingsHooksSalvage.js +54 -0
- package/dist/subagentContentStore.d.ts +12 -0
- package/dist/subagentContentStore.js +73 -12
- package/dist/supportFloor.js +2 -0
- package/dist/taskNotificationAdmission.d.ts +19 -0
- package/dist/taskNotificationAdmission.js +84 -0
- package/dist/toolResult.js +20 -6
- package/dist/webSearchWireCaps.js +9 -4
- package/dist/wireFailureShape.d.ts +20 -0
- package/dist/wireFailureShape.js +37 -6
- package/dist/wireRefusalCopy.d.ts +23 -2
- package/dist/wireRefusalCopy.js +93 -4
- package/docs/INTEGRATION-CLIENTS.md +1959 -15
- package/package.json +4 -4
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare const LOCAL_ENGINE_STOP_LADDER: Readonly<{
|
|
2
|
+
g: 1000;
|
|
3
|
+
P: 5000;
|
|
4
|
+
H: 20000;
|
|
5
|
+
}>;
|
|
6
|
+
export type StopLadderSignal = 'term' | 'term-2' | 'kill';
|
|
7
|
+
export interface StopLadderState {
|
|
8
|
+
readonly mode: 'idle' | 'forced';
|
|
9
|
+
readonly target: {
|
|
10
|
+
readonly pid: number;
|
|
11
|
+
readonly startedAt?: number;
|
|
12
|
+
};
|
|
13
|
+
readonly t0: number;
|
|
14
|
+
readonly sent: readonly StopLadderSignal[];
|
|
15
|
+
readonly processedAt?: number;
|
|
16
|
+
readonly deadlineAt?: number;
|
|
17
|
+
readonly killedAt?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface StopLadderObservation {
|
|
20
|
+
readonly nowMs: number;
|
|
21
|
+
readonly alive: 'alive' | 'gone' | 'unknown';
|
|
22
|
+
readonly health: object | null;
|
|
23
|
+
}
|
|
24
|
+
export interface StopLadderStepResult {
|
|
25
|
+
readonly state: StopLadderState;
|
|
26
|
+
readonly action: StopLadderSignal | 'wait' | 'done' | 'gave-up';
|
|
27
|
+
readonly waitMs?: number;
|
|
28
|
+
}
|
|
29
|
+
export declare function stopLadderStep(s: StopLadderState, obs: StopLadderObservation): StopLadderStepResult;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { readEngineHealthFacts } from './engineHealthFacts.js';
|
|
2
|
+
import { readOwnDataValue } from './ownKey.js';
|
|
3
|
+
export const LOCAL_ENGINE_STOP_LADDER = Object.freeze({ g: 1000, P: 5000, H: 20000 });
|
|
4
|
+
const SIGTERM_ACTION_ID = 'signal:SIGTERM';
|
|
5
|
+
function own(v, key) {
|
|
6
|
+
if (typeof v !== 'object' || v === null)
|
|
7
|
+
return undefined;
|
|
8
|
+
try {
|
|
9
|
+
return readOwnDataValue(v, key);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function isTime(v) {
|
|
16
|
+
return typeof v === 'number' && Number.isSafeInteger(v) && v >= 0;
|
|
17
|
+
}
|
|
18
|
+
function isPid(v) {
|
|
19
|
+
return typeof v === 'number' && Number.isSafeInteger(v) && v > 0;
|
|
20
|
+
}
|
|
21
|
+
const SIGNALS = Object.freeze(['term', 'term-2', 'kill']);
|
|
22
|
+
function stateOf(s) {
|
|
23
|
+
const mode = own(s, 'mode');
|
|
24
|
+
if (mode !== 'idle' && mode !== 'forced')
|
|
25
|
+
return undefined;
|
|
26
|
+
const target = own(s, 'target');
|
|
27
|
+
const pid = own(target, 'pid');
|
|
28
|
+
if (!isPid(pid))
|
|
29
|
+
return undefined;
|
|
30
|
+
const startedAt = own(target, 'startedAt');
|
|
31
|
+
const t0 = own(s, 't0');
|
|
32
|
+
if (!isTime(t0))
|
|
33
|
+
return undefined;
|
|
34
|
+
const sentRaw = own(s, 'sent');
|
|
35
|
+
let sent = [];
|
|
36
|
+
try {
|
|
37
|
+
if (!Array.isArray(sentRaw))
|
|
38
|
+
return undefined;
|
|
39
|
+
for (let i = 0; i < sentRaw.length; i++) {
|
|
40
|
+
const x = own(sentRaw, String(i));
|
|
41
|
+
if (!SIGNALS.includes(x))
|
|
42
|
+
return undefined;
|
|
43
|
+
if (!sent.includes(x))
|
|
44
|
+
sent.push(x);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
sent = SIGNALS.filter((x) => sent.includes(x));
|
|
51
|
+
const processedAt = own(s, 'processedAt');
|
|
52
|
+
const deadlineAt = own(s, 'deadlineAt');
|
|
53
|
+
const killedAt = own(s, 'killedAt');
|
|
54
|
+
if (processedAt !== undefined && !isTime(processedAt))
|
|
55
|
+
return undefined;
|
|
56
|
+
if (deadlineAt !== undefined && !isTime(deadlineAt))
|
|
57
|
+
return undefined;
|
|
58
|
+
if (killedAt !== undefined && !isTime(killedAt))
|
|
59
|
+
return undefined;
|
|
60
|
+
return {
|
|
61
|
+
mode,
|
|
62
|
+
target: isTime(startedAt) && startedAt > 0 ? { pid, startedAt } : { pid },
|
|
63
|
+
t0,
|
|
64
|
+
sent,
|
|
65
|
+
...(processedAt !== undefined ? { processedAt } : {}),
|
|
66
|
+
...(deadlineAt !== undefined ? { deadlineAt } : {}),
|
|
67
|
+
...(killedAt !== undefined ? { killedAt } : {}),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function frozen(s) {
|
|
71
|
+
return Object.freeze({ ...s, target: Object.freeze({ ...s.target }), sent: Object.freeze([...s.sent]) });
|
|
72
|
+
}
|
|
73
|
+
function result(s, action, waitMs) {
|
|
74
|
+
return Object.freeze({ state: frozen(s), action, ...(waitMs !== undefined ? { waitMs: Math.max(0, waitMs) } : {}) });
|
|
75
|
+
}
|
|
76
|
+
export function stopLadderStep(s, obs) {
|
|
77
|
+
const st = stateOf(s);
|
|
78
|
+
if (st === undefined) {
|
|
79
|
+
return Object.freeze({ state: s, action: 'gave-up' });
|
|
80
|
+
}
|
|
81
|
+
const nowMs = own(obs, 'nowMs');
|
|
82
|
+
const alive = own(obs, 'alive');
|
|
83
|
+
if (alive === 'gone')
|
|
84
|
+
return result(st, 'done');
|
|
85
|
+
if (!isTime(nowMs))
|
|
86
|
+
return result(st, 'wait');
|
|
87
|
+
const { g, P, H } = LOCAL_ENGINE_STOP_LADDER;
|
|
88
|
+
const health = own(obs, 'health');
|
|
89
|
+
let facts;
|
|
90
|
+
try {
|
|
91
|
+
facts = typeof health === 'object' && health !== null ? readEngineHealthFacts(health) : undefined;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
facts = undefined;
|
|
95
|
+
}
|
|
96
|
+
const draining = facts?.draining;
|
|
97
|
+
let next = st;
|
|
98
|
+
if (draining !== undefined) {
|
|
99
|
+
if (st.mode === 'idle') {
|
|
100
|
+
next = { ...next, deadlineAt: Math.max(st.deadlineAt ?? draining.deadlineAt, draining.deadlineAt) };
|
|
101
|
+
}
|
|
102
|
+
else if (st.processedAt === undefined && st.sent.includes('term') && draining.actionId === SIGTERM_ACTION_ID && draining.since >= st.t0) {
|
|
103
|
+
next = { ...next, processedAt: nowMs };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const isAlive = alive === 'alive';
|
|
107
|
+
if (next.sent.length === 0) {
|
|
108
|
+
if (isAlive)
|
|
109
|
+
return result({ ...next, sent: ['term'] }, 'term');
|
|
110
|
+
return result(next, nowMs >= next.t0 + H ? 'gave-up' : 'wait', next.t0 + H - nowMs);
|
|
111
|
+
}
|
|
112
|
+
const killAt = next.mode === 'idle'
|
|
113
|
+
? Math.max(next.t0, next.deadlineAt ?? next.t0) + H
|
|
114
|
+
: next.processedAt !== undefined && next.processedAt <= next.t0 + P
|
|
115
|
+
? next.processedAt + g + H
|
|
116
|
+
: next.t0 + H;
|
|
117
|
+
if (next.killedAt !== undefined || next.sent.includes('kill')) {
|
|
118
|
+
const giveUpAt = (next.killedAt ?? killAt) + H;
|
|
119
|
+
if (nowMs >= giveUpAt)
|
|
120
|
+
return result(next, 'gave-up');
|
|
121
|
+
return result(next, 'wait', giveUpAt - nowMs);
|
|
122
|
+
}
|
|
123
|
+
if (!isAlive) {
|
|
124
|
+
if (nowMs >= killAt)
|
|
125
|
+
return result(next, 'gave-up');
|
|
126
|
+
return result(next, 'wait', killAt - nowMs);
|
|
127
|
+
}
|
|
128
|
+
if (nowMs >= killAt)
|
|
129
|
+
return result({ ...next, sent: [...next.sent, 'kill'], killedAt: nowMs }, 'kill');
|
|
130
|
+
if (next.mode === 'forced' &&
|
|
131
|
+
next.processedAt !== undefined &&
|
|
132
|
+
nowMs >= next.processedAt + g &&
|
|
133
|
+
draining !== undefined &&
|
|
134
|
+
!next.sent.includes('term-2')) {
|
|
135
|
+
return result({ ...next, sent: [...next.sent.filter((x) => x !== 'kill'), 'term-2'] }, 'term-2');
|
|
136
|
+
}
|
|
137
|
+
const term2At = next.mode === 'forced' && next.processedAt !== undefined && !next.sent.includes('term-2') ? next.processedAt + g : Infinity;
|
|
138
|
+
return result(next, 'wait', Math.min(killAt, term2At) - nowMs);
|
|
139
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { LocalEngineAnchoredProcess, LocalEngineRef } from './localEngineJudge.js';
|
|
2
|
+
import type { LocalEngineOccupancy } from './localEngineOccupancy.js';
|
|
3
|
+
import type { UiLanguage } from './env/uiLanguage.js';
|
|
4
|
+
export declare const LOCAL_ENGINE_SWAP_CAUSES: readonly ["version", "config", "credential", "model-default", "user", "unresponsive"];
|
|
5
|
+
export type LocalEngineSwapCause = (typeof LOCAL_ENGINE_SWAP_CAUSES)[number];
|
|
6
|
+
export interface LocalEngineSwapPolicy {
|
|
7
|
+
readonly credentialAuto?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare function localEngineSwapAutoable(cause: LocalEngineSwapCause, serving: boolean, policy?: LocalEngineSwapPolicy): boolean;
|
|
10
|
+
export type EngineSwapCardTarget = LocalEngineRef | LocalEngineAnchoredProcess;
|
|
11
|
+
export type EngineSwapCardUnknown = 'others' | 'inflight' | 'bg' | 'self';
|
|
12
|
+
export interface EngineSwapCardInput {
|
|
13
|
+
readonly cause: LocalEngineSwapCause;
|
|
14
|
+
readonly target: EngineSwapCardTarget;
|
|
15
|
+
readonly running?: string;
|
|
16
|
+
readonly own?: string;
|
|
17
|
+
readonly occupancy: LocalEngineOccupancy;
|
|
18
|
+
readonly bgIds: readonly string[] | null;
|
|
19
|
+
}
|
|
20
|
+
export interface EngineSwapCardOthers {
|
|
21
|
+
readonly total: number;
|
|
22
|
+
readonly midTurn: number;
|
|
23
|
+
}
|
|
24
|
+
export interface EngineSwapCard {
|
|
25
|
+
readonly cause: LocalEngineSwapCause;
|
|
26
|
+
readonly target: EngineSwapCardTarget;
|
|
27
|
+
readonly running?: string;
|
|
28
|
+
readonly own?: string;
|
|
29
|
+
readonly others: EngineSwapCardOthers | null;
|
|
30
|
+
readonly inflight: number | null;
|
|
31
|
+
readonly bgLive: number | null;
|
|
32
|
+
readonly unknown: readonly EngineSwapCardUnknown[];
|
|
33
|
+
readonly forcedWithinMs: number;
|
|
34
|
+
readonly fingerprint: string;
|
|
35
|
+
}
|
|
36
|
+
export declare function engineSwapCardOf(input: EngineSwapCardInput): EngineSwapCard | undefined;
|
|
37
|
+
export interface EngineSwapCardText {
|
|
38
|
+
readonly title: string;
|
|
39
|
+
readonly lines: readonly string[];
|
|
40
|
+
readonly force: string;
|
|
41
|
+
readonly wait: string;
|
|
42
|
+
}
|
|
43
|
+
export declare function engineSwapCardText(card: EngineSwapCard, lang?: UiLanguage): EngineSwapCardText | undefined;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { engineIdentityOf } from './engineIdentity.js';
|
|
2
|
+
import { readOwnDataValue } from './ownKey.js';
|
|
3
|
+
import { displayUntrusted } from './displayUntrusted.js';
|
|
4
|
+
import { LOCAL_ENGINE_STOP_LADDER } from './localEngineStopLadder.js';
|
|
5
|
+
import { msg, renderMessage } from './i18n/translate.js';
|
|
6
|
+
export const LOCAL_ENGINE_SWAP_CAUSES = Object.freeze(['version', 'config', 'credential', 'model-default', 'user', 'unresponsive']);
|
|
7
|
+
function isCause(v) {
|
|
8
|
+
return typeof v === 'string' && LOCAL_ENGINE_SWAP_CAUSES.includes(v);
|
|
9
|
+
}
|
|
10
|
+
function isRecord(v) {
|
|
11
|
+
if (typeof v !== 'object' || v === null)
|
|
12
|
+
return false;
|
|
13
|
+
try {
|
|
14
|
+
return !Array.isArray(v);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function isArray(v) {
|
|
21
|
+
try {
|
|
22
|
+
return Array.isArray(v);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function own(v, key) {
|
|
29
|
+
if (!isRecord(v) && !isArray(v))
|
|
30
|
+
return undefined;
|
|
31
|
+
try {
|
|
32
|
+
return readOwnDataValue(v, key);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function isCount(v) {
|
|
39
|
+
return typeof v === 'number' && Number.isSafeInteger(v) && v >= 0;
|
|
40
|
+
}
|
|
41
|
+
function isPid(v) {
|
|
42
|
+
return typeof v === 'number' && Number.isSafeInteger(v) && v > 0;
|
|
43
|
+
}
|
|
44
|
+
export function localEngineSwapAutoable(cause, serving, policy) {
|
|
45
|
+
if (!isCause(cause))
|
|
46
|
+
return false;
|
|
47
|
+
if (serving === false)
|
|
48
|
+
return true;
|
|
49
|
+
if (cause === 'version' || cause === 'user')
|
|
50
|
+
return true;
|
|
51
|
+
return cause === 'credential' && own(policy, 'credentialAuto') === true;
|
|
52
|
+
}
|
|
53
|
+
const FORCED_WITHIN_MS = LOCAL_ENGINE_STOP_LADDER.P + LOCAL_ENGINE_STOP_LADDER.g + LOCAL_ENGINE_STOP_LADDER.H;
|
|
54
|
+
function targetOf(t) {
|
|
55
|
+
const pid = own(t, 'pid');
|
|
56
|
+
if (!isPid(pid))
|
|
57
|
+
return undefined;
|
|
58
|
+
const identity = own(t, 'identity');
|
|
59
|
+
if (isRecord(identity)) {
|
|
60
|
+
const port = own(t, 'port');
|
|
61
|
+
if (typeof port !== 'number' || !Number.isInteger(port) || port < 1 || port > 65535)
|
|
62
|
+
return undefined;
|
|
63
|
+
const id = Object.freeze({ ...engineIdentityOf(identity) });
|
|
64
|
+
const version = own(t, 'version');
|
|
65
|
+
const target = Object.freeze({ pid, port, identity: id, ...(typeof version === 'string' && version.length > 0 ? { version } : {}) });
|
|
66
|
+
return { target, key: id };
|
|
67
|
+
}
|
|
68
|
+
const startedAt = own(t, 'startedAt');
|
|
69
|
+
const target = Object.freeze({ pid, ...(typeof startedAt === 'number' && Number.isFinite(startedAt) && startedAt > 0 ? { startedAt } : {}) });
|
|
70
|
+
return { target, key: engineIdentityOf(target) };
|
|
71
|
+
}
|
|
72
|
+
export function engineSwapCardOf(input) {
|
|
73
|
+
try {
|
|
74
|
+
const cause = own(input, 'cause');
|
|
75
|
+
if (!isCause(cause))
|
|
76
|
+
return undefined;
|
|
77
|
+
const t = targetOf(own(input, 'target'));
|
|
78
|
+
if (t === undefined)
|
|
79
|
+
return undefined;
|
|
80
|
+
const occ = own(input, 'occupancy');
|
|
81
|
+
const unknown = [];
|
|
82
|
+
const o = own(occ, 'others');
|
|
83
|
+
const total = own(o, 'total');
|
|
84
|
+
const midTurn = own(o, 'midTurn');
|
|
85
|
+
const others = own(o, 'known') === true && isCount(total) && isCount(midTurn) ? Object.freeze({ total, midTurn }) : null;
|
|
86
|
+
if (others === null)
|
|
87
|
+
unknown.push('others');
|
|
88
|
+
const inflightRaw = own(occ, 'inflight');
|
|
89
|
+
const inflight = isCount(inflightRaw) ? inflightRaw : null;
|
|
90
|
+
if (inflight === null)
|
|
91
|
+
unknown.push('inflight');
|
|
92
|
+
const bgRaw = own(occ, 'bgLive');
|
|
93
|
+
const idsRaw = own(input, 'bgIds');
|
|
94
|
+
let bgIds = null;
|
|
95
|
+
if (Array.isArray(idsRaw)) {
|
|
96
|
+
const list = [];
|
|
97
|
+
let readable = true;
|
|
98
|
+
for (let i = 0; i < idsRaw.length && readable; i++) {
|
|
99
|
+
const id = own(idsRaw, String(i));
|
|
100
|
+
if (typeof id === 'string')
|
|
101
|
+
list.push(id);
|
|
102
|
+
else
|
|
103
|
+
readable = false;
|
|
104
|
+
}
|
|
105
|
+
bgIds = readable ? list : null;
|
|
106
|
+
}
|
|
107
|
+
const bgLive = isCount(bgRaw) && bgIds !== null && bgIds.length === bgRaw ? bgRaw : null;
|
|
108
|
+
if (bgLive === null) {
|
|
109
|
+
unknown.push('bg');
|
|
110
|
+
bgIds = null;
|
|
111
|
+
}
|
|
112
|
+
const self = own(occ, 'self');
|
|
113
|
+
if (self !== 'idle' && self !== 'busy')
|
|
114
|
+
unknown.push('self');
|
|
115
|
+
const runningRaw = own(input, 'running');
|
|
116
|
+
const ownRaw = own(input, 'own');
|
|
117
|
+
const running = typeof runningRaw === 'string' && runningRaw.length > 0 ? runningRaw : undefined;
|
|
118
|
+
const mine = typeof ownRaw === 'string' && ownRaw.length > 0 ? ownRaw : undefined;
|
|
119
|
+
const k = t.key;
|
|
120
|
+
const fingerprint = JSON.stringify([
|
|
121
|
+
'local-engine-swap-card/1',
|
|
122
|
+
cause,
|
|
123
|
+
[t.target.pid, k.pid ?? null, k.instanceId ?? null, k.startedAt ?? null],
|
|
124
|
+
running ?? null,
|
|
125
|
+
mine ?? null,
|
|
126
|
+
others === null ? null : [others.total, others.midTurn],
|
|
127
|
+
inflight,
|
|
128
|
+
bgLive,
|
|
129
|
+
bgIds === null ? null : [...bgIds].sort(),
|
|
130
|
+
unknown,
|
|
131
|
+
self === 'idle' || self === 'busy' ? self : null,
|
|
132
|
+
]);
|
|
133
|
+
return Object.freeze({
|
|
134
|
+
cause,
|
|
135
|
+
target: t.target,
|
|
136
|
+
...(running !== undefined ? { running } : {}),
|
|
137
|
+
...(mine !== undefined ? { own: mine } : {}),
|
|
138
|
+
others,
|
|
139
|
+
inflight,
|
|
140
|
+
bgLive,
|
|
141
|
+
unknown: Object.freeze(unknown),
|
|
142
|
+
forcedWithinMs: FORCED_WITHIN_MS,
|
|
143
|
+
fingerprint,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const TITLE_KEYS = Object.freeze({
|
|
151
|
+
version: 'localEngine.card.title.version',
|
|
152
|
+
config: 'localEngine.card.title.config',
|
|
153
|
+
credential: 'localEngine.card.title.credential',
|
|
154
|
+
'model-default': 'localEngine.card.title.modelDefault',
|
|
155
|
+
user: 'localEngine.card.title.user',
|
|
156
|
+
unresponsive: 'localEngine.card.title.unresponsive',
|
|
157
|
+
});
|
|
158
|
+
const VERSION_DISPLAY_MAX = 64;
|
|
159
|
+
export function engineSwapCardText(card, lang) {
|
|
160
|
+
try {
|
|
161
|
+
const cause = own(card, 'cause');
|
|
162
|
+
const forcedWithinMs = own(card, 'forcedWithinMs');
|
|
163
|
+
if (!isCause(cause) || !isCount(forcedWithinMs))
|
|
164
|
+
return undefined;
|
|
165
|
+
const say = (key, params) => renderMessage(msg(key, params), lang);
|
|
166
|
+
const lines = [];
|
|
167
|
+
const running = own(card, 'running');
|
|
168
|
+
const mine = own(card, 'own');
|
|
169
|
+
if (typeof running === 'string' && running.length > 0 && typeof mine === 'string' && mine.length > 0) {
|
|
170
|
+
lines.push(say('localEngine.card.versions', { running: displayUntrusted(running, { max: VERSION_DISPLAY_MAX }), own: displayUntrusted(mine, { max: VERSION_DISPLAY_MAX }) }));
|
|
171
|
+
}
|
|
172
|
+
const others = own(card, 'others');
|
|
173
|
+
const total = own(others, 'total');
|
|
174
|
+
const midTurn = own(others, 'midTurn');
|
|
175
|
+
if (isCount(total) && isCount(midTurn))
|
|
176
|
+
lines.push(say(total === 1 ? 'localEngine.card.others.one' : 'localEngine.card.others.other', { total, midTurn }));
|
|
177
|
+
else
|
|
178
|
+
lines.push(say('localEngine.card.others.unknown'));
|
|
179
|
+
const inflight = own(card, 'inflight');
|
|
180
|
+
if (isCount(inflight))
|
|
181
|
+
lines.push(say(inflight === 1 ? 'localEngine.card.inflight.one' : 'localEngine.card.inflight.other', { count: inflight }));
|
|
182
|
+
else
|
|
183
|
+
lines.push(say('localEngine.card.inflight.unknown'));
|
|
184
|
+
const bgLive = own(card, 'bgLive');
|
|
185
|
+
if (isCount(bgLive))
|
|
186
|
+
lines.push(say(bgLive === 1 ? 'localEngine.card.background.one' : 'localEngine.card.background.other', { count: bgLive }));
|
|
187
|
+
else
|
|
188
|
+
lines.push(say('localEngine.card.background.unknown'));
|
|
189
|
+
if (isCount(inflight) && inflight > 0 && isCount(bgLive) && bgLive > 0)
|
|
190
|
+
lines.push(say('localEngine.card.overlap'));
|
|
191
|
+
const unknown = own(card, 'unknown');
|
|
192
|
+
if (Array.isArray(unknown) && unknown.includes('self'))
|
|
193
|
+
lines.push(say('localEngine.card.selfUnknown'));
|
|
194
|
+
lines.push(say('localEngine.card.parked'));
|
|
195
|
+
return Object.freeze({
|
|
196
|
+
title: say(TITLE_KEYS[cause]),
|
|
197
|
+
lines: Object.freeze(lines),
|
|
198
|
+
force: say('localEngine.card.force', { seconds: Math.ceil(forcedWithinMs / 1000) }),
|
|
199
|
+
wait: say('localEngine.card.wait'),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
}
|
package/dist/mcpReconnect.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
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 } from './wireFailureShape.js';
|
|
4
5
|
import { readMcpAuthorization } from './mcpLiveness.js';
|
|
5
6
|
export const MCP_RECONNECT_OUTCOMES = Object.freeze(['accepted', 'refused', 'unsupported']);
|
|
6
7
|
export const MCP_RECONNECT_TRANSACTION_NOTICE = 'Reconnecting closes the current connection before dialing again. On a healthy server this is a transaction, ' +
|
|
@@ -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;
|
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;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { SETTINGS_RESOLVE_CATALOG_DEFAULT_MODEL, type SETTINGS_DEFAULT_ROLE_TIER_LADDER } from '../generated/settingsSchemaFns.js';
|
|
2
|
+
import { SETTINGS_MODEL_PLANE_REFUSAL_CODES } from '../generated/settingsSchemaTables.js';
|
|
3
|
+
import type { UiLanguage } from '../env/uiLanguage.js';
|
|
2
4
|
export type CatalogDefaultModelsInput = Parameters<typeof SETTINGS_RESOLVE_CATALOG_DEFAULT_MODEL>[0];
|
|
3
5
|
export type CatalogDefaultSource = 'roles.default' | 'fallback' | `tierGroup.${(typeof SETTINGS_DEFAULT_ROLE_TIER_LADDER)[number]}`;
|
|
4
6
|
export interface CatalogDefaultDangling {
|
|
@@ -22,3 +24,26 @@ export interface CatalogDefaultSelect {
|
|
|
22
24
|
}
|
|
23
25
|
export type CatalogDefaultModel = CatalogDefaultNamed | CatalogDefaultSelect;
|
|
24
26
|
export declare function resolveCatalogDefaultModel(models: CatalogDefaultModelsInput, has: (name: string) => boolean, fallback: string): CatalogDefaultModel;
|
|
27
|
+
export type CatalogDefaultRefusalCode = Extract<(typeof SETTINGS_MODEL_PLANE_REFUSAL_CODES)[number], 'config.default_model_missing'>;
|
|
28
|
+
export interface CatalogDefaultModelVerdictInput {
|
|
29
|
+
readonly models: CatalogDefaultModelsInput;
|
|
30
|
+
readonly has: (name: string) => boolean;
|
|
31
|
+
readonly fallback: string;
|
|
32
|
+
readonly face: object;
|
|
33
|
+
}
|
|
34
|
+
export interface CatalogDefaultResolvable {
|
|
35
|
+
readonly ok: true;
|
|
36
|
+
readonly default: CatalogDefaultModel;
|
|
37
|
+
}
|
|
38
|
+
export interface CatalogDefaultRefused {
|
|
39
|
+
readonly ok: false;
|
|
40
|
+
readonly code: CatalogDefaultRefusalCode;
|
|
41
|
+
readonly default: CatalogDefaultModel;
|
|
42
|
+
readonly detail: {
|
|
43
|
+
readonly cause: string;
|
|
44
|
+
readonly role: string;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export type CatalogDefaultModelVerdict = CatalogDefaultResolvable | CatalogDefaultRefused;
|
|
48
|
+
export declare function catalogDefaultModelVerdict(input: CatalogDefaultModelVerdictInput): CatalogDefaultModelVerdict | undefined;
|
|
49
|
+
export declare function catalogDefaultRefusalContent(verdict: CatalogDefaultModelVerdict, lang?: UiLanguage): string | undefined;
|
|
@@ -1,4 +1,48 @@
|
|
|
1
1
|
import { SETTINGS_RESOLVE_CATALOG_DEFAULT_MODEL } from '../generated/settingsSchemaFns.js';
|
|
2
|
+
import { modelFaceResolvable } from '../coreValuePorts.js';
|
|
3
|
+
import { slotReadOf } from '../wireFailureShape.js';
|
|
4
|
+
import { displayUntrusted } from '../displayUntrusted.js';
|
|
5
|
+
import { msg, renderMessage } from '../i18n/translate.js';
|
|
2
6
|
export function resolveCatalogDefaultModel(models, has, fallback) {
|
|
3
7
|
return SETTINGS_RESOLVE_CATALOG_DEFAULT_MODEL(models, has, fallback);
|
|
4
8
|
}
|
|
9
|
+
const CATALOG_DEFAULT_REFUSAL_CODE = 'config.default_model_missing';
|
|
10
|
+
export function catalogDefaultModelVerdict(input) {
|
|
11
|
+
const { models, has, fallback, face } = input;
|
|
12
|
+
const chain = resolveCatalogDefaultModel(models, has, fallback);
|
|
13
|
+
const verdict = modelFaceResolvable(face, { spec: { modelRole: 'default' } });
|
|
14
|
+
if (verdict === undefined || typeof verdict !== 'object' || verdict === null)
|
|
15
|
+
return undefined;
|
|
16
|
+
const okSlot = slotReadOf(verdict, 'ok');
|
|
17
|
+
if (okSlot.presence !== 'present')
|
|
18
|
+
return undefined;
|
|
19
|
+
if (okSlot.value === true)
|
|
20
|
+
return Object.freeze({ ok: true, default: chain });
|
|
21
|
+
if (okSlot.value !== false)
|
|
22
|
+
return undefined;
|
|
23
|
+
const codeSlot = slotReadOf(verdict, 'code');
|
|
24
|
+
const roleSlot = slotReadOf(verdict, 'role');
|
|
25
|
+
const code = codeSlot.presence === 'present' ? codeSlot.value : undefined;
|
|
26
|
+
const role = roleSlot.presence === 'present' ? roleSlot.value : undefined;
|
|
27
|
+
if (typeof code !== 'string' || code.length === 0 || typeof role !== 'string' || role.length === 0)
|
|
28
|
+
return undefined;
|
|
29
|
+
return Object.freeze({
|
|
30
|
+
ok: false,
|
|
31
|
+
code: CATALOG_DEFAULT_REFUSAL_CODE,
|
|
32
|
+
default: chain,
|
|
33
|
+
detail: Object.freeze({ cause: code, role }),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export function catalogDefaultRefusalContent(verdict, lang) {
|
|
37
|
+
if (typeof verdict !== 'object' || verdict === null || verdict.ok !== false)
|
|
38
|
+
return undefined;
|
|
39
|
+
const d = verdict.default;
|
|
40
|
+
if (typeof d !== 'object' || d === null)
|
|
41
|
+
return undefined;
|
|
42
|
+
if (d.kind === 'named' && typeof d.name === 'string') {
|
|
43
|
+
return renderMessage(msg('model.catalogDefault.refused.named', { name: displayUntrusted(d.name, { max: 64 }) }), lang);
|
|
44
|
+
}
|
|
45
|
+
if (d.kind === 'select')
|
|
46
|
+
return renderMessage(msg('model.catalogDefault.refused.select'), lang);
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
@@ -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 = {
|