@unbrained/pm-cli 2026.8.16 → 2026.8.18

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 (225) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/AGENTS.md +4 -3
  3. package/CHANGELOG.md +65 -6
  4. package/README.md +16 -16
  5. package/dist/cli/error-guidance.js +7 -4
  6. package/dist/cli/public.d.ts +2 -0
  7. package/dist/cli/public.js +21 -0
  8. package/dist/cli/register-list-query.js +132 -51
  9. package/dist/cli/register-mutation.js +4 -4
  10. package/dist/cli-bundle/bundle-manifest.json +148 -148
  11. package/dist/cli-bundle/chunks/chunk-5UZZAJKR.js +3 -0
  12. package/dist/cli-bundle/chunks/chunk-ALDJGKAK.js +2 -0
  13. package/dist/cli-bundle/chunks/chunk-BSK2IN3C.js +8 -0
  14. package/dist/cli-bundle/chunks/chunk-DY4DMUMC.js +197 -0
  15. package/dist/cli-bundle/chunks/{chunk-73UGEBRS.js → chunk-GNFAFIJI.js} +10 -10
  16. package/dist/cli-bundle/chunks/chunk-L5Q2CLPE.js +35 -0
  17. package/dist/cli-bundle/chunks/{chunk-RIS565OA.js → chunk-PVRUN5ZS.js} +5 -5
  18. package/dist/cli-bundle/chunks/chunk-RZQTVMRQ.js +2 -0
  19. package/dist/cli-bundle/chunks/chunk-SARFF5H5.js +3 -0
  20. package/dist/cli-bundle/chunks/chunk-UYCLQVL2.js +13 -0
  21. package/dist/cli-bundle/chunks/{chunk-URW4QRMI.js → chunk-YRGOLZA7.js} +59 -55
  22. package/dist/cli-bundle/chunks/register-list-query-I23LALEE.js +11 -0
  23. package/dist/cli-bundle/chunks/register-mutation-6YGU3GPH.js +20 -0
  24. package/dist/cli-bundle/chunks/{register-operations-KGDNRMCL.js → register-operations-4HTFC6J7.js} +2 -2
  25. package/dist/cli-bundle/chunks/{register-setup-NVXBOD5I.js → register-setup-PPPEF3SN.js} +2 -2
  26. package/dist/cli-bundle/focused-chunks/chunk-2PN4TJXH.js +2 -0
  27. package/dist/cli-bundle/focused-chunks/{chunk-C7JUBRFP.js → chunk-3TKWMNKF.js} +3 -3
  28. package/dist/cli-bundle/focused-chunks/chunk-5AF3ZBNB.js +155 -0
  29. package/dist/cli-bundle/focused-chunks/chunk-C5IW2NDA.js +2 -0
  30. package/dist/cli-bundle/focused-chunks/{chunk-CODV5LUT.js → chunk-EUEGXZ2A.js} +3 -3
  31. package/dist/cli-bundle/focused-chunks/{chunk-4JPEBFFC.js → chunk-EX6MKP2X.js} +2 -2
  32. package/dist/cli-bundle/focused-chunks/{chunk-SSNDYZTM.js → chunk-K43KKAFS.js} +2 -2
  33. package/dist/cli-bundle/focused-chunks/{chunk-6ROKKT4X.js → chunk-M62NO7EI.js} +2 -2
  34. package/dist/cli-bundle/focused-chunks/{chunk-T46KLOIB.js → chunk-ODOVCP45.js} +47 -47
  35. package/dist/cli-bundle/focused-chunks/chunk-RNDNMARJ.js +16 -0
  36. package/dist/cli-bundle/focused-chunks/chunk-TN4AE665.js +26 -0
  37. package/dist/cli-bundle/focused-chunks/chunk-TVV2DONO.js +2 -0
  38. package/dist/cli-bundle/focused-chunks/chunk-UI6AJ5TD.js +4 -0
  39. package/dist/cli-bundle/focused-chunks/chunk-V5XTGLK7.js +2 -0
  40. package/dist/cli-bundle/focused-chunks/chunk-WGHQVU6P.js +2 -0
  41. package/dist/cli-bundle/focused-chunks/chunk-XYNBXTDQ.js +2 -0
  42. package/dist/cli-bundle/focused-chunks/chunk-Z56ECT7I.js +2 -0
  43. package/dist/cli-bundle/main.js +7 -7
  44. package/dist/cli-bundle/sdk-authoring.js +1 -1
  45. package/dist/cli-bundle/sdk-contracts.js +1 -1
  46. package/dist/cli-bundle/sdk-core.js +31 -31
  47. package/dist/cli-bundle/sdk-governance.js +1 -1
  48. package/dist/cli-bundle/sdk-graph.js +1 -1
  49. package/dist/cli-bundle/sdk-merge.js +32 -32
  50. package/dist/cli-bundle/sdk-query.js +1 -1
  51. package/dist/cli-bundle/sdk-runtime.js +1 -1
  52. package/dist/cli-bundle/sdk-testing.js +1 -1
  53. package/dist/cli-bundle/sdk.js +4 -2
  54. package/dist/core/config/nested-settings.js +8 -2
  55. package/dist/core/extensions/exporter-output-contract.d.ts +12 -0
  56. package/dist/core/extensions/exporter-output-contract.js +67 -0
  57. package/dist/core/extensions/extension-types.d.ts +36 -1
  58. package/dist/core/extensions/extension-types.js +2 -2
  59. package/dist/core/extensions/loader.js +43 -48
  60. package/dist/core/extensions/manifest-schema.d.ts +20 -0
  61. package/dist/core/extensions/manifest-schema.js +52 -0
  62. package/dist/core/history/event-index.d.ts +5 -0
  63. package/dist/core/history/event-index.js +133 -27
  64. package/dist/core/history/replay.js +32 -14
  65. package/dist/core/schema/fields-file.d.ts +1 -1
  66. package/dist/core/schema/fields-file.js +2 -2
  67. package/dist/core/schema/runtime-field-values.js +115 -9
  68. package/dist/core/schema/runtime-schema.d.ts +3 -1
  69. package/dist/core/schema/runtime-schema.js +124 -3
  70. package/dist/core/sentry/helpers.js +3 -3
  71. package/dist/core/shared/constants.js +5 -2
  72. package/dist/core/shared/errors.d.ts +10 -0
  73. package/dist/core/shared/errors.js +2 -2
  74. package/dist/core/shared/time.d.ts +2 -0
  75. package/dist/core/shared/time.js +27 -2
  76. package/dist/core/store/settings-validator.d.ts +4 -0
  77. package/dist/core/store/settings-validator.js +3 -2
  78. package/dist/core/store/settings.js +8 -2
  79. package/dist/mcp/tool-definitions.js +12 -7
  80. package/dist/sdk/agent/refusal-reachability.d.ts +46 -6
  81. package/dist/sdk/agent/refusal-reachability.js +350 -8
  82. package/dist/sdk/authoring.d.ts +1 -1
  83. package/dist/sdk/authoring.js +2 -2
  84. package/dist/sdk/cli-bootstrap.d.ts +2 -2
  85. package/dist/sdk/cli-bootstrap.js +4 -4
  86. package/dist/sdk/cli-contracts/command-aliases.d.ts +34 -2
  87. package/dist/sdk/cli-contracts/command-aliases.js +64 -13
  88. package/dist/sdk/cli-contracts/enum-contracts.d.ts +4 -0
  89. package/dist/sdk/cli-contracts/enum-contracts.js +9 -2
  90. package/dist/sdk/cli-contracts/flag-contracts.js +11 -7
  91. package/dist/sdk/cli-contracts/grammar-contracts.d.ts +70 -0
  92. package/dist/sdk/cli-contracts/grammar-contracts.js +345 -0
  93. package/dist/sdk/cli-contracts/runtime-contracts.d.ts +19 -2
  94. package/dist/sdk/cli-contracts/runtime-contracts.js +61 -14
  95. package/dist/sdk/cli-contracts/tool-parameter-tables.js +10 -4
  96. package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
  97. package/dist/sdk/cli-contracts/tool-schema.js +20 -11
  98. package/dist/sdk/cli-contracts.d.ts +2 -2
  99. package/dist/sdk/cli-contracts.js +4 -4
  100. package/dist/sdk/completion.js +13 -15
  101. package/dist/sdk/compose.d.ts +9 -5
  102. package/dist/sdk/compose.js +33 -3
  103. package/dist/sdk/context-intent-contracts.d.ts +8 -4
  104. package/dist/sdk/context-intent-contracts.js +76 -31
  105. package/dist/sdk/contracts.d.ts +1 -0
  106. package/dist/sdk/contracts.js +3 -2
  107. package/dist/sdk/core.d.ts +1 -1
  108. package/dist/sdk/core.js +3 -3
  109. package/dist/sdk/dependency-flag-validation.d.ts +9 -2
  110. package/dist/sdk/dependency-flag-validation.js +101 -35
  111. package/dist/sdk/environment/host-environment-errors.js +25 -7
  112. package/dist/sdk/generated/generated-error-code-catalog-part-1.js +78 -14
  113. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +62 -14
  114. package/dist/sdk/governance/assurance-action.d.ts +3 -7
  115. package/dist/sdk/governance/assurance-action.js +97 -2
  116. package/dist/sdk/governance/boundary-fixtures.d.ts +86 -0
  117. package/dist/sdk/governance/boundary-fixtures.js +201 -0
  118. package/dist/sdk/governance/defect-recurrence-signals.d.ts +10 -0
  119. package/dist/sdk/governance/defect-recurrence-signals.js +25 -0
  120. package/dist/sdk/governance/defect-recurrence.d.ts +242 -0
  121. package/dist/sdk/governance/defect-recurrence.js +523 -0
  122. package/dist/sdk/governance.d.ts +2 -0
  123. package/dist/sdk/governance.js +4 -2
  124. package/dist/sdk/graph/governance.js +3 -2
  125. package/dist/sdk/guide-topics.js +193 -4
  126. package/dist/sdk/index.d.ts +7 -3
  127. package/dist/sdk/index.js +8 -5
  128. package/dist/sdk/init-agent-guidance.js +3 -3
  129. package/dist/sdk/lifecycle/create.js +4 -2
  130. package/dist/sdk/lifecycle/update-many.js +15 -2
  131. package/dist/sdk/lifecycle/update.js +5 -2
  132. package/dist/sdk/linked-artifacts.js +63 -14
  133. package/dist/sdk/mutation-events.d.ts +18 -2
  134. package/dist/sdk/mutation-events.js +59 -16
  135. package/dist/sdk/output.d.ts +44 -0
  136. package/dist/sdk/output.js +28 -2
  137. package/dist/sdk/query/complete-list.d.ts +94 -0
  138. package/dist/sdk/query/complete-list.js +173 -0
  139. package/dist/sdk/query/get.d.ts +2 -0
  140. package/dist/sdk/query/get.js +32 -7
  141. package/dist/sdk/query/list.js +5 -2
  142. package/dist/sdk/query.d.ts +1 -0
  143. package/dist/sdk/query.js +3 -2
  144. package/dist/sdk/read-output-contracts.d.ts +2 -2
  145. package/dist/sdk/read-output-contracts.js +36 -9
  146. package/dist/sdk/relationship-analytics.js +90 -14
  147. package/dist/sdk/relationship-kinds/contract.d.ts +4 -0
  148. package/dist/sdk/relationship-kinds/contract.js +3 -2
  149. package/dist/sdk/relationships.d.ts +1 -1
  150. package/dist/sdk/relationships.js +11 -2
  151. package/dist/sdk/runtime-extended-actions.js +10 -2
  152. package/dist/sdk/runtime.d.ts +8 -2
  153. package/dist/sdk/runtime.js +11 -8
  154. package/dist/sdk/schema.d.ts +1 -0
  155. package/dist/sdk/schema.js +2 -2
  156. package/dist/sdk/test/execution.d.ts +6 -2
  157. package/dist/sdk/test/execution.js +39 -13
  158. package/dist/types.d.ts +34 -2
  159. package/dist/types.js +4 -2
  160. package/docs/AGENT_GUIDE.md +25 -9
  161. package/docs/CLI_GRAMMAR.md +106 -0
  162. package/docs/COMMANDS.md +23 -23
  163. package/docs/CONFIGURATION.md +58 -1
  164. package/docs/CONTEXT_INTEGRITY_CONTRACTS.md +58 -0
  165. package/docs/DEFECT_RECURRENCE.md +134 -0
  166. package/docs/DEPENDENCY_KIND_CONTRACT.md +12 -2
  167. package/docs/ONBOARDING.md +4 -4
  168. package/docs/QUICKSTART.md +1 -1
  169. package/docs/README.md +4 -0
  170. package/docs/RELATIONSHIP_GRAPH.md +16 -2
  171. package/docs/RELEASING.md +30 -6
  172. package/docs/SDK.md +74 -7
  173. package/docs/SDK_ARTIFACT_OUTPUT.md +72 -0
  174. package/docs/SDK_CONTEXT_COORDINATION.md +46 -12
  175. package/docs/SDK_RUNTIME_BOUNDARIES.md +9 -1
  176. package/docs/TESTING.md +18 -1
  177. package/docs/TRUSTWORTHY_CONTEXT_EVIDENCE.md +1 -1
  178. package/docs/agent-task-token-baseline.json +5 -5
  179. package/marketplace.json +2 -2
  180. package/package.json +8 -5
  181. package/packages/pm-beads/package.json +1 -1
  182. package/packages/pm-calendar/package.json +1 -1
  183. package/packages/pm-command-kit/package.json +1 -1
  184. package/packages/pm-digital-twin/package.json +1 -1
  185. package/packages/pm-governance-audit/package.json +1 -1
  186. package/packages/pm-guide-shell/package.json +1 -1
  187. package/packages/pm-kanban/package.json +1 -1
  188. package/packages/pm-lifecycle-hooks/package.json +1 -1
  189. package/packages/pm-linked-test-adapters/package.json +1 -1
  190. package/packages/pm-search-advanced/package.json +1 -1
  191. package/packages/pm-templates/package.json +1 -1
  192. package/packages/pm-todos/package.json +1 -1
  193. package/packages/pm-vcs/package.json +1 -1
  194. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  195. package/plugins/pm-claude/skills/pm-audit/SKILL.md +26 -0
  196. package/plugins/pm-claude/skills/pm-developer/SKILL.md +26 -0
  197. package/plugins/pm-claude/skills/pm-planner/SKILL.md +26 -0
  198. package/plugins/pm-claude/skills/pm-release/SKILL.md +26 -0
  199. package/plugins/pm-claude/skills/pm-workflow/SKILL.md +26 -0
  200. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  201. package/plugins/pm-codex/skills/pm-auditor/SKILL.md +26 -0
  202. package/plugins/pm-codex/skills/pm-native/SKILL.md +26 -0
  203. package/plugins/pm-codex/skills/pm-release/SKILL.md +26 -0
  204. package/sdk/public-surface.json +1243 -54
  205. package/dist/cli-bundle/chunks/chunk-47OQEOQB.js +0 -2
  206. package/dist/cli-bundle/chunks/chunk-HCIYWD6M.js +0 -35
  207. package/dist/cli-bundle/chunks/chunk-I2LUWWR5.js +0 -3
  208. package/dist/cli-bundle/chunks/chunk-IC5W2T34.js +0 -2
  209. package/dist/cli-bundle/chunks/chunk-OAC5NIV4.js +0 -3
  210. package/dist/cli-bundle/chunks/chunk-PANOOBIS.js +0 -13
  211. package/dist/cli-bundle/chunks/chunk-TK6N7HGM.js +0 -8
  212. package/dist/cli-bundle/chunks/chunk-TOFG2URD.js +0 -197
  213. package/dist/cli-bundle/chunks/register-list-query-EYCXHZAG.js +0 -10
  214. package/dist/cli-bundle/chunks/register-mutation-7YVIG54J.js +0 -20
  215. package/dist/cli-bundle/focused-chunks/chunk-5NMFSX26.js +0 -155
  216. package/dist/cli-bundle/focused-chunks/chunk-725JSCMP.js +0 -2
  217. package/dist/cli-bundle/focused-chunks/chunk-DQ6SMTBF.js +0 -2
  218. package/dist/cli-bundle/focused-chunks/chunk-HAACPSUW.js +0 -2
  219. package/dist/cli-bundle/focused-chunks/chunk-J4UFYHOD.js +0 -2
  220. package/dist/cli-bundle/focused-chunks/chunk-JX5Z344Q.js +0 -2
  221. package/dist/cli-bundle/focused-chunks/chunk-L4RDYBIQ.js +0 -2
  222. package/dist/cli-bundle/focused-chunks/chunk-NJ5RLEFD.js +0 -26
  223. package/dist/cli-bundle/focused-chunks/chunk-QYZERYR5.js +0 -4
  224. package/dist/cli-bundle/focused-chunks/chunk-TZATVC4G.js +0 -16
  225. package/dist/cli-bundle/focused-chunks/chunk-WY3WRPVN.js +0 -2
