@jini-ai/core 0.4.0 → 0.4.2

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 (120) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +18 -1
  3. package/dist/agent-tools.d.ts +1 -0
  4. package/dist/approval.d.ts +134 -0
  5. package/dist/approval.js +141 -0
  6. package/dist/index.d.ts +5 -0
  7. package/dist/index.js +5 -0
  8. package/dist/model-facing-tool-errors.d.ts +152 -0
  9. package/dist/model-facing-tool-errors.js +124 -0
  10. package/dist/native-regexp.d.ts +10 -0
  11. package/dist/native-regexp.js +2 -0
  12. package/dist/primitives/index.d.ts +2 -0
  13. package/dist/primitives/index.js +1 -0
  14. package/dist/primitives/sse.d.ts +60 -0
  15. package/dist/primitives/sse.js +122 -0
  16. package/dist/redact.js +4 -3
  17. package/dist/registration-kit.d.ts +47 -0
  18. package/dist/registration-kit.js +55 -0
  19. package/dist/settings/agent-tools.d.ts +10 -0
  20. package/dist/settings/agent-tools.js +204 -0
  21. package/dist/settings/agent-value-write-tools.d.ts +22 -0
  22. package/dist/settings/agent-value-write-tools.js +157 -0
  23. package/dist/settings/agent-writable-preferences.d.ts +67 -0
  24. package/dist/settings/agent-writable-preferences.js +132 -0
  25. package/dist/settings/agent-write-denylist.d.ts +19 -0
  26. package/dist/settings/agent-write-denylist.js +15 -0
  27. package/dist/settings/change-feed.d.ts +83 -0
  28. package/dist/settings/change-feed.js +61 -0
  29. package/dist/settings/definitions-dispatch.d.ts +56 -0
  30. package/dist/settings/definitions-dispatch.js +86 -0
  31. package/dist/settings/dictionaries/index.d.ts +48 -0
  32. package/dist/settings/dictionaries/index.js +69 -0
  33. package/dist/settings/dictionaries/settings-dialog.ar.d.ts +3 -0
  34. package/dist/settings/dictionaries/settings-dialog.ar.js +27 -0
  35. package/dist/settings/dictionaries/settings-dialog.bn.d.ts +3 -0
  36. package/dist/settings/dictionaries/settings-dialog.bn.js +27 -0
  37. package/dist/settings/dictionaries/settings-dialog.de.d.ts +3 -0
  38. package/dist/settings/dictionaries/settings-dialog.de.js +27 -0
  39. package/dist/settings/dictionaries/settings-dialog.es.d.ts +3 -0
  40. package/dist/settings/dictionaries/settings-dialog.es.js +27 -0
  41. package/dist/settings/dictionaries/settings-dialog.fa.d.ts +3 -0
  42. package/dist/settings/dictionaries/settings-dialog.fa.js +27 -0
  43. package/dist/settings/dictionaries/settings-dialog.fr.d.ts +3 -0
  44. package/dist/settings/dictionaries/settings-dialog.fr.js +27 -0
  45. package/dist/settings/dictionaries/settings-dialog.hi.d.ts +3 -0
  46. package/dist/settings/dictionaries/settings-dialog.hi.js +27 -0
  47. package/dist/settings/dictionaries/settings-dialog.hu.d.ts +3 -0
  48. package/dist/settings/dictionaries/settings-dialog.hu.js +27 -0
  49. package/dist/settings/dictionaries/settings-dialog.id.d.ts +3 -0
  50. package/dist/settings/dictionaries/settings-dialog.id.js +27 -0
  51. package/dist/settings/dictionaries/settings-dialog.it.d.ts +3 -0
  52. package/dist/settings/dictionaries/settings-dialog.it.js +27 -0
  53. package/dist/settings/dictionaries/settings-dialog.ja.d.ts +3 -0
  54. package/dist/settings/dictionaries/settings-dialog.ja.js +27 -0
  55. package/dist/settings/dictionaries/settings-dialog.ko.d.ts +3 -0
  56. package/dist/settings/dictionaries/settings-dialog.ko.js +27 -0
  57. package/dist/settings/dictionaries/settings-dialog.pl.d.ts +3 -0
  58. package/dist/settings/dictionaries/settings-dialog.pl.js +27 -0
  59. package/dist/settings/dictionaries/settings-dialog.pt-BR.d.ts +3 -0
  60. package/dist/settings/dictionaries/settings-dialog.pt-BR.js +27 -0
  61. package/dist/settings/dictionaries/settings-dialog.ru.d.ts +3 -0
  62. package/dist/settings/dictionaries/settings-dialog.ru.js +27 -0
  63. package/dist/settings/dictionaries/settings-dialog.th.d.ts +3 -0
  64. package/dist/settings/dictionaries/settings-dialog.th.js +27 -0
  65. package/dist/settings/dictionaries/settings-dialog.tr.d.ts +3 -0
  66. package/dist/settings/dictionaries/settings-dialog.tr.js +27 -0
  67. package/dist/settings/dictionaries/settings-dialog.uk.d.ts +3 -0
  68. package/dist/settings/dictionaries/settings-dialog.uk.js +27 -0
  69. package/dist/settings/dictionaries/settings-dialog.ur.d.ts +3 -0
  70. package/dist/settings/dictionaries/settings-dialog.ur.js +27 -0
  71. package/dist/settings/dictionaries/settings-dialog.zh-CN.d.ts +3 -0
  72. package/dist/settings/dictionaries/settings-dialog.zh-CN.js +27 -0
  73. package/dist/settings/dictionaries/settings-dialog.zh-TW.d.ts +3 -0
  74. package/dist/settings/dictionaries/settings-dialog.zh-TW.js +27 -0
  75. package/dist/settings/ensure-definitions.d.ts +101 -0
  76. package/dist/settings/ensure-definitions.js +101 -0
  77. package/dist/settings/errors.d.ts +42 -0
  78. package/dist/settings/errors.js +46 -0
  79. package/dist/settings/express/cms-adapter.d.ts +20 -0
  80. package/dist/settings/express/cms-adapter.js +124 -0
  81. package/dist/settings/express/contracts.d.ts +135 -0
  82. package/dist/settings/express/contracts.js +2 -0
  83. package/dist/settings/express/http.d.ts +10 -0
  84. package/dist/settings/express/http.js +211 -0
  85. package/dist/settings/express/index.d.ts +5 -0
  86. package/dist/settings/express/index.js +4 -0
  87. package/dist/settings/express/shared.d.ts +68 -0
  88. package/dist/settings/express/shared.js +54 -0
  89. package/dist/settings/express/stream.d.ts +24 -0
  90. package/dist/settings/express/stream.js +147 -0
  91. package/dist/settings/index.d.ts +40 -0
  92. package/dist/settings/index.js +37 -0
  93. package/dist/settings/ports.d.ts +118 -0
  94. package/dist/settings/ports.js +2 -0
  95. package/dist/settings/principal-lookup.d.ts +9 -0
  96. package/dist/settings/principal-lookup.js +2 -0
  97. package/dist/settings/purge-service.d.ts +54 -0
  98. package/dist/settings/purge-service.js +106 -0
  99. package/dist/settings/repo.memory.d.ts +119 -0
  100. package/dist/settings/repo.memory.js +199 -0
  101. package/dist/settings/settings.d.ts +139 -0
  102. package/dist/settings/settings.js +259 -0
  103. package/dist/settings/tool-registrations.d.ts +54 -0
  104. package/dist/settings/tool-registrations.js +250 -0
  105. package/dist/settings/types.d.ts +125 -0
  106. package/dist/settings/types.js +3 -0
  107. package/dist/settings/ui-tab-definitions.d.ts +41 -0
  108. package/dist/settings/ui-tab-definitions.js +148 -0
  109. package/dist/settings/write-service.d.ts +320 -0
  110. package/dist/settings/write-service.js +683 -0
  111. package/dist/surface-exchanges.d.ts +209 -0
  112. package/dist/surface-exchanges.js +55 -0
  113. package/dist/tool-metadata.d.ts +71 -0
  114. package/dist/tool-metadata.js +56 -0
  115. package/dist/tool-permissions.d.ts +76 -0
  116. package/dist/tool-permissions.js +57 -0
  117. package/dist/tool-registry.d.ts +3 -0
  118. package/package.json +62 -4
  119. package/dist/internal.d.ts +0 -41
  120. package/dist/internal.js +0 -39
