@sema-agent/client-core 0.111.0 → 0.112.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/README.md +16 -7
  3. package/dist/agentSession/engineSessionBackgroundStop.js +2 -2
  4. package/dist/checkpointRowVerdict.d.ts +3 -0
  5. package/dist/checkpointRowVerdict.js +14 -1
  6. package/dist/consoleCaPin.d.ts +136 -0
  7. package/dist/consoleCaPin.js +827 -0
  8. package/dist/decideReceipt.d.ts +2 -1
  9. package/dist/deviceRebind.js +3 -3
  10. package/dist/effectiveFacts.d.ts +9 -0
  11. package/dist/effectiveFacts.js +33 -0
  12. package/dist/engineAddress.d.ts +1 -0
  13. package/dist/engineAddress.js +4 -1
  14. package/dist/engineCredentialRelease.d.ts +54 -0
  15. package/dist/engineCredentialRelease.js +146 -0
  16. package/dist/engineErrorCodes.d.ts +1 -0
  17. package/dist/engineErrorCodes.js +1 -0
  18. package/dist/engineHealthFacts.d.ts +0 -11
  19. package/dist/engineHealthFacts.js +0 -21
  20. package/dist/engineHealthProbeVerdict.d.ts +1 -0
  21. package/dist/engineHealthProbeVerdict.js +21 -13
  22. package/dist/engineUnavailable.d.ts +3 -2
  23. package/dist/engineUnavailable.js +16 -12
  24. package/dist/engineUrl.js +4 -2
  25. package/dist/gateFailureCodes.js +2 -2
  26. package/dist/generated/sdkFactTables.d.ts +1 -0
  27. package/dist/generated/sdkFactTables.js +13 -0
  28. package/dist/generated/settingsSchemaFns.d.ts +1 -0
  29. package/dist/generated/settingsSchemaFns.js +3 -0
  30. package/dist/generated/settingsSchemaHooksClassifier.js +38 -9
  31. package/dist/generated/settingsSchemaTables.d.ts +52 -0
  32. package/dist/generated/settingsSchemaTables.js +18 -0
  33. package/dist/generated/settingsSchemaTypes.d.ts +23 -13
  34. package/dist/generated/settingsSchemaUrlShape.d.ts +1 -0
  35. package/dist/generated/settingsSchemaUrlShape.js +17 -0
  36. package/dist/hitl/decisionRefusal.d.ts +4 -0
  37. package/dist/hitl/decisionRefusal.js +12 -2
  38. package/dist/hitl/hitlBridge.d.ts +1 -0
  39. package/dist/hitl/hitlBridge.js +17 -4
  40. package/dist/hitl/localAllowRedemption.d.ts +9 -6
  41. package/dist/hitl/localAllowRedemption.js +20 -0
  42. package/dist/hitl/parkOwnership.js +2 -1
  43. package/dist/hitl/parkResolver.js +7 -0
  44. package/dist/hitl/planReviewCard.d.ts +3 -2
  45. package/dist/hitl/planReviewCard.js +2 -0
  46. package/dist/hitl/planReviewWire.d.ts +4 -2
  47. package/dist/hitl/planReviewWire.js +134 -97
  48. package/dist/hitl/screenDecisionCards.js +3 -2
  49. package/dist/hitl/sessionPolicyWire.js +2 -2
  50. package/dist/hitl/standingRows.d.ts +4 -2
  51. package/dist/hitl/standingRows.js +10 -3
  52. package/dist/hooksReaderGenerations.js +1 -1
  53. package/dist/i18n/messages.en.d.ts +165 -0
  54. package/dist/i18n/messages.en.js +166 -1
  55. package/dist/i18n/messages.zh.js +177 -1
  56. package/dist/index.d.ts +30 -5
  57. package/dist/index.js +15 -2
  58. package/dist/liveModelCatalog.d.ts +4 -0
  59. package/dist/liveModelCatalog.js +10 -1
  60. package/dist/localConfig/documents.d.ts +61 -0
  61. package/dist/localConfig/documents.js +51 -0
  62. package/dist/localConfig/kernel.d.ts +149 -0
  63. package/dist/localConfig/kernel.js +597 -0
  64. package/dist/localConfig/mcp/edit.d.ts +161 -0
  65. package/dist/localConfig/mcp/edit.js +846 -0
  66. package/dist/localConfig/mcp/servers.d.ts +165 -0
  67. package/dist/localConfig/mcp/servers.js +776 -0
  68. package/dist/localConfig/mcp/views.d.ts +170 -0
  69. package/dist/localConfig/mcp/views.js +429 -0
  70. package/dist/localConfig/modelPool.d.ts +262 -0
  71. package/dist/localConfig/modelPool.js +1489 -0
  72. package/dist/localConfig/modelPoolWords.d.ts +13 -0
  73. package/dist/localConfig/modelPoolWords.js +20 -0
  74. package/dist/localConfig/receiptShape.d.ts +7 -0
  75. package/dist/localConfig/receiptShape.js +135 -0
  76. package/dist/localConfig/settingsWrite.d.ts +127 -0
  77. package/dist/localConfig/settingsWrite.js +237 -0
  78. package/dist/localConfig/skills.d.ts +124 -0
  79. package/dist/localConfig/skills.js +397 -0
  80. package/dist/localConfig/skillsWords.d.ts +10 -0
  81. package/dist/localConfig/skillsWords.js +5 -0
  82. package/dist/mcpProbeWire.js +2 -1
  83. package/dist/mcpWireCaps.js +1 -1
  84. package/dist/memoryVerbsWire.js +3 -2
  85. package/dist/model/onboardJudgement.d.ts +14 -4
  86. package/dist/model/onboardJudgement.js +91 -139
  87. package/dist/modelConfigGapNotice.d.ts +20 -0
  88. package/dist/modelConfigGapNotice.js +102 -0
  89. package/dist/modsKernelIdentity.js +2 -47
  90. package/dist/notifications.js +14 -12
  91. package/dist/ownKey.d.ts +1 -0
  92. package/dist/ownKey.js +40 -8
  93. package/dist/permissionsDeclaration.d.ts +2 -1
  94. package/dist/permissionsDeclaration.js +31 -17
  95. package/dist/planReviewOutcomeCause.d.ts +2 -0
  96. package/dist/planReviewOutcomeCause.js +12 -0
  97. package/dist/promptBlockedCopy.js +2 -2
  98. package/dist/resumeRefusalCopy.d.ts +3 -1
  99. package/dist/resumeRefusalCopy.js +12 -4
  100. package/dist/runLedgerSnapshot.js +3 -3
  101. package/dist/runPermissionModeSwitch.js +2 -3
  102. package/dist/seatContract.d.ts +154 -4
  103. package/dist/seatContract.js +228 -2
  104. package/dist/sessionIdSegment.d.ts +1 -0
  105. package/dist/sessionIdSegment.js +4 -1
  106. package/dist/sessionMemoryErase.js +2 -1
  107. package/dist/sha256Bytes.d.ts +1 -0
  108. package/dist/sha256Bytes.js +46 -0
  109. package/dist/skillOverrideChain.d.ts +83 -0
  110. package/dist/skillOverrideChain.js +378 -0
  111. package/dist/skillsWireCaps.d.ts +3 -0
  112. package/dist/skillsWireCaps.js +16 -1
  113. package/dist/turnFailurePresentation.js +4 -10
  114. package/docs/INTEGRATION-CLIENTS.md +680 -14
  115. package/package.json +3 -3