@@ -5,7 +5,7 @@
5
5
  * for cursor-resumable cross-item mutation event reads.
6
6
  */
7
7
 
8
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="f1205949-16d8-51ce-98e8-04abc4c78ee8")}catch(e){}}();
8
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a84b308f-7eef-535c-9531-b33b0775c651")}catch(e){}}();
9
9
  import fs from "node:fs/promises";
10
10
  import path from "node:path";
11
11
  import { randomUUID } from "node:crypto";
@@ -13,9 +13,76 @@ import { createRequire } from "node:module";
13
13
  import { readHistoryEntries } from "./read.js";
14
14
  const EVENT_INDEX_FILENAME = "history-event-index.sqlite";
15
15
  const EVENT_INDEX_VERSION = "2";
16
+ const AUTHORITATIVE_HISTORY_CACHE_LIMIT = 8;
17
+ const authoritativeHistoryCaches = new Map();
16
18
  function eventIndexPath(pmRoot) {
17
19
  return path.join(pmRoot, "runtime", EVENT_INDEX_FILENAME);
18
20
  }
21
+ async function readAuthoritativeHistoryEvents(pmRoot) {
22
+ const historyRoot = path.join(pmRoot, "history");
23
+ const entries = await fs
24
+ .readdir(historyRoot, { withFileTypes: true })
25
+ .catch((error) => {
26
+ if (typeof error === "object" &&
27
+ error !== null &&
28
+ "code" in error &&
29
+ error.code === "ENOENT") {
30
+ return [];
31
+ }
32
+ throw error;
33
+ });
34
+ const cached = authoritativeHistoryCaches.get(pmRoot) ?? {
35
+ streams: new Map(),
36
+ ordered_events: [],
37
+ };
38
+ const presentStreams = new Set();
39
+ let changed = !authoritativeHistoryCaches.has(pmRoot);
40
+ for (const historyFile of entries
41
+ .filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl"))
42
+ .sort((left, right) => left.name.localeCompare(right.name))) {
43
+ const streamId = historyFile.name.slice(0, -".jsonl".length);
44
+ presentStreams.add(streamId);
45
+ const historyPath = path.join(historyRoot, historyFile.name);
46
+ const stats = await fs.stat(historyPath, { bigint: true });
47
+ const signature = [
48
+ stats.dev,
49
+ stats.ino,
50
+ stats.size,
51
+ stats.mtimeNs,
52
+ stats.ctimeNs,
53
+ ].join(":");
54
+ if (cached.streams.get(streamId)?.signature === signature)
55
+ continue;
56
+ const history = await readHistoryEntries(historyPath, streamId);
57
+ cached.streams.set(streamId, {
58
+ signature,
59
+ events: history.map((entry, streamOffset) => ({
60
+ stream_id: streamId,
61
+ stream_offset: streamOffset,
62
+ entry,
63
+ })),
64
+ });
65
+ changed = true;
66
+ }
67
+ for (const streamId of cached.streams.keys()) {
68
+ if (!presentStreams.has(streamId)) {
69
+ cached.streams.delete(streamId);
70
+ changed = true;
71
+ }
72
+ }
73
+ if (changed) {
74
+ cached.ordered_events = [...cached.streams.values()]
75
+ .flatMap((stream) => stream.events)
76
+ .sort(compareHistoryEventPosition);
77
+ }
78
+ authoritativeHistoryCaches.delete(pmRoot);
79
+ authoritativeHistoryCaches.set(pmRoot, cached);
80
+ if (authoritativeHistoryCaches.size > AUTHORITATIVE_HISTORY_CACHE_LIMIT) {
81
+ const oldestRoot = authoritativeHistoryCaches.keys().next().value;
82
+ authoritativeHistoryCaches.delete(oldestRoot);
83
+ }
84
+ return cached.ordered_events;
85
+ }
19
86
  function loadDatabaseSync(loadModule) {
20
87
  try {
21
88
  const loaded = loadModule(["node", "sqlite"].join(":"));
@@ -85,21 +152,6 @@ export async function rebuildHistoryEventIndex(pmRoot) {
85
152
  const Database = resolveDatabaseSync();
86
153
  if (!Database)
87
154
  return false;
88
- const historyRoot = path.join(pmRoot, "history");
89
- const entries = await fs
90
- .readdir(historyRoot, { withFileTypes: true })
91
- .catch((error) => {
92
- if (typeof error === "object" &&
93
- error !== null &&
94
- "code" in error &&
95
- error.code === "ENOENT") {
96
- return [];
97
- }
98
- throw error;
99
- });
100
- const historyFiles = entries
101
- .filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl"))
102
- .sort((left, right) => left.name.localeCompare(right.name));
103
155
  const targetPath = eventIndexPath(pmRoot);
104
156
  await fs.mkdir(path.dirname(targetPath), { recursive: true });
105
157
  const temporaryPath = `${targetPath}.${randomUUID()}.tmp`;
@@ -108,16 +160,8 @@ export async function rebuildHistoryEventIndex(pmRoot) {
108
160
  database = new Database(temporaryPath);
109
161
  createSchema(database);
110
162
  database.exec("BEGIN IMMEDIATE");
111
- for (const historyFile of historyFiles) {
112
- const streamId = historyFile.name.slice(0, -".jsonl".length);
113
- const history = await readHistoryEntries(path.join(historyRoot, historyFile.name), streamId);
114
- for (const [streamOffset, entry] of history.entries()) {
115
- insertEvent(database, {
116
- stream_id: streamId,
117
- stream_offset: streamOffset,
118
- entry,
119
- });
120
- }
163
+ for (const event of await readAuthoritativeHistoryEvents(pmRoot)) {
164
+ insertEvent(database, event);
121
165
  }
122
166
  database.exec("COMMIT");
123
167
  database.close();
@@ -132,6 +176,68 @@ export async function rebuildHistoryEventIndex(pmRoot) {
132
176
  throw error;
133
177
  }
134
178
  }
179
+ function matchesSet(value, accepted) {
180
+ return !accepted || accepted.length === 0 || accepted.includes(value);
181
+ }
182
+ function compareHistoryEventPosition(left, right) {
183
+ return (left.entry.ts.localeCompare(right.entry.ts) ||
184
+ left.stream_id.localeCompare(right.stream_id) ||
185
+ left.stream_offset - right.stream_offset);
186
+ }
187
+ function isInsideHistoryEventWindow(event, query) {
188
+ if (query.after_ts !== undefined &&
189
+ query.after_stream_id !== undefined &&
190
+ query.after_stream_offset !== undefined) {
191
+ if (compareHistoryEventPosition(event, {
192
+ stream_id: query.after_stream_id,
193
+ stream_offset: query.after_stream_offset,
194
+ entry: { ...event.entry, ts: query.after_ts },
195
+ }) <= 0) {
196
+ return false;
197
+ }
198
+ }
199
+ else if (query.since_ts !== undefined &&
200
+ event.entry.ts < query.since_ts) {
201
+ return false;
202
+ }
203
+ return true;
204
+ }
205
+ function matchesHistoryEventHarness(event, query) {
206
+ if (!query.harnesses || query.harnesses.length === 0)
207
+ return true;
208
+ if (query.harnesses.includes(event.entry.agent_harness ?? ""))
209
+ return true;
210
+ return (event.entry.agent_harness === undefined &&
211
+ (query.harness_alias_authors ?? []).includes(event.entry.author));
212
+ }
213
+ function matchesHistoryEventQuery(event, query) {
214
+ if (!isInsideHistoryEventWindow(event, query) ||
215
+ !matchesSet(event.entry.op, query.ops) ||
216
+ !matchesSet(event.entry.author, query.authors) ||
217
+ !matchesSet(event.entry.agent_instance ?? "", query.agent_instances) ||
218
+ !matchesSet(event.stream_id, query.stream_ids) ||
219
+ !matchesHistoryEventHarness(event, query)) {
220
+ return false;
221
+ }
222
+ return (query.provenance ?? []).every((predicate) => {
223
+ const value = event.entry.agent_provenance?.[predicate.dimension]?.value;
224
+ return value !== undefined && predicate.values.includes(value);
225
+ });
226
+ }
227
+ /**
228
+ * Query authoritative history streams when the optional SQLite accelerator is
229
+ * unavailable. The result preserves the index contract at higher read cost.
230
+ */
231
+ export async function queryHistoryEventStreams(pmRoot, query) {
232
+ const limit = Math.max(0, Math.floor(query.limit));
233
+ const rows = (await readAuthoritativeHistoryEvents(pmRoot))
234
+ .filter((event) => matchesHistoryEventQuery(event, query))
235
+ .slice(0, limit + 1);
236
+ return {
237
+ events: rows.slice(0, query.limit),
238
+ has_more: rows.length > query.limit,
239
+ };
240
+ }
135
241
  /**
136
242
  * Update an already-built event projection after one authoritative append.
137
243
  * Absence is a cheap no-op; corrupt projections are removed for lazy rebuild.
@@ -268,4 +374,4 @@ export const _testOnly = {
268
374
  },
269
375
  };
270
376
  //# sourceMappingURL=event-index.js.map
271
- //# debugId=f1205949-16d8-51ce-98e8-04abc4c78ee8
377
+ //# debugId=a84b308f-7eef-535c-9531-b33b0775c651
@@ -4,7 +4,7 @@
4
4
  * Implements append-only history and replay behavior for Replay.
5
5
  */
6
6
 
7
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="0c37a722-fda7-523e-991c-f8abfb7a1495")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="bf8797ec-53d3-5d23-8a6e-8296a027ddf2")}catch(e){}}();
8
8
  import jsonPatch from "fast-json-patch";
