@kbediako/codex-orchestrator 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +238 -0
  3. package/dist/bin/codex-orchestrator.js +507 -0
  4. package/dist/orchestrator/src/agents/builder.js +16 -0
  5. package/dist/orchestrator/src/agents/index.js +4 -0
  6. package/dist/orchestrator/src/agents/planner.js +17 -0
  7. package/dist/orchestrator/src/agents/reviewer.js +13 -0
  8. package/dist/orchestrator/src/agents/tester.js +13 -0
  9. package/dist/orchestrator/src/cli/adapters/CommandBuilder.js +20 -0
  10. package/dist/orchestrator/src/cli/adapters/CommandPlanner.js +164 -0
  11. package/dist/orchestrator/src/cli/adapters/CommandReviewer.js +32 -0
  12. package/dist/orchestrator/src/cli/adapters/CommandTester.js +33 -0
  13. package/dist/orchestrator/src/cli/adapters/index.js +4 -0
  14. package/dist/orchestrator/src/cli/config/userConfig.js +28 -0
  15. package/dist/orchestrator/src/cli/doctor.js +48 -0
  16. package/dist/orchestrator/src/cli/events/runEvents.js +84 -0
  17. package/dist/orchestrator/src/cli/exec/command.js +56 -0
  18. package/dist/orchestrator/src/cli/exec/context.js +108 -0
  19. package/dist/orchestrator/src/cli/exec/experience.js +77 -0
  20. package/dist/orchestrator/src/cli/exec/finalization.js +140 -0
  21. package/dist/orchestrator/src/cli/exec/learning.js +62 -0
  22. package/dist/orchestrator/src/cli/exec/stageRunner.js +71 -0
  23. package/dist/orchestrator/src/cli/exec/summary.js +109 -0
  24. package/dist/orchestrator/src/cli/exec/telemetry.js +18 -0
  25. package/dist/orchestrator/src/cli/exec/tfgrpo.js +200 -0
  26. package/dist/orchestrator/src/cli/exec/tfgrpoArtifacts.js +19 -0
  27. package/dist/orchestrator/src/cli/exec/types.js +1 -0
  28. package/dist/orchestrator/src/cli/init.js +64 -0
  29. package/dist/orchestrator/src/cli/mcp.js +124 -0
  30. package/dist/orchestrator/src/cli/metrics/metricsAggregator.js +404 -0
  31. package/dist/orchestrator/src/cli/metrics/metricsRecorder.js +138 -0
  32. package/dist/orchestrator/src/cli/orchestrator.js +554 -0
  33. package/dist/orchestrator/src/cli/pipelines/defaultDiagnostics.js +32 -0
  34. package/dist/orchestrator/src/cli/pipelines/designReference.js +72 -0
  35. package/dist/orchestrator/src/cli/pipelines/hiFiDesignToolkit.js +71 -0
  36. package/dist/orchestrator/src/cli/pipelines/index.js +34 -0
  37. package/dist/orchestrator/src/cli/run/environment.js +24 -0
  38. package/dist/orchestrator/src/cli/run/manifest.js +367 -0
  39. package/dist/orchestrator/src/cli/run/manifestPersister.js +88 -0
  40. package/dist/orchestrator/src/cli/run/runPaths.js +30 -0
  41. package/dist/orchestrator/src/cli/selfCheck.js +12 -0
  42. package/dist/orchestrator/src/cli/services/commandRunner.js +420 -0
  43. package/dist/orchestrator/src/cli/services/controlPlaneService.js +107 -0
  44. package/dist/orchestrator/src/cli/services/execRuntime.js +69 -0
  45. package/dist/orchestrator/src/cli/services/pipelineResolver.js +47 -0
  46. package/dist/orchestrator/src/cli/services/runPreparation.js +82 -0
  47. package/dist/orchestrator/src/cli/services/runSummaryWriter.js +35 -0
  48. package/dist/orchestrator/src/cli/services/schedulerService.js +42 -0
  49. package/dist/orchestrator/src/cli/tasks/taskMetadata.js +19 -0
  50. package/dist/orchestrator/src/cli/telemetry/schema.js +8 -0
  51. package/dist/orchestrator/src/cli/types.js +1 -0
  52. package/dist/orchestrator/src/cli/ui/HudApp.js +112 -0
  53. package/dist/orchestrator/src/cli/ui/controller.js +26 -0
  54. package/dist/orchestrator/src/cli/ui/store.js +240 -0
  55. package/dist/orchestrator/src/cli/utils/enforcementMode.js +12 -0
  56. package/dist/orchestrator/src/cli/utils/fs.js +8 -0
  57. package/dist/orchestrator/src/cli/utils/interactive.js +25 -0
  58. package/dist/orchestrator/src/cli/utils/jsonlWriter.js +10 -0
  59. package/dist/orchestrator/src/cli/utils/optionalDeps.js +30 -0
  60. package/dist/orchestrator/src/cli/utils/packageInfo.js +25 -0
  61. package/dist/orchestrator/src/cli/utils/planFormatter.js +49 -0
  62. package/dist/orchestrator/src/cli/utils/runId.js +7 -0
  63. package/dist/orchestrator/src/cli/utils/specGuardRunner.js +26 -0
  64. package/dist/orchestrator/src/cli/utils/strings.js +8 -0
  65. package/dist/orchestrator/src/cli/utils/time.js +6 -0
  66. package/dist/orchestrator/src/control-plane/drift-reporter.js +109 -0
  67. package/dist/orchestrator/src/control-plane/index.js +3 -0
  68. package/dist/orchestrator/src/control-plane/request-builder.js +217 -0
  69. package/dist/orchestrator/src/control-plane/types.js +1 -0
  70. package/dist/orchestrator/src/control-plane/validator.js +50 -0
  71. package/dist/orchestrator/src/credentials/CredentialBroker.js +1 -0
  72. package/dist/orchestrator/src/events/EventBus.js +25 -0
  73. package/dist/orchestrator/src/learning/crystalizer.js +108 -0
  74. package/dist/orchestrator/src/learning/harvester.js +146 -0
  75. package/dist/orchestrator/src/learning/manifest.js +56 -0
  76. package/dist/orchestrator/src/learning/runner.js +177 -0
  77. package/dist/orchestrator/src/learning/validator.js +164 -0
  78. package/dist/orchestrator/src/logger.js +20 -0
  79. package/dist/orchestrator/src/manager.js +388 -0
  80. package/dist/orchestrator/src/persistence/ArtifactStager.js +95 -0
  81. package/dist/orchestrator/src/persistence/ExperienceStore.js +210 -0
  82. package/dist/orchestrator/src/persistence/PersistenceCoordinator.js +65 -0
  83. package/dist/orchestrator/src/persistence/RunManifestWriter.js +23 -0
  84. package/dist/orchestrator/src/persistence/TaskStateStore.js +172 -0
  85. package/dist/orchestrator/src/persistence/identifierGuards.js +1 -0
  86. package/dist/orchestrator/src/persistence/lockFile.js +26 -0
  87. package/dist/orchestrator/src/persistence/sanitizeIdentifier.js +26 -0
  88. package/dist/orchestrator/src/persistence/sanitizeRunId.js +8 -0
  89. package/dist/orchestrator/src/persistence/sanitizeTaskId.js +8 -0
  90. package/dist/orchestrator/src/persistence/writeAtomicFile.js +4 -0
  91. package/dist/orchestrator/src/privacy/guard.js +111 -0
  92. package/dist/orchestrator/src/scheduler/index.js +1 -0
  93. package/dist/orchestrator/src/scheduler/plan.js +171 -0
  94. package/dist/orchestrator/src/scheduler/types.js +1 -0
  95. package/dist/orchestrator/src/sync/CloudRunsClient.js +1 -0
  96. package/dist/orchestrator/src/sync/CloudRunsHttpClient.js +82 -0
  97. package/dist/orchestrator/src/sync/CloudSyncWorker.js +206 -0
  98. package/dist/orchestrator/src/sync/createCloudSyncWorker.js +15 -0
  99. package/dist/orchestrator/src/types.js +1 -0
  100. package/dist/orchestrator/src/utils/atomicWrite.js +15 -0
  101. package/dist/orchestrator/src/utils/errorMessage.js +14 -0
  102. package/dist/orchestrator/src/utils/executionMode.js +69 -0
  103. package/dist/packages/control-plane-schemas/src/index.js +1 -0
  104. package/dist/packages/control-plane-schemas/src/run-request.js +548 -0
  105. package/dist/packages/orchestrator/src/exec/handle-service.js +203 -0
  106. package/dist/packages/orchestrator/src/exec/session-manager.js +147 -0
  107. package/dist/packages/orchestrator/src/exec/unified-exec.js +432 -0
  108. package/dist/packages/orchestrator/src/index.js +3 -0
  109. package/dist/packages/orchestrator/src/instructions/loader.js +101 -0
  110. package/dist/packages/orchestrator/src/instructions/promptPacks.js +151 -0
  111. package/dist/packages/orchestrator/src/notifications/index.js +74 -0
  112. package/dist/packages/orchestrator/src/telemetry/otel-exporter.js +142 -0
  113. package/dist/packages/orchestrator/src/tool-orchestrator.js +161 -0
  114. package/dist/packages/sdk-node/src/orchestrator.js +195 -0
  115. package/dist/packages/shared/config/designConfig.js +495 -0
  116. package/dist/packages/shared/config/env.js +37 -0
  117. package/dist/packages/shared/config/index.js +2 -0
  118. package/dist/packages/shared/design-artifacts/writer.js +221 -0
  119. package/dist/packages/shared/events/serializer.js +84 -0
  120. package/dist/packages/shared/events/types.js +1 -0
  121. package/dist/packages/shared/manifest/artifactUtils.js +36 -0
  122. package/dist/packages/shared/manifest/designArtifacts.js +665 -0
  123. package/dist/packages/shared/manifest/fileIO.js +29 -0
  124. package/dist/packages/shared/manifest/toolRuns.js +78 -0
  125. package/dist/packages/shared/manifest/toolkitArtifacts.js +223 -0
  126. package/dist/packages/shared/manifest/types.js +5 -0
  127. package/dist/packages/shared/manifest/validator.js +73 -0
  128. package/dist/packages/shared/manifest/writer.js +2 -0
  129. package/dist/packages/shared/streams/stdio.js +112 -0
  130. package/dist/scripts/design/pipeline/advanced-assets.js +466 -0
  131. package/dist/scripts/design/pipeline/componentize.js +74 -0
  132. package/dist/scripts/design/pipeline/context.js +34 -0
  133. package/dist/scripts/design/pipeline/extract.js +249 -0
  134. package/dist/scripts/design/pipeline/optionalDeps.js +107 -0
  135. package/dist/scripts/design/pipeline/prepare.js +46 -0
  136. package/dist/scripts/design/pipeline/reference.js +94 -0
  137. package/dist/scripts/design/pipeline/state.js +206 -0
  138. package/dist/scripts/design/pipeline/toolkit/common.js +94 -0
  139. package/dist/scripts/design/pipeline/toolkit/extract.js +258 -0
  140. package/dist/scripts/design/pipeline/toolkit/publish.js +202 -0
  141. package/dist/scripts/design/pipeline/toolkit/publishActions.js +12 -0
  142. package/dist/scripts/design/pipeline/toolkit/reference.js +846 -0
  143. package/dist/scripts/design/pipeline/toolkit/snapshot.js +882 -0
  144. package/dist/scripts/design/pipeline/toolkit/tokens.js +456 -0
  145. package/dist/scripts/design/pipeline/visual-regression.js +137 -0
  146. package/dist/scripts/design/pipeline/write-artifacts.js +61 -0
  147. package/package.json +97 -0
  148. package/schemas/manifest.json +1064 -0
  149. package/templates/README.md +12 -0
  150. package/templates/codex/mcp-client.json +8 -0