@@ -0,0 +1,597 @@
1
+ import { SETTINGS_ROOT_DOCUMENT_UNREADABLE_REASONS } from '../generated/settingsSchemaTables.js';
2
+ import { ownSnapshotRecord, ownSnapshotRecordFaithful, readOwnDataValue, appendOwnElement } from '../ownKey.js';
3
+ import { capForDisplay } from '../fleetTaskDesc.js';
4
+ import { msg, renderMessage } from '../i18n/translate.js';
5
+ import { isWritableLocalConfigDocument, localConfigDocumentOf } from './documents.js';
6
+ export const LOCAL_CONFIG_UNREADABLE_CAUSES = SETTINGS_ROOT_DOCUMENT_UNREADABLE_REASONS;
7
+ export const LOCAL_CONFIG_REFUSALS = Object.freeze([
8
+ 'invalid_request',
9
+ 'scope_unreadable',
10
+ 'scope_unwritable',
11
+ 'untrusted_folder',
12
+ 'policy_denied',
13
+ 'not_found',
14
+ 'already_exists',
15
+ 'lock_busy',
16
+ 'lock_failed',
17
+ 'changed_during_write',
18
+ 'write_failed',
19
+ 'not_written',
20
+ 'name_unusable',
21
+ 'name_reserved',
22
+ 'mcp_entry_invalid',
23
+ 'ambiguous_scope',
24
+ 'credentials_need_reentry',
25
+ 'base_conflict',
26
+ 'skill_locked_by_higher_layer',
27
+ 'skill_managed_by_plugin',
28
+ 'skill_locked_by_author',
29
+ 'skill_name_unstorable',
30
+ 'entry_in_use',
31
+ 'credential_unpaired',
32
+ 'env_catalog_present',
33
+ 'probe_failed',
34
+ 'probe_unverified',
35
+ ]);
36
+ export const LOCAL_CONFIG_NOT_WRITTEN_CAUSES = Object.freeze(['lock_busy', 'lock_failed', 'changed_during_write', 'write_failed']);
37
+ export const LOCAL_CONFIG_UNAVAILABLE_CAUSES = Object.freeze(['engine_is_remote', 'not_single_user', 'config_home_unpinned', 'domain_not_hosted']);
38
+ export const LOCAL_CONFIG_UNKNOWN_CAUSES = Object.freeze(['commit_unconfirmed', 'report_unreadable']);
39
+ export const LOCAL_CONFIG_APPLIES_ON = Object.freeze(['next_turn', 'engine_reload', 'next_engine_start']);
40
+ export const LOCAL_CONFIG_RESIDUES = Object.freeze(['orphan_key_slot', 'legacy_tier_groups_left']);
41
+ export const LOCAL_CONFIG_DISCLOSURE_KINDS = Object.freeze(['decided_by', 'shadowed_by', 'tier_group_overridden']);
42
+ export const LOCAL_CONFIG_STEP_ROLES = Object.freeze(['prepare', 'effect', 'cleanup']);
43
+ export const LOCAL_CONFIG_WRITE_PRECONDITIONS = Object.freeze([
44
+ 'shared_path_lock',
45
+ 'lock_loss_returned',
46
+ 'writers_joined',
47
+ 'shared_rank',
48
+ 'global_state_migrated',
49
+ 'targets_bound_by_host',
50
+ 'write_route',
51
+ ]);
52
+ const ENGINE_WORDS = ['local', 'cloud'];
53
+ const ROUTE_WORDS = ['host', 'seat_proxy', 'bff_direct'];
54
+ function own(v, key) {
55
+ try {
56
+ return readOwnDataValue(v, key);
57
+ }
58
+ catch {
59
+ return undefined;
60
+ }
61
+ }
62
+ const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
63
+ const blocked = (cause, missing = []) => Object.freeze({ ok: false, cause, missing: Object.freeze([...missing]) });
64
+ const OPEN = Object.freeze({ ok: true });
65
+ function sharedRankSetOf(v) {
66
+ const out = new Set();
67
+ if (!Array.isArray(v))
68
+ return out;
69
+ try {
70
+ for (let i = 0; i < v.length; i++) {
71
+ const name = own(v, String(i));
72
+ if (typeof name === 'string' && name !== '')
73
+ out.add(name);
74
+ }
75
+ }
76
+ catch {
77
+ return new Set();
78
+ }
79
+ return out;
80
+ }
81
+ export function localConfigAccess(facts, request) {
82
+ try {
83
+ if (!isRecord(facts))
84
+ return blocked('domain_not_hosted');
85
+ const engine = own(facts, 'engine');
86
+ if (engine === 'cloud')
87
+ return blocked('engine_is_remote');
88
+ if (!ENGINE_WORDS.some((w) => w === engine))
89
+ return blocked('domain_not_hosted');
90
+ if (own(facts, 'tenancy') !== 'single_user')
91
+ return blocked('not_single_user');
92
+ if (own(facts, 'configHomePinned') !== true)
93
+ return blocked('config_home_unpinned');
94
+ const route = own(facts, 'route');
95
+ if (!ROUTE_WORDS.some((w) => w === route))
96
+ return blocked('domain_not_hosted');
97
+ if (!isRecord(request))
98
+ return blocked('domain_not_hosted');
99
+ const access = own(request, 'access');
100
+ if (access !== 'read' && access !== 'write')
101
+ return blocked('domain_not_hosted');
102
+ const missing = new Set();
103
+ if (route !== 'host' && own(facts, 'targetsBoundByHost') !== true)
104
+ missing.add('targets_bound_by_host');
105
+ if (access === 'write') {
106
+ const docs = own(request, 'docs');
107
+ if (!Array.isArray(docs) || docs.length === 0)
108
+ return blocked('domain_not_hosted');
109
+ const names = [];
110
+ for (let i = 0; i < docs.length; i++) {
111
+ const name = own(docs, String(i));
112
+ if (!isWritableLocalConfigDocument(name))
113
+ return blocked('domain_not_hosted');
114
+ names.push(name);
115
+ }
116
+ if (route === 'bff_direct')
117
+ missing.add('write_route');
118
+ if (own(facts, 'sharedPathLock') !== true)
119
+ missing.add('shared_path_lock');
120
+ if (own(facts, 'lockLossReturned') !== true)
121
+ missing.add('lock_loss_returned');
122
+ if (own(facts, 'writersJoined') !== true)
123
+ missing.add('writers_joined');
124
+ const shared = sharedRankSetOf(own(facts, 'sharedRanks'));
125
+ if (names.some((n) => !shared.has(localConfigDocumentOf(n)?.lockRank ?? '')))
126
+ missing.add('shared_rank');
127
+ if (names.includes('globalConfig') && own(facts, 'globalStateMigrated') !== true)
128
+ missing.add('global_state_migrated');
129
+ }
130
+ if (missing.size > 0)
131
+ return blocked('domain_not_hosted', LOCAL_CONFIG_WRITE_PRECONDITIONS.filter((p) => missing.has(p)));
132
+ return OPEN;
133
+ }
134
+ catch {
135
+ return blocked('domain_not_hosted');
136
+ }
137
+ }
138
+ const MAX_STEPS = 8;
139
+ const DETAIL_MAX = 240;
140
+ function docListOf(v, writable) {
141
+ if (v === undefined)
142
+ return [];
143
+ if (!Array.isArray(v) || v.length > 16)
144
+ return undefined;
145
+ const out = [];
146
+ for (let i = 0; i < v.length; i++) {
147
+ const name = own(v, String(i));
148
+ const row = localConfigDocumentOf(name);
149
+ if (row === undefined || (writable && row.lockRank === null))
150
+ return undefined;
151
+ if (!out.includes(row.name))
152
+ out.push(row.name);
153
+ }
154
+ return out;
155
+ }
156
+ function shapeIntent(intent) {
157
+ if (!isRecord(intent))
158
+ return undefined;
159
+ const appliesOn = own(intent, 'appliesOn');
160
+ const when = LOCAL_CONFIG_APPLIES_ON.find((w) => w === appliesOn);
161
+ if (when === undefined)
162
+ return undefined;
163
+ const raw = own(intent, 'steps');
164
+ if (!Array.isArray(raw) || raw.length === 0 || raw.length > MAX_STEPS)
165
+ return undefined;
166
+ const steps = [];
167
+ let phase = 0;
168
+ for (let i = 0; i < raw.length; i++) {
169
+ const s = own(raw, String(i));
170
+ if (!isRecord(s))
171
+ return undefined;
172
+ const doc = own(s, 'doc');
173
+ if (!isWritableLocalConfigDocument(doc))
174
+ return undefined;
175
+ const role = LOCAL_CONFIG_STEP_ROLES.find((w) => w === own(s, 'role'));
176
+ if (role === undefined)
177
+ return undefined;
178
+ const prior = steps.filter((x) => x.doc === doc);
179
+ if (prior.length > 0 && !(role === 'cleanup' && prior.length === 1 && prior[0].role === 'prepare'))
180
+ return undefined;
181
+ const residueRaw = own(s, 'residue');
182
+ const residue = LOCAL_CONFIG_RESIDUES.find((w) => w === residueRaw);
183
+ if (role === 'effect' ? residueRaw !== undefined : residue === undefined)
184
+ return undefined;
185
+ if (role === 'prepare' && phase !== 0)
186
+ return undefined;
187
+ if (role === 'effect') {
188
+ if (phase !== 0)
189
+ return undefined;
190
+ phase = 1;
191
+ }
192
+ if (role === 'cleanup' && phase !== 1)
193
+ return undefined;
194
+ const extraLock = docListOf(own(s, 'lock'), true);
195
+ const snap = docListOf(own(s, 'snapshot'), false);
196
+ const plan = own(s, 'plan');
197
+ if (extraLock === undefined || snap === undefined || typeof plan !== 'function')
198
+ return undefined;
199
+ const lock = [doc, ...extraLock.filter((d) => d !== doc && isWritableLocalConfigDocument(d))];
200
+ steps.push({ doc, role, residue, lock: Object.freeze(lock), snapshot: Object.freeze(snap.filter((d) => !lock.some((l) => l === d))), plan: plan });
201
+ }
202
+ if (phase !== 1)
203
+ return undefined;
204
+ return { appliesOn: when, steps, docs: Object.freeze(steps.map((s) => s.doc)) };
205
+ }
206
+ const UNREADABLE_IO = Object.freeze({ status: 'unreadable', cause: 'io' });
207
+ const ABSENT = Object.freeze({ status: 'absent' });
208
+ const OUTSIDE_FENCE = Object.freeze({ status: 'outside_fence' });
209
+ function readingOf(v) {
210
+ if (!isRecord(v))
211
+ return UNREADABLE_IO;
212
+ const status = own(v, 'status');
213
+ if (status === 'absent')
214
+ return ABSENT;
215
+ if (status === 'outside_fence')
216
+ return OUTSIDE_FENCE;
217
+ if (status === 'unreadable') {
218
+ const cause = LOCAL_CONFIG_UNREADABLE_CAUSES.find((w) => w === own(v, 'cause'));
219
+ return cause === undefined ? UNREADABLE_IO : Object.freeze({ status: 'unreadable', cause });
220
+ }
221
+ if (status === 'loaded') {
222
+ const revision = own(v, 'revision');
223
+ const rawDoc = own(v, 'doc');
224
+ if (typeof revision !== 'string' || revision === '' || !isRecord(rawDoc))
225
+ return UNREADABLE_IO;
226
+ const doc = ownSnapshotRecordFaithful(rawDoc);
227
+ return doc === undefined ? UNREADABLE_IO : Object.freeze({ status: 'loaded', doc: doc, revision });
228
+ }
229
+ return UNREADABLE_IO;
230
+ }
231
+ function filesOf(v, names) {
232
+ const out = {};
233
+ for (const name of names)
234
+ out[name] = readingOf(isRecord(v) ? own(v, name) : undefined);
235
+ return Object.freeze(out);
236
+ }
237
+ const refuseVerdict = (reason, detail) => ({ kind: 'refuse', reason, detail });
238
+ function disclosuresOf(v) {
239
+ if (v === undefined)
240
+ return Object.freeze([]);
241
+ if (!Array.isArray(v) || v.length > 16)
242
+ return undefined;
243
+ const out = [];
244
+ for (let i = 0; i < v.length; i++) {
245
+ const d = own(v, String(i));
246
+ if (!isRecord(d))
247
+ return undefined;
248
+ const kind = LOCAL_CONFIG_DISCLOSURE_KINDS.find((w) => w === own(d, 'kind'));
249
+ const layerRaw = own(d, 'layer');
250
+ const layer = layerRaw === undefined ? undefined : localConfigDocumentOf(layerRaw)?.name;
251
+ const detailRaw = own(d, 'detail');
252
+ if (kind === undefined || (layerRaw !== undefined && layer === undefined) || (detailRaw !== undefined && typeof detailRaw !== 'string'))
253
+ return undefined;
254
+ const detail = typeof detailRaw === 'string' && detailRaw !== '' ? capForDisplay(detailRaw, DETAIL_MAX) : undefined;
255
+ appendOwnElement(out, Object.freeze({ kind, ...(layer === undefined ? {} : { layer }), ...(detail === undefined ? {} : { detail }) }));
256
+ }
257
+ return Object.freeze(out);
258
+ }
259
+ function planVerdictOf(v) {
260
+ if (!isRecord(v))
261
+ return refuseVerdict('invalid_request');
262
+ const kind = own(v, 'kind');
263
+ const carryRaw = own(v, 'carry');
264
+ const carry = carryRaw === undefined ? undefined : isRecord(carryRaw) ? ownSnapshotRecordFaithful(carryRaw) : undefined;
265
+ if ((kind === 'write' || kind === 'unchanged') && carryRaw !== undefined && carry === undefined)
266
+ return refuseVerdict('invalid_request');
267
+ const disclosures = kind === 'write' || kind === 'unchanged' ? disclosuresOf(own(v, 'disclose')) : undefined;
268
+ if ((kind === 'write' || kind === 'unchanged') && disclosures === undefined)
269
+ return refuseVerdict('invalid_request');
270
+ if (kind === 'unchanged')
271
+ return { kind, carry, disclosures: disclosures ?? Object.freeze([]) };
272
+ if (kind === 'write') {
273
+ const rawNext = own(v, 'next');
274
+ const next = isRecord(rawNext) ? ownSnapshotRecordFaithful(rawNext) : undefined;
275
+ return next === undefined ? refuseVerdict('invalid_request') : { kind, next, carry, disclosures: disclosures ?? Object.freeze([]) };
276
+ }
277
+ if (kind === 'refuse') {
278
+ const reason = LOCAL_CONFIG_REFUSALS.find((w) => w === own(v, 'reason'));
279
+ const detail = own(v, 'detail');
280
+ if (reason === undefined)
281
+ return refuseVerdict('invalid_request');
282
+ return refuseVerdict(reason, typeof detail === 'string' && detail !== '' ? capForDisplay(detail, DETAIL_MAX) : undefined);
283
+ }
284
+ return refuseVerdict('invalid_request');
285
+ }
286
+ function sameJson(a, b) {
287
+ if (a === b)
288
+ return true;
289
+ if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null)
290
+ return false;
291
+ if (Array.isArray(a) !== Array.isArray(b))
292
+ return false;
293
+ if (Array.isArray(a) && Array.isArray(b))
294
+ return a.length === b.length && a.every((x, i) => sameJson(x, b[i]));
295
+ const ka = Object.keys(a);
296
+ const kb = Object.keys(b);
297
+ if (ka.length !== kb.length)
298
+ return false;
299
+ return ka.every((k) => Object.hasOwn(b, k) && sameJson(a[k], b[k]));
300
+ }
301
+ function planStep(step, fresh, carried, mode) {
302
+ const target = fresh[step.doc];
303
+ if (target === undefined || target.status === 'unreadable')
304
+ return refuseVerdict('scope_unreadable');
305
+ if (target.status === 'outside_fence')
306
+ return refuseVerdict('scope_unwritable');
307
+ let raw;
308
+ try {
309
+ raw = step.plan(Object.freeze({ fresh, carried, mode }));
310
+ }
311
+ catch {
312
+ return refuseVerdict('invalid_request');
313
+ }
314
+ const verdict = planVerdictOf(raw);
315
+ if (verdict.kind === 'write' && target.status === 'loaded' && sameJson(target.doc, verdict.next))
316
+ return { kind: 'unchanged', carry: verdict.carry, disclosures: verdict.disclosures };
317
+ return verdict;
318
+ }
319
+ function addDisclosures(into, more) {
320
+ for (const d of more)
321
+ if (!into.some((x) => x.kind === d.kind && x.layer === d.layer && x.detail === d.detail))
322
+ appendOwnElement(into, d);
323
+ }
324
+ const disclosuresField = (list) => list.length === 0 ? {} : { disclosures: Object.freeze([...list]) };
325
+ const mergeCarry = (carried, extra) => extra === undefined ? carried : (ownSnapshotRecord({ ...carried, ...extra }) ?? carried);
326
+ function portMethod(store, name) {
327
+ if (typeof store !== 'object' || store === null)
328
+ return undefined;
329
+ try {
330
+ const m = store[name];
331
+ return typeof m === 'function' ? m : undefined;
332
+ }
333
+ catch {
334
+ return undefined;
335
+ }
336
+ }
337
+ async function runStep(store, transact, step, carried) {
338
+ const st = { calls: 0, handedOut: false, verdict: undefined };
339
+ const names = [...step.lock, ...step.snapshot];
340
+ const build = (freshRaw) => {
341
+ st.calls += 1;
342
+ if (st.calls > 1)
343
+ throw new Error('local config: build must be called exactly once per step');
344
+ const fresh = filesOf(freshRaw, names);
345
+ const verdict = planStep(step, fresh, carried, 'commit');
346
+ st.verdict = verdict;
347
+ if (verdict.kind !== 'write')
348
+ return undefined;
349
+ const target = fresh[step.doc];
350
+ st.handedOut = true;
351
+ return Object.freeze({ doc: step.doc, next: verdict.next, expectRevision: target !== undefined && target.status === 'loaded' ? target.revision : null });
352
+ };
353
+ let report;
354
+ let threw = false;
355
+ try {
356
+ report = await transact.call(store, Object.freeze({ lock: step.lock, snapshot: step.snapshot }), build);
357
+ }
358
+ catch {
359
+ threw = true;
360
+ }
361
+ const notCommittedCause = () => {
362
+ if (!isRecord(report) || !Object.hasOwn(report, 'notCommitted'))
363
+ return undefined;
364
+ return LOCAL_CONFIG_NOT_WRITTEN_CAUSES.find((w) => w === own(report, 'notCommitted')) ?? 'not_written';
365
+ };
366
+ const v = st.verdict;
367
+ if (st.handedOut) {
368
+ if (st.calls > 1)
369
+ return { kind: 'unknown', cause: 'report_unreadable' };
370
+ if (threw)
371
+ return { kind: 'unknown', cause: 'commit_unconfirmed' };
372
+ if (report === 'committed')
373
+ return v !== undefined && v.kind === 'write' ? { kind: 'committed', carry: v.carry, disclosures: v.disclosures } : { kind: 'unknown', cause: 'report_unreadable' };
374
+ const cause = notCommittedCause();
375
+ return cause !== undefined ? { kind: 'refused', reason: cause, detail: undefined } : { kind: 'unknown', cause: 'report_unreadable' };
376
+ }
377
+ if (v !== undefined && v.kind === 'refuse')
378
+ return { kind: 'refused', reason: v.reason, detail: v.detail };
379
+ if (v !== undefined && v.kind === 'unchanged')
380
+ return { kind: 'unchanged', carry: v.carry, disclosures: v.disclosures };
381
+ const cause = threw ? undefined : notCommittedCause();
382
+ return { kind: 'refused', reason: cause ?? 'not_written', detail: undefined };
383
+ }
384
+ const residueField = (list) => list.length === 0 ? {} : { residue: Object.freeze([...list]) };
385
+ const refusedReceipt = (reason, detail, residue) => Object.freeze({ kind: 'refused', reason, ...(detail === undefined ? {} : { detail }), ...residueField(residue) });
386
+ const unknownReceipt = (cause, residue) => Object.freeze({ kind: 'unknown', cause, ...residueField(residue) });
387
+ const unavailableReceipt = (cause) => Object.freeze({ kind: 'unavailable', cause });
388
+ export async function applyLocalConfigIntent(intent, store, facts) {
389
+ let portTouched = false;
390
+ const prepared = [];
391
+ try {
392
+ const shaped = shapeIntent(intent);
393
+ if (shaped === undefined)
394
+ return refusedReceipt('invalid_request', undefined, []);
395
+ const access = localConfigAccess(facts, { access: 'write', docs: shaped.docs });
396
+ if (!access.ok)
397
+ return unavailableReceipt(access.cause);
398
+ const transact = portMethod(store, 'transact');
399
+ if (transact === undefined)
400
+ return unavailableReceipt('domain_not_hosted');
401
+ let carried = Object.freeze({});
402
+ let changed = false;
403
+ const leftover = [];
404
+ const disclosed = [];
405
+ for (let i = 0; i < shaped.steps.length; i++) {
406
+ const step = shaped.steps[i];
407
+ portTouched = true;
408
+ const out = await runStep(store, transact, step, carried);
409
+ if (out.kind === 'committed' || out.kind === 'unchanged') {
410
+ if (out.kind === 'committed') {
411
+ changed = true;
412
+ if (step.role === 'prepare' && step.residue !== undefined)
413
+ appendOwnElement(prepared, step.residue);
414
+ }
415
+ addDisclosures(disclosed, out.disclosures);
416
+ carried = mergeCarry(carried, out.carry);
417
+ continue;
418
+ }
419
+ if (step.role === 'cleanup') {
420
+ for (const rest of shaped.steps.slice(i))
421
+ if (rest.residue !== undefined && !leftover.includes(rest.residue))
422
+ appendOwnElement(leftover, rest.residue);
423
+ break;
424
+ }
425
+ if (out.kind === 'refused')
426
+ return refusedReceipt(out.reason, out.detail, prepared);
427
+ const unsure = step.role === 'prepare' && step.residue !== undefined && !prepared.includes(step.residue) ? [...prepared, step.residue] : prepared;
428
+ return unknownReceipt(out.cause, unsure);
429
+ }
430
+ return Object.freeze({ kind: 'applied', appliesOn: shaped.appliesOn, changed, ...residueField(leftover), ...disclosuresField(disclosed) });
431
+ }
432
+ catch {
433
+ return portTouched ? unknownReceipt('commit_unconfirmed', prepared) : refusedReceipt('invalid_request', undefined, []);
434
+ }
435
+ }
436
+ export async function previewLocalConfigIntent(intent, store, facts) {
437
+ try {
438
+ const shaped = shapeIntent(intent);
439
+ if (shaped === undefined)
440
+ return Object.freeze({ kind: 'refused', reason: 'invalid_request' });
441
+ const access = localConfigAccess(facts, { access: 'write', docs: shaped.docs });
442
+ if (!access.ok)
443
+ return unavailableReceipt(access.cause);
444
+ const read = portMethod(store, 'read');
445
+ if (read === undefined)
446
+ return unavailableReceipt('domain_not_hosted');
447
+ const names = [];
448
+ for (const s of shaped.steps)
449
+ for (const d of [...s.lock, ...s.snapshot])
450
+ if (!names.includes(d))
451
+ names.push(d);
452
+ let raw;
453
+ try {
454
+ raw = await read.call(store, Object.freeze([...names]));
455
+ }
456
+ catch {
457
+ return Object.freeze({ kind: 'refused', reason: 'scope_unreadable' });
458
+ }
459
+ const fresh = filesOf(raw, names);
460
+ let carried = Object.freeze({});
461
+ const writes = [];
462
+ const disclosed = [];
463
+ for (const step of shaped.steps) {
464
+ const v = planStep(step, fresh, carried, 'preview');
465
+ if (v.kind === 'refuse')
466
+ return Object.freeze({ kind: 'refused', reason: v.reason, ...(v.detail === undefined ? {} : { detail: v.detail }) });
467
+ if (v.kind === 'write')
468
+ writes.push(step.doc);
469
+ addDisclosures(disclosed, v.disclosures);
470
+ carried = mergeCarry(carried, v.carry);
471
+ }
472
+ return Object.freeze({ kind: 'preview', appliesOn: shaped.appliesOn, changed: writes.length > 0, writes: Object.freeze(writes), ...disclosuresField(disclosed) });
473
+ }
474
+ catch {
475
+ return Object.freeze({ kind: 'refused', reason: 'invalid_request' });
476
+ }
477
+ }
478
+ const REFUSAL_KEYS = {
479
+ invalid_request: 'config.localConfig.refused.invalidRequest',
480
+ scope_unreadable: 'config.localConfig.refused.scopeUnreadable',
481
+ scope_unwritable: 'config.localConfig.refused.scopeUnwritable',
482
+ untrusted_folder: 'config.localConfig.refused.untrustedFolder',
483
+ policy_denied: 'config.localConfig.refused.policyDenied',
484
+ not_found: 'config.localConfig.refused.notFound',
485
+ already_exists: 'config.localConfig.refused.alreadyExists',
486
+ lock_busy: 'config.localConfig.refused.lockBusy',
487
+ lock_failed: 'config.localConfig.refused.lockFailed',
488
+ changed_during_write: 'config.localConfig.refused.changedDuringWrite',
489
+ write_failed: 'config.localConfig.refused.writeFailed',
490
+ not_written: 'config.localConfig.refused.notWritten',
491
+ name_unusable: 'config.localConfig.refused.nameUnusable',
492
+ name_reserved: 'config.localConfig.refused.nameReserved',
493
+ mcp_entry_invalid: 'config.localConfig.refused.mcpEntryInvalid',
494
+ ambiguous_scope: 'config.localConfig.refused.ambiguousScope',
495
+ credentials_need_reentry: 'config.localConfig.refused.credentialsNeedReentry',
496
+ base_conflict: 'config.localConfig.refused.baseConflict',
497
+ skill_locked_by_higher_layer: 'config.localConfig.refused.skillLockedByHigherLayer',
498
+ skill_managed_by_plugin: 'config.localConfig.refused.skillManagedByPlugin',
499
+ skill_locked_by_author: 'config.localConfig.refused.skillLockedByAuthor',
500
+ skill_name_unstorable: 'config.localConfig.refused.skillNameUnstorable',
501
+ entry_in_use: 'config.localConfig.refused.entryInUse',
502
+ credential_unpaired: 'config.localConfig.refused.credentialUnpaired',
503
+ env_catalog_present: 'config.localConfig.refused.envCatalogPresent',
504
+ probe_failed: 'config.localConfig.refused.probeFailed',
505
+ probe_unverified: 'config.localConfig.refused.probeUnverified',
506
+ };
507
+ const UNAVAILABLE_KEYS = {
508
+ engine_is_remote: 'config.localConfig.unavailable.engineIsRemote',
509
+ not_single_user: 'config.localConfig.unavailable.notSingleUser',
510
+ config_home_unpinned: 'config.localConfig.unavailable.configHomeUnpinned',
511
+ domain_not_hosted: 'config.localConfig.unavailable.domainNotHosted',
512
+ };
513
+ const UNKNOWN_KEYS = {
514
+ commit_unconfirmed: 'config.localConfig.unknown.commitUnconfirmed',
515
+ report_unreadable: 'config.localConfig.unknown.reportUnreadable',
516
+ };
517
+ const APPLIED_KEYS = {
518
+ next_turn: 'config.localConfig.applied.nextTurn',
519
+ engine_reload: 'config.localConfig.applied.engineReload',
520
+ next_engine_start: 'config.localConfig.applied.nextEngineStart',
521
+ };
522
+ const PREVIEW_KEYS = {
523
+ next_turn: 'config.localConfig.preview.nextTurn',
524
+ engine_reload: 'config.localConfig.preview.engineReload',
525
+ next_engine_start: 'config.localConfig.preview.nextEngineStart',
526
+ };
527
+ const RESIDUE_KEYS = {
528
+ orphan_key_slot: 'config.localConfig.residue.orphanKeySlot',
529
+ legacy_tier_groups_left: 'config.localConfig.residue.legacyTierGroupsLeft',
530
+ };
531
+ const DISCLOSURE_KEYS = {
532
+ decided_by: 'config.localConfig.disclosure.decidedBy',
533
+ shadowed_by: 'config.localConfig.disclosure.shadowed',
534
+ tier_group_overridden: 'config.localConfig.disclosure.tierGroupOverridden',
535
+ };
536
+ const DISCLOSURE_NAMED_KEYS = {
537
+ tier_group_overridden: 'config.localConfig.disclosure.tierGroupOverriddenNamed',
538
+ };
539
+ const LAYER_KEYS = {
540
+ userSettings: 'config.localConfig.layer.userSettings',
541
+ projectSettings: 'config.localConfig.layer.projectSettings',
542
+ localSettings: 'config.localConfig.layer.localSettings',
543
+ globalConfig: 'config.localConfig.layer.globalConfig',
544
+ projectMcp: 'config.localConfig.layer.projectMcp',
545
+ models: 'config.localConfig.layer.models',
546
+ deploymentMcp: 'config.localConfig.layer.deploymentMcp',
547
+ policySettings: 'config.localConfig.layer.policySettings',
548
+ };
549
+ function pick(table, word, fallback) {
550
+ return typeof word === 'string' && Object.hasOwn(table, word) ? table[word] : fallback;
551
+ }
552
+ export function localConfigReceiptSentence(receipt, lang) {
553
+ let key = REFUSAL_KEYS.not_written;
554
+ try {
555
+ if (isRecord(receipt)) {
556
+ const kind = own(receipt, 'kind');
557
+ if (kind === 'applied' || kind === 'preview') {
558
+ const changed = own(receipt, 'changed');
559
+ key = changed === false ? (kind === 'applied' ? 'config.localConfig.applied.unchanged' : 'config.localConfig.preview.unchanged') : pick(kind === 'applied' ? APPLIED_KEYS : PREVIEW_KEYS, own(receipt, 'appliesOn'), kind === 'applied' ? APPLIED_KEYS.next_turn : PREVIEW_KEYS.next_turn);
560
+ }
561
+ else if (kind === 'refused')
562
+ key = pick(REFUSAL_KEYS, own(receipt, 'reason'), REFUSAL_KEYS.not_written);
563
+ else if (kind === 'unavailable')
564
+ key = pick(UNAVAILABLE_KEYS, own(receipt, 'cause'), UNAVAILABLE_KEYS.domain_not_hosted);
565
+ else if (kind === 'unknown')
566
+ key = pick(UNKNOWN_KEYS, own(receipt, 'cause'), UNKNOWN_KEYS.commit_unconfirmed);
567
+ }
568
+ }
569
+ catch {
570
+ key = REFUSAL_KEYS.not_written;
571
+ }
572
+ return renderMessage(msg(key), lang);
573
+ }
574
+ export function localConfigDisclosureSentence(disclosure, lang) {
575
+ try {
576
+ if (!isRecord(disclosure))
577
+ return undefined;
578
+ const kind = own(disclosure, 'kind');
579
+ if (typeof kind !== 'string' || !Object.hasOwn(DISCLOSURE_KEYS, kind))
580
+ return undefined;
581
+ const layer = own(disclosure, 'layer');
582
+ const layerKey = typeof layer === 'string' && Object.hasOwn(LAYER_KEYS, layer) ? LAYER_KEYS[layer] : 'config.localConfig.layer.other';
583
+ const detail = own(disclosure, 'detail');
584
+ if (Object.hasOwn(DISCLOSURE_NAMED_KEYS, kind) && typeof detail === 'string' && detail !== '') {
585
+ return renderMessage(msg(DISCLOSURE_NAMED_KEYS[kind], { layer: msg(layerKey), names: capForDisplay(detail, DETAIL_MAX) }), lang);
586
+ }
587
+ return renderMessage(msg(DISCLOSURE_KEYS[kind], { layer: msg(layerKey) }), lang);
588
+ }
589
+ catch {
590
+ return undefined;
591
+ }
592
+ }
593
+ export function localConfigResidueSentence(residue, lang) {
594
+ if (typeof residue !== 'string' || !Object.hasOwn(RESIDUE_KEYS, residue))
595
+ return undefined;
596
+ return renderMessage(msg(RESIDUE_KEYS[residue]), lang);
597
+ }