9
9
  import { ITEM_METADATA_KEY_ORDER } from "../shared/constants.js";
10
10
  import { canonicalDocument } from "../item/item-format.js";
@@ -123,11 +123,30 @@ export function tryApplyReplayPatch(current, patch) {
123
123
  return { ok: false, error };
124
124
  }
125
125
  }
126
+ /** Return the distinct supported hash epochs explicitly declared by a stream. */
127
+ function supportedExplicitHistoryItemHashVersions(entries) {
128
+ return [
129
+ ...new Set(entries
130
+ .map((entry) => entry.item_hash_version)
131
+ .filter((version) => version !== undefined &&
132
+ SUPPORTED_HISTORY_ITEM_HASH_VERSIONS.includes(version))),
133
+ ];
134
+ }
135
+ /** Select the hash epochs allowed for one entry under an authoritative stream marker. */
136
+ function historyEntryHashCandidates(explicitVersion, authoritativeVersion) {
137
+ if (explicitVersion !== undefined) {
138
+ return [explicitVersion];
139
+ }
140
+ return authoritativeVersion === undefined
141
+ ? [1, CURRENT_HISTORY_ITEM_HASH_VERSION]
142
+ : [authoritativeVersion];
143
+ }
126
144
  /** Deterministically verify a history chain: each entry's before_hash must equal the prior replayed after_hash, the patch must strictly apply, and the recorded after_hash must equal the replayed result. */