@@ -0,0 +1,78 @@
1
+ import { loadJsonManifest, resolveIO, writeJsonManifest } from './fileIO.js';
2
+ export function sanitizeToolRunRecord(record) {
3
+ const { metadata, ...rest } = record;
4
+ const sanitized = {
5
+ ...rest,
6
+ metadata: metadata ? { ...metadata } : undefined,
7
+ events: record.events ? record.events.map(sanitizeToolRunEvent) : undefined
8
+ };
9
+ if (!sanitized.id) {
10
+ throw new Error('Tool run record must include an id.');
11
+ }
12
+ if (!sanitized.tool) {
13
+ throw new Error(`Tool run ${sanitized.id} must include a tool identifier.`);
14
+ }
15
+ if (sanitized.retryCount < 0) {
16
+ sanitized.retryCount = 0;
17
+ }
18
+ if (sanitized.attemptCount < 1) {
19
+ sanitized.attemptCount = 1;
20
+ }
21
+ return sanitized;
22
+ }
23
+ export function sanitizeToolRunEvent(event) {
24
+ if (!event.timestamp) {
25
+ throw new Error('Tool run events must include a timestamp.');
26
+ }
27
+ if (!event.correlationId) {
28
+ throw new Error('Tool run events must include a correlationId.');
29
+ }
30
+ if (event.attempt < 1) {
31
+ throw new Error('Tool run events must reference an attempt greater than or equal to 1.');
32
+ }
33
+ switch (event.type) {
34
+ case 'exec:begin':
35
+ return { ...event };
36
+ case 'exec:chunk': {
37
+ const sequence = event.sequence ?? null;
38
+ if (sequence === null || sequence < 1) {
39
+ throw new Error('exec:chunk events must have a positive sequence value.');
40
+ }
41
+ const bytes = event.bytes ?? null;
42
+ if (bytes !== null && bytes < 0) {
43
+ throw new Error('exec:chunk events must report a non-negative byte count.');
44
+ }
45
+ return { ...event, sequence, bytes };
46
+ }
47
+ case 'exec:end':
48
+ return { ...event };
49
+ case 'exec:retry': {
50
+ const delayMs = event.delayMs ?? null;
51
+ if (delayMs !== null && delayMs < 0) {
52
+ throw new Error('exec:retry events must report a non-negative delay.');
53
+ }
54
+ return { ...event, delayMs };
55
+ }
56
+ default:
57
+ return { ...event };
58
+ }
59
+ }
60
+ export function mergeToolRunRecord(manifest, record) {
61
+ const sanitized = sanitizeToolRunRecord(record);
62
+ const existingRuns = Array.isArray(manifest.toolRuns) ? [...manifest.toolRuns] : [];
63
+ const index = existingRuns.findIndex((entry) => entry.id === sanitized.id);
64
+ if (index >= 0) {
65
+ existingRuns[index] = { ...existingRuns[index], ...sanitized };
66
+ }
67
+ else {
68
+ existingRuns.push(sanitized);
69
+ }
70
+ return { ...manifest, toolRuns: existingRuns };
71
+ }
72
+ export async function persistToolRunRecord(manifestPath, record, options = {}) {
73
+ const io = resolveIO(options);
74
+ const manifest = (await loadJsonManifest(manifestPath, io)) ?? {};
75
+ const merged = mergeToolRunRecord(manifest, record);
76
+ await writeJsonManifest(manifestPath, merged, io);
77
+ return merged.toolRuns.find((entry) => entry.id === record.id);
78
+ }
@@ -0,0 +1,223 @@
1
+ import { sanitizeRelativeArtifactPath } from './artifactUtils.js';
2
+ const TOOLKIT_STAGE_SET = new Set([
3
+ 'extract',
4
+ 'tokens',
5
+ 'styleguide',
6
+ 'reference',
7
+ 'self-correct',
8
+ 'publish'
9
+ ]);
10
+ const MAX_TOOLKIT_ARTIFACTS = 200;
11
+ export function mergeToolkitArtifacts(existing, incoming) {
12
+ const sanitizedExisting = existing
13
+ .map((entry) => {
14
+ try {
15
+ return sanitizeToolkitArtifactRecord(entry);
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ })
21
+ .filter((entry) => entry !== null);
22
+ const merged = [...sanitizedExisting];
23
+ for (const record of incoming) {
24
+ const sanitized = sanitizeToolkitArtifactRecord(record);
25
+ const index = merged.findIndex((entry) => entry.id === sanitized.id &&
26
+ entry.stage === sanitized.stage &&
27
+ entry.relative_path === sanitized.relative_path);
28
+ if (index >= 0) {
29
+ merged[index] = { ...merged[index], ...sanitized };
30
+ }
31
+ else {
32
+ merged.push(sanitized);
33
+ }
34
+ }
35
+ if (merged.length > MAX_TOOLKIT_ARTIFACTS) {
36
+ return merged.slice(-MAX_TOOLKIT_ARTIFACTS);
37
+ }
38
+ return merged;
39
+ }
40
+ export function sanitizeToolkitArtifactRecord(record) {
41
+ if (!record || typeof record !== 'object') {
42
+ throw new Error('design toolkit artifact record must be an object');
43
+ }
44
+ const input = record;
45
+ const id = coerceToolkitString(input.id);
46
+ if (!id) {
47
+ throw new Error('design toolkit artifact record must include an id');
48
+ }
49
+ const stage = input.stage;
50
+ if (typeof stage !== 'string' || !TOOLKIT_STAGE_SET.has(stage)) {
51
+ throw new Error(`invalid design toolkit artifact stage '${String(stage)}'`);
52
+ }
53
+ const status = input.status;
54
+ if (status !== 'succeeded' && status !== 'failed' && status !== 'skipped') {
55
+ throw new Error(`invalid design toolkit artifact status '${String(status)}'`);
56
+ }
57
+ const path = coerceToolkitString(input.relative_path);
58
+ if (!path) {
59
+ throw new Error('design toolkit artifact must include a relative_path string');
60
+ }
61
+ const artifact = {
62
+ id,
63
+ stage: stage,
64
+ status: status,
65
+ relative_path: sanitizeRelativeArtifactPath(path)
66
+ };
67
+ const description = coerceToolkitString(input.description);
68
+ if (description) {
69
+ artifact.description = description;
70
+ }
71
+ if (Array.isArray(input.approvals)) {
72
+ const approvals = input.approvals
73
+ .map((entry) => sanitizeApprovalRecord(entry))
74
+ .filter((entry) => Boolean(entry));
75
+ if (approvals.length > 0) {
76
+ artifact.approvals = approvals;
77
+ }
78
+ }
79
+ if (input.retention && typeof input.retention === 'object') {
80
+ const retention = input.retention;
81
+ const days = coerceNumber(retention.days, null, {
82
+ min: 1
83
+ });
84
+ const expiry = coerceToolkitString(retention.expiry);
85
+ if (days !== null && expiry) {
86
+ artifact.retention = {
87
+ days,
88
+ autoPurge: coerceBoolean(retention.auto_purge ?? retention.autoPurge, false),
89
+ expiry,
90
+ policy: coerceToolkitString(retention.policy) ?? undefined
91
+ };
92
+ }
93
+ }
94
+ if (input.metrics && typeof input.metrics === 'object') {
95
+ artifact.metrics = sanitizeToolkitMetrics(input.metrics);
96
+ }
97
+ if (Array.isArray(input.privacy_notes)) {
98
+ const notes = input.privacy_notes
99
+ .map((value) => (typeof value === 'string' ? value.trim() : ''))
100
+ .filter((value) => value.length > 0);
101
+ if (notes.length > 0) {
102
+ artifact.privacy_notes = notes.slice(0, 10);
103
+ }
104
+ }
105
+ return artifact;
106
+ }
107
+ export function sanitizeToolkitSummary(summary) {
108
+ const stages = (summary.stages ?? []).map((entry) => {
109
+ const artifacts = Number(entry.artifacts ?? 0);
110
+ return {
111
+ stage: entry.stage,
112
+ artifacts: Number.isFinite(artifacts) && artifacts >= 0 ? artifacts : 0,
113
+ metrics: sanitizeToolkitMetrics(entry.metrics),
114
+ notes: Array.isArray(entry.notes)
115
+ ? entry.notes.map((note) => (typeof note === 'string' ? note.trim() : '')).filter((note) => note.length > 0)
116
+ : undefined
117
+ };
118
+ });
119
+ const approvals = Array.isArray(summary.approvals)
120
+ ? summary.approvals
121
+ .map((value) => (typeof value === 'string' ? value.trim() : ''))
122
+ .filter((value) => value.length > 0)
123
+ : undefined;
124
+ return {
125
+ generated_at: coerceToolkitString(summary.generated_at) ?? new Date().toISOString(),
126
+ stages,
127
+ totals: sanitizeToolkitNumericMetrics(summary.totals),
128
+ approvals
129
+ };
130
+ }
131
+ export function sanitizeToolkitMetrics(record) {
132
+ if (!record || typeof record !== 'object') {
133
+ return undefined;
134
+ }
135
+ const sanitized = {};
136
+ for (const [key, value] of Object.entries(record)) {
137
+ if (!key) {
138
+ continue;
139
+ }
140
+ if (typeof value === 'number' && Number.isFinite(value)) {
141
+ sanitized[key] = value;
142
+ }
143
+ else if (typeof value === 'string') {
144
+ const trimmed = value.trim();
145
+ if (trimmed.length > 0) {
146
+ sanitized[key] = trimmed;
147
+ }
148
+ }
149
+ }
150
+ return Object.keys(sanitized).length > 0 ? sanitized : undefined;
151
+ }
152
+ export function sanitizeToolkitNumericMetrics(record) {
153
+ if (!record || typeof record !== 'object') {
154
+ return undefined;
155
+ }
156
+ const sanitized = {};
157
+ for (const [key, value] of Object.entries(record)) {
158
+ if (!key) {
159
+ continue;
160
+ }
161
+ if (typeof value === 'number' && Number.isFinite(value)) {
162
+ sanitized[key] = value;
163
+ }
164
+ }
165
+ return Object.keys(sanitized).length > 0 ? sanitized : undefined;
166
+ }
167
+ function sanitizeApprovalRecord(entry) {
168
+ if (!entry || typeof entry !== 'object') {
169
+ return null;
170
+ }
171
+ const record = entry;
172
+ const id = typeof record.id === 'string' ? record.id.trim() : '';
173
+ const actor = typeof record.actor === 'string' ? record.actor.trim() : '';
174
+ const reason = typeof record.reason === 'string' ? record.reason.trim() : '';
175
+ const timestamp = typeof record.timestamp === 'string' ? record.timestamp.trim() : '';
176
+ if (!id || !actor || !timestamp) {
177
+ return null;
178
+ }
179
+ return {
180
+ id,
181
+ actor,
182
+ reason,
183
+ timestamp
184
+ };
185
+ }
186
+ function coerceToolkitString(value) {
187
+ if (typeof value === 'string') {
188
+ const trimmed = value.trim();
189
+ return trimmed.length > 0 ? trimmed : null;
190
+ }
191
+ return null;
192
+ }
193
+ function coerceNumber(value, fallback, options = {}) {
194
+ if (typeof value === 'number' && Number.isFinite(value)) {
195
+ const numeric = options.min !== undefined ? Math.max(options.min, value) : value;
196
+ return numeric;
197
+ }
198
+ if (typeof value === 'string' && value.trim().length > 0) {
199
+ const parsed = Number(value);
200
+ if (Number.isFinite(parsed)) {
201
+ return options.min !== undefined ? Math.max(options.min, parsed) : parsed;
202
+ }
203
+ }
204
+ return fallback ?? null;
205
+ }
206
+ function coerceBoolean(value, fallback) {
207
+ if (typeof value === 'boolean') {
208
+ return value;
209
+ }
210
+ if (typeof value === 'number') {
211
+ return value !== 0;
212
+ }
213
+ if (typeof value === 'string') {
214
+ const normalized = value.trim().toLowerCase();
215
+ if (['true', '1', 'yes', 'on'].includes(normalized)) {
216
+ return true;
217
+ }
218
+ if (['false', '0', 'no', 'off'].includes(normalized)) {
219
+ return false;
220
+ }
221
+ }
222
+ return fallback;
223
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * THIS FILE IS AUTO-GENERATED.
3
+ * Run "npm run generate:manifest-types" after editing schemas/manifest.json.
4
+ */
5
+ export {};
@@ -0,0 +1,73 @@
1
+ import { readFileSync, existsSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import { dirname, join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { Ajv as AjvCtor } from 'ajv';
6
+ const ajv = new AjvCtor({
7
+ allErrors: true,
8
+ strict: false
9
+ });
10
+ const manifestSchema = loadManifestSchema();
11
+ const validate = ajv.compile(manifestSchema);
12
+ export const manifestValidator = validate;
13
+ export function getManifestSchema() {
14
+ return manifestSchema;
15
+ }
16
+ export function validateManifest(candidate) {
17
+ const valid = validate(candidate);
18
+ if (valid) {
19
+ return { valid: true, errors: [], value: candidate };
20
+ }
21
+ return {
22
+ valid: false,
23
+ errors: formatErrors(validate.errors),
24
+ value: null
25
+ };
26
+ }
27
+ function formatErrors(errors) {
28
+ if (!errors || errors.length === 0) {
29
+ return ['Unknown validation error'];
30
+ }
31
+ return errors.map((error) => {
32
+ if (error.instancePath) {
33
+ return `${error.instancePath}: ${error.message ?? 'invalid value'}`;
34
+ }
35
+ if (error.schemaPath) {
36
+ return `${error.schemaPath}: ${error.message ?? 'invalid value'}`;
37
+ }
38
+ return error.message ?? 'invalid value';
39
+ });
40
+ }
41
+ export function resolveManifestSchemaPath(options = {}) {
42
+ if (!options.skipImports) {
43
+ const require = createRequire(import.meta.url);
44
+ try {
45
+ return require.resolve('#co/schema/manifest');
46
+ }
47
+ catch {
48
+ // fall through to fallback
49
+ }
50
+ }
51
+ return resolveManifestSchemaPathFallback(options.fromUrl ?? import.meta.url);
52
+ }
53
+ function resolveManifestSchemaPathFallback(fromUrl) {
54
+ let current = dirname(fileURLToPath(fromUrl));
55
+ while (current) {
56
+ const candidate = join(current, 'package.json');
57
+ if (existsSync(candidate)) {
58
+ return join(current, 'schemas', 'manifest.json');
59
+ }
60
+ const parent = dirname(current);
61
+ if (parent === current) {
62
+ current = null;
63
+ continue;
64
+ }
65
+ current = parent;
66
+ }
67
+ throw new Error('Unable to locate schemas/manifest.json');
68
+ }
69
+ function loadManifestSchema() {
70
+ const schemaPath = resolveManifestSchemaPath();
71
+ const raw = readFileSync(schemaPath, 'utf8');
72
+ return JSON.parse(raw);
73
+ }
@@ -0,0 +1,2 @@
1
+ export { sanitizeToolRunRecord, sanitizeToolRunEvent, mergeToolRunRecord, persistToolRunRecord } from './toolRuns.js';
2
+ export { persistDesignManifest } from './designArtifacts.js';
@@ -0,0 +1,112 @@
1
+ import { Buffer } from 'node:buffer';
2
+ /**
3
+ * Creates a tracker that sequences stdout/stderr chunks while maintaining
4
+ * bounded in-memory buffers for each stream.
5
+ */
6
+ export function createStdioTracker(options = {}) {
7
+ const encoding = options.encoding ?? 'utf-8';
8
+ const maxBufferBytes = options.maxBufferBytes ?? 64 * 1024;
9
+ const now = options.now ?? (() => new Date());
10
+ let sequence = Math.max(0, options.startSequence ?? 0);
11
+ const buffers = {
12
+ stdout: createInternalBuffer(),
13
+ stderr: createInternalBuffer()
14
+ };
15
+ const push = (stream, chunk) => {
16
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding);
17
+ const target = buffers[stream];
18
+ appendWithLimit(target, buffer, maxBufferBytes);
19
+ const chunkRecord = {
20
+ sequence: ++sequence,
21
+ stream,
22
+ bytes: buffer.byteLength,
23
+ data: buffer.toString(encoding),
24
+ timestamp: now().toISOString()
25
+ };
26
+ return chunkRecord;
27
+ };
28
+ const getBuffered = (stream) => {
29
+ const target = buffers[stream];
30
+ if (target.byteLength === 0) {
31
+ return '';
32
+ }
33
+ const activeChunks = snapshotChunks(target);
34
+ if (activeChunks.length === 0) {
35
+ return '';
36
+ }
37
+ if (activeChunks.length === 1) {
38
+ return activeChunks[0].toString(encoding);
39
+ }
40
+ return Buffer.concat(activeChunks, target.byteLength).toString(encoding);
41
+ };
42
+ const getBufferedBytes = (stream) => buffers[stream].byteLength;
43
+ const reset = () => {
44
+ buffers.stdout = createInternalBuffer();
45
+ buffers.stderr = createInternalBuffer();
46
+ sequence = Math.max(0, options.startSequence ?? 0);
47
+ };
48
+ return {
49
+ push,
50
+ getBuffered,
51
+ getBufferedBytes,
52
+ reset
53
+ };
54
+ }
55
+ function createInternalBuffer() {
56
+ return { chunks: [], start: 0, byteLength: 0 };
57
+ }
58
+ function snapshotChunks(buffer) {
59
+ if (buffer.start === 0) {
60
+ return buffer.chunks;
61
+ }
62
+ return buffer.chunks.slice(buffer.start);
63
+ }
64
+ function appendWithLimit(target, incoming, limit) {
65
+ if (limit <= 0) {
66
+ target.chunks = [];
67
+ target.start = 0;
68
+ target.byteLength = 0;
69
+ return;
70
+ }
71
+ if (!incoming || incoming.byteLength === 0) {
72
+ trimExcess(target, limit);
73
+ return;
74
+ }
75
+ if (incoming.byteLength >= limit) {
76
+ const trimmed = Buffer.from(incoming.subarray(incoming.byteLength - limit));
77
+ target.chunks = [trimmed];
78
+ target.start = 0;
79
+ target.byteLength = trimmed.byteLength;
80
+ return;
81
+ }
82
+ target.chunks.push(incoming);
83
+ target.byteLength += incoming.byteLength;
84
+ trimExcess(target, limit);
85
+ }
86
+ function trimExcess(target, limit) {
87
+ if (target.byteLength <= limit) {
88
+ return;
89
+ }
90
+ let startIndex = target.start;
91
+ const chunks = target.chunks;
92
+ while (target.byteLength > limit && startIndex < chunks.length) {
93
+ const head = chunks[startIndex];
94
+ if (!head) {
95
+ break;
96
+ }
97
+ const overflow = target.byteLength - limit;
98
+ if (head.byteLength <= overflow) {
99
+ target.byteLength -= head.byteLength;
100
+ startIndex += 1;
101
+ continue;
102
+ }
103
+ chunks[startIndex] = head.subarray(overflow);
104
+ target.byteLength -= overflow;
105
+ break;
106
+ }
107
+ target.start = startIndex;
108
+ if (target.start > 0 && target.start * 2 >= chunks.length) {
109
+ target.chunks = chunks.slice(target.start);
110
+ target.start = 0;
111
+ }
112
+ }