@@ -0,0 +1,320 @@
1
+ import type { Clock, IdGenerator, JsonValue, UUID } from "../primitives/index.js";
2
+ import type { SettingsPrincipalLookupPort } from "./principal-lookup.js";
3
+ import type { SettingsRepoPort } from "./ports.js";
4
+ import { type DefinitionInput } from "./settings.js";
5
+ import { type SettingScope, type SettingValueSchema } from "./types.js";
6
+ /**
7
+ * @file `SettingsWriteService` — the single write chokepoint.
8
+ *
9
+ * Purpose:
10
+ * The ONLY value/definition-mutation path. Repo write methods
11
+ * (`saveDefinition`/`save*Value`/`appendRevision`/`delete*Value`) must never
12
+ * be called from outside this file (Code Review enforces this as a file-
13
+ * boundary check).
14
+ *
15
+ * Every export here: `authorize` first (fail-closed) -> validate ->
16
+ * write value/definition row + revision in one transaction.
17
+ *
18
+ * `deriveRequiredPermission` is the sole source of truth for the self-vs-
19
+ * other permission rule (behavior.spec.md §1.3) — closes Red-Team.
20
+ * target-principal checks use a host-supplied active-principal lookup over core Principal;
21
+ * identity and membership policy remain at the host boundary.
22
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
23
+ */
24
+ /** Matches `core/commands/command.ts`'s `AuthorizeFn` shape structurally — no import, kept decoupled. */
25
+ export type AuthorizeFn = (params: {
26
+ principalId: UUID;
27
+ permission: string;
28
+ workspaceId: UUID;
29
+ entityType?: string;
30
+ entityId?: UUID;
31
+ }) => Promise<{
32
+ allowed: boolean;
33
+ reason: string;
34
+ }>;
35
+ export interface SettingsWriteServiceDeps {
36
+ repo: SettingsRepoPort;
37
+ clock: Clock;
38
+ ids: IdGenerator;
39
+ authorize: AuthorizeFn;
40
+ /** Host lookup must hide disabled and out-of-workspace principals. See docs/decisions/DR-003-settings-ledger-invariants.md. */
41
+ principals: SettingsPrincipalLookupPort;
42
+ }
43
+ /**
44
+ * behavior.spec.md §1.3 / `[internal-invariant]` — the self-vs-other
45
+ * permission derivation. `targetPrincipalId` omitted or equal to the caller
46
+ * -> `settings.user.self.write`; any other principal -> `settings.user.write`.
47
+ * A divergence between this function and any other implementation of the
48
+ * rule is a fail-open authorization bug (Red-Team ) — this is the only
49
+ * place the rule may be encoded.
50
+ *
51
+ * @complexity O(1), pure.
52
+ * @overallScore 100
53
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
54
+ */
55
+ export declare function deriveRequiredPermission(input: {
56
+ scope: SettingScope;
57
+ targetPrincipalId?: UUID | undefined;
58
+ callerPrincipalId: UUID;
59
+ }): string;
60
+ export interface RegisterDefinitionsRequired {
61
+ deps: SettingsWriteServiceDeps;
62
+ input: {
63
+ definitions: DefinitionInput[];
64
+ callerPrincipalId: UUID;
65
+ /** The workspace the caller is authorizing in (always required — even platform-def registration is authorized within a workspace context). */
66
+ authWorkspaceId: UUID;
67
+ };
68
+ }
69
+ /** chokepoint write: authorize `settings.definitions.manage` -> validate -> write + revision, one per definition, each its own tx. See docs/decisions/DR-003-settings-ledger-invariants.md. */
70
+ export declare function registerDefinitions(required: RegisterDefinitionsRequired): Promise<{
71
+ registered: string[];
72
+ }>;
73
+ export interface SetValueRequired {
74
+ deps: SettingsWriteServiceDeps;
75
+ input: {
76
+ namespace: string;
77
+ key: string;
78
+ scope: SettingScope;
79
+ value: JsonValue;
80
+ workspaceId?: UUID | undefined;
81
+ principalId?: UUID | undefined;
82
+ callerPrincipalId: UUID;
83
+ /**
84
+ * The ambient workspace to authorize the caller's grant in. Optional and
85
+ * additive: when omitted, falls back to `input.workspaceId ??
86
+ * input.callerPrincipalId`. Callers that DO have a real ambient workspace
87
+ * (every HTTP route in a single-workspace host) should pass it
88
+ * explicitly: for `scope=global` (no `workspaceId`), falling back to
89
+ * `callerPrincipalId` as a stand-in "workspace" id is nonsensical against
90
+ * the real `authorize()` (identity's own authorize looks the principal up
91
+ * by `{workspaceId, id}` — a bogus workspaceId means `findById` returns
92
+ * null and every request is denied `principal_disabled`, including the
93
+ * wildcard-owner).
94
+ */
95
+ authWorkspaceId?: UUID | undefined;
96
+ /**
97
+ * `deriveRequiredPermission` unconditionally derives `settings.workspace.write` for
98
+ * `scope: "workspace"` — a caller that already authorized a narrower, domain-specific
99
+ * permission at its own route layer (e.g. a comments module's `comments.configure`, an SEO
100
+ * module's `admin.seo.manage`) still hits this chokepoint's generic `settings.workspace.write`
101
+ * check and gets a masked 500 if it lacks that broader grant, even though its own domain
102
+ * permission is exactly what the feature's admin UI advertises as sufficient. Optional and
103
+ * additive: omitted, behavior is unchanged (every pre-existing caller still gets the
104
+ * scope-derived permission). When a domain-settings module supplies this, the chokepoint
105
+ * authorizes THIS permission instead of the scope-derived one — still a mandatory
106
+ * `authorize` call before any write ( fail-closed discipline is not relaxed, only
107
+ * which permission string is checked).
108
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
109
+ */
110
+ requiredPermissionOverride?: string | undefined;
111
+ };
112
+ }
113
+ /** chokepoint write: authorize -> validate -> value + revision, same tx. See docs/decisions/DR-003-settings-ledger-invariants.md. */
114
+ export declare function set(required: SetValueRequired): Promise<{
115
+ value: JsonValue;
116
+ revisionSeq: number;
117
+ }>;
118
+ export interface ClearValueRequired {
119
+ deps: SettingsWriteServiceDeps;
120
+ input: {
121
+ namespace: string;
122
+ key: string;
123
+ scope: SettingScope;
124
+ workspaceId?: UUID | undefined;
125
+ principalId?: UUID | undefined;
126
+ callerPrincipalId: UUID;
127
+ /** Set by `resetNamespace` when looping `clear` in its own reset-authorized context — bypasses the inner authorize re-check, still writes a normal revision. See docs/decisions/DR-003-settings-ledger-invariants.md. */
128
+ skipAuthorize?: boolean | undefined;
129
+ /** Set by a caller that has ALREADY opened a transaction around this write —
130
+ * see `resetNamespace`. Explicit rather than an ambient depth counter,
131
+ * because an ambient one cannot tell a genuine nesting from a concurrent
132
+ * unrelated transaction and silently merges the two. */
133
+ skipTransaction?: boolean | undefined;
134
+ /** See `SetValueRequired.input.authWorkspaceId`'s doc. Optional/additive. */
135
+ authWorkspaceId?: UUID | undefined;
136
+ };
137
+ }
138
+ /** chokepoint write: authorize -> value + revision (state='cleared'), same tx. See docs/decisions/DR-003-settings-ledger-invariants.md. */
139
+ export declare function clear(required: ClearValueRequired): Promise<{
140
+ revisionSeq: number;
141
+ }>;
142
+ export interface ResetNamespaceRequired {
143
+ deps: SettingsWriteServiceDeps;
144
+ input: {
145
+ namespace: string;
146
+ scope: Exclude<SettingScope, never>;
147
+ workspaceId?: UUID | undefined;
148
+ principalId?: UUID | undefined;
149
+ callerPrincipalId: UUID;
150
+ /** See `SetValueRequired.input.authWorkspaceId`'s doc. Optional/additive. */
151
+ authWorkspaceId?: UUID | undefined;
152
+ };
153
+ }
154
+ /**
155
+ * — an explicit, human-invoked orchestrator: authorize
156
+ * the matching `settings.reset.*` permission once, then loop `clear` for
157
+ * every setting in the namespace in the reset-authorized internal context
158
+ * (`skipAuthorize: true`) — the outer reset permission is sufficient on its
159
+ * own; each inner clear still emits its own `op='clear'` revision.
160
+ *
161
+ * `revisionSeqs` collects each inner `clear` call's own `revisionSeq` so a
162
+ * host's admin HTTP route can surface the full contract without a second
163
+ * read. Existing callers that only read `.clearedCount` are unaffected.
164
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
165
+ */
166
+ export declare function resetNamespace(required: ResetNamespaceRequired, keysInNamespace: string[]): Promise<{
167
+ clearedCount: number;
168
+ revisionSeqs: number[];
169
+ }>;
170
+ export interface RenameDefinitionRequired {
171
+ deps: SettingsWriteServiceDeps;
172
+ input: {
173
+ namespace: string;
174
+ key: string;
175
+ workspaceId: UUID | null;
176
+ newNamespace: string;
177
+ newKey: string;
178
+ callerPrincipalId: UUID;
179
+ /** The workspace the caller is authorizing in (see `registerDefinitions`' identical field). */
180
+ authWorkspaceId: UUID;
181
+ };
182
+ }
183
+ /**
184
+ * The rename mechanism : a same-tx pair —
185
+ * (1) UPDATE the active definition row's `(namespace,key)` to the new name,
186
+ * same `setting_id`/`version` (ledgered `op='alias'`); (2) INSERT a fresh v1
187
+ * alias marker at the OLD name pointing at the new name. Because identity
188
+ * never moves, value rows (keyed on `setting_id` only) stay attached.
189
+ *
190
+ * Sequential rename (A->B then B->C) retargets every prior marker pointing
191
+ * at the OLD name to the NEW name in the same tx. A rename target that
192
+ * itself resolves to an existing alias is rejected `ALIAS_DEPTH_EXCEEDED`
193
+ * (behavior.spec.md §7: "the rename target resolves to an alias").
194
+ *
195
+ * Never touches `schema`/`defaultValue`/`scopes` -- a pure rename can never
196
+ * trigger `RENAME_RETYPE_CONFLICT` by construction; that guard lives in
197
+ * `retypeDefinition` ("no rename+retype in one op").
198
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
199
+ */
200
+ export declare function renameDefinition(required: RenameDefinitionRequired): Promise<{
201
+ settingId: string;
202
+ markerSettingId: string;
203
+ }>;
204
+ export interface RetypeDefinitionRequired {
205
+ deps: SettingsWriteServiceDeps;
206
+ input: {
207
+ namespace: string;
208
+ key: string;
209
+ workspaceId: UUID | null;
210
+ schema: SettingValueSchema;
211
+ defaultValue: JsonValue | null;
212
+ /** A total coercer id/tag ( registry in `settings.ts`) for reading values recorded under any prior version. See docs/decisions/DR-003-settings-ledger-invariants.md. */
213
+ coercionTag: string;
214
+ /**
215
+ * Present only to detect a combined rename+retype request;
216
+ * retype never actually moves `(namespace,key)` -- if provided and it
217
+ * differs from the current name while `schema` also differs, the whole
218
+ * op is rejected `RENAME_RETYPE_CONFLICT` rather than silently doing
219
+ * only the schema half.
220
+ */
221
+ newNamespace?: string | undefined;
222
+ newKey?: string | undefined;
223
+ callerPrincipalId: UUID;
224
+ authWorkspaceId: UUID;
225
+ };
226
+ }
227
+ /**
228
+ * The retype mechanism : a same-tx pair --
229
+ * (1) UPDATE the prior active version's `status` to `deprecated` FIRST (else
230
+ * the insert in step 2 collides with the one-active-row-per-slot
231
+ * invariant); (2) INSERT the new `version+1` row as `active`, same
232
+ * `setting_id`/`namespace`/`key`. Rejected unless every prior version
233
+ * (2..N) already carries a total coercer -- `coercionTag` is `null` only for
234
+ * version 1 by construction (`types.ts`), so this walks 2..N and requires
235
+ * each to be non-null.
236
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
237
+ */
238
+ export declare function retypeDefinition(required: RetypeDefinitionRequired): Promise<{
239
+ settingId: string;
240
+ version: number;
241
+ }>;
242
+ export interface ReconcileDefinitionDefaultRequired {
243
+ deps: SettingsWriteServiceDeps;
244
+ input: {
245
+ namespace: string;
246
+ key: string;
247
+ workspaceId: UUID | null;
248
+ /** The default the SOURCE declares. Wins over a differing stored default. */
249
+ defaultValue: JsonValue;
250
+ callerPrincipalId: UUID;
251
+ authWorkspaceId: UUID;
252
+ };
253
+ }
254
+ /**
255
+ * Reconciles a stored `default_json` that has drifted from the default its
256
+ * source declares. Idempotent and a no-op when they already agree.
257
+ *
258
+ * Why this exists — the bug it closes:
259
+ * `ensure-definitions.ts` registers each definition once and then skips it
260
+ * forever (`if (existing) continue;`). That makes a changed `defaultValue` in
261
+ * SOURCE unreachable in any install that has already booted, for every
262
+ * settings-dialog tab a host registers this way. It can surface as a stored
263
+ * default staying "system" in the database while source says "light",
264
+ * rendering a settings panel with the wrong initial state — a visibly wrong
265
+ * value produced by entirely correct-looking source. The next occurrence
266
+ * could be a silently wrong default with no visual tell at all.
267
+ *
268
+ * Why `core`-only:
269
+ * `ownerKind` already encodes who owns a definition. `core` definitions
270
+ * configure a platform capability and are code-owned, so code is the authority
271
+ * on their default and may overwrite a drifted stored one. `site` and `theme`
272
+ * definitions are operator-owned — silently overwriting those would destroy a
273
+ * deliberate choice, so they are rejected rather than reconciled.
274
+ *
275
+ * Why this is NOT a `retype`:
276
+ * `retype` deprecates the active row and inserts `version+1` because a changed
277
+ * SCHEMA means every stored value needs a total coercer. A changed DEFAULT
278
+ * needs none — the schema is untouched, so every stored value stays valid and
279
+ * only the resolution of UNSET values moves. The row is therefore updated in
280
+ * place at the same version, and the paired revision carries the old and new
281
+ * default in `beforeJson`/`afterJson` so the change is auditable.
282
+ *
283
+ * @complexity O(1) — one definition lookup plus at most one row update and one
284
+ * revision append, in a single transaction.
285
+ */
286
+ export declare function reconcileDefinitionDefault(required: ReconcileDefinitionDefaultRequired): Promise<{
287
+ settingId: string;
288
+ changed: boolean;
289
+ }>;
290
+ export interface DeprecateDefinitionRequired {
291
+ deps: SettingsWriteServiceDeps;
292
+ input: {
293
+ namespace: string;
294
+ key: string;
295
+ workspaceId: UUID | null;
296
+ callerPrincipalId: UUID;
297
+ authWorkspaceId: UUID;
298
+ };
299
+ }
300
+ export interface TombstoneDefinitionRequired {
301
+ deps: SettingsWriteServiceDeps;
302
+ input: {
303
+ namespace: string;
304
+ key: string;
305
+ workspaceId: UUID | null;
306
+ callerPrincipalId: UUID;
307
+ authWorkspaceId: UUID;
308
+ };
309
+ }
310
+ /** Flips the active definition's status to `deprecated`, ledgered `op='deprecate'` -- same-tx, chokepoint-gated. */
311
+ export declare function deprecateDefinition(required: DeprecateDefinitionRequired): Promise<{
312
+ settingId: string;
313
+ version: number;
314
+ }>;
315
+ /** Flips the active definition's status to `tombstone` (kills a core key), ledgered `op='tombstone'` -- same-tx, chokepoint-gated. */
316
+ export declare function tombstoneDefinition(required: TombstoneDefinitionRequired): Promise<{
317
+ settingId: string;
318
+ version: number;
319
+ }>;
320
+ //# sourceMappingURL=write-service.d.ts.map