127
145
  /** Verify a chain and report the explicit or auto-detected item hash epoch. */
128
146
  export function verifyHistoryChainWithVersion(entries) {
129
147
  let replay = cloneEmptyReplayDocument();
130
148
  let detectedVersion;
149
+ let authoritativeExplicitVersion;
131
150
  for (let index = 0; index < entries.length; index += 1) {
132
151
  const entry = entries[index];
133
152
  const explicitVersion = entry.item_hash_version;
@@ -147,9 +166,10 @@ export function verifyHistoryChainWithVersion(entries) {
147
166
  errors: [`verify_failed:patch_apply_failed:entry_${index + 1}`],
148
167
  };
149
168
  }
150
- const candidates = explicitVersion === undefined
151
- ? [CURRENT_HISTORY_ITEM_HASH_VERSION, 1]
152
- : [explicitVersion];
169
+ // Prefer legacy epoch 1 when an unversioned entry is valid under both
170
+ // algorithms, but retain compatibility with transitional epoch-2 writers
171
+ // that shipped before item_hash_version became explicit.
172
+ const candidates = historyEntryHashCandidates(explicitVersion, authoritativeExplicitVersion);
153
173
  const beforeMatches = candidates.filter((version) => replayHash(replay, version) === entry.before_hash);
154
174
  if (beforeMatches.length === 0) {
155
175
  return {
@@ -166,6 +186,9 @@ export function verifyHistoryChainWithVersion(entries) {
166
186
  }
167
187
  replay = applied.document;
168
188
  detectedVersion = version;
189
+ if (explicitVersion !== undefined) {
190
+ authoritativeExplicitVersion = version;
191
+ }
169
192
  }
170
193
  return {
171
194
  ok: true,
@@ -228,19 +251,14 @@ export function lenientApplyReplayPatch(current, patch) {
228
251
  * irreconcilably mixed stream falls forward to the current epoch.
229
252
  */
230
253
  export function resolveHistoryRepairItemHashVersion(entries) {
254
+ const explicitVersions = supportedExplicitHistoryItemHashVersions(entries);
255
+ if (explicitVersions.length === 1) {
256
+ return explicitVersions[0];
257
+ }
231
258
  const verified = verifyHistoryChainWithVersion(entries);
232
259
  if (verified.ok && verified.item_hash_version !== undefined) {
233
260
  return verified.item_hash_version;
234
261
  }
235
- const explicitVersions = [
236
- ...new Set(entries
237
- .map((entry) => entry.item_hash_version)
238
- .filter((version) => version !== undefined &&
239
- SUPPORTED_HISTORY_ITEM_HASH_VERSIONS.includes(version))),
240
- ];
241
- if (explicitVersions.length === 1) {
242
- return explicitVersions[0];
243
- }
244
262
  if (explicitVersions.length === 0) {
245
263
  return 1;
246
264
  }
@@ -333,4 +351,4 @@ export function historyEntriesToRaw(entries) {
333
351
  return `${entries.map((entry) => JSON.stringify(entry)).join("\n")}\n`;
334
352
  }
335
353
  //# sourceMappingURL=replay.js.map
336
- //# debugId=0c37a722-fda7-523e-991c-f8abfb7a1495
354
+ //# debugId=bf8797ec-53d3-5d23-8a6e-8296a027ddf2
@@ -1,6 +1,6 @@
1
1
  import type { RuntimeFieldCommand, RuntimeFieldDefinition, RuntimeFieldType } from "../../types/index.js";
2
2
  export { normalizeStatusToken as normalizeFieldKey } from "./status-token.js";
3
- export type { RuntimeFieldCommand, RuntimeFieldDefinition, RuntimeFieldType, } from "../../types/index.js";
3
+ export type { RuntimeFieldCommand, RuntimeFieldDefinition, RuntimeFieldValueSchema, RuntimeFieldType, } from "../../types/index.js";
4
4
  /** Built-in item-metadata field names a custom field key must never shadow. A custom key that collides with one of these would let `pm create --<key>` write over reserved metadata, so add-field rejects them up front (symmetric with the built-in-type guard in item-types-file.ts). */
5
5
  export declare const BUILTIN_FIELD_KEYS: ReadonlySet<string>;
6
6
  /** The shape persisted at `.agents/pm/schema/fields.json`. */
@@ -4,7 +4,7 @@
4
4
  * Resolves configurable schema, fields, statuses, and workflows for Fields File.
5
5
  */
6
6
 
7
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="575b0c3a-9ce1-504f-a24e-d8854e898f17")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="2b5a573e-1e36-58b9-b93b-4a4088f71e9a")}catch(e){}}();
8
8
  import { RUNTIME_FIELD_COMMAND_VALUES, RUNTIME_FIELD_TYPE_VALUES, } from "../../types/index.js";
9
9
  import { ITEM_METADATA_KEY_ORDER } from "../shared/constants.js";
10
10
  import { normalizeStatusToken as normalizeFieldKey } from "./status-token.js";
@@ -286,4 +286,4 @@ function applyBooleanFlag(definition, key, value) {
286
286
  }
287
287
  }
