@sema-agent/client-core 0.110.0 → 0.112.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.
Files changed (243) hide show
  1. package/CHANGELOG.md +244 -0
  2. package/README.md +59 -18
  3. package/dist/abortableSleep.d.ts +7 -0
  4. package/dist/abortableSleep.js +37 -0
  5. package/dist/adapt/arms.js +25 -4
  6. package/dist/adapt/toolCards.d.ts +3 -1
  7. package/dist/adapt/toolCards.js +4 -2
  8. package/dist/adapter/activeRunSelfHeal.d.ts +39 -0
  9. package/dist/adapter/activeRunSelfHeal.js +171 -3
  10. package/dist/adapter/downstream/eventToSdkMessage.js +10 -10
  11. package/dist/adapter/downstream/terminalToSdkResult.d.ts +2 -0
  12. package/dist/adapter/downstream/terminalToSdkResult.js +11 -0
  13. package/dist/adapter/runStream.d.ts +5 -0
  14. package/dist/adapter/runStream.js +15 -3
  15. package/dist/agentSession/engineSessionBackgroundStop.js +2 -2
  16. package/dist/attachmentRoute.d.ts +20 -0
  17. package/dist/attachmentRoute.js +39 -0
  18. package/dist/attachmentsWireCaps.js +1 -1
  19. package/dist/builtinOutputStyles.d.ts +12 -0
  20. package/dist/builtinOutputStyles.js +147 -0
  21. package/dist/checkpointRowVerdict.d.ts +3 -0
  22. package/dist/checkpointRowVerdict.js +14 -1
  23. package/dist/compactionStatus.d.ts +9 -0
  24. package/dist/compactionStatus.js +29 -5
  25. package/dist/consoleCaPin.d.ts +136 -0
  26. package/dist/consoleCaPin.js +827 -0
  27. package/dist/controlRouter.js +2 -1
  28. package/dist/coreValuePorts.d.ts +39 -1
  29. package/dist/coreValuePorts.js +10 -0
  30. package/dist/decideReceipt.d.ts +2 -1
  31. package/dist/deviceRebind.js +3 -3
  32. package/dist/displayUntrusted.d.ts +1 -0
  33. package/dist/displayUntrusted.js +6 -0
  34. package/dist/effectiveFacts.d.ts +9 -0
  35. package/dist/effectiveFacts.js +33 -0
  36. package/dist/engineAddress.d.ts +1 -0
  37. package/dist/engineAddress.js +4 -1
  38. package/dist/engineCredentialRelease.d.ts +54 -0
  39. package/dist/engineCredentialRelease.js +146 -0
  40. package/dist/engineErrorCodes.d.ts +1 -0
  41. package/dist/engineErrorCodes.js +1 -0
  42. package/dist/engineHealthProbeVerdict.d.ts +24 -0
  43. package/dist/engineHealthProbeVerdict.js +123 -0
  44. package/dist/engineNoticeCodes.js +5 -7
  45. package/dist/engineRepoint.d.ts +12 -5
  46. package/dist/engineRepoint.js +44 -19
  47. package/dist/engineRowRef.d.ts +3 -0
  48. package/dist/engineRowRef.js +11 -1
  49. package/dist/engineSwapCrossing.d.ts +57 -0
  50. package/dist/engineSwapCrossing.js +297 -0
  51. package/dist/engineUnavailable.d.ts +45 -0
  52. package/dist/engineUnavailable.js +496 -0
  53. package/dist/engineUrl.js +4 -2
  54. package/dist/eventLeg.js +3 -3
  55. package/dist/gateFailureCodes.js +2 -2
  56. package/dist/generated/sdkFactTables.d.ts +2 -1
  57. package/dist/generated/sdkFactTables.js +14 -0
  58. package/dist/generated/settingsSchemaFns.d.ts +1 -0
  59. package/dist/generated/settingsSchemaFns.js +3 -0
  60. package/dist/generated/settingsSchemaHooksClassifier.js +38 -9
  61. package/dist/generated/settingsSchemaTables.d.ts +56 -0
  62. package/dist/generated/settingsSchemaTables.js +19 -0
  63. package/dist/generated/settingsSchemaTypes.d.ts +23 -13
  64. package/dist/generated/settingsSchemaUrlShape.d.ts +1 -0
  65. package/dist/generated/settingsSchemaUrlShape.js +17 -0
  66. package/dist/hitl/approvalDelivery.d.ts +1 -0
  67. package/dist/hitl/approvalDelivery.js +1 -1
  68. package/dist/hitl/approvalResolution.d.ts +1 -1
  69. package/dist/hitl/approvalResolution.js +2 -1
  70. package/dist/hitl/askGateWire.js +5 -6
  71. package/dist/hitl/decisionRefusal.d.ts +6 -0
  72. package/dist/hitl/decisionRefusal.js +19 -10
  73. package/dist/hitl/decisionRefusalCarrier.d.ts +3 -0
  74. package/dist/hitl/decisionRefusalCarrier.js +348 -0
  75. package/dist/hitl/decisionSend.d.ts +7 -0
  76. package/dist/hitl/decisionSend.js +34 -12
  77. package/dist/hitl/editedApprovalAcceptance.js +1 -1
  78. package/dist/hitl/elicitationRefusal.d.ts +4 -0
  79. package/dist/hitl/elicitationRefusal.js +23 -0
  80. package/dist/hitl/frameRouter.d.ts +2 -2
  81. package/dist/hitl/frameRouter.js +8 -15
  82. package/dist/hitl/gateIdentity.d.ts +5 -0
  83. package/dist/hitl/gateIdentity.js +23 -0
  84. package/dist/hitl/hitlBridge.d.ts +6 -0
  85. package/dist/hitl/hitlBridge.js +10 -4
  86. package/dist/hitl/hitlHostSurface.d.ts +2 -1
  87. package/dist/hitl/hitlHostSurface.js +7 -2
  88. package/dist/hitl/localAllowRedemption.d.ts +70 -0
  89. package/dist/hitl/localAllowRedemption.js +276 -0
  90. package/dist/hitl/parkOwnership.js +2 -1
  91. package/dist/hitl/parkResolver.js +14 -4
  92. package/dist/hitl/persistedRulesWire.d.ts +16 -0
  93. package/dist/hitl/persistedRulesWire.js +70 -0
  94. package/dist/hitl/planReviewWire.d.ts +3 -0
  95. package/dist/hitl/planReviewWire.js +23 -6
  96. package/dist/hitl/policyDenyCause.d.ts +1 -1
  97. package/dist/hitl/policyDenyCause.js +1 -1
  98. package/dist/hitl/resumeRunningCard.d.ts +1 -0
  99. package/dist/hitl/resumeRunningCard.js +1 -0
  100. package/dist/hitl/screenCardCarriers.d.ts +15 -0
  101. package/dist/hitl/screenCardCarriers.js +41 -0
  102. package/dist/hitl/screenDecisionCards.d.ts +98 -0
  103. package/dist/hitl/screenDecisionCards.js +363 -0
  104. package/dist/hitl/sessionPolicyWire.js +2 -2
  105. package/dist/hitl/standingRows.d.ts +6 -2
  106. package/dist/hitl/standingRows.js +27 -2
  107. package/dist/hitl/toolApprovalWire.d.ts +30 -8
  108. package/dist/hitl/toolApprovalWire.js +264 -121
  109. package/dist/hooksReaderGenerations.js +1 -1
  110. package/dist/hooksWireCaps.js +3 -3
  111. package/dist/hostSettingNoticeArm.d.ts +3 -0
  112. package/dist/hostSettingNoticeArm.js +12 -0
  113. package/dist/hostTurnSettings.d.ts +3 -0
  114. package/dist/hostTurnSettings.js +38 -5
  115. package/dist/humanTurnRead.js +11 -1
  116. package/dist/i18n/messages.en.d.ts +292 -0
  117. package/dist/i18n/messages.en.js +298 -7
  118. package/dist/i18n/messages.zh.js +316 -6
  119. package/dist/i18n/translate.d.ts +3 -0
  120. package/dist/i18n/translate.js +78 -10
  121. package/dist/i18n/zh.d.ts +1 -0
  122. package/dist/i18n/zh.js +1 -0
  123. package/dist/index.d.ts +71 -20
  124. package/dist/index.js +44 -13
  125. package/dist/interactiveHalt.js +2 -1
  126. package/dist/interactiveInterrupt.js +20 -2
  127. package/dist/liveModelCatalog.d.ts +4 -0
  128. package/dist/liveModelCatalog.js +12 -3
  129. package/dist/localConfig/documents.d.ts +61 -0
  130. package/dist/localConfig/documents.js +51 -0
  131. package/dist/localConfig/kernel.d.ts +149 -0
  132. package/dist/localConfig/kernel.js +597 -0
  133. package/dist/localConfig/mcp/edit.d.ts +161 -0
  134. package/dist/localConfig/mcp/edit.js +846 -0
  135. package/dist/localConfig/mcp/servers.d.ts +165 -0
  136. package/dist/localConfig/mcp/servers.js +776 -0
  137. package/dist/localConfig/mcp/views.d.ts +170 -0
  138. package/dist/localConfig/mcp/views.js +429 -0
  139. package/dist/localConfig/modelPool.d.ts +262 -0
  140. package/dist/localConfig/modelPool.js +1489 -0
  141. package/dist/localConfig/modelPoolWords.d.ts +13 -0
  142. package/dist/localConfig/modelPoolWords.js +20 -0
  143. package/dist/localConfig/receiptShape.d.ts +7 -0
  144. package/dist/localConfig/receiptShape.js +135 -0
  145. package/dist/localConfig/settingsWrite.d.ts +127 -0
  146. package/dist/localConfig/settingsWrite.js +237 -0
  147. package/dist/localConfig/skills.d.ts +124 -0
  148. package/dist/localConfig/skills.js +397 -0
  149. package/dist/localConfig/skillsWords.d.ts +10 -0
  150. package/dist/localConfig/skillsWords.js +5 -0
  151. package/dist/localEngineSeatReading.d.ts +2 -2
  152. package/dist/localEngineSeatReading.js +5 -3
  153. package/dist/localEngineSpawn.d.ts +0 -7
  154. package/dist/localEngineSpawn.js +0 -92
  155. package/dist/localEngineStep.d.ts +2 -2
  156. package/dist/localEngineStep.js +2 -0
  157. package/dist/mcpProbeWire.js +2 -1
  158. package/dist/mcpReconnect.js +3 -3
  159. package/dist/mcpWireCaps.js +1 -1
  160. package/dist/memoryVerbsWire.js +3 -2
  161. package/dist/model/engineModelWords.d.ts +3 -0
  162. package/dist/model/engineModelWords.js +13 -0
  163. package/dist/model/onboardJudgement.d.ts +14 -4
  164. package/dist/model/onboardJudgement.js +91 -139
  165. package/dist/model/subagentModelWord.d.ts +21 -2
  166. package/dist/model/subagentModelWord.js +100 -29
  167. package/dist/modelCapabilityProbe.js +3 -40
  168. package/dist/modelConfigGapNotice.d.ts +20 -0
  169. package/dist/modelConfigGapNotice.js +102 -0
  170. package/dist/modsKernelIdentity.js +2 -47
  171. package/dist/notifications.d.ts +19 -0
  172. package/dist/notifications.js +114 -12
  173. package/dist/ownKey.d.ts +7 -0
  174. package/dist/ownKey.js +71 -8
  175. package/dist/pendingUserRow.d.ts +4 -0
  176. package/dist/pendingUserRow.js +16 -0
  177. package/dist/permissionsDeclaration.d.ts +3 -1
  178. package/dist/permissionsDeclaration.js +33 -9
  179. package/dist/planReviewCardFacts.d.ts +3 -0
  180. package/dist/planReviewCardFacts.js +52 -2
  181. package/dist/pluginHooksWire.d.ts +9 -1
  182. package/dist/pluginHooksWire.js +40 -6
  183. package/dist/printToolResultFrame.js +4 -2
  184. package/dist/promptBlockedCopy.js +12 -7
  185. package/dist/promptSuggestionBatch.d.ts +7 -0
  186. package/dist/promptSuggestionBatch.js +9 -0
  187. package/dist/promptSuggestionsTail.d.ts +39 -0
  188. package/dist/promptSuggestionsTail.js +116 -0
  189. package/dist/request/taskRequest.js +10 -6
  190. package/dist/requestCoordinate.d.ts +1 -0
  191. package/dist/requestCoordinate.js +13 -0
  192. package/dist/resumeRefusalCopy.d.ts +3 -1
  193. package/dist/resumeRefusalCopy.js +12 -4
  194. package/dist/rewindArchiveReading.js +4 -4
  195. package/dist/runCancelContext.d.ts +2 -1
  196. package/dist/runCancelContext.js +1 -0
  197. package/dist/runLedgerSnapshot.js +3 -3
  198. package/dist/runPermissionModeSwitch.js +3 -4
  199. package/dist/scenarioRefusal.js +11 -13
  200. package/dist/scenarioSkills.js +11 -6
  201. package/dist/seam.d.ts +16 -2
  202. package/dist/seam.js +10 -0
  203. package/dist/seatContract.d.ts +259 -9
  204. package/dist/seatContract.js +444 -15
  205. package/dist/sessionCostReading.d.ts +70 -0
  206. package/dist/sessionCostReading.js +150 -0
  207. package/dist/sessionHooksRoster.d.ts +4 -0
  208. package/dist/sessionHooksRoster.js +158 -0
  209. package/dist/sessionIdSegment.d.ts +1 -0
  210. package/dist/sessionIdSegment.js +4 -1
  211. package/dist/sessionMemoryErase.js +2 -1
  212. package/dist/sessionMemoryStatus.js +2 -2
  213. package/dist/sessionReadRedaction.d.ts +7 -0
  214. package/dist/sessionReadRedaction.js +26 -0
  215. package/dist/sessionSyncFileHistory.js +8 -9
  216. package/dist/settingsHooksSalvage.js +2 -1
  217. package/dist/sha256Bytes.d.ts +1 -0
  218. package/dist/sha256Bytes.js +46 -0
  219. package/dist/skillOverrideChain.d.ts +83 -0
  220. package/dist/skillOverrideChain.js +378 -0
  221. package/dist/skillsWireCaps.d.ts +3 -0
  222. package/dist/skillsWireCaps.js +16 -1
  223. package/dist/sseIdleTriage.js +2 -1
  224. package/dist/storeDurabilityWord.d.ts +15 -0
  225. package/dist/storeDurabilityWord.js +18 -0
  226. package/dist/storePostureCapability.d.ts +7 -3
  227. package/dist/storePostureCapability.js +69 -39
  228. package/dist/subagent/engineSubagentResume.js +2 -2
  229. package/dist/subagent/engineSubagentTail.js +2 -1
  230. package/dist/subagentContentStore.d.ts +11 -0
  231. package/dist/subagentContentStore.js +28 -2
  232. package/dist/toolResult.d.ts +1 -0
  233. package/dist/toolResult.js +2 -0
  234. package/dist/turnFailurePresentation.js +4 -10
  235. package/dist/userAttachmentEnvelope.d.ts +2 -1
  236. package/dist/userAttachmentEnvelope.js +10 -0
  237. package/dist/wireErrorTriage.js +2 -1
  238. package/dist/wireFailureShape.d.ts +4 -0
  239. package/dist/wireFailureShape.js +141 -19
  240. package/dist/wireRefusalCopy.d.ts +6 -5
  241. package/dist/wireRefusalCopy.js +49 -92
  242. package/docs/INTEGRATION-CLIENTS.md +2718 -17
  243. package/package.json +7 -3
