@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,164 @@
1
+ import { PLANNER_EXECUTION_MODE_PARSER, resolveRequiresCloudPolicy } from '../../utils/executionMode.js';
2
+ export class CommandPlanner {
3
+ pipeline;
4
+ options;
5
+ cachedPlan = null;
6
+ constructor(pipeline, options = {}) {
7
+ this.pipeline = pipeline;
8
+ this.options = options;
9
+ }
10
+ async plan(task) {
11
+ void task;
12
+ if (!this.cachedPlan) {
13
+ const items = this.pipeline.stages.map((stage, index) => this.buildPlanItem(stage, index));
14
+ const targetId = this.resolveTargetId(items);
15
+ const normalizedItems = items.map((item) => ({
16
+ ...item,
17
+ selected: item.id === targetId
18
+ }));
19
+ this.cachedPlan = {
20
+ items: normalizedItems,
21
+ notes: this.pipeline.description,
22
+ targetId: targetId ?? null
23
+ };
24
+ }
25
+ return clonePlanResult(this.cachedPlan);
26
+ }
27
+ buildPlanItem(stage, index) {
28
+ const stagePlanHints = extractStagePlanHints(stage);
29
+ const requiresCloud = resolveStageRequiresCloud(stage, stagePlanHints);
30
+ const runnable = resolveStageRunnable(stagePlanHints);
31
+ const metadata = {
32
+ pipelineId: this.pipeline.id,
33
+ stageId: stage.id,
34
+ stageKind: stage.kind,
35
+ index
36
+ };
37
+ if (stagePlanHints.aliases.length > 0) {
38
+ metadata.aliases = stagePlanHints.aliases;
39
+ }
40
+ if (stagePlanHints.defaultTarget) {
41
+ metadata.defaultTarget = true;
42
+ }
43
+ if (stagePlanHints.executionMode) {
44
+ metadata.executionMode = stagePlanHints.executionMode;
45
+ }
46
+ metadata.requiresCloud = requiresCloud;
47
+ return {
48
+ id: `${this.pipeline.id}:${stage.id}`,
49
+ description: stage.title,
50
+ requires_cloud: requiresCloud,
51
+ requiresCloud,
52
+ runnable,
53
+ metadata
54
+ };
55
+ }
56
+ resolveTargetId(items) {
57
+ const explicit = this.normalizeTargetId(this.options.targetStageId ?? null, items);
58
+ if (explicit) {
59
+ return explicit;
60
+ }
61
+ const flagged = items.find((item) => item.metadata?.defaultTarget === true);
62
+ if (flagged) {
63
+ return flagged.id;
64
+ }
65
+ const runnableItems = items.filter((item) => item.runnable !== false);
66
+ if (runnableItems.length > 0) {
67
+ return runnableItems[0].id;
68
+ }
69
+ return items[0]?.id ?? null;
70
+ }
71
+ normalizeTargetId(candidate, items) {
72
+ if (!candidate) {
73
+ return null;
74
+ }
75
+ const exact = items.find((item) => item.id === candidate);
76
+ if (exact) {
77
+ return exact.id;
78
+ }
79
+ const normalized = candidate.includes(':') ? candidate.split(':').pop() ?? candidate : candidate;
80
+ const lowerNormalized = normalized.toLowerCase();
81
+ for (const item of items) {
82
+ const stageId = item.metadata?.stageId?.toLowerCase();
83
+ if (stageId && stageId === lowerNormalized) {
84
+ return item.id;
85
+ }
86
+ const aliases = Array.isArray(item.metadata?.aliases)
87
+ ? item.metadata?.aliases
88
+ : [];
89
+ if (aliases.some((alias) => alias.toLowerCase() === lowerNormalized)) {
90
+ return item.id;
91
+ }
92
+ }
93
+ return null;
94
+ }
95
+ }
96
+ function extractStagePlanHints(stage) {
97
+ const stageRecord = stage;
98
+ const planConfig = stageRecord.plan ?? {};
99
+ const aliases = Array.isArray(planConfig.aliases)
100
+ ? planConfig.aliases.map((alias) => String(alias))
101
+ : [];
102
+ const defaultTarget = Boolean(planConfig.defaultTarget ?? planConfig.default ?? planConfig.primary);
103
+ const requiresCloud = typeof planConfig.requiresCloud === 'boolean'
104
+ ? planConfig.requiresCloud
105
+ : typeof planConfig.requires_cloud === 'boolean'
106
+ ? planConfig.requires_cloud
107
+ : undefined;
108
+ const rawExecutionMode = typeof planConfig.executionMode === 'string'
109
+ ? planConfig.executionMode
110
+ : typeof stageRecord.executionMode === 'string'
111
+ ? stageRecord.executionMode
112
+ : typeof stageRecord.execution_mode === 'string'
113
+ ? stageRecord.execution_mode
114
+ : typeof stageRecord.mode === 'string'
115
+ ? stageRecord.mode
116
+ : undefined;
117
+ const executionMode = typeof rawExecutionMode === 'string'
118
+ ? rawExecutionMode.trim().toLowerCase() || null
119
+ : null;
120
+ return {
121
+ runnable: planConfig.runnable,
122
+ defaultTarget,
123
+ aliases,
124
+ requiresCloud,
125
+ executionMode
126
+ };
127
+ }
128
+ function resolveStageRequiresCloud(stage, hints) {
129
+ const stageRecord = stage;
130
+ const requiresCloud = resolveRequiresCloudPolicy({
131
+ boolFlags: [
132
+ hints.requiresCloud,
133
+ typeof stageRecord.requires_cloud === 'boolean'
134
+ ? stageRecord.requires_cloud
135
+ : undefined,
136
+ typeof stageRecord.requiresCloud === 'boolean'
137
+ ? stageRecord.requiresCloud
138
+ : undefined
139
+ ],
140
+ metadata: {
141
+ executionMode: hints.executionMode ?? null,
142
+ mode: null
143
+ },
144
+ metadataOrder: ['executionMode'],
145
+ parseMode: PLANNER_EXECUTION_MODE_PARSER
146
+ });
147
+ return requiresCloud ?? false;
148
+ }
149
+ function resolveStageRunnable(hints) {
150
+ if (typeof hints.runnable === 'boolean') {
151
+ return hints.runnable;
152
+ }
153
+ return true;
154
+ }
155
+ function clonePlanResult(plan) {
156
+ return {
157
+ items: plan.items.map((item) => ({
158
+ ...item,
159
+ metadata: item.metadata ? { ...item.metadata } : undefined
160
+ })),
161
+ notes: plan.notes,
162
+ targetId: plan.targetId ?? null
163
+ };
164
+ }
@@ -0,0 +1,32 @@
1
+ export class CommandReviewer {
2
+ getResult;
3
+ constructor(getResult) {
4
+ this.getResult = getResult;
5
+ }
6
+ async review(input) {
7
+ void input;
8
+ const result = this.requireResult();
9
+ const summaryLines = [
10
+ result.success
11
+ ? 'Diagnostics pipeline succeeded.'
12
+ : 'Diagnostics pipeline failed — inspect manifest for details.',
13
+ `Manifest: ${result.manifestPath}`,
14
+ `Runner log: ${result.logPath}`
15
+ ];
16
+ const summary = summaryLines.join('\n');
17
+ return {
18
+ summary,
19
+ decision: {
20
+ approved: result.success,
21
+ feedback: result.notes.join('\n') || undefined
22
+ }
23
+ };
24
+ }
25
+ requireResult() {
26
+ const result = this.getResult();
27
+ if (!result) {
28
+ throw new Error('Pipeline result unavailable during reviewer stage.');
29
+ }
30
+ return result;
31
+ }
32
+ }
@@ -0,0 +1,33 @@
1
+ import { ensureGuardrailStatus } from '../run/manifest.js';
2
+ export class CommandTester {
3
+ getResult;
4
+ constructor(getResult) {
5
+ this.getResult = getResult;
6
+ }
7
+ async test(input) {
8
+ const result = this.requireResult();
9
+ const guardrailStatus = ensureGuardrailStatus(result.manifest);
10
+ const reports = [
11
+ {
12
+ name: 'guardrails',
13
+ status: guardrailStatus.present ? 'passed' : 'failed',
14
+ details: guardrailStatus.present
15
+ ? guardrailStatus.summary
16
+ : guardrailStatus.recommendation ?? guardrailStatus.summary
17
+ }
18
+ ];
19
+ return {
20
+ subtaskId: input.build.subtaskId,
21
+ success: guardrailStatus.present && result.success,
22
+ reports,
23
+ runId: input.runId
24
+ };
25
+ }
26
+ requireResult() {
27
+ const result = this.getResult();
28
+ if (!result) {
29
+ throw new Error('Pipeline result unavailable during tester stage.');
30
+ }
31
+ return result;
32
+ }
33
+ }
@@ -0,0 +1,4 @@
1
+ export { CommandPlanner } from './CommandPlanner.js';
2
+ export { CommandBuilder } from './CommandBuilder.js';
3
+ export { CommandTester } from './CommandTester.js';
4
+ export { CommandReviewer } from './CommandReviewer.js';
@@ -0,0 +1,28 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { logger } from '../../logger.js';
4
+ export async function loadUserConfig(env) {
5
+ const configPath = join(env.repoRoot, 'codex.orchestrator.json');
6
+ try {
7
+ const raw = await readFile(configPath, 'utf8');
8
+ const parsed = JSON.parse(raw);
9
+ logger.info(`[codex-config] Loaded user config from ${configPath}`);
10
+ if (parsed && Array.isArray(parsed.pipelines)) {
11
+ return parsed;
12
+ }
13
+ return parsed ?? null;
14
+ }
15
+ catch (error) {
16
+ if (error.code === 'ENOENT') {
17
+ logger.warn(`[codex-config] Missing codex.orchestrator.json at ${configPath}`);
18
+ return null;
19
+ }
20
+ throw error;
21
+ }
22
+ }
23
+ export function findPipeline(config, id) {
24
+ if (!config?.pipelines) {
25
+ return null;
26
+ }
27
+ return config.pipelines.find((pipeline) => pipeline.id === id) ?? null;
28
+ }
@@ -0,0 +1,48 @@
1
+ import process from 'node:process';
2
+ import { resolveOptionalDependency } from './utils/optionalDeps.js';
3
+ const OPTIONAL_DEPENDENCIES = [
4
+ {
5
+ name: 'playwright',
6
+ install: 'npm install --save-dev playwright && npx playwright install'
7
+ },
8
+ { name: 'pngjs', install: 'npm install --save-dev pngjs' },
9
+ { name: 'pixelmatch', install: 'npm install --save-dev pixelmatch' },
10
+ { name: 'cheerio', install: 'npm install --save-dev cheerio' }
11
+ ];
12
+ export function runDoctor(cwd = process.cwd()) {
13
+ const dependencies = OPTIONAL_DEPENDENCIES.map((entry) => {
14
+ const resolved = resolveOptionalDependency(entry.name, cwd);
15
+ if (resolved.path) {
16
+ return { name: entry.name, status: 'ok', source: resolved.source };
17
+ }
18
+ return {
19
+ name: entry.name,
20
+ status: 'missing',
21
+ source: null,
22
+ install: entry.install
23
+ };
24
+ });
25
+ const missing = dependencies.filter((dep) => dep.status === 'missing').map((dep) => dep.name);
26
+ return {
27
+ status: missing.length === 0 ? 'ok' : 'warning',
28
+ missing,
29
+ dependencies
30
+ };
31
+ }
32
+ export function formatDoctorSummary(result) {
33
+ const lines = [];
34
+ lines.push(`Status: ${result.status}`);
35
+ for (const dep of result.dependencies) {
36
+ if (dep.status === 'ok') {
37
+ const source = dep.source ? ` (${dep.source})` : '';
38
+ lines.push(` - ${dep.name}: ok${source}`);
39
+ }
40
+ else {
41
+ lines.push(` - ${dep.name}: missing`);
42
+ if (dep.install) {
43
+ lines.push(` install: ${dep.install}`);
44
+ }
45
+ }
46
+ }
47
+ return lines;
48
+ }
@@ -0,0 +1,84 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { isoTimestamp } from '../utils/time.js';
3
+ export class RunEventEmitter {
4
+ emitter = new EventEmitter({ captureRejections: false });
5
+ emit(event) {
6
+ this.emitter.emit(event.type, event);
7
+ this.emitter.emit('*', event);
8
+ }
9
+ on(type, listener) {
10
+ const wrapped = (payload) => listener(payload);
11
+ this.emitter.on(type, wrapped);
12
+ return () => {
13
+ this.emitter.off(type, wrapped);
14
+ };
15
+ }
16
+ dispose() {
17
+ this.emitter.removeAllListeners();
18
+ }
19
+ }
20
+ export class RunEventPublisher {
21
+ emitter;
22
+ context;
23
+ constructor(emitter, context) {
24
+ this.emitter = emitter;
25
+ this.context = context;
26
+ }
27
+ runStarted(stages, status) {
28
+ this.emit('run:started', {
29
+ pipelineId: this.context.pipelineId,
30
+ pipelineTitle: this.context.pipelineTitle,
31
+ manifestPath: this.context.manifestPath,
32
+ logPath: this.context.logPath,
33
+ status,
34
+ stages
35
+ });
36
+ }
37
+ stageStarted(payload) {
38
+ this.emit('stage:started', payload);
39
+ }
40
+ stageCompleted(payload) {
41
+ this.emit('stage:completed', payload);
42
+ }
43
+ runCompleted(payload) {
44
+ this.emit('run:completed', payload);
45
+ }
46
+ runError(payload) {
47
+ this.emit('run:error', payload);
48
+ }
49
+ log(payload) {
50
+ this.emit('log', payload);
51
+ }
52
+ toolCall(payload) {
53
+ this.emit('tool:call', payload);
54
+ }
55
+ emit(type, payload) {
56
+ if (!this.emitter) {
57
+ return;
58
+ }
59
+ const base = {
60
+ type: type,
61
+ timestamp: isoTimestamp(),
62
+ taskId: this.context.taskId,
63
+ runId: this.context.runId
64
+ };
65
+ const event = { ...base, ...payload };
66
+ this.emitter.emit(event);
67
+ }
68
+ }
69
+ export function snapshotStages(manifest, pipeline) {
70
+ return manifest.commands.map((command) => {
71
+ const stage = pipeline.stages[command.index - 1];
72
+ return {
73
+ index: command.index,
74
+ id: command.id,
75
+ title: command.title,
76
+ kind: stage?.kind === 'subpipeline' ? 'subpipeline' : 'command',
77
+ status: command.status,
78
+ summary: command.summary,
79
+ exitCode: command.exit_code,
80
+ logPath: command.log_path,
81
+ subRunId: command.sub_run_id
82
+ };
83
+ });
84
+ }
@@ -0,0 +1,56 @@
1
+ import { serializeRunSummaryEvent } from '../../../../packages/shared/events/serializer.js';
2
+ import { appendMetricsEntry } from '../metrics/metricsRecorder.js';
3
+ import { resolveTfgrpoContext } from './tfgrpo.js';
4
+ import { bootstrapExecContext } from './context.js';
5
+ import { runExecStage } from './stageRunner.js';
6
+ import { finalizeCommandLifecycle } from './finalization.js';
7
+ import { handleTfgrpoArtifacts } from './tfgrpoArtifacts.js';
8
+ import { buildExecRunSummary, createRunSummaryPayload, emitCommandError, persistRunOutputs, renderRunOutput } from './summary.js';
9
+ import { deliverNotifications, flushTelemetry, recordSummaryTelemetry, shutdownSinks } from './telemetry.js';
10
+ import { maybeTriggerLearning } from './learning.js';
11
+ export async function executeExecCommand(context, invocation) {
12
+ if (!invocation.command) {
13
+ throw new Error('exec command requires a command to run.');
14
+ }
15
+ const runContext = await bootstrapExecContext(context, invocation);
16
+ const stageResult = await runExecStage(runContext);
17
+ const finalization = await finalizeCommandLifecycle(runContext, stageResult);
18
+ const tfgrpoContext = resolveTfgrpoContext();
19
+ const runMetricSummary = await handleTfgrpoArtifacts(runContext, finalization, tfgrpoContext);
20
+ const summaryPayload = createRunSummaryPayload({
21
+ env: runContext.env,
22
+ paths: runContext.paths,
23
+ manifest: runContext.manifest,
24
+ runStatus: finalization.runStatus,
25
+ shellCommand: runContext.shellCommand,
26
+ argv: runContext.argv,
27
+ resultSummary: finalization.summarySnapshot,
28
+ toolRecord: finalization.toolRecord,
29
+ execEvents: runContext.execEvents,
30
+ exitCode: finalization.exitCode,
31
+ signal: finalization.signal,
32
+ notificationTargets: runContext.notificationSink.targets,
33
+ cwd: runContext.stage.cwd ?? null,
34
+ metrics: runMetricSummary
35
+ });
36
+ const summaryEvent = serializeRunSummaryEvent(summaryPayload);
37
+ await deliverNotifications(runContext, summaryPayload, summaryEvent);
38
+ recordSummaryTelemetry(runContext, summaryEvent);
39
+ renderRunOutput(runContext, summaryPayload, summaryEvent);
40
+ await flushTelemetry(runContext);
41
+ await persistRunOutputs(runContext, summaryEvent);
42
+ await appendMetricsEntry(runContext.env, runContext.paths, runContext.manifest, runContext.persister);
43
+ await maybeTriggerLearning(runContext, finalization.runStatus);
44
+ await runContext.persister.flush();
45
+ await shutdownSinks(runContext);
46
+ emitCommandError(runContext, finalization.commandError);
47
+ return buildExecRunSummary({
48
+ manifest: runContext.manifest,
49
+ summaryPayload,
50
+ summaryEvent,
51
+ shellCommand: runContext.shellCommand,
52
+ argv: runContext.argv,
53
+ events: runContext.execEvents,
54
+ toolRecord: finalization.toolRecord
55
+ });
56
+ }
@@ -0,0 +1,108 @@
1
+ import process from 'node:process';
2
+ import { bootstrapManifest } from '../run/manifest.js';
3
+ import { generateRunId } from '../utils/runId.js';
4
+ import { JsonlWriter } from '../utils/jsonlWriter.js';
5
+ import { ExperienceStore } from '../../persistence/ExperienceStore.js';
6
+ import { createTelemetrySink } from '../../../../packages/orchestrator/src/telemetry/otel-exporter.js';
7
+ import { createNotificationSink } from '../../../../packages/orchestrator/src/notifications/index.js';
8
+ import { ManifestPersister, persistManifest } from '../run/manifestPersister.js';
9
+ export async function bootstrapExecContext(context, invocation) {
10
+ const argv = [invocation.command, ...(invocation.args ?? [])];
11
+ const shellCommand = buildShellCommand(argv);
12
+ const stdout = context.stdout ?? process.stdout;
13
+ const stderr = context.stderr ?? process.stderr;
14
+ const outputMode = invocation.outputMode;
15
+ if (outputMode === 'interactive') {
16
+ stdout.write(`$ ${shellCommand}\n`);
17
+ }
18
+ const runIdFactory = context.runIdFactory ?? generateRunId;
19
+ const env = context.env;
20
+ const experienceStore = new ExperienceStore({
21
+ outDir: env.outRoot,
22
+ runsDir: env.runsRoot
23
+ });
24
+ const runId = runIdFactory();
25
+ const pipeline = createPipeline(shellCommand, invocation, env);
26
+ const stage = pipeline.stages[0];
27
+ const { manifest, paths } = await bootstrapManifest(runId, {
28
+ env,
29
+ pipeline,
30
+ taskSlug: null,
31
+ approvalPolicy: null
32
+ });
33
+ const persister = new ManifestPersister({
34
+ manifest,
35
+ paths,
36
+ persistIntervalMs: Math.max(1000, manifest.heartbeat_interval_seconds * 1000)
37
+ });
38
+ manifest.status = 'in_progress';
39
+ await persistManifest(paths, manifest, persister, { force: true });
40
+ const telemetrySink = context.telemetrySink ?? createTelemetrySink({
41
+ endpoint: invocation.otelEndpoint,
42
+ enabled: Boolean(invocation.otelEndpoint)
43
+ });
44
+ const envNotifications = parseNotificationEnv(process.env.CODEX_ORCHESTRATOR_NOTIFY);
45
+ const notificationSink = context.notificationSink ??
46
+ createNotificationSink({
47
+ targets: invocation.notifyTargets,
48
+ envTargets: envNotifications
49
+ });
50
+ const jsonlWriter = outputMode === 'jsonl' ? new JsonlWriter(stdout) : null;
51
+ return {
52
+ env,
53
+ invocation,
54
+ argv,
55
+ shellCommand,
56
+ outputMode,
57
+ stdout,
58
+ stderr,
59
+ runId,
60
+ pipeline,
61
+ stage,
62
+ manifest,
63
+ paths,
64
+ persister,
65
+ telemetrySink,
66
+ notificationSink,
67
+ jsonlWriter,
68
+ experienceStore,
69
+ execEvents: [],
70
+ telemetryTasks: []
71
+ };
72
+ }
73
+ export function createPipeline(shellCommand, invocation, env) {
74
+ const stage = {
75
+ kind: 'command',
76
+ id: 'exec',
77
+ title: invocation.description ?? `Execute ${shellCommand}`,
78
+ command: shellCommand,
79
+ cwd: invocation.cwd ?? env.repoRoot,
80
+ env: invocation.env,
81
+ session: invocation.session
82
+ };
83
+ return {
84
+ id: 'exec',
85
+ title: 'CLI Exec Command',
86
+ stages: [stage],
87
+ guardrailsRequired: false
88
+ };
89
+ }
90
+ export function buildShellCommand(argv) {
91
+ return argv.map(shellEscape).join(' ');
92
+ }
93
+ export function parseNotificationEnv(value) {
94
+ if (!value) {
95
+ return null;
96
+ }
97
+ const tokens = value.split(',').map((entry) => entry.trim()).filter(Boolean);
98
+ return tokens.length ? tokens : null;
99
+ }
100
+ function shellEscape(value) {
101
+ if (value === '') {
102
+ return "''";
103
+ }
104
+ if (/^[A-Za-z0-9_/.:=-]+$/.test(value)) {
105
+ return value;
106
+ }
107
+ return `'${value.replace(/'/g, `'\\''`)}'`;
108
+ }
@@ -0,0 +1,77 @@
1
+ const DEFAULT_POLICY = {
2
+ maxSummaryWords: 32,
3
+ rewardFloor: 0
4
+ };
5
+ export function summarizeTrajectory(request) {
6
+ const reward = request.reward ?? { gtScore: 0, relativeRank: 0 };
7
+ const summary = buildTrajectorySummary(request.frames, request.baseSummary);
8
+ const toolStats = request.frames.map(toToolStat);
9
+ return {
10
+ runId: request.runId,
11
+ taskId: request.taskId,
12
+ epoch: request.epoch,
13
+ groupId: request.groupId,
14
+ summary,
15
+ reward,
16
+ toolStats,
17
+ stampSignature: request.stampSignature,
18
+ domain: request.domain
19
+ };
20
+ }
21
+ export function optimizeExperience(input, policy = DEFAULT_POLICY) {
22
+ const limit = Math.max(1, policy.maxSummaryWords);
23
+ return {
24
+ ...input,
25
+ summary: truncateSummary(input.summary, limit),
26
+ reward: {
27
+ gtScore: Math.max(input.reward.gtScore, policy.rewardFloor ?? 0),
28
+ relativeRank: input.reward.relativeRank
29
+ }
30
+ };
31
+ }
32
+ export function framesFromToolMetrics(metrics, representativeEvent) {
33
+ return metrics.map((metric) => ({
34
+ event: representativeEvent,
35
+ tool: metric.tool,
36
+ tokens: metric.tokens,
37
+ costUsd: metric.costUsd,
38
+ latencyMs: metric.latencyMs
39
+ }));
40
+ }
41
+ export function formatExperienceInjections(experiences, slots) {
42
+ if (slots <= 0) {
43
+ return [];
44
+ }
45
+ return experiences.slice(0, slots).map((experience) => {
46
+ const rewardScore = (experience.reward.gtScore + experience.reward.relativeRank).toFixed(2);
47
+ const statText = experience.toolStats
48
+ .map((stat) => `${stat.tool}: ${stat.tokens}t/${stat.costUsd.toFixed(3)}usd/${Math.round(stat.latencyMs)}ms`)
49
+ .join('; ');
50
+ return `[exp ${experience.id} | epoch ${experience.epoch ?? 'n/a'} | reward ${rewardScore}] ${experience.summary32} (stats: ${statText})`;
51
+ });
52
+ }
53
+ function buildTrajectorySummary(frames, fallback) {
54
+ const terminal = frames[frames.length - 1];
55
+ if (terminal?.event.type === 'exec:end') {
56
+ const stdout = terminal.event.payload.stdout?.trim();
57
+ if (stdout) {
58
+ return stdout.split('\n').slice(0, 2).join(' ');
59
+ }
60
+ }
61
+ return fallback ?? 'TF-GRPO trajectory summary unavailable.';
62
+ }
63
+ function toToolStat(frame) {
64
+ return {
65
+ tool: frame.tool,
66
+ tokens: frame.tokens,
67
+ costUsd: frame.costUsd,
68
+ latencyMs: frame.latencyMs
69
+ };
70
+ }
71
+ function truncateSummary(value, maxWords) {
72
+ const tokens = value.trim().split(/\s+/u).filter(Boolean);
73
+ if (tokens.length <= maxWords) {
74
+ return tokens.join(' ');
75
+ }
76
+ return tokens.slice(0, maxWords).join(' ');
77
+ }