288
288
  //# sourceMappingURL=fields-file.js.map
289
- //# debugId=575b0c3a-9ce1-504f-a24e-d8854e898f17
289
+ //# debugId=2b5a573e-1e36-58b9-b93b-4a4088f71e9a
@@ -4,10 +4,13 @@
4
4
  * Resolves configurable schema, fields, statuses, and workflows for Runtime Field Values.
5
5
  */
6
6
 
7
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="9bef4fe4-11a8-51ae-a42a-a93cc9c57384")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="91a3969e-59b3-506e-9349-ca08e73a1228")}catch(e){}}();
8
8
  import { EXIT_CODE } from "../shared/constants.js";
9
9
  import { PmCliError } from "../shared/errors.js";
10
+ import { stableStringify } from "../shared/serialization.js";
11
+ import { isRfc3339DateTime } from "../shared/time.js";
10
12
  import { runtimeFieldOptionTarget, } from "./runtime-schema.js";
13
+ const MAX_VALUE_SCHEMA_DEPTH = 16;
11
14
  function toCamelToken(value) {
12
15
  const segments = value
13
16
  .trim()
@@ -116,6 +119,109 @@ function parseJsonContainerValue(raw, label, expectedType) {
116
119
  }
117
120
  throw new PmCliError(`${label} must be valid JSON ${expectedType}`, EXIT_CODE.USAGE);
118
121
  }