@@ -0,0 +1,378 @@
1
+ import { SETTINGS_IS_RESERVED_MEMBER_NAME } from './generated/settingsSchemaFns.js';
2
+ import { readOwnDataValue } from './ownKey.js';
3
+ export const SKILL_OVERRIDE_STATES = Object.freeze(['on', 'name-only', 'user-invocable-only', 'off']);
4
+ export const SKILL_ORIGINS = Object.freeze(['skills', 'synced', 'commands', 'mcp', 'plugin', 'builtin', 'bundled']);
5
+ export const SKILL_OVERRIDE_LAYERS = Object.freeze(['policySettings', 'flagSettings', 'localSettings', 'projectSettings', 'userSettings']);
6
+ export const SKILL_OVERRIDE_DECIDERS = Object.freeze([...SKILL_OVERRIDE_LAYERS, 'session', 'default', 'plugin', 'builtin_clamp', 'hidden_behind_plugin']);
7
+ export const SKILL_OVERRIDE_LOCK_SOURCES = Object.freeze(['policy', 'flag', 'plugin', 'skill-author', 'reserved-name']);
8
+ const MANAGED = ['policySettings', 'flagSettings'];
9
+ const PLAIN = ['localSettings', 'projectSettings', 'userSettings'];
10
+ const MERGED = ['policySettings', 'flagSettings', 'localSettings', 'projectSettings', 'userSettings'];
11
+ const RANK = { on: 0, 'name-only': 1, 'user-invocable-only': 2, off: 3 };
12
+ const LIST_BUDGET = 4096;
13
+ function own(v, key) {
14
+ if (typeof v !== 'object' || v === null)
15
+ return undefined;
16
+ try {
17
+ return readOwnDataValue(v, key);
18
+ }
19
+ catch {
20
+ return undefined;
21
+ }
22
+ }
23
+ const isState = (v) => typeof v === 'string' && SKILL_OVERRIDE_STATES.some((w) => w === v);
24
+ const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
25
+ function stringsOf(v) {
26
+ const out = [];
27
+ if (!Array.isArray(v))
28
+ return out;
29
+ let n;
30
+ try {
31
+ n = own(v, 'length');
32
+ }
33
+ catch {
34
+ return out;
35
+ }
36
+ if (typeof n !== 'number' || !Number.isSafeInteger(n) || n < 0 || n > LIST_BUDGET)
37
+ return out;
38
+ for (let i = 0; i < n; i++) {
39
+ const s = own(v, String(i));
40
+ if (typeof s === 'string' && s !== '')
41
+ out.push(s);
42
+ }
43
+ return out;
44
+ }
45
+ export function shapeSkillSubject(v) {
46
+ if (!isRecord(v))
47
+ return undefined;
48
+ const name = own(v, 'name');
49
+ const originRaw = own(v, 'origin');
50
+ const origin = SKILL_ORIGINS.find((w) => w === originRaw);
51
+ if (typeof name !== 'string' || name === '' || origin === undefined)
52
+ return undefined;
53
+ const unq = own(v, 'unqualifiedName');
54
+ const synced = [];
55
+ const rawSynced = own(v, 'syncedNames');
56
+ if (Array.isArray(rawSynced)) {
57
+ const n = own(rawSynced, 'length');
58
+ if (typeof n === 'number' && Number.isSafeInteger(n) && n >= 0 && n <= LIST_BUDGET) {
59
+ for (let i = 0; i < n; i++) {
60
+ const e = own(rawSynced, String(i));
61
+ const sn = own(e, 'name');
62
+ const sb = own(e, 'bare');
63
+ if (typeof sn === 'string' && sn !== '' && typeof sb === 'string' && sb !== '')
64
+ synced.push(Object.freeze({ name: sn, bare: sb }));
65
+ }
66
+ }
67
+ }
68
+ return Object.freeze({
69
+ name,
70
+ origin,
71
+ unqualifiedName: typeof unq === 'string' && unq !== '' ? unq : undefined,
72
+ bareNameReserved: own(v, 'bareNameReserved') === true,
73
+ aliases: Object.freeze(stringsOf(own(v, 'aliases'))),
74
+ mcpLabels: Object.freeze(stringsOf(own(v, 'mcpLabels')).filter((s) => s.includes(':'))),
75
+ reservedLabels: Object.freeze(stringsOf(own(v, 'reservedLabels'))),
76
+ syncedNames: Object.freeze(synced),
77
+ followsSyncedSkillState: own(v, 'followsSyncedSkillState') === true,
78
+ disableModelInvocation: own(v, 'disableModelInvocation') === true,
79
+ hiddenBehindPlugin: own(v, 'hiddenBehindPlugin') === true,
80
+ });
81
+ }
82
+ function overridesOf(doc) {
83
+ const ov = own(doc, 'skillOverrides');
84
+ return isRecord(ov) ? ov : undefined;
85
+ }
86
+ export function shapeSkillLayers(v) {
87
+ const docs = {};
88
+ for (const l of SKILL_OVERRIDE_LAYERS)
89
+ docs[l] = overridesOf(own(v, l));
90
+ const disabled = new Set(stringsOf(own(v, 'disabledSources')));
91
+ const session = own(v, 'session');
92
+ const enabled = (layer) => layer === 'policySettings' || layer === 'flagSettings' || !disabled.has(layer);
93
+ let fromSettings = false;
94
+ for (const l of MERGED) {
95
+ if (!enabled(l))
96
+ continue;
97
+ const b = own(own(v, l), 'disableBundledSkills');
98
+ if (typeof b === 'boolean') {
99
+ fromSettings = b;
100
+ break;
101
+ }
102
+ }
103
+ return Object.freeze({
104
+ docs: Object.freeze(docs),
105
+ session: isRecord(session) ? session : undefined,
106
+ enabled,
107
+ bundledDisabled: fromSettings || own(v, 'bundledSkillsDisabledByEnv') === true,
108
+ });
109
+ }
110
+ function at(ov, key) {
111
+ if (ov === undefined || key === undefined)
112
+ return undefined;
113
+ const w = own(ov, key);
114
+ return isState(w) ? w : undefined;
115
+ }
116
+ const hitAt = (ov, key, by) => {
117
+ const w = at(ov, key);
118
+ return w === undefined ? undefined : { value: w, by };
119
+ };
120
+ function stricter(a, b) {
121
+ if (a === undefined)
122
+ return b;
123
+ return b !== undefined && RANK[b.value] > RANK[a.value] ? b : a;
124
+ }
125
+ function strictestIn(ov, names, by) {
126
+ let r;
127
+ for (const n of names) {
128
+ const w = at(ov, n);
129
+ if (w !== undefined && (r === undefined || RANK[w] > RANK[r.value]))
130
+ r = { value: w, by };
131
+ }
132
+ return r;
133
+ }
134
+ export function bareNameOf(s) {
135
+ if (s.unqualifiedName === undefined)
136
+ return undefined;
137
+ if (s.origin !== 'synced')
138
+ return s.unqualifiedName;
139
+ return s.bareNameReserved ? undefined : s.unqualifiedName;
140
+ }
141
+ const freeLabels = (s) => s.mcpLabels.filter((l) => !s.reservedLabels.includes(l));
142
+ function aliasNamesOf(s) {
143
+ return [...s.aliases, ...(s.unqualifiedName !== undefined ? [s.unqualifiedName] : []), ...freeLabels(s)];
144
+ }
145
+ export function secondaryNamesOf(s) {
146
+ const bare = bareNameOf(s);
147
+ return [...(bare !== undefined ? [bare] : []), ...(s.origin === 'mcp' ? freeLabels(s) : [])];
148
+ }
149
+ function reservedTighten(ov, s, by) {
150
+ const r = strictestIn(ov, s.reservedLabels, by);
151
+ return r === undefined || r.value === 'on' ? undefined : r;
152
+ }
153
+ function mergedAt(L, key) {
154
+ for (const l of MERGED) {
155
+ if (!L.enabled(l))
156
+ continue;
157
+ const h = hitAt(L.docs[l], key, l);
158
+ if (h !== undefined)
159
+ return h;
160
+ }
161
+ return undefined;
162
+ }
163
+ function viewThenSession(L, names) {
164
+ for (const n of names) {
165
+ const h = mergedAt(L, n);
166
+ if (h !== undefined)
167
+ return h;
168
+ }
169
+ for (const n of names) {
170
+ const h = hitAt(L.session, n, 'session');
171
+ if (h !== undefined)
172
+ return h;
173
+ }
174
+ return undefined;
175
+ }
176
+ function managedOnly(L, names) {
177
+ for (const n of names)
178
+ for (const l of MANAGED) {
179
+ const h = hitAt(L.docs[l], n, l);
180
+ if (h !== undefined)
181
+ return h;
182
+ }
183
+ return undefined;
184
+ }
185
+ function mcpChain(L, s, rest) {
186
+ const names = [s.name, ...rest];
187
+ let m;
188
+ let tight;
189
+ for (const l of MANAGED) {
190
+ m = stricter(m, strictestIn(L.docs[l], names, l));
191
+ tight = stricter(tight, reservedTighten(L.docs[l], s, l));
192
+ }
193
+ if (m !== undefined)
194
+ return stricter(m, tight);
195
+ for (const l of PLAIN) {
196
+ if (!L.enabled(l))
197
+ continue;
198
+ const b = strictestIn(L.docs[l], names, l);
199
+ if (b !== undefined)
200
+ return stricter(b, tight);
201
+ }
202
+ return stricter(strictestIn(L.session, names, 'session'), tight);
203
+ }
204
+ function overrideChain(L, s, mode) {
205
+ const rest = secondaryNamesOf(s);
206
+ if (s.origin === 'mcp')
207
+ return mcpChain(L, s, rest);
208
+ let v = (mode === 'all' ? viewThenSession : managedOnly)(L, [s.name, ...rest]);
209
+ const aliases = aliasNamesOf(s);
210
+ if (aliases.length === 0)
211
+ return v;
212
+ for (const l of MANAGED) {
213
+ const ov = L.docs[l];
214
+ if (at(ov, s.name) !== undefined)
215
+ break;
216
+ const w = strictestIn(ov, aliases, l);
217
+ if (w !== undefined && (v === undefined || RANK[w.value] > RANK[v.value]))
218
+ v = w;
219
+ if (rest.some((d) => at(ov, d) !== undefined))
220
+ break;
221
+ }
222
+ return v;
223
+ }
224
+ function managedLock(L, s, mode) {
225
+ const aliases = aliasNamesOf(s);
226
+ let found;
227
+ for (const l of MANAGED) {
228
+ const ov = L.docs[l];
229
+ const b = s.origin === 'mcp'
230
+ ? stricter(strictestIn(ov, [s.name, ...aliases], l), reservedTighten(ov, s, l))?.value
231
+ : (at(ov, s.name) ?? strictestIn(ov, aliases, l)?.value ?? reservedTighten(ov, s, l)?.value);
232
+ if (b === undefined)
233
+ continue;
234
+ if (s.origin !== 'mcp') {
235
+ found = { value: b, source: l };
236
+ break;
237
+ }
238
+ if (found === undefined || RANK[b] > RANK[found.value])
239
+ found = { value: b, source: l };
240
+ }
241
+ if (found === undefined)
242
+ return undefined;
243
+ const chain = overrideChain(L, s, mode);
244
+ return chain !== undefined ? { value: chain.value, source: found.source, by: chain.by } : { value: found.value, source: found.source, by: found.source };
245
+ }
246
+ function pluginManaged(L, s) {
247
+ if (s.origin !== 'plugin')
248
+ return managedLock(L, s, 'all');
249
+ let n;
250
+ for (const sn of s.syncedNames) {
251
+ const h = Object.freeze({
252
+ ...EMPTY_SUBJECT,
253
+ name: sn.name,
254
+ origin: 'synced',
255
+ unqualifiedName: sn.bare,
256
+ bareNameReserved: !s.aliases.includes(sn.bare),
257
+ });
258
+ const y = managedLock(L, h, 'administrator');
259
+ if (y !== undefined && (n === undefined || RANK[y.value] > RANK[n.value]))
260
+ n = y;
261
+ }
262
+ return n;
263
+ }
264
+ const EMPTY_SUBJECT = Object.freeze({
265
+ name: '',
266
+ origin: 'skills',
267
+ unqualifiedName: undefined,
268
+ bareNameReserved: false,
269
+ aliases: Object.freeze([]),
270
+ mcpLabels: Object.freeze([]),
271
+ reservedLabels: Object.freeze([]),
272
+ syncedNames: Object.freeze([]),
273
+ followsSyncedSkillState: false,
274
+ disableModelInvocation: false,
275
+ hiddenBehindPlugin: false,
276
+ });
277
+ function pluginFollow(L, s) {
278
+ let r;
279
+ for (const sn of s.syncedNames) {
280
+ const y = viewThenSession(L, s.followsSyncedSkillState || s.aliases.includes(sn.bare) ? [sn.name, sn.bare] : [sn.name]);
281
+ if (s.followsSyncedSkillState || y?.value === 'user-invocable-only')
282
+ r = stricter(r, y);
283
+ }
284
+ return r;
285
+ }
286
+ const effective = (value, decidedBy) => Object.freeze({ value, decidedBy });
287
+ export function effectiveOfShaped(L, s) {
288
+ if (s.hiddenBehindPlugin)
289
+ return effective('off', 'hidden_behind_plugin');
290
+ if (s.origin === 'plugin') {
291
+ const m = pluginManaged(L, s);
292
+ const v = stricter(m === undefined ? undefined : { value: m.value, by: m.by }, pluginFollow(L, s));
293
+ return v === undefined ? effective('on', 'default') : effective(v.value, v.by);
294
+ }
295
+ const r = overrideChain(L, s, 'all');
296
+ const value = r?.value ?? 'on';
297
+ if (s.origin === 'builtin' && L.bundledDisabled)
298
+ return value === 'off' ? effective('off', r?.by ?? 'default') : effective('user-invocable-only', 'builtin_clamp');
299
+ return effective(value, r?.by ?? 'default');
300
+ }
301
+ const locked = (value, source) => Object.freeze({ value, source });
302
+ export function lockOfShaped(L, s) {
303
+ const n = pluginManaged(L, s);
304
+ if (n !== undefined) {
305
+ const t = s.origin === 'plugin' ? effectiveOfShaped(L, s).value : n.value;
306
+ return locked(t, t !== n.value ? 'plugin' : n.source === 'policySettings' ? 'policy' : 'flag');
307
+ }
308
+ if (SETTINGS_IS_RESERVED_MEMBER_NAME(s.name))
309
+ return locked(s.disableModelInvocation ? 'user-invocable-only' : 'on', 'reserved-name');
310
+ if (s.disableModelInvocation)
311
+ return locked('user-invocable-only', 'skill-author');
312
+ if (s.origin === 'plugin')
313
+ return locked(effectiveOfShaped(L, s).value, 'plugin');
314
+ return undefined;
315
+ }
316
+ export function skillOverrideEffectiveOf(layers, skill) {
317
+ try {
318
+ const s = shapeSkillSubject(skill);
319
+ if (s === undefined)
320
+ return undefined;
321
+ return effectiveOfShaped(shapeSkillLayers(layers), s);
322
+ }
323
+ catch {
324
+ return undefined;
325
+ }
326
+ }
327
+ export function skillOverrideLockOf(layers, skill) {
328
+ try {
329
+ const s = shapeSkillSubject(skill);
330
+ if (s === undefined)
331
+ return locked('on', 'reserved-name');
332
+ return lockOfShaped(shapeSkillLayers(layers), s);
333
+ }
334
+ catch {
335
+ return locked('on', 'reserved-name');
336
+ }
337
+ }
338
+ export function ownValueIn(ov, s) {
339
+ return s.origin === 'mcp' ? strictestIn(ov, [s.name, ...secondaryNamesOf(s)], 'default')?.value : at(ov, s.name);
340
+ }
341
+ export function stateAt(ov, key) {
342
+ return at(ov, key);
343
+ }
344
+ export function lowerTierOf(L, s, target, skipTargetBare) {
345
+ const doc = (l) => ((l === 'localSettings' && target === 'localSettings') || L.enabled(l) ? L.docs[l] : undefined);
346
+ if (s.origin === 'mcp') {
347
+ if (target === 'userSettings')
348
+ return undefined;
349
+ const names = [s.name, ...secondaryNamesOf(s)];
350
+ for (const l of ['projectSettings', 'userSettings']) {
351
+ if (!L.enabled(l))
352
+ continue;
353
+ const m = strictestIn(L.docs[l], names, l);
354
+ if (m !== undefined)
355
+ return m.value;
356
+ }
357
+ return undefined;
358
+ }
359
+ const bare = bareNameOf(s);
360
+ const bareChain = () => {
361
+ if (bare === undefined)
362
+ return undefined;
363
+ for (const l of PLAIN) {
364
+ if (skipTargetBare && l === target)
365
+ continue;
366
+ const w = at(doc(l), bare);
367
+ if (w !== undefined)
368
+ return w;
369
+ }
370
+ return undefined;
371
+ };
372
+ if (target === 'userSettings')
373
+ return bareChain();
374
+ return at(doc('projectSettings'), s.name) ?? at(doc('userSettings'), s.name) ?? bareChain();
375
+ }
376
+ export function withLayerDoc(L, layer, doc) {
377
+ return Object.freeze({ ...L, docs: Object.freeze({ ...L.docs, [layer]: overridesOf(doc) }) });
378
+ }
@@ -1,5 +1,6 @@
1
1
  import type { SkillSpec } from '@sema-agent/sdk';
