@jini-ai/cms 0.3.10 → 0.4.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 (258) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +47 -19
  3. package/dist/content-types/agent-tools.d.ts +3 -56
  4. package/dist/content-types/agent-tools.js +22 -6
  5. package/dist/content-types/cleanup.d.ts +18 -13
  6. package/dist/content-types/cleanup.js +16 -11
  7. package/dist/content-types/errors.d.ts +57 -24
  8. package/dist/content-types/errors.js +56 -39
  9. package/dist/content-types/field-defs.d.ts +8 -4
  10. package/dist/content-types/field-defs.js +19 -14
  11. package/dist/content-types/index-provisioning.d.ts +61 -47
  12. package/dist/content-types/index-provisioning.js +90 -66
  13. package/dist/content-types/index.d.ts +4 -3
  14. package/dist/content-types/lifecycle-dispatch.d.ts +11 -5
  15. package/dist/content-types/lifecycle-dispatch.js +5 -4
  16. package/dist/content-types/lifecycle.d.ts +23 -18
  17. package/dist/content-types/lifecycle.js +71 -64
  18. package/dist/content-types/repo.memory.d.ts +5 -4
  19. package/dist/content-types/repo.memory.js +3 -2
  20. package/dist/content-types/tool-registrations.d.ts +6 -8
  21. package/dist/content-types/tool-registrations.js +24 -28
  22. package/dist/content-types/types.d.ts +17 -13
  23. package/dist/content-types/types.js +11 -5
  24. package/dist/content-types/write-service.d.ts +20 -22
  25. package/dist/content-types/write-service.js +78 -75
  26. package/dist/core/authorization.d.ts +15 -0
  27. package/dist/core/authorization.js +2 -0
  28. package/dist/core/commands/change-set.d.ts +15 -7
  29. package/dist/core/commands/command.d.ts +45 -26
  30. package/dist/core/commands/command.js +73 -63
  31. package/dist/core/entity-liveness.d.ts +11 -7
  32. package/dist/core/entity-liveness.js +5 -4
  33. package/dist/core/index.d.ts +12 -5
  34. package/dist/core/index.js +10 -4
  35. package/dist/core/ports.d.ts +25 -24
  36. package/dist/core/ports.js +1 -0
  37. package/dist/core/tools/args.d.ts +49 -0
  38. package/dist/core/tools/args.js +46 -0
  39. package/dist/core/tools/index.d.ts +3 -0
  40. package/dist/core/tools/index.js +3 -0
  41. package/dist/entries/agent-tools.d.ts +1 -73
  42. package/dist/entries/agent-tools.js +19 -13
  43. package/dist/entries/errors.d.ts +28 -14
  44. package/dist/entries/errors.js +17 -11
  45. package/dist/entries/field-validation.d.ts +5 -3
  46. package/dist/entries/field-validation.js +7 -5
  47. package/dist/entries/index.d.ts +2 -2
  48. package/dist/entries/lifecycle-dispatch.d.ts +5 -2
  49. package/dist/entries/lifecycle-dispatch.js +2 -1
  50. package/dist/entries/list.d.ts +4 -2
  51. package/dist/entries/list.js +4 -3
  52. package/dist/entries/repo.memory.d.ts +12 -10
  53. package/dist/entries/repo.memory.js +11 -10
  54. package/dist/entries/tool-registrations.d.ts +6 -8
  55. package/dist/entries/tool-registrations.js +42 -41
  56. package/dist/entries/types.d.ts +0 -7
  57. package/dist/entries/write-service.d.ts +28 -29
  58. package/dist/entries/write-service.js +114 -105
  59. package/dist/http/settings/cms-adapter.d.ts +20 -0
  60. package/dist/http/settings/cms-adapter.js +124 -0
  61. package/dist/http/settings/contracts.d.ts +135 -0
  62. package/dist/http/settings/contracts.js +2 -0
  63. package/dist/http/settings/http.d.ts +10 -0
  64. package/dist/http/settings/http.js +211 -0
  65. package/dist/http/settings/index.d.ts +5 -0
  66. package/dist/http/settings/index.js +4 -0
  67. package/dist/http/settings/shared.d.ts +68 -0
  68. package/dist/http/settings/shared.js +54 -0
  69. package/dist/http/settings/stream.d.ts +24 -0
  70. package/dist/http/settings/stream.js +147 -0
  71. package/dist/media/agent-tools.d.ts +1 -66
  72. package/dist/media/agent-tools.js +5 -1
  73. package/dist/media/blob-gc-lock.d.ts +6 -2
  74. package/dist/media/blob-gc-lock.js +4 -2
  75. package/dist/media/blob-gc.d.ts +64 -51
  76. package/dist/media/blob-gc.js +62 -52
  77. package/dist/media/blob-key.d.ts +1 -1
  78. package/dist/media/content-type-sniffer.d.ts +3 -1
  79. package/dist/media/content-type-sniffer.js +1 -1
  80. package/dist/media/html-attributes.d.ts +9 -5
  81. package/dist/media/html-attributes.js +8 -6
  82. package/dist/media/image-transformer.js +1 -1
  83. package/dist/media/image-transformer.sharp.d.ts +6 -0
  84. package/dist/media/image-transformer.sharp.js +12 -6
  85. package/dist/media/import/fetch-image.d.ts +24 -0
  86. package/dist/media/import/fetch-image.js +137 -0
  87. package/dist/media/import/index.d.ts +3 -0
  88. package/dist/media/import/index.js +3 -0
  89. package/dist/media/import/ports.d.ts +38 -0
  90. package/dist/media/index.d.ts +7 -1
  91. package/dist/media/index.js +1 -0
  92. package/dist/media/media-service.d.ts +14 -10
  93. package/dist/media/media-service.js +57 -52
  94. package/dist/media/ports.d.ts +3 -2
  95. package/dist/media/rendition-service.d.ts +3 -3
  96. package/dist/media/rendition-service.js +38 -37
  97. package/dist/media/repo.memory.d.ts +18 -7
  98. package/dist/media/repo.memory.js +12 -6
  99. package/dist/media/tool-registrations.d.ts +16 -15
  100. package/dist/media/tool-registrations.js +84 -72
  101. package/dist/media/transform-lock.d.ts +22 -12
  102. package/dist/media/transform-lock.js +14 -13
  103. package/dist/media/transform-registry.d.ts +3 -3
  104. package/dist/media/transform-registry.js +19 -18
  105. package/dist/media/transform-types.d.ts +8 -3
  106. package/dist/media/transform-types.js +10 -5
  107. package/dist/media/types.d.ts +21 -4
  108. package/dist/media/types.js +15 -2
  109. package/dist/navigation/agent-tools.d.ts +1 -80
  110. package/dist/navigation/agent-tools.js +15 -3
  111. package/dist/navigation/contracts.d.ts +1 -1
  112. package/dist/navigation/index.d.ts +2 -2
  113. package/dist/navigation/menu-service.d.ts +57 -40
  114. package/dist/navigation/menu-service.js +80 -69
  115. package/dist/navigation/ports.d.ts +1 -1
  116. package/dist/navigation/read-model.d.ts +3 -1
  117. package/dist/navigation/read-model.js +2 -2
  118. package/dist/navigation/reconcile.d.ts +2 -2
  119. package/dist/navigation/reconcile.js +2 -1
  120. package/dist/navigation/repo.memory.d.ts +7 -3
  121. package/dist/navigation/repo.memory.js +4 -2
  122. package/dist/navigation/resolver.d.ts +4 -1
  123. package/dist/navigation/resolver.js +1 -1
  124. package/dist/navigation/tool-registrations.d.ts +6 -8
  125. package/dist/navigation/tool-registrations.js +44 -42
  126. package/dist/navigation/types.d.ts +1 -1
  127. package/dist/presentation/presentation.d.ts +11 -3
  128. package/dist/presentation/presentation.js +13 -6
  129. package/dist/presentation/repo.memory.d.ts +6 -2
  130. package/dist/presentation/repo.memory.js +3 -2
  131. package/dist/settings/agent-tools.d.ts +2 -75
  132. package/dist/settings/agent-tools.js +49 -45
  133. package/dist/settings/agent-value-write-tools.d.ts +22 -0
  134. package/dist/settings/agent-value-write-tools.js +154 -0
  135. package/dist/settings/agent-writable-preferences.js +8 -20
  136. package/dist/settings/agent-write-denylist.d.ts +19 -0
  137. package/dist/settings/agent-write-denylist.js +15 -0
  138. package/dist/settings/change-feed.d.ts +11 -2
  139. package/dist/settings/change-feed.js +4 -4
  140. package/dist/settings/definitions-dispatch.d.ts +8 -3
  141. package/dist/settings/definitions-dispatch.js +5 -5
  142. package/dist/settings/dictionaries/index.d.ts +5 -2
  143. package/dist/settings/dictionaries/index.js +2 -2
  144. package/dist/settings/dictionaries/settings-dialog.ar.js +0 -1
  145. package/dist/settings/dictionaries/settings-dialog.bn.js +0 -1
  146. package/dist/settings/dictionaries/settings-dialog.de.js +0 -1
  147. package/dist/settings/dictionaries/settings-dialog.es.js +0 -1
  148. package/dist/settings/dictionaries/settings-dialog.fa.js +0 -1
  149. package/dist/settings/dictionaries/settings-dialog.fr.js +0 -1
  150. package/dist/settings/dictionaries/settings-dialog.hi.js +0 -1
  151. package/dist/settings/dictionaries/settings-dialog.hu.js +0 -1
  152. package/dist/settings/dictionaries/settings-dialog.id.js +0 -1
  153. package/dist/settings/dictionaries/settings-dialog.it.js +0 -1
  154. package/dist/settings/dictionaries/settings-dialog.ja.js +0 -1
  155. package/dist/settings/dictionaries/settings-dialog.ko.js +0 -1
  156. package/dist/settings/dictionaries/settings-dialog.pl.js +0 -1
  157. package/dist/settings/dictionaries/settings-dialog.pt-BR.js +0 -1
  158. package/dist/settings/dictionaries/settings-dialog.ru.js +0 -1
  159. package/dist/settings/dictionaries/settings-dialog.th.js +0 -1
  160. package/dist/settings/dictionaries/settings-dialog.tr.js +0 -1
  161. package/dist/settings/dictionaries/settings-dialog.uk.js +0 -1
  162. package/dist/settings/dictionaries/settings-dialog.ur.js +0 -1
  163. package/dist/settings/dictionaries/settings-dialog.zh-CN.js +0 -1
  164. package/dist/settings/dictionaries/settings-dialog.zh-TW.js +0 -1
  165. package/dist/settings/ensure-definitions.d.ts +5 -5
  166. package/dist/settings/errors.d.ts +7 -1
  167. package/dist/settings/errors.js +7 -1
  168. package/dist/settings/index.d.ts +4 -1
  169. package/dist/settings/index.js +1 -0
  170. package/dist/settings/ports.d.ts +6 -5
  171. package/dist/settings/principal-lookup.d.ts +9 -0
  172. package/dist/settings/principal-lookup.js +2 -0
  173. package/dist/settings/purge-service.d.ts +5 -4
  174. package/dist/settings/purge-service.js +3 -2
  175. package/dist/settings/settings.d.ts +22 -7
  176. package/dist/settings/settings.js +47 -26
  177. package/dist/settings/tool-registrations.d.ts +25 -32
  178. package/dist/settings/tool-registrations.js +66 -56
  179. package/dist/settings/types.d.ts +1 -1
  180. package/dist/settings/ui-tab-definitions.d.ts +1 -1
  181. package/dist/settings/write-service.d.ts +32 -26
  182. package/dist/settings/write-service.js +26 -20
  183. package/dist/taxonomy/agent-tools.d.ts +1 -66
  184. package/dist/taxonomy/agent-tools.js +25 -15
  185. package/dist/taxonomy/content-lookup.js +1 -1
  186. package/dist/taxonomy/index.d.ts +2 -2
  187. package/dist/taxonomy/merge-term.d.ts +7 -4
  188. package/dist/taxonomy/merge-term.js +7 -5
  189. package/dist/taxonomy/repo.memory.d.ts +42 -19
  190. package/dist/taxonomy/repo.memory.js +14 -12
  191. package/dist/taxonomy/validation-chain.d.ts +39 -18
  192. package/dist/taxonomy/validation-chain.js +35 -25
  193. package/dist/taxonomy/write-service.d.ts +106 -45
  194. package/dist/taxonomy/write-service.js +221 -179
  195. package/dist/trash/adapters/record-store.d.ts +52 -0
  196. package/dist/trash/adapters/record-store.js +36 -0
  197. package/dist/trash/cursor.d.ts +20 -0
  198. package/dist/trash/cursor.js +36 -0
  199. package/dist/trash/follow-ups.d.ts +76 -0
  200. package/dist/trash/follow-ups.js +46 -0
  201. package/dist/trash/index.d.ts +8 -0
  202. package/dist/trash/index.js +8 -0
  203. package/dist/trash/ports.d.ts +308 -0
  204. package/dist/trash/ports.js +2 -0
  205. package/dist/trash/repo.memory.d.ts +50 -0
  206. package/dist/trash/repo.memory.js +114 -0
  207. package/dist/trash/sweeper.d.ts +77 -0
  208. package/dist/trash/sweeper.js +127 -0
  209. package/dist/trash/write-service.d.ts +100 -0
  210. package/dist/trash/write-service.js +242 -0
  211. package/dist/workspace/agent-tools.d.ts +1 -59
  212. package/dist/workspace/agent-tools.js +27 -12
  213. package/dist/workspace/create.d.ts +31 -6
  214. package/dist/workspace/create.js +18 -5
  215. package/dist/workspace/delete.d.ts +18 -13
  216. package/dist/workspace/delete.js +20 -12
  217. package/dist/workspace/index.d.ts +1 -1
  218. package/dist/workspace/repo.memory.d.ts +19 -4
  219. package/dist/workspace/repo.memory.js +26 -4
  220. package/dist/workspace/tool-registrations.d.ts +2 -2
  221. package/dist/workspace/tool-registrations.js +13 -13
  222. package/dist/workspace/update.d.ts +6 -5
  223. package/dist/workspace/update.js +9 -8
  224. package/package.json +44 -38
  225. package/dist/identity/admin-crud-service.d.ts +0 -280
  226. package/dist/identity/admin-crud-service.js +0 -468
  227. package/dist/identity/agent-tool-input.d.ts +0 -58
  228. package/dist/identity/agent-tool-input.js +0 -126
  229. package/dist/identity/agent-tools.d.ts +0 -101
  230. package/dist/identity/agent-tools.js +0 -290
  231. package/dist/identity/auth-service.d.ts +0 -118
  232. package/dist/identity/auth-service.js +0 -170
  233. package/dist/identity/authorize.d.ts +0 -90
  234. package/dist/identity/authorize.js +0 -94
  235. package/dist/identity/grant-service.d.ts +0 -198
  236. package/dist/identity/grant-service.js +0 -361
  237. package/dist/identity/hasher.d.ts +0 -39
  238. package/dist/identity/hasher.js +0 -39
  239. package/dist/identity/index.d.ts +0 -62
  240. package/dist/identity/index.js +0 -54
  241. package/dist/identity/password-policy.d.ts +0 -57
  242. package/dist/identity/password-policy.js +0 -66
  243. package/dist/identity/permission-migrations.d.ts +0 -86
  244. package/dist/identity/permission-migrations.js +0 -67
  245. package/dist/identity/permissions.d.ts +0 -35
  246. package/dist/identity/permissions.js +0 -603
  247. package/dist/identity/ports.d.ts +0 -201
  248. package/dist/identity/repo.memory.d.ts +0 -164
  249. package/dist/identity/repo.memory.js +0 -224
  250. package/dist/identity/seed.d.ts +0 -54
  251. package/dist/identity/seed.js +0 -429
  252. package/dist/identity/tool-registrations.d.ts +0 -50
  253. package/dist/identity/tool-registrations.js +0 -419
  254. package/dist/identity/types.d.ts +0 -185
  255. package/dist/identity/types.js +0 -64
  256. package/dist/identity/username.d.ts +0 -12
  257. package/dist/identity/username.js +0 -14
  258. /package/dist/{identity → media/import}/ports.js +0 -0