122
+ function valueMatchesSchemaType(value, type) {
123
+ if (type === undefined)
124
+ return true;
125
+ if (type === "array")
126
+ return Array.isArray(value);
127
+ if (type === "object") {
128
+ return typeof value === "object" && value !== null && !Array.isArray(value);
129
+ }
130
+ return typeof value === type;
131
+ }
132
+ function runtimeFieldPrimitiveSchemaError(value, schema, path) {
133
+ if (schema.const !== undefined &&
134
+ stableStringify(value) !== stableStringify(schema.const)) {
135
+ return `${path} must equal ${stableStringify(schema.const)}`;
136
+ }
137
+ if (schema.enum !== undefined &&
138
+ !schema.enum.some((candidate) => stableStringify(candidate) === stableStringify(value))) {
139
+ return `${path} must be one of ${schema.enum.map((entry) => stableStringify(entry)).join("|")}`;
140
+ }
141
+ if (!valueMatchesSchemaType(value, schema.type)) {
142
+ return `${path} must match configured type ${String(schema.type)}`;
143
+ }
144
+ if (typeof value === "string") {
145
+ if (schema.min_length !== undefined && value.length < schema.min_length) {
146
+ return `${path} must contain at least ${schema.min_length} characters`;
147
+ }
148
+ if (schema.format === "date-time" && !isRfc3339DateTime(value)) {
149
+ return `${path} must be an RFC 3339 date-time`;
150
+ }
151
+ }
152
+ return undefined;
153
+ }
154
+ function runtimeFieldArraySchemaError(value, schema, path, depth) {
155
+ if (!Array.isArray(value))
156
+ return undefined;
157
+ if (schema.min_items !== undefined && value.length < schema.min_items) {
158
+ return `${path} must contain at least ${schema.min_items} items`;
159
+ }
160
+ if (!schema.items)
161
+ return undefined;
162
+ for (const [index, entry] of value.entries()) {
163
+ const error = runtimeFieldSchemaError(entry, schema.items, `${path}[${index}]`, depth + 1);
164
+ if (error)
165
+ return error;
166
+ }
167
+ return undefined;
168
+ }
169
+ function runtimeFieldObjectSchemaError(value, schema, path, depth) {
170
+ if (typeof value !== "object" || value === null || Array.isArray(value))
171
+ return undefined;
172
+ const record = value;
173
+ for (const key of schema.required ?? []) {
174
+ if (!Object.hasOwn(record, key))
175
+ return `${path}.${key} is required`;
176
+ }
177
+ for (const [key, entry] of Object.entries(record)) {
178
+ const propertySchema = schema.properties?.[key];
179
+ if (!propertySchema) {
180
+ if (schema.additional_properties === false)
181
+ return `${path}.${key} is not allowed`;
182
+ continue;
183
+ }
184
+ const error = runtimeFieldSchemaError(entry, propertySchema, `${path}.${key}`, depth + 1);
185
+ if (error)
186
+ return error;
187
+ }
188
+ return undefined;
189
+ }
190
+ /** Return the first semantic schema mismatch without mutating the input value. */
191
+ function runtimeFieldSchemaError(value, schema, path, depth = 0) {
192
+ if (depth > MAX_VALUE_SCHEMA_DEPTH)
193
+ return `${path} value schema exceeds maximum depth`;
194
+ const directError = runtimeFieldPrimitiveSchemaError(value, schema, path);
195
+ if (directError)
196
+ return directError;
197
+ const arrayError = runtimeFieldArraySchemaError(value, schema, path, depth);
198
+ if (arrayError)
199
+ return arrayError;
200
+ const objectError = runtimeFieldObjectSchemaError(value, schema, path, depth);
201
+ if (objectError)
202
+ return objectError;
203
+ if (!schema.one_of)
204
+ return undefined;
205
+ let matches = 0;
206
+ for (const candidate of schema.one_of) {
207
+ if (runtimeFieldSchemaError(value, candidate, path, depth + 1) === undefined) {
208
+ matches += 1;
209
+ if (matches > 1)
210
+ break;
211
+ }
212
+ }
213
+ return matches === 1
214
+ ? undefined
215
+ : `${path} must match exactly one configured schema variant`;
216
+ }
217
+ function validateRuntimeFieldValue(definition, value, label) {
218
+ if (!definition.value_schema || value === undefined)
219
+ return value;
220
+ const error = runtimeFieldSchemaError(value, definition.value_schema, label);
221
+ if (error)
222
+ throw new PmCliError(error, EXIT_CODE.USAGE);
223
+ return value;
224
+ }
119
225
  /** Implements coerce runtime field value for the public runtime surface of this module. */