2
2
  import { type SkillToolListsReading } from './skillToolListsCapability.js';
3
+ import { type SkillOverrideLayers, type SkillOverrideSubject } from './skillOverrideChain.js';
3
4
  export interface SkillCmdLike {
4
5
  type?: string;
5
6
  name?: string;
@@ -10,6 +11,7 @@ export interface SkillCmdLike {
10
11
  skillRoot?: string;
11
12
  allowedTools?: readonly string[];
12
13
  disallowedTools?: readonly string[];
14
+ skillOverrideSubject?: SkillOverrideSubject;
13
15
  }
14
16
  export declare function toCappedSpec(name: string, description: string, whenToUse: string, body: string): SkillSpec | null;
15
17
  export type SkillSpecOnWire = SkillSpec & {
@@ -19,6 +21,7 @@ export interface SkillSpecsOptions {
19
21
  toolsRunHere?: boolean;
20
22
  platform?: 'posix' | 'win32';
21
23
  skillToolLists?: SkillToolListsReading;
24
+ skillOverrides?: SkillOverrideLayers;
22
25
  }
23
26
  export declare function skillBaseDirOf(skillRoot: string | undefined, platform?: 'posix' | 'win32'): string | undefined;
24
27
  export type SkillToolListKey = 'allowedTools' | 'disallowedTools';
@@ -1,6 +1,7 @@
1
1
  import { skillToolListsAvailable } from './skillToolListsCapability.js';
2
2
  import { msg, renderMessage } from './i18n/translate.js';
3
3
  import { appendOwnElement } from './ownKey.js';
4
+ import { skillOverrideEffectiveOf } from './skillOverrideChain.js';
4
5
  export function toCappedSpec(name, description, whenToUse, body) {
5
6
  const n = name.trim();
6
7
  const c = body.trim();
@@ -11,6 +12,13 @@ export function toCappedSpec(name, description, whenToUse, body) {
11
12
  .join(' — ');
12
13
  return { name: n, description: desc, content: c };
13
14
  }
15
+ const ORIGIN_OF_SOURCE = { plugin: 'plugin', builtin: 'builtin', bundled: 'bundled', mcp: 'mcp' };
16
+ function overrideSubjectOf(cmd, name) {
17
+ if (cmd.skillOverrideSubject !== undefined)
18
+ return cmd.skillOverrideSubject;
19
+ const src = typeof cmd.source === 'string' && Object.hasOwn(ORIGIN_OF_SOURCE, cmd.source) ? ORIGIN_OF_SOURCE[cmd.source] : undefined;
20
+ return { name, origin: src ?? 'skills' };
21
+ }
14
22
  export function skillBaseDirOf(skillRoot, platform = 'posix') {
15
23
  if (skillRoot === undefined || skillRoot.length === 0 || skillRoot.length > 4096)
16
24
  return undefined;
@@ -120,10 +128,17 @@ export function skillCommandsToSpecs(cmds, resolveBody, opts) {
120
128
  for (const cmd of cmds) {
121
129
  if (cmd.type !== 'prompt' || cmd.disableModelInvocation)
122
130
  continue;
131
+ let nameOnly = false;
132
+ if (opts?.skillOverrides !== undefined) {
133
+ const eff = skillOverrideEffectiveOf(opts.skillOverrides, overrideSubjectOf(cmd, (cmd.name ?? '').trim()));
134
+ if (eff === undefined || eff.value === 'off' || eff.value === 'user-invocable-only')
135
+ continue;
136
+ nameOnly = eff.value === 'name-only';
137
+ }
123
138
  const body = resolveBody(cmd);
124
139
  if (!body)
125
140
  continue;
126
- const spec = toCappedSpec(cmd.name ?? '', cmd.description ?? '', cmd.whenToUse ?? '', body);
141
+ const spec = toCappedSpec(cmd.name ?? '', nameOnly ? '' : (cmd.description ?? ''), nameOnly ? '' : (cmd.whenToUse ?? ''), body);
127
142
  if (!spec || seen.has(spec.name))
128
143
  continue;
129
144
  seen.add(spec.name);
@@ -1,5 +1,6 @@
1
1
  import { hostEnv } from './hostEnv.js';
2
2
  import { SseIdleError, StreamEndedWithoutTerminalError, probeHealth as sdkProbeHealth } from '@sema-agent/sdk';
3
+ import { classifyHealthProbe } from './engineHealthProbeVerdict.js';
3
4
  import { THROWN_SLOT_UNREADABLE, readThrownSlot, thrownIsInstanceOf } from './wireFailureShape.js';
4
5
  import { SDK_STREAM_ENDED_REASONS } from './generated/sdkFactTables.js';
5
6
  import { msg, renderMessage } from './i18n/translate.js';
@@ -62,7 +63,7 @@ export function isStreamEndedWithoutTerminalError(err) {
62
63
  return reading.kind !== 'unknown' || reading.why !== 'not_this_class';
63
64
  }
64
65
  export async function probeEngineAlive(baseUrl) {
65
- return (await sdkProbeHealth(baseUrl)).alive;
66
+ return classifyHealthProbe(await sdkProbeHealth(baseUrl))?.kind === 'answered_ok';
66
67
  }
67
68
  export async function* diagnoseSseIdleTear(inner, baseUrl, signal) {
68
69
  try {
@@ -0,0 +1,15 @@
1
+ declare const STORE_DURABILITY_WORDS: readonly ["durable", "ephemeral", "unknown"];
2
+ export type EngineHealthStoreDurability = (typeof STORE_DURABILITY_WORDS)[number];
3
+ export type StoreDurabilityRead = {
4
+ readonly kind: 'word';
5
+ readonly durability: EngineHealthStoreDurability;
6
+ } | {
7
+ readonly kind: 'unrecognized';
8
+ readonly word: string;
9
+ } | {
10
+ readonly kind: 'absent';
11
+ } | {
12
+ readonly kind: 'unreadable';
13
+ };
14
+ export declare function readStoreDurability(section: object): StoreDurabilityRead;
15
+ export {};
@@ -0,0 +1,18 @@
1
+ import { ownDataField } from './ownKey.js';
2
+ const STORE_DURABILITY_WORDS = ['durable', 'ephemeral', 'unknown'];
3
+ export function readStoreDurability(section) {
4
+ try {
5
+ const raw = ownDataField(section, 'durability');
6
+ if (raw === undefined)
7
+ return Object.freeze({ kind: 'absent' });
8
+ if (typeof raw !== 'string' || raw === '')
9
+ return Object.freeze({ kind: 'unreadable' });
10
+ for (const w of STORE_DURABILITY_WORDS)
11
+ if (raw === w)
12
+ return Object.freeze({ kind: 'word', durability: w });
13
+ return Object.freeze({ kind: 'unrecognized', word: raw });
14
+ }
15
+ catch {
16
+ return Object.freeze({ kind: 'unreadable' });
17
+ }
18
+ }
@@ -1,8 +1,12 @@
1
1
  import { type SqlEngineCapabilityView } from './sqlEngineCapability.js';
2
+ import { type EngineHealthStoreDurability } from './storeDurabilityWord.js';
3
+ import type { UiLanguage } from './env/uiLanguage.js';
2
4
  export interface StorePostureView {
3
5
  declared: string;
4
6
  backend: string;
5
- durable: boolean;
7
+ durable?: boolean;
8
+ durability?: EngineHealthStoreDurability;
9
+ unrecognizedDurability?: string;
6
10
  degradedReason?: string;
7
11
  sql?: SqlEngineCapabilityView;
8
12
  }
@@ -19,5 +23,5 @@ export type StorePostureReading = {
19
23
  export declare function projectStorePostureCapability(caps: unknown): StorePostureReading | undefined;
20
24
  export declare function observedStorePosture(baseUrl?: string | undefined): StorePostureReading;
21
25
  export declare function storePersistence(reading: StorePostureReading): 'durable' | 'not_durable' | 'unknown';
22
- export declare function storePostureHeadWord(reading: StorePostureReading): string | undefined;
23
- export declare function storePostureDoctorDetail(reading: StorePostureReading): string;
26
+ export declare function storePostureHeadWord(reading: StorePostureReading, lang?: UiLanguage): string | undefined;
27
+ export declare function storePostureDoctorDetail(reading: StorePostureReading, lang?: UiLanguage): string;
@@ -3,6 +3,8 @@ import { engineWireTargetBaseUrl } from './engineWireTarget.js';
3
3
  import { storeSeatOf, createEngineCapReader, unreadableReadingDetail } from './engineCapReader.js';
4
4
  import { projectSqlEngineCapability } from './sqlEngineCapability.js';
5
5
  import { ownDataField } from './ownKey.js';
6
+ import { readStoreDurability } from './storeDurabilityWord.js';
7
+ import { msg, renderMessage } from './i18n/translate.js';
6
8
  const nonEmpty = (v) => typeof v === 'string' && v !== '';
7
9
  export function projectStorePostureCapability(caps) {
8
10
  try {
@@ -21,8 +23,8 @@ function projectStorePostureOf(caps) {
21
23
  const store = seat.store;
22
24
  const declared = ownDataField(store, 'declared');
23
25
  const backend = ownDataField(store, 'backend');
24
- const durable = ownDataField(store, 'durable');
25
- if (!nonEmpty(declared) || !nonEmpty(backend) || typeof durable !== 'boolean')
26
+ const durability = durabilityOf(store, 'wire');
27
+ if (!nonEmpty(declared) || !nonEmpty(backend) || durability.kind === 'malformed')
26
28
  return undefined;
27
29
  const reason = ownDataField(store, 'degradedReason');
28
30
  if (reason !== undefined && !nonEmpty(reason))
@@ -35,12 +37,44 @@ function projectStorePostureOf(caps) {
35
37
  view: {
36
38
  declared,
37
39
  backend,
38
- durable,
40
+ ...(durability.kind === 'legacy' ? { durable: durability.durable } : {}),
41
+ ...(durability.kind === 'word' ? { durability: durability.durability } : {}),
42
+ ...(durability.kind === 'unrecognized' ? { unrecognizedDurability: durability.word } : {}),
39
43
  ...(nonEmpty(reason) ? { degradedReason: reason } : {}),
40
44
  ...(sqlReading.kind === 'present' ? { sql: sqlReading.view } : {}),
41
45
  },
42
46
  };
43
47
  }
48
+ const MALFORMED_DURABILITY = Object.freeze({ kind: 'malformed' });
49
+ function durabilityOf(section, side) {
50
+ try {
51
+ const word = readStoreDurability(section);
52
+ const durable = ownDataField(section, 'durable');
53
+ const unrecognized = side === 'view' ? ownDataField(section, 'unrecognizedDurability') : undefined;
54
+ const present = (word.kind === 'absent' ? 0 : 1) + (durable === undefined ? 0 : 1) + (unrecognized === undefined ? 0 : 1);
55
+ if (present !== 1)
56
+ return MALFORMED_DURABILITY;
57
+ if (word.kind === 'word')
58
+ return { kind: 'word', durability: word.durability };
59
+ if (word.kind === 'unrecognized')
60
+ return { kind: 'unrecognized', word: word.word };
61
+ if (typeof durable === 'boolean')
62
+ return { kind: 'legacy', durable };
63
+ if (nonEmpty(unrecognized))
64
+ return { kind: 'unrecognized', word: unrecognized };
65
+ return MALFORMED_DURABILITY;
66
+ }
67
+ catch {
68
+ return MALFORMED_DURABILITY;
69
+ }
70
+ }
71
+ function persistenceOf(d) {
72
+ if (d.kind === 'legacy')
73
+ return d.durable ? 'durable' : 'not_durable';
74
+ if (d.kind !== 'word')
75
+ return 'unknown';
76
+ return d.durability === 'durable' ? 'durable' : d.durability === 'ephemeral' ? 'not_durable' : 'unknown';
77
+ }
44
78
  const reader = createEngineCapReader({
45
79
  name: 'store',
46
80
  project: projectStorePostureCapability,
@@ -70,58 +104,54 @@ export function storePersistence(reading) {
70
104
  const r = presentView(reading);
71
105
  if (r?.kind !== 'present')
72
106
  return 'unknown';
73
- let durable;
74
- try {
75
- durable = r.view.durable;
76
- }
77
- catch {
78
- return 'unknown';
79
- }
80
- return durable === true ? 'durable' : durable === false ? 'not_durable' : 'unknown';
107
+ return persistenceOf(durabilityOf(r.view, 'view'));
81
108
  }
82
- const STORE_NOT_DURABLE_HEAD = 'NOT durable';
83
- export function storePostureHeadWord(reading) {
84
- return storePersistence(reading) === 'not_durable' ? STORE_NOT_DURABLE_HEAD : undefined;
109
+ const notDurableHead = () => msg('notice.capability.storePosture.notDurableHead');
110
+ export function storePostureHeadWord(reading, lang) {
111
+ return storePersistence(reading) === 'not_durable' ? renderMessage(notDurableHead(), lang) : undefined;
85
112
  }
86
113
  const WORD_DETAIL_MAX = 40;
87
114
  const word = (v) => capForDisplay(v, WORD_DETAIL_MAX);
88
- export function storePostureDoctorDetail(reading) {
89
- const unobserved = 'store posture not observed — the engine reports it on /v1/capabilities; this process has no usable capabilities reading cached for it, so whether this deployment persists its state is unknown';
115
+ export function storePostureDoctorDetail(reading, lang) {
116
+ const say = (ref) => renderMessage(ref, lang);
117
+ const unobserved = msg('notice.capability.storePosture.unobserved');
90
118
  const r = presentView(reading);
91
119
  if (r?.kind === 'unreadable')
92
- return unreadableReadingDetail('store posture');
93
- if (r?.kind === 'not_reported') {
94
- return 'store posture not reported by this engine; this does not say whether this deployment persists its state';
95
- }
120
+ return unreadableReadingDetail(say(msg('notice.capability.subject.storePosture')), lang);
121
+ if (r?.kind === 'not_reported')
122
+ return say(msg('notice.capability.storePosture.notReported'));
96
123
  if (r?.kind !== 'present')
97
- return unobserved;
124
+ return say(unobserved);
98
125
  let declared;
99
126
  let backend;
100
- let durable;
101
127
  let reason;
102
128
  try {
103
129
  declared = r.view.declared;
104
130
  backend = r.view.backend;
105
- durable = r.view.durable;
106
131
  reason = r.view.degradedReason;
107
132
  }
108
133
  catch {
109
- return unobserved;
110
- }
111
- if (!nonEmpty(declared) || !nonEmpty(backend) || typeof durable !== 'boolean')
112
- return unobserved;
113
- const assembled = declared === backend ? `store ${word(backend)}` : `store declared ${word(declared)}, running on ${word(backend)}`;
114
- if (durable) {
115
- return `${assembled} — durable: this replica persists its state, so it survives a restart`;
134
+ return say(unobserved);
116
135
  }
117
- const lost = `${STORE_NOT_DURABLE_HEAD}: this replica holds its state in memory and loses it on restart`;
136
+ const durability = durabilityOf(r.view, 'view');
137
+ if (!nonEmpty(declared) || !nonEmpty(backend) || durability.kind === 'malformed')
138
+ return say(unobserved);
139
+ const assembled = declared === backend
140
+ ? msg('notice.capability.storePosture.assembled', { backend: word(backend) })
141
+ : msg('notice.capability.storePosture.assembledDeclared', { declared: word(declared), backend: word(backend) });
142
+ if (durability.kind === 'unrecognized')
143
+ return say(msg('notice.capability.storePosture.unrecognized', { assembled, word: word(durability.word) }));
144
+ const persistence = persistenceOf(durability);
145
+ if (persistence === 'durable')
146
+ return say(msg('notice.capability.storePosture.durable', { assembled }));
147
+ if (persistence === 'unknown')
148
+ return say(msg('notice.capability.storePosture.unknown', { assembled }));
149
+ const line = msg(durability.kind === 'legacy' ? 'notice.capability.storePosture.memory' : 'notice.capability.storePosture.ephemeral', { assembled, head: notDurableHead() });
118
150
  if (!nonEmpty(reason))
119
- return `${assembled} — ${lost}`;
120
- if (reason === 'db_unreachable') {
121
- return `${assembled} — ${lost} (degraded: the declared database could not be opened at start-up, so the engine fell back rather than refusing to start)`;
122
- }
123
- if (reason === 'local_unwritable') {
124
- return `${assembled} — ${lost} (degraded: the default local file store could not be created — a read-only or full data directory is one possible cause, and this client cannot tell which applies)`;
125
- }
126
- return `${assembled} — ${lost} (degraded for a reason this client does not know yet: "${word(reason)}")`;
151
+ return say(line);
152
+ if (reason === 'db_unreachable')
153
+ return say(msg('notice.capability.storePosture.degraded.dbUnreachable', { line }));
154
+ if (reason === 'local_unwritable')
155
+ return say(msg('notice.capability.storePosture.degraded.localUnwritable', { line }));
156
+ return say(msg('notice.capability.storePosture.degraded.other', { line, reason: word(reason) }));
127
157
  }