@@ -1,5 +1,5 @@
1
- import type { ClockPort, IdGeneratorPort, JsonValue, UUID } from "../core/ports.js";
2
- import type { PrincipalRepoPort } from "../identity/index.js";
1
+ import type { Clock, IdGenerator, JsonValue, UUID } from "@jini-ai/core/primitives";
2
+ import type { SettingsPrincipalLookupPort } from "./principal-lookup.js";
3
3
  import type { SettingsRepoPort } from "./ports.js";
4
4
  import { type SettingValueSchema } from "./types.js";
5
5
  /**
@@ -54,9 +54,9 @@ export interface SettingDefinitionSpec {
54
54
  }
55
55
  export interface EnsureSettingDefinitionsDeps {
56
56
  settingsRepo: SettingsRepoPort;
57
- clock: ClockPort;
58
- ids: IdGeneratorPort;
59
- principals: PrincipalRepoPort;
57
+ clock: Clock;
58
+ ids: IdGenerator;
59
+ principals: SettingsPrincipalLookupPort;
60
60
  }
61
61
  export interface EnsureSettingDefinitionsInput {
62
62
  namespace: string;
@@ -12,6 +12,9 @@ export declare class DefinitionInvalidError extends Error {
12
12
  export declare class ScopeNotAllowedError extends Error {
13
13
  }
14
14
  export declare class SecretNotSupportedError extends Error {
15
+ constructor({ message }: {
16
+ message: string;
17
+ }, optional?: ErrorOptions);
15
18
  }
16
19
  export declare class ValueValidationFailedError extends Error {
17
20
  }
@@ -24,10 +27,13 @@ export declare class DefinitionTombstonedError extends Error {
24
27
  export declare class DefinitionNotFoundError extends Error {
25
28
  }
26
29
  export declare class PurgeRequiredError extends Error {
30
+ constructor({ message }: {
31
+ message: string;
32
+ }, optional?: ErrorOptions);
27
33
  }
28
34
  export declare class ForbiddenError extends Error {
29
35
  }
30
- /** REQ-13: the target `principalId` does not resolve to an active principal in the request's workspace (INV-09). */
36
+ /** : the target `principalId` does not resolve to an active principal in the request's workspace. See docs/decisions/DR-003-settings-ledger-invariants.md. */
31
37
  export declare class PrincipalNotFoundError extends Error {
32
38
  readonly principalId: string;
33
39
  readonly workspaceId: string;
@@ -12,6 +12,9 @@ export class DefinitionInvalidError extends Error {
12
12
  export class ScopeNotAllowedError extends Error {
13
13
  }
14
14
  export class SecretNotSupportedError extends Error {
15
+ constructor({ message }, optional = {}) {
16
+ super(message, optional);
17
+ }
15
18
  }
16
19
  export class ValueValidationFailedError extends Error {
17
20
  }
@@ -24,10 +27,13 @@ export class DefinitionTombstonedError extends Error {
24
27
  export class DefinitionNotFoundError extends Error {
25
28
  }
26
29
  export class PurgeRequiredError extends Error {
30
+ constructor({ message }, optional = {}) {
31
+ super(message, optional);
32
+ }
27
33
  }
28
34
  export class ForbiddenError extends Error {
29
35
  }
30
- /** REQ-13: the target `principalId` does not resolve to an active principal in the request's workspace (INV-09). */
36
+ /** : the target `principalId` does not resolve to an active principal in the request's workspace. See docs/decisions/DR-003-settings-ledger-invariants.md. */
31
37
  export class PrincipalNotFoundError extends Error {
32
38
  principalId;
33
39
  workspaceId;
@@ -23,7 +23,7 @@ export { type SettingDefinitionSpec, type EnsureSettingDefinitionsDeps, type Ens
23
23
  export { INSTRUCTIONS_NAMESPACE, NOTIFICATIONS_NAMESPACE, PRIVACY_NAMESPACE, APPEARANCE_NAMESPACE, LANGUAGE_NAMESPACE, type EnsureSettingsUiTabDefinitionsInput, ensureSettingsUiTabDefinitions, } from "./ui-tab-definitions.js";
24
24
  export { type AgentWritablePreference, AGENT_PREFERENCE_WRITE_SCOPE, AGENT_WRITABLE_PREFERENCES, AGENT_WRITABLE_PREFERENCE_IDS, resolveAgentWritablePreference, AGENT_PREFERENCE_REQUIRED_SCOPE_BIT, } from "./agent-writable-preferences.js";
25
25
  /** The agent-tool catalog for this domain. */
26
- export { getSettingsAgentToolCatalog, type AgentToolDefinition as SettingsAgentToolDefinition, type AgentToolSideEffect as SettingsAgentToolSideEffect, } from "./agent-tools.js";
26
+ export { getSettingsAgentToolCatalog, } from "./agent-tools.js";
27
27
  /**
28
28
  * The agent-tool wiring for this domain.
29
29
  *
@@ -34,4 +34,7 @@ export { getSettingsAgentToolCatalog, type AgentToolDefinition as SettingsAgentT
34
34
  export { buildSettingsRegistrations, settingsDerivedRisk, type SettingsToolDeps, } from "./tool-registrations.js";
35
35
  /** The settings-dialog chrome copy (24 generic tab labels/subtitles), shared across every host. */
36
36
  export { SETTINGS_DIALOG_DICTIONARIES, translateSettingsDialog } from "./dictionaries/index.js";
37
+ export { AGENT_WRITE_CONFIRMATION_SETTINGS, AGENT_WRITE_DENIED_SETTINGS, findAgentWriteRule, type AgentSettingWriteRule } from "./agent-write-denylist.js";
38
+ export type { SettingsWriteConfirmation, SettingsValueWriteToolId } from "./agent-value-write-tools.js";
39
+ export type { SettingsPrincipalLookupPort } from "./principal-lookup.js";
37
40
  //# sourceMappingURL=index.d.ts.map
@@ -33,4 +33,5 @@ export { getSettingsAgentToolCatalog, } from "./agent-tools.js";
33
33
  export { buildSettingsRegistrations, settingsDerivedRisk, } from "./tool-registrations.js";
34
34
  /** The settings-dialog chrome copy (24 generic tab labels/subtitles), shared across every host. */
35
35
  export { SETTINGS_DIALOG_DICTIONARIES, translateSettingsDialog } from "./dictionaries/index.js";
36
+ export { AGENT_WRITE_CONFIRMATION_SETTINGS, AGENT_WRITE_DENIED_SETTINGS, findAgentWriteRule } from "./agent-write-denylist.js";
36
37
  //# sourceMappingURL=index.js.map
@@ -1,19 +1,20 @@
1
- import type { UUID } from "../core/ports.js";
1
+ import type { UUID } from "@jini-ai/core/primitives";
2
2
  import type { SettingDefinitionRecord, SettingRevisionRecord, SettingValueRecord } from "./types.js";
3
3
  /**
4
4
  * @file Port contract for the `settings` library.
5
5
  *
6
6
  * Purpose:
7
7
  * Dependency-inversion seam (port/adapter rule-of-two) for the 5 settings tables.
8
- * Deliberately does NOT declare a principal-lookup method — REQ-13's
9
- * target-principal check reuses `identity.PrincipalRepoPort.findById`
10
- * directly (reuse, not a new port).
8
+ * Persistence deliberately has no principal-lookup method: target-principal checks use
9
+ * the separate host-owned SettingsPrincipalLookupPort, keeping membership/status policy
10
+ * outside the settings persistence contract.
11
11
  *
12
12
  * `transaction` gives `write-service.ts`/`purge-service.ts` the same-tx
13
- * guarantee INV-01/INV-06 depend on (value/definition row + its revision row
13
+ * guarantee depend on (value/definition row + its revision row
14
14
  * must commit together, or not at all).
15
15
  *
16
16
  * Interfaces and types only — no feature logic.
17
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
17
18
  */
18
19
  export interface SettingsRepoPort {
19
20
  findActiveDefinition(required: {
@@ -0,0 +1,9 @@
1
+ import type { Principal } from "@jini-ai/core";
2
+ /** Resolve only an active principal in the requested workspace. The host owns status and membership policy. */
3
+ export interface SettingsPrincipalLookupPort {
4
+ findActiveById(required: {
5
+ workspaceId: string;
6
+ id: string;
7
+ }): Promise<Principal | null>;
8
+ }
9
+ //# sourceMappingURL=principal-lookup.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=principal-lookup.js.map
@@ -1,4 +1,4 @@
1
- import type { ClockPort, UUID } from "../core/ports.js";
1
+ import type { Clock, UUID } from "@jini-ai/core/primitives";
2
2
  import type { SettingsRepoPort } from "./ports.js";
3
3
  import type { AuthorizeFn } from "./write-service.js";
4
4
  /**
@@ -8,7 +8,7 @@ import type { AuthorizeFn } from "./write-service.js";
8
8
  * A host's value-table FKs (`setting_values_workspace`/`_user` -> `workspaces`)
9
9
  * are typically `ON DELETE RESTRICT`, not `CASCADE` — a raw workspace delete
10
10
  * cannot silently drop value rows without a ledgered revision. This module is
11
- * the explicit, ledgered alternative that requires: `authorize()` once
11
+ * the explicit, ledgered alternative that requires: `authorize` once
12
12
  * -> enumerate every affected row -> append a redacted `op='purge'` revision
13
13
  * per row -> delete the row, all in ONE transaction. The `setting_revisions`
14
14
  * ledger itself is NEVER touched by a purge — only value rows are removed,
@@ -17,13 +17,14 @@ import type { AuthorizeFn } from "./write-service.js";
17
17
  *
18
18
  * Deviation from the outline's `SettingsWriteServiceDeps`: purge needs
19
19
  * neither `ids` (no new definition/value rows are created) nor `principals`
20
- * (REQ-13's target-principal check is a write-time guard; purge only removes
20
+ * ( target-principal check is a write-time guard; purge only removes
21
21
  * pre-existing rows, it never writes a new user-scope row for an unverified
22
22
  * principal) — so it declares its own smaller `PurgeServiceDeps`.
23
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
23
24
  */
24
25
  export interface PurgeServiceDeps {
25
26
  repo: SettingsRepoPort;
26
- clock: ClockPort;
27
+ clock: Clock;
27
28
  authorize: AuthorizeFn;
28
29
  }
29
30
  export interface PurgeTenantSettingsRequired {
@@ -1,3 +1,4 @@
1
+ import { nowIso as kernelNowIso } from "@jini-ai/core/primitives";
1
2
  import { ForbiddenError } from "./errors.js";
2
3
  import { invalidateWorkspaceSettingsCache } from "./settings.js";
3
4
  /**
@@ -18,7 +19,7 @@ export async function purgeTenantSettings(required) {
18
19
  throw new ForbiddenError(`principal '${input.callerPrincipalId}' is not authorized for 'settings.definitions.manage' (${authResult.reason})`);
19
20
  }
20
21
  const result = await deps.repo.transaction(async () => {
21
- const now = deps.clock.nowIso();
22
+ const now = kernelNowIso({ clock: deps.clock });
22
23
  let purgedCount = 0;
23
24
  if (input.principalId) {
24
25
  const userValues = await deps.repo.listUserValues({
@@ -99,7 +100,7 @@ export async function purgeTenantSettings(required) {
99
100
  }
100
101
  return { purgedCount };
101
102
  });
102
- invalidateWorkspaceSettingsCache(deps.repo, input.workspaceId, input.principalId);
103
+ invalidateWorkspaceSettingsCache({ repo: deps.repo, workspaceId: input.workspaceId }, { principalId: input.principalId });
103
104
  return result;
104
105
  }
105
106
  //# sourceMappingURL=purge-service.js.map
@@ -1,16 +1,17 @@
1
- import type { JsonValue } from "../core/ports.js";
1
+ import type { JsonValue } from "@jini-ai/core/primitives";
2
2
  import { DefinitionInvalidError, SecretNotSupportedError } from "./errors.js";
3
3
  import type { SettingsRepoPort } from "./ports.js";
4
4
  import { type SettingDefinitionRecord, type SettingOwnerKind, type SettingScopeContext, type SettingValueSchema } from "./types.js";
5
5
  /**
6
6
  * @file The settings resolver + pure definition-registration validation
7
- * (REQ-02, REQ-03, REQ-09; "one evaluator").
7
+ * ("one evaluator").
8
8
  *
9
9
  * Purpose:
10
10
  * `validateDefinitionInput` is pure (no I/O) — the write-side chokepoint in
11
11
  * `write-service.ts` calls it before persisting. `getEffective`/`getLayer`/
12
12
  * `resolveDefinition` are read-only against `SettingsRepoPort`. Neither
13
13
  * mutates anything; every mutation goes through `write-service.ts`.
14
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
14
15
  */
15
16
  export interface DefinitionInput {
16
17
  namespace: string;
@@ -24,13 +25,14 @@ export interface DefinitionInput {
24
25
  secret: boolean;
25
26
  }
26
27
  /**
27
- * REQ-02/REQ-09/INV-05/INV-08 — the pure half of `registerDefinitions`.
28
+ * — the pure half of `registerDefinitions`.
28
29
  * Never throws; returns a discriminated result so the chokepoint decides how
29
30
  * to surface the failure (matches `ValueValidationFailedError`'s pattern of
30
31
  * keeping I/O out of validation).
31
32
  *
32
33
  * @complexity O(1) per definition.
33
34
  * @overallScore 100
35
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
34
36
  */
35
37
  export declare function validateDefinitionInput(input: DefinitionInput): {
36
38
  valid: true;
@@ -39,7 +41,12 @@ export declare function validateDefinitionInput(input: DefinitionInput): {
39
41
  error: DefinitionInvalidError | SecretNotSupportedError;
40
42
  };
41
43
  export declare function validateValueAgainstSchema(schema: SettingValueSchema, value: JsonValue): boolean;
42
- export declare function registerCoercer(tag: string, fn: (value: JsonValue) => JsonValue): void;
44
+ export declare function registerCoercer({ tag, fn }: {
45
+ tag: string;
46
+ fn: (required: {
47
+ value: JsonValue;
48
+ }) => JsonValue;
49
+ }, _optional?: Record<string, never>): void;
43
50
  /**
44
51
  * Bumps the namespace's definition-cache epoch (lazy invalidation) — every
45
52
  * previously-cached definition-cache entry for this
@@ -64,13 +71,20 @@ export declare function invalidateDefinitionNamespaceCache(repo: SettingsRepoPor
64
71
  * reintroduces caching. Deleting the call site would remove the place that question is asked. A
65
72
  * cache added without restoring a purge hook is a tenant-teardown leak, so this stays as the seam.
66
73
  */
67
- export declare function invalidateWorkspaceSettingsCache(_repo: SettingsRepoPort, _workspaceId: string, _principalId?: string): void;
74
+ export declare function invalidateWorkspaceSettingsCache(_required: {
75
+ repo: SettingsRepoPort;
76
+ workspaceId: string;
77
+ }, _optional?: {
78
+ principalId?: string | undefined;
79
+ }): void;
68
80
  /**
69
81
  * Follows an alias marker (depth <=1) to the current row and returns it as
70
82
  * stored — status intact, including `tombstone`. `resolveDefinition` (below)
71
- * is the read-path wrapper that collapses tombstone to typed-absent (EC-10);
83
+ * is the read-path wrapper that collapses tombstone to typed-absent;
72
84
  * `write-service.ts` uses this raw form directly so it can report
73
85
  * `DEFINITION_TOMBSTONED` distinctly from "not found".
86
+ * Malformed alias targets, cycles and chains deeper than one fail closed as absent.
87
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
74
88
  */
75
89
  export declare function resolveDefinitionRaw(deps: {
76
90
  repo: SettingsRepoPort;
@@ -103,7 +117,7 @@ export interface ResolvedSetting {
103
117
  defVersion: number;
104
118
  }
105
119
  /**
106
- * REQ-03/INV-02 — total for a live key: never throws, never returns
120
+ * — total for a live key: never throws, never returns
107
121
  * undefined. Precedence `user ?? workspace ?? global ?? default`. A
108
122
  * `cleared` row is treated as absent at that layer (behavior.spec §1.2).
109
123
  *
@@ -113,6 +127,7 @@ export interface ResolvedSetting {
113
127
  *
114
128
  * @complexity O(1) — up to 3 layer reads + 1 definition read.
115
129
  * @overallScore 100
130
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
116
131
  */
117
132
  export declare function getEffective(deps: {
118
133
  repo: SettingsRepoPort;
@@ -6,25 +6,31 @@ const NAMESPACE_FENCE = {
6
6
  site: (input) => input.namespace.startsWith("site.") && input.workspaceId !== null,
7
7
  };
8
8
  /**
9
- * REQ-02/REQ-09/INV-05/INV-08 — the pure half of `registerDefinitions`.
9
+ * — the pure half of `registerDefinitions`.
10
10
  * Never throws; returns a discriminated result so the chokepoint decides how
11
11
  * to surface the failure (matches `ValueValidationFailedError`'s pattern of
12
12
  * keeping I/O out of validation).
13
13
  *
14
14
  * @complexity O(1) per definition.
15
15
  * @overallScore 100
16
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
16
17
  */
17
18
  export function validateDefinitionInput(input) {
18
19
  if (input.secret) {
19
20
  return {
20
21
  valid: false,
21
- error: new SecretNotSupportedError("secret:true definitions are not supported in the core-only subset (REQ-09/INV-08)"),
22
+ error: new SecretNotSupportedError({
23
+ // See docs/decisions/DR-003-settings-ledger-invariants.md for the invariant behind this refusal; keep external identifiers out of caller-facing messages.
24
+ message: "secret:true definitions are not supported in the core-only subset",
25
+ }),
22
26
  };
23
27
  }
24
28
  if (!NAMESPACE_FENCE[input.ownerKind](input)) {
25
29
  return {
26
30
  valid: false,
27
- error: new DefinitionInvalidError(`namespace '${input.namespace}' does not match the owner fence for owner_kind '${input.ownerKind}' (REQ-02)`),
31
+ error: new DefinitionInvalidError(
32
+ // See docs/decisions/DR-003-settings-ledger-invariants.md for the invariant behind this refusal; keep external identifiers out of caller-facing messages.
33
+ `namespace '${input.namespace}' does not match the owner fence for owner_kind '${input.ownerKind}'`),
28
34
  };
29
35
  }
30
36
  if (input.scopes < 1 || input.scopes > 7) {
@@ -33,19 +39,23 @@ export function validateDefinitionInput(input) {
33
39
  error: new DefinitionInvalidError(`scopes bitmask ${input.scopes} is out of range 1..7`),
34
40
  };
35
41
  }
36
- // INV-05: a site-owned def (workspace_id NOT NULL) may never declare the global scope bit.
42
+ // a site-owned def (workspace_id NOT NULL) may never declare the global scope bit. See docs/decisions/DR-003-settings-ledger-invariants.md.
37
43
  if (input.workspaceId !== null && (input.scopes & SCOPE_BIT.global) !== 0) {
38
44
  return {
39
45
  valid: false,
40
- error: new DefinitionInvalidError("a site-owned definition may not declare the global scope bit (INV-05)"),
46
+ error: new DefinitionInvalidError(
47
+ // See docs/decisions/DR-003-settings-ledger-invariants.md for the invariant behind this refusal; keep external identifiers out of caller-facing messages.
48
+ "a site-owned definition may not declare the global scope bit"),
41
49
  };
42
50
  }
43
51
  // Totality (behavior.spec §3): every non-secret def needs a non-null default so
44
- // getEffective is always total (INV-02) and factory-reset is provably bootable.
52
+ // getEffective is always total and factory-reset is provably bootable. See docs/decisions/DR-003-settings-ledger-invariants.md.
45
53
  if (input.defaultValue === null) {
46
54
  return {
47
55
  valid: false,
48
- error: new DefinitionInvalidError("non-secret definitions require a non-null default_json (totality, INV-02)"),
56
+ error: new DefinitionInvalidError(
57
+ // See docs/decisions/DR-003-settings-ledger-invariants.md for the invariant behind this refusal; keep external identifiers out of caller-facing messages.
58
+ "non-secret definitions require a non-null default_json (totality)"),
49
59
  };
50
60
  }
51
61
  return { valid: true };
@@ -69,9 +79,9 @@ export function validateValueAgainstSchema(schema, value) {
69
79
  return true;
70
80
  }
71
81
  }
72
- /** Identity-registry of total coercers keyed by `coercionTag` (EC-08). `"identity"` is always registered. */
73
- const coercers = new Map([["identity", (v) => v]]);
74
- export function registerCoercer(tag, fn) {
82
+ /** Identity-registry of total coercers keyed by `coercionTag`. `"identity"` is always registered. See docs/decisions/DR-003-settings-ledger-invariants.md. */
83
+ const coercers = new Map([["identity", ({ value }) => value]]);
84
+ export function registerCoercer({ tag, fn }, _optional = {}) {
75
85
  coercers.set(tag, fn);
76
86
  }
77
87
  const cacheByRepo = new WeakMap();
@@ -119,38 +129,48 @@ export function invalidateDefinitionNamespaceCache(repo, namespace) {
119
129
  * reintroduces caching. Deleting the call site would remove the place that question is asked. A
120
130
  * cache added without restoring a purge hook is a tenant-teardown leak, so this stays as the seam.
121
131
  */
122
- export function invalidateWorkspaceSettingsCache(_repo, _workspaceId, _principalId) {
132
+ export function invalidateWorkspaceSettingsCache(_required, _optional = {}) {
123
133
  // Intentionally empty — see doc above. Do not delete without re-reading `purge-service.ts`.
124
134
  }
125
135
  /**
126
136
  * Follows an alias marker (depth <=1) to the current row and returns it as
127
137
  * stored — status intact, including `tombstone`. `resolveDefinition` (below)
128
- * is the read-path wrapper that collapses tombstone to typed-absent (EC-10);
138
+ * is the read-path wrapper that collapses tombstone to typed-absent;
129
139
  * `write-service.ts` uses this raw form directly so it can report
130
140
  * `DEFINITION_TOMBSTONED` distinctly from "not found".
141
+ * Malformed alias targets, cycles and chains deeper than one fail closed as absent.
142
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
131
143
  */
132
144
  export async function resolveDefinitionRaw(deps, input) {
133
- // Namespace fencing (REQ-02) makes `site.*` (non-null workspace_id) and
145
+ // Namespace fencing makes `site.*` (non-null workspace_id) and. See docs/decisions/DR-003-settings-ledger-invariants.md.
134
146
  // `core.*`/`theme.*` (null workspace_id) disjoint by construction, so a
135
147
  // caller resolving inside a workspace context may still be asking for a
136
148
  // platform definition. Try the caller's own partition first, then fall
137
149
  // back to the platform (null) partition.
138
- const found = (await deps.repo.findActiveDefinition(input)) ??
139
- (input.workspaceId !== null
140
- ? await deps.repo.findActiveDefinition({ ...input, workspaceId: null })
141
- : null);
142
- if (!found)
143
- return null;
144
- if (found.status === "alias") {
145
- if (found.aliasOfNamespace == null || found.aliasOfKey == null)
150
+ const visited = new Set();
151
+ let cursor = input;
152
+ for (let depth = 0; depth <= 1; depth++) {
153
+ const address = JSON.stringify([cursor.workspaceId, cursor.namespace, cursor.key]);
154
+ if (visited.has(address))
155
+ return null;
156
+ visited.add(address);
157
+ const found = (await deps.repo.findActiveDefinition(cursor)) ??
158
+ (cursor.workspaceId !== null
159
+ ? await deps.repo.findActiveDefinition({ ...cursor, workspaceId: null })
160
+ : null);
161
+ if (!found)
146
162
  return null;
147
- return resolveDefinitionRaw(deps, {
163
+ if (found.status !== "alias")
164
+ return found;
165
+ if (depth === 1 || found.aliasOfNamespace == null || found.aliasOfKey == null)
166
+ return null;
167
+ cursor = {
148
168
  namespace: found.aliasOfNamespace,
149
169
  key: found.aliasOfKey,
150
170
  workspaceId: input.workspaceId,
151
- });
171
+ };
152
172
  }
153
- return found;
173
+ return null;
154
174
  }
155
175
  /**
156
176
  * The read-path resolver: typed-absent (`null`) for a tombstoned or
@@ -175,7 +195,7 @@ export async function resolveDefinition(deps, input) {
175
195
  return resolved;
176
196
  }
177
197
  /**
178
- * REQ-03/INV-02 — total for a live key: never throws, never returns
198
+ * — total for a live key: never throws, never returns
179
199
  * undefined. Precedence `user ?? workspace ?? global ?? default`. A
180
200
  * `cleared` row is treated as absent at that layer (behavior.spec §1.2).
181
201
  *
@@ -185,6 +205,7 @@ export async function resolveDefinition(deps, input) {
185
205
  *
186
206
  * @complexity O(1) — up to 3 layer reads + 1 definition read.
187
207
  * @overallScore 100
208
+ * See docs/decisions/DR-003-settings-ledger-invariants.md.
188
209
  */
189
210
  export async function getEffective(deps, input) {
190
211
  const definition = await resolveDefinition(deps, {
@@ -198,7 +219,7 @@ export async function getEffective(deps, input) {
198
219
  if (defVersion === definition.version)
199
220
  return value;
200
221
  const coercer = coercers.get(definition.coercionTag ?? "identity") ?? coercers.get("identity");
201
- return coercer(value);
222
+ return coercer({ value });
202
223
  };
203
224
  // Every layer read below goes straight to the repo. See this module's cache header for why there
204
225
  // is no per-layer cache wrapping these three calls.
@@ -1,54 +1,47 @@
1
+ import type { IdGenerator } from "@jini-ai/core/primitives";
2
+ import type { Clock } from "@jini-ai/core/primitives";
1
3
  /**
2
- * @file Settings' tool-registration wiring: maps the wireable subset of `agent-tools.ts`'s eight
3
- * catalog entries onto three reads and one curated write, as `ToolRegistration`s. See
4
- * `agent-tools.ts`'s own file header for why the four GENERIC write entries are documented but
5
- * deliberately never wired ({@link UNWIRED_SETTINGS_TOOL_IDS}), and why `settings_set_ui_preference`
6
- * is not one of them.
7
- *
8
- * The one write here is shaped so that its blast radius is a property of the code rather than of
9
- * the caller's restraint: its target key comes from a fixed enum
10
- * (`agent-writable-preferences.ts`), its scope is a constant, and it never names a principal — so
11
- * "write another operator's settings" and "write a key outside the list" are unrepresentable
12
- * inputs, not merely unauthorized ones.
13
- *
14
- * Authorization shape: `getEffective`/`resolveDefinition` (`settings.ts`) and a direct
15
- * `settingsRepo.listActiveDefinitions`/`getGlobalValue`/`getWorkspaceValue`/`getUserValue` read
16
- * carry no `authorize()` call of their own — a host's admin routes gate inline — so every handler
17
- * here calls the kit's `requireToolPermission` itself, mirroring those routes' identical checks.
18
- * `resolveOwnOrOtherPrincipalRead` below deliberately duplicates (rather than imports) any HTTP
19
- * admin layer's read-target resolution logic — this file is domain-owned wiring, and importing
20
- * logic from a host's HTTP admin layer would invert this library's ports/adapters direction
21
- * (features must not depend on a host's server/routes layer); the two are kept behaviorally
22
- * identical by inspection, the same discipline that logic's own host-side callers should apply.
4
+ * @file Settings tools: three reads and three bounded value writes. Bulk reset and definition
5
+ * registration stay unwired. The generic tools reuse write-service authorization/validation and
6
+ * return the previous override. Key policy and the optional host confirmation port enforce the
7
+ * owner's 2026-10-01 decision: privacy/instructions/self-configuration require a human card.
8
+ * Direct reads supply their own permission checks; writes pre-check before reading undo values
9
+ * and the service repeats authorization at mutation time. No host HTTP/runtime imports.
23
10
  */
24
11
  import type { AuthorizeFn } from "../core/commands/command.js";
25
- import { type DerivedRiskByToolId, type ToolRegistration } from "../core/tools/registration-kit.js";
26
- import type { PrincipalRepoPort } from "../identity/index.js";
12
+ import { type SettingsWriteConfirmation } from "./agent-value-write-tools.js";
13
+ import { type AgentSettingWriteRule } from "./agent-write-denylist.js";
14
+ import { type DerivedRiskByToolId, type ToolRegistration } from "@jini-ai/core";
15
+ import type { SettingsPrincipalLookupPort } from "./principal-lookup.js";
27
16
  import type { SettingsRepoPort } from "./ports.js";
17
+ import { set as setSettingValue } from "./write-service.js";
28
18
  /**
29
19
  * The exact slice of a host's route-deps bag Settings' tool handlers read. Declared structurally
30
20
  * (rather than importing any host's own route-deps type) so this module carries no back-edge into
31
21
  * a host's composition root. A host satisfies this structurally by passing its existing route deps
32
- * object; nothing there needs to change shape.
22
+ * object after binding kernel clock/IDs and an active, workspace-scoped principal lookup.
33
23
  */
34
24
  export interface SettingsToolDeps {
35
25
  authorize: AuthorizeFn;
36
26
  workspaceId: string;
37
- clock: {
38
- nowIso(): string;
39
- };
40
- idGen: {
41
- newId(): string;
42
- };
27
+ clock: Clock;
28
+ idGen: IdGenerator;
43
29
  settingsReady: Promise<void>;
44
30
  settingsUiTabsReady: Promise<void>;
45
31
  settingsRepo: SettingsRepoPort;
46
- principalRepo: PrincipalRepoPort;
32
+ principalRepo: SettingsPrincipalLookupPort;
33
+ /** Host extensions: explicit deny or human-confirmation rules, never agent inputs. */
34
+ extraDeniedSettings?: readonly AgentSettingWriteRule[];
35
+ extraConfirmationSettings?: readonly AgentSettingWriteRule[];
36
+ /** Must obtain an authenticated human card decision; absent means protected keys fail closed. */
37
+ confirmWrite?: (request: SettingsWriteConfirmation) => Promise<boolean>;
38
+ /** Host's validation wrapper; defaults to the package's existing set chokepoint. */
39
+ setValue?: typeof setSettingValue;
47
40
  }
48
41
  /**
49
42
  * This wiring layer's OWN risk classification, authored from what each handler below actually
50
43
  * calls. See `DerivedRiskByToolId` in the kit for why it is independent of the catalog's own
51
- * `sideEffects` declaration — and note that the four excluded writes appear NOWHERE here, which is
44
+ * `sideEffects` declaration — and note that the two excluded bulk/schema writes appear NOWHERE here, which is
52
45
  * itself the strongest of the guards: an unclassified id cannot be wired at all.
53
46
  */
54
47
  export declare const settingsDerivedRisk: DerivedRiskByToolId;