120
226
  export function coerceRuntimeFieldValue(definition, rawValue, labelOverride) {
121
227
  const label = labelOverride ?? `--${definition.cli_flag}`;
@@ -126,17 +232,17 @@ export function coerceRuntimeFieldValue(definition, rawValue, labelOverride) {
126
232
  if (containerRaw === undefined) {
127
233
  return undefined;
128
234
  }
129
- return parseJsonContainerValue(containerRaw, label, definition.type);
235
+ return validateRuntimeFieldValue(definition, parseJsonContainerValue(containerRaw, label, definition.type), label);
130
236
  }
131
237
  if (definition.repeatable || definition.type === "string_array") {
132
238
  const values = normalizeStringArrayValue(rawValue);
133
239
  if (definition.type === "number") {
134
- return values.map((value) => parseNumberValue(value, label));
240
+ return validateRuntimeFieldValue(definition, values.map((value) => parseNumberValue(value, label)), label);
135
241
  }
136
242
  if (definition.type === "boolean") {
137
- return values.map((value) => parseBooleanValue(value, label));
243
+ return validateRuntimeFieldValue(definition, values.map((value) => parseBooleanValue(value, label)), label);
138
244
  }
139
- return values;
245
+ return validateRuntimeFieldValue(definition, values, label);
140
246
  }
141
247
  const scalarRaw = Array.isArray(rawValue)
142
248
  ? rawValue[rawValue.length - 1]
@@ -145,12 +251,12 @@ export function coerceRuntimeFieldValue(definition, rawValue, labelOverride) {
145
251
  return undefined;
146
252
  }
147
253
  if (definition.type === "number") {
148
- return parseNumberValue(scalarRaw, label);
254
+ return validateRuntimeFieldValue(definition, parseNumberValue(scalarRaw, label), label);
149
255
  }
150
256
  if (definition.type === "boolean") {
151
- return parseBooleanValue(scalarRaw, label);
257
+ return validateRuntimeFieldValue(definition, parseBooleanValue(scalarRaw, label), label);
152
258
  }
153
- return typeof scalarRaw === "string" ? scalarRaw : String(scalarRaw);
259
+ return validateRuntimeFieldValue(definition, typeof scalarRaw === "string" ? scalarRaw : String(scalarRaw), label);
154
260
  }
