@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.
- package/.claude-plugin/marketplace.json +2 -2
- package/AGENTS.md +4 -3
- package/CHANGELOG.md +65 -6
- package/README.md +16 -16
- package/dist/cli/error-guidance.js +7 -4
- package/dist/cli/public.d.ts +2 -0
- package/dist/cli/public.js +21 -0
- package/dist/cli/register-list-query.js +132 -51
- package/dist/cli/register-mutation.js +4 -4
- package/dist/cli-bundle/bundle-manifest.json +148 -148
- package/dist/cli-bundle/chunks/chunk-5UZZAJKR.js +3 -0
- package/dist/cli-bundle/chunks/chunk-ALDJGKAK.js +2 -0
- package/dist/cli-bundle/chunks/chunk-BSK2IN3C.js +8 -0
- package/dist/cli-bundle/chunks/chunk-DY4DMUMC.js +197 -0
- package/dist/cli-bundle/chunks/{chunk-73UGEBRS.js → chunk-GNFAFIJI.js} +10 -10
- package/dist/cli-bundle/chunks/chunk-L5Q2CLPE.js +35 -0
- package/dist/cli-bundle/chunks/{chunk-RIS565OA.js → chunk-PVRUN5ZS.js} +5 -5
- package/dist/cli-bundle/chunks/chunk-RZQTVMRQ.js +2 -0
- package/dist/cli-bundle/chunks/chunk-SARFF5H5.js +3 -0
- package/dist/cli-bundle/chunks/chunk-UYCLQVL2.js +13 -0
- package/dist/cli-bundle/chunks/{chunk-URW4QRMI.js → chunk-YRGOLZA7.js} +59 -55
- package/dist/cli-bundle/chunks/register-list-query-I23LALEE.js +11 -0
- package/dist/cli-bundle/chunks/register-mutation-6YGU3GPH.js +20 -0
- package/dist/cli-bundle/chunks/{register-operations-KGDNRMCL.js → register-operations-4HTFC6J7.js} +2 -2
- package/dist/cli-bundle/chunks/{register-setup-NVXBOD5I.js → register-setup-PPPEF3SN.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-2PN4TJXH.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-C7JUBRFP.js → chunk-3TKWMNKF.js} +3 -3
- package/dist/cli-bundle/focused-chunks/chunk-5AF3ZBNB.js +155 -0
- package/dist/cli-bundle/focused-chunks/chunk-C5IW2NDA.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-CODV5LUT.js → chunk-EUEGXZ2A.js} +3 -3
- package/dist/cli-bundle/focused-chunks/{chunk-4JPEBFFC.js → chunk-EX6MKP2X.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-SSNDYZTM.js → chunk-K43KKAFS.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-6ROKKT4X.js → chunk-M62NO7EI.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-T46KLOIB.js → chunk-ODOVCP45.js} +47 -47
- package/dist/cli-bundle/focused-chunks/chunk-RNDNMARJ.js +16 -0
- package/dist/cli-bundle/focused-chunks/chunk-TN4AE665.js +26 -0
- package/dist/cli-bundle/focused-chunks/chunk-TVV2DONO.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-UI6AJ5TD.js +4 -0
- package/dist/cli-bundle/focused-chunks/chunk-V5XTGLK7.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-WGHQVU6P.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-XYNBXTDQ.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-Z56ECT7I.js +2 -0
- package/dist/cli-bundle/main.js +7 -7
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +1 -1
- package/dist/cli-bundle/sdk-core.js +31 -31
- package/dist/cli-bundle/sdk-governance.js +1 -1
- package/dist/cli-bundle/sdk-graph.js +1 -1
- package/dist/cli-bundle/sdk-merge.js +32 -32
- package/dist/cli-bundle/sdk-query.js +1 -1
- package/dist/cli-bundle/sdk-runtime.js +1 -1
- package/dist/cli-bundle/sdk-testing.js +1 -1
- package/dist/cli-bundle/sdk.js +4 -2
- package/dist/core/config/nested-settings.js +8 -2
- package/dist/core/extensions/exporter-output-contract.d.ts +12 -0
- package/dist/core/extensions/exporter-output-contract.js +67 -0
- package/dist/core/extensions/extension-types.d.ts +36 -1
- package/dist/core/extensions/extension-types.js +2 -2
- package/dist/core/extensions/loader.js +43 -48
- package/dist/core/extensions/manifest-schema.d.ts +20 -0
- package/dist/core/extensions/manifest-schema.js +52 -0
- package/dist/core/history/event-index.d.ts +5 -0
- package/dist/core/history/event-index.js +133 -27
- package/dist/core/history/replay.js +32 -14
- package/dist/core/schema/fields-file.d.ts +1 -1
- package/dist/core/schema/fields-file.js +2 -2
- package/dist/core/schema/runtime-field-values.js +115 -9
- package/dist/core/schema/runtime-schema.d.ts +3 -1
- package/dist/core/schema/runtime-schema.js +124 -3
- package/dist/core/sentry/helpers.js +3 -3
- package/dist/core/shared/constants.js +5 -2
- package/dist/core/shared/errors.d.ts +10 -0
- package/dist/core/shared/errors.js +2 -2
- package/dist/core/shared/time.d.ts +2 -0
- package/dist/core/shared/time.js +27 -2
- package/dist/core/store/settings-validator.d.ts +4 -0
- package/dist/core/store/settings-validator.js +3 -2
- package/dist/core/store/settings.js +8 -2
- package/dist/mcp/tool-definitions.js +12 -7
- package/dist/sdk/agent/refusal-reachability.d.ts +46 -6
- package/dist/sdk/agent/refusal-reachability.js +350 -8
- package/dist/sdk/authoring.d.ts +1 -1
- package/dist/sdk/authoring.js +2 -2
- package/dist/sdk/cli-bootstrap.d.ts +2 -2
- package/dist/sdk/cli-bootstrap.js +4 -4
- package/dist/sdk/cli-contracts/command-aliases.d.ts +34 -2
- package/dist/sdk/cli-contracts/command-aliases.js +64 -13
- package/dist/sdk/cli-contracts/enum-contracts.d.ts +4 -0
- package/dist/sdk/cli-contracts/enum-contracts.js +9 -2
- package/dist/sdk/cli-contracts/flag-contracts.js +11 -7
- package/dist/sdk/cli-contracts/grammar-contracts.d.ts +70 -0
- package/dist/sdk/cli-contracts/grammar-contracts.js +345 -0
- package/dist/sdk/cli-contracts/runtime-contracts.d.ts +19 -2
- package/dist/sdk/cli-contracts/runtime-contracts.js +61 -14
- package/dist/sdk/cli-contracts/tool-parameter-tables.js +10 -4
- package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
- package/dist/sdk/cli-contracts/tool-schema.js +20 -11
- package/dist/sdk/cli-contracts.d.ts +2 -2
- package/dist/sdk/cli-contracts.js +4 -4
- package/dist/sdk/completion.js +13 -15
- package/dist/sdk/compose.d.ts +9 -5
- package/dist/sdk/compose.js +33 -3
- package/dist/sdk/context-intent-contracts.d.ts +8 -4
- package/dist/sdk/context-intent-contracts.js +76 -31
- package/dist/sdk/contracts.d.ts +1 -0
- package/dist/sdk/contracts.js +3 -2
- package/dist/sdk/core.d.ts +1 -1
- package/dist/sdk/core.js +3 -3
- package/dist/sdk/dependency-flag-validation.d.ts +9 -2
- package/dist/sdk/dependency-flag-validation.js +101 -35
- package/dist/sdk/environment/host-environment-errors.js +25 -7
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +78 -14
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +62 -14
- package/dist/sdk/governance/assurance-action.d.ts +3 -7
- package/dist/sdk/governance/assurance-action.js +97 -2
- package/dist/sdk/governance/boundary-fixtures.d.ts +86 -0
- package/dist/sdk/governance/boundary-fixtures.js +201 -0
- package/dist/sdk/governance/defect-recurrence-signals.d.ts +10 -0
- package/dist/sdk/governance/defect-recurrence-signals.js +25 -0
- package/dist/sdk/governance/defect-recurrence.d.ts +242 -0
- package/dist/sdk/governance/defect-recurrence.js +523 -0
- package/dist/sdk/governance.d.ts +2 -0
- package/dist/sdk/governance.js +4 -2
- package/dist/sdk/graph/governance.js +3 -2
- package/dist/sdk/guide-topics.js +193 -4
- package/dist/sdk/index.d.ts +7 -3
- package/dist/sdk/index.js +8 -5
- package/dist/sdk/init-agent-guidance.js +3 -3
- package/dist/sdk/lifecycle/create.js +4 -2
- package/dist/sdk/lifecycle/update-many.js +15 -2
- package/dist/sdk/lifecycle/update.js +5 -2
- package/dist/sdk/linked-artifacts.js +63 -14
- package/dist/sdk/mutation-events.d.ts +18 -2
- package/dist/sdk/mutation-events.js +59 -16
- package/dist/sdk/output.d.ts +44 -0
- package/dist/sdk/output.js +28 -2
- package/dist/sdk/query/complete-list.d.ts +94 -0
- package/dist/sdk/query/complete-list.js +173 -0
- package/dist/sdk/query/get.d.ts +2 -0
- package/dist/sdk/query/get.js +32 -7
- package/dist/sdk/query/list.js +5 -2
- package/dist/sdk/query.d.ts +1 -0
- package/dist/sdk/query.js +3 -2
- package/dist/sdk/read-output-contracts.d.ts +2 -2
- package/dist/sdk/read-output-contracts.js +36 -9
- package/dist/sdk/relationship-analytics.js +90 -14
- package/dist/sdk/relationship-kinds/contract.d.ts +4 -0
- package/dist/sdk/relationship-kinds/contract.js +3 -2
- package/dist/sdk/relationships.d.ts +1 -1
- package/dist/sdk/relationships.js +11 -2
- package/dist/sdk/runtime-extended-actions.js +10 -2
- package/dist/sdk/runtime.d.ts +8 -2
- package/dist/sdk/runtime.js +11 -8
- package/dist/sdk/schema.d.ts +1 -0
- package/dist/sdk/schema.js +2 -2
- package/dist/sdk/test/execution.d.ts +6 -2
- package/dist/sdk/test/execution.js +39 -13
- package/dist/types.d.ts +34 -2
- package/dist/types.js +4 -2
- package/docs/AGENT_GUIDE.md +25 -9
- package/docs/CLI_GRAMMAR.md +106 -0
- package/docs/COMMANDS.md +23 -23
- package/docs/CONFIGURATION.md +58 -1
- package/docs/CONTEXT_INTEGRITY_CONTRACTS.md +58 -0
- package/docs/DEFECT_RECURRENCE.md +134 -0
- package/docs/DEPENDENCY_KIND_CONTRACT.md +12 -2
- package/docs/ONBOARDING.md +4 -4
- package/docs/QUICKSTART.md +1 -1
- package/docs/README.md +4 -0
- package/docs/RELATIONSHIP_GRAPH.md +16 -2
- package/docs/RELEASING.md +30 -6
- package/docs/SDK.md +74 -7
- package/docs/SDK_ARTIFACT_OUTPUT.md +72 -0
- package/docs/SDK_CONTEXT_COORDINATION.md +46 -12
- package/docs/SDK_RUNTIME_BOUNDARIES.md +9 -1
- package/docs/TESTING.md +18 -1
- package/docs/TRUSTWORTHY_CONTEXT_EVIDENCE.md +1 -1
- package/docs/agent-task-token-baseline.json +5 -5
- package/marketplace.json +2 -2
- package/package.json +8 -5
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-claude/skills/pm-audit/SKILL.md +26 -0
- package/plugins/pm-claude/skills/pm-developer/SKILL.md +26 -0
- package/plugins/pm-claude/skills/pm-planner/SKILL.md +26 -0
- package/plugins/pm-claude/skills/pm-release/SKILL.md +26 -0
- package/plugins/pm-claude/skills/pm-workflow/SKILL.md +26 -0
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/pm-codex/skills/pm-auditor/SKILL.md +26 -0
- package/plugins/pm-codex/skills/pm-native/SKILL.md +26 -0
- package/plugins/pm-codex/skills/pm-release/SKILL.md +26 -0
- package/sdk/public-surface.json +1243 -54
- package/dist/cli-bundle/chunks/chunk-47OQEOQB.js +0 -2
- package/dist/cli-bundle/chunks/chunk-HCIYWD6M.js +0 -35
- package/dist/cli-bundle/chunks/chunk-I2LUWWR5.js +0 -3
- package/dist/cli-bundle/chunks/chunk-IC5W2T34.js +0 -2
- package/dist/cli-bundle/chunks/chunk-OAC5NIV4.js +0 -3
- package/dist/cli-bundle/chunks/chunk-PANOOBIS.js +0 -13
- package/dist/cli-bundle/chunks/chunk-TK6N7HGM.js +0 -8
- package/dist/cli-bundle/chunks/chunk-TOFG2URD.js +0 -197
- package/dist/cli-bundle/chunks/register-list-query-EYCXHZAG.js +0 -10
- package/dist/cli-bundle/chunks/register-mutation-7YVIG54J.js +0 -20
- package/dist/cli-bundle/focused-chunks/chunk-5NMFSX26.js +0 -155
- package/dist/cli-bundle/focused-chunks/chunk-725JSCMP.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-DQ6SMTBF.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-HAACPSUW.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-J4UFYHOD.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-JX5Z344Q.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-L4RDYBIQ.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-NJ5RLEFD.js +0 -26
- package/dist/cli-bundle/focused-chunks/chunk-QYZERYR5.js +0 -4
- package/dist/cli-bundle/focused-chunks/chunk-TZATVC4G.js +0 -16
- 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]="
|
|
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
|
|
112
|
-
|
|
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=
|
|
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]="
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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=
|
|
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]="
|
|
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=
|
|
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]="
|
|
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=
|
|
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]="
|
|
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
|
|
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=
|
|
815
|
+
//# debugId=88b9179f-569e-5cd6-b67c-c27431961554
|