155
261
  function shouldRequireFieldOnCreate(definition, itemTypeName) {
156
262
  if (!definition.required && !definition.required_on_create) {
@@ -205,4 +311,4 @@ export function collectRuntimeUpdateFieldValues(options, fieldRegistry, commands
205
311
  return values;
206
312
  }
207
313
  //# sourceMappingURL=runtime-field-values.js.map
208
- //# debugId=9bef4fe4-11a8-51ae-a42a-a93cc9c57384
314
+ //# debugId=91a3969e-59b3-506e-9349-ca08e73a1228
@@ -1,4 +1,4 @@
1
- import type { ItemTypeDefinition, RuntimeFieldCommand, RuntimeFieldType, RuntimeSchemaSettings, RuntimeStatusDefinition, RuntimeStatusRole, RuntimeWorkflowDefinition } from "../../types/index.js";
1
+ import type { ItemTypeDefinition, RuntimeFieldCommand, RuntimeFieldValueSchema, RuntimeFieldType, RuntimeSchemaSettings, RuntimeStatusDefinition, RuntimeStatusRole, RuntimeWorkflowDefinition } from "../../types/index.js";
2
2
  export type { RuntimeFieldCommand } from "../../types/index.js";
3
3
  /** Fallback runtime schema file paths used when callers do not provide an override. */
4
4
  export declare const DEFAULT_RUNTIME_SCHEMA_FILE_PATHS: Readonly<{
@@ -50,6 +50,8 @@ export interface RuntimeFieldDefinitionResolved {
50
50
  required_types: string[];
51
51
  /** Value that configures or reports allow unset for this contract. */
52
52
  allow_unset: boolean;
53
+ /** Optional semantic JSON-value constraints enforced on create and update. */
54
+ value_schema?: RuntimeFieldValueSchema;
53
55
  }
54
56
  /** Documents the runtime status registry payload exchanged by command, SDK, and package integrations. */
55
57
  export interface RuntimeStatusRegistry {
@@ -4,7 +4,7 @@
4
4
  * Resolves configurable schema, fields, statuses, and workflows for Runtime Schema.
5
5
  */
6
6
 
7
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a9ff482a-f9f9-5e35-88d0-240db815ec08")}catch(e){}}();
7
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="88b9179f-569e-5cd6-b67c-c27431961554")}catch(e){}}();
8
8
  import path from "node:path";
9
9
  import { pathExists, readFileIfExists, writeFileAtomic, } from "../fs/fs-utils.js";
10
10
  import { DEFAULT_STATUS_DEFINITIONS, DEFAULT_WORKFLOW_DEFINITION, } from "../shared/constants.js";
@@ -32,6 +32,119 @@ const RUNTIME_STATUS_ROLE_SET = new Set(RUNTIME_STATUS_ROLE_VALUES);
32
32
  const RUNTIME_FIELD_TYPE_SET = new Set(RUNTIME_FIELD_TYPE_VALUES);
33
33
  const RUNTIME_FIELD_COMMAND_SET = new Set(RUNTIME_FIELD_COMMAND_VALUES);
34
34
  const RUNTIME_UNKNOWN_FIELD_POLICY_SET = new Set(RUNTIME_UNKNOWN_FIELD_POLICY_VALUES);
35
+ const RUNTIME_FIELD_VALUE_TYPE_SET = new Set([
36
+ "string",
37
+ "number",
38
+ "boolean",
39
+ "array",
40
+ "object",
41
+ ]);
42
+ const MAX_RUNTIME_FIELD_VALUE_SCHEMA_DEPTH = 16;
43
+ function isPlainObject(value) {
44
+ return typeof value === "object" && value !== null && !Array.isArray(value);
45
+ }
46
+ const RUNTIME_FIELD_VALUE_SCHEMA_SHAPE_VALIDATORS = [
47
+ (schema) => schema.type === undefined ||
48
+ (typeof schema.type === "string" &&
49
+ RUNTIME_FIELD_VALUE_TYPE_SET.has(schema.type)),
50
+ (schema) => !Object.hasOwn(schema, "const") || isStructuredJsonValue(schema.const),
51
+ (schema) => schema.enum === undefined ||
52
+ (Array.isArray(schema.enum) &&
53
+ schema.enum.every((entry) => isStructuredJsonValue(entry))),
54
+ (schema) => schema.min_length === undefined ||
55
+ (Number.isInteger(schema.min_length) &&
56
+ schema.min_length >= 0),
57
+ (schema) => schema.min_items === undefined ||
58
+ (Number.isInteger(schema.min_items) && schema.min_items >= 0),
59
+ (schema) => schema.format === undefined || schema.format === "date-time",
60
+ (schema) => schema.required === undefined ||
61
+ (Array.isArray(schema.required) &&
62
+ schema.required.every((entry) => typeof entry === "string" && entry.length > 0)),
63
+ (schema) => schema.additional_properties === undefined ||
64
+ typeof schema.additional_properties === "boolean",
65
+ (schema) => schema.properties === undefined ||
66
+ isPlainObject(schema.properties),
67
+ (schema) => schema.one_of === undefined ||
68
+ (Array.isArray(schema.one_of) && schema.one_of.length > 0),
69
+ ];
70
+ function isStructuredJsonValue(value, ancestors = new Set()) {
71
+ if (value === null ||
72
+ typeof value === "string" ||
73
+ typeof value === "boolean") {
74
+ return true;
75
+ }
76
+ if (typeof value === "number")
77
+ return Number.isFinite(value);
78
+ if (typeof value !== "object" || ancestors.has(value))
79
+ return false;
80
+ ancestors.add(value);
81
+ const valid = Array.isArray(value)
82
+ ? value.every((entry) => isStructuredJsonValue(entry, ancestors))
83
+ : Object.values(value).every((entry) => isStructuredJsonValue(entry, ancestors));
84
+ ancestors.delete(value);
85
+ return valid;
86
+ }
87
+ /** Normalize an untrusted recursive custom-field value schema or reject it. */
88
+ function normalizeRuntimeFieldValueSchema(value, depth = 0, ancestors = new Set()) {
89
+ if (!isPlainObject(value) ||
90
+ depth > MAX_RUNTIME_FIELD_VALUE_SCHEMA_DEPTH ||
91
+ ancestors.has(value)) {
92
+ return undefined;
93
+ }
94
+ ancestors.add(value);
95
+ const schema = value;
96
+ const type = schema.type;
97
+ const enumValues = schema.enum;
98
+ const oneOf = schema.one_of;
99
+ const properties = schema.properties;
100
+ const items = schema.items;
101
+ const required = schema.required;
102
+ const minLength = schema.min_length;
103
+ const minItems = schema.min_items;
104
+ const format = schema.format;
105
+ const additionalProperties = schema.additional_properties;
106
+ if (!RUNTIME_FIELD_VALUE_SCHEMA_SHAPE_VALIDATORS.every((check) => check(schema))) {
107
+ ancestors.delete(value);
108
+ return undefined;
109
+ }
110
+ const normalizedProperties = properties === undefined
111
+ ? undefined
112
+ : Object.fromEntries(Object.entries(properties).map(([key, entry]) => [
113
+ key,
114
+ normalizeRuntimeFieldValueSchema(entry, depth + 1, ancestors),
115
+ ]));
116
+ const normalizedItems = items === undefined
117
+ ? undefined
118
+ : normalizeRuntimeFieldValueSchema(items, depth + 1, ancestors);
119
+ const normalizedOneOf = oneOf === undefined
120
+ ? undefined
121
+ : oneOf.map((entry) => normalizeRuntimeFieldValueSchema(entry, depth + 1, ancestors));
122
+ const normalizedNestedSchemas = [
123
+ ...Object.values(normalizedProperties ?? {}),
124
+ ...(items === undefined ? [] : [normalizedItems]),
125
+ ...(normalizedOneOf ?? []),
126
+ ];
127
+ if (normalizedNestedSchemas.some((entry) => entry === undefined)) {
128
+ ancestors.delete(value);
129
+ return undefined;
130
+ }
131
+ ancestors.delete(value);
132
+ return Object.fromEntries(Object.entries({
133
+ type: type,
134
+ const: Object.hasOwn(schema, "const") ? schema.const : undefined,
135
+ enum: enumValues,
136
+ min_length: minLength,
137
+ min_items: minItems,
138
+ format: format,
139
+ properties: normalizedProperties,
140
+ required: required === undefined
141
+ ? undefined
142
+ : [...new Set(required)],
143
+ additional_properties: additionalProperties,
144
+ items: normalizedItems,
145
+ one_of: normalizedOneOf,
146
+ }).filter(([, entry]) => entry !== undefined));
147
+ }
35
148
  function normalizeStringList(values) {
36
149
  const candidates = Array.isArray(values) ? values : [];
37
150
  return [
@@ -202,6 +315,12 @@ function normalizeRuntimeFieldDefinition(definition) {
202
315
  })();
203
316
  const description = definition.description?.trim();
204
317
  const requiredTypes = normalizeStringList(definition.required_types);
318
+ const valueSchema = definition.value_schema === undefined
319
+ ? undefined
320
+ : normalizeRuntimeFieldValueSchema(definition.value_schema);
321
+ if (definition.value_schema !== undefined && valueSchema === undefined) {
322
+ return null;
323
+ }
205
324
  return {
206
325
  key,
207
326
  metadata_key: metadataKey,
@@ -210,11 +329,12 @@ function normalizeRuntimeFieldDefinition(definition) {
210
329
  description: description && description.length > 0 ? description : undefined,
211
330
  type,
212
331
  commands,
213
- repeatable: definition.repeatable === true || type === "string_array",
332
+ repeatable: [definition.repeatable === true, type === "string_array"].includes(true),
214
333
  required: definition.required === true,
215
334
  required_on_create: definition.required_on_create === true,
216
335
  required_types: requiredTypes,
217
336
  allow_unset: definition.allow_unset !== false,
337
+ value_schema: valueSchema,
218
338
  };
219
339
  }
220
340
  function normalizeRuntimeStatusDefinitions(statusesSource) {
@@ -277,6 +397,7 @@ function serializeRuntimeFieldDefinition(definition) {
277
397
  ? [...definition.required_types]
278
398
  : undefined,
279
399
  allow_unset: definition.allow_unset === false ? false : undefined,
400
+ value_schema: definition.value_schema,
280
401
  };
281
402
  }
282
403
  function normalizeUnknownFieldPolicy(policy) {
@@ -691,4 +812,4 @@ export function statusIsTerminal(status, registry) {
691
812
  return registry.terminal_statuses.has(normalized);
692
813
  }
693
814
  //# sourceMappingURL=runtime-schema.js.map
694
- //# debugId=a9ff482a-f9f9-5e35-88d0-240db815ec08
815
+ //# debugId=88b9179f-569e-5cd6-b67c-c27431961554