@renseiai/agentfactory 0.8.18 → 0.8.20
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/dist/src/config/repository-config.d.ts +7 -0
- package/dist/src/config/repository-config.d.ts.map +1 -1
- package/dist/src/config/repository-config.js +15 -1
- package/dist/src/config/repository-config.test.js +1 -1
- package/dist/src/governor/decision-engine-adapter.d.ts +43 -0
- package/dist/src/governor/decision-engine-adapter.d.ts.map +1 -0
- package/dist/src/governor/decision-engine-adapter.js +417 -0
- package/dist/src/governor/decision-engine-adapter.test.d.ts +2 -0
- package/dist/src/governor/decision-engine-adapter.test.d.ts.map +1 -0
- package/dist/src/governor/decision-engine-adapter.test.js +362 -0
- package/dist/src/governor/decision-engine.js +3 -7
- package/dist/src/governor/decision-engine.test.js +5 -5
- package/dist/src/governor/index.d.ts +1 -0
- package/dist/src/governor/index.d.ts.map +1 -1
- package/dist/src/governor/index.js +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/manifest/route-manifest.d.ts.map +1 -1
- package/dist/src/manifest/route-manifest.js +4 -0
- package/dist/src/merge-queue/adapters/local.d.ts +68 -0
- package/dist/src/merge-queue/adapters/local.d.ts.map +1 -0
- package/dist/src/merge-queue/adapters/local.js +136 -0
- package/dist/src/merge-queue/adapters/local.test.d.ts +2 -0
- package/dist/src/merge-queue/adapters/local.test.d.ts.map +1 -0
- package/dist/src/merge-queue/adapters/local.test.js +176 -0
- package/dist/src/merge-queue/index.d.ts +13 -5
- package/dist/src/merge-queue/index.d.ts.map +1 -1
- package/dist/src/merge-queue/index.js +13 -6
- package/dist/src/merge-queue/merge-queue.integration.test.js +19 -0
- package/dist/src/merge-queue/merge-worker.d.ts.map +1 -1
- package/dist/src/merge-queue/merge-worker.js +29 -0
- package/dist/src/merge-queue/types.d.ts +1 -1
- package/dist/src/merge-queue/types.d.ts.map +1 -1
- package/dist/src/orchestrator/index.d.ts +4 -0
- package/dist/src/orchestrator/index.d.ts.map +1 -1
- package/dist/src/orchestrator/index.js +3 -0
- package/dist/src/orchestrator/orchestrator.d.ts +58 -0
- package/dist/src/orchestrator/orchestrator.d.ts.map +1 -1
- package/dist/src/orchestrator/orchestrator.js +552 -97
- package/dist/src/orchestrator/parse-work-result.d.ts.map +1 -1
- package/dist/src/orchestrator/parse-work-result.js +3 -1
- package/dist/src/orchestrator/parse-work-result.test.js +6 -0
- package/dist/src/orchestrator/quality-baseline.d.ts +83 -0
- package/dist/src/orchestrator/quality-baseline.d.ts.map +1 -0
- package/dist/src/orchestrator/quality-baseline.js +313 -0
- package/dist/src/orchestrator/quality-baseline.test.d.ts +2 -0
- package/dist/src/orchestrator/quality-baseline.test.d.ts.map +1 -0
- package/dist/src/orchestrator/quality-baseline.test.js +448 -0
- package/dist/src/orchestrator/quality-ratchet.d.ts +70 -0
- package/dist/src/orchestrator/quality-ratchet.d.ts.map +1 -0
- package/dist/src/orchestrator/quality-ratchet.js +162 -0
- package/dist/src/orchestrator/quality-ratchet.test.d.ts +2 -0
- package/dist/src/orchestrator/quality-ratchet.test.d.ts.map +1 -0
- package/dist/src/orchestrator/quality-ratchet.test.js +335 -0
- package/dist/src/orchestrator/types.d.ts +2 -0
- package/dist/src/orchestrator/types.d.ts.map +1 -1
- package/dist/src/providers/claude-provider.d.ts.map +1 -1
- package/dist/src/providers/claude-provider.js +11 -0
- package/dist/src/providers/codex-app-server-provider.d.ts +237 -0
- package/dist/src/providers/codex-app-server-provider.d.ts.map +1 -0
- package/dist/src/providers/codex-app-server-provider.js +1041 -0
- package/dist/src/providers/codex-app-server-provider.test.d.ts +2 -0
- package/dist/src/providers/codex-app-server-provider.test.d.ts.map +1 -0
- package/dist/src/providers/codex-app-server-provider.test.js +589 -0
- package/dist/src/providers/codex-approval-bridge.d.ts +49 -0
- package/dist/src/providers/codex-approval-bridge.d.ts.map +1 -0
- package/dist/src/providers/codex-approval-bridge.js +117 -0
- package/dist/src/providers/codex-approval-bridge.test.d.ts +2 -0
- package/dist/src/providers/codex-approval-bridge.test.d.ts.map +1 -0
- package/dist/src/providers/codex-approval-bridge.test.js +188 -0
- package/dist/src/providers/codex-provider.d.ts +24 -4
- package/dist/src/providers/codex-provider.d.ts.map +1 -1
- package/dist/src/providers/codex-provider.js +58 -6
- package/dist/src/providers/index.d.ts +1 -0
- package/dist/src/providers/index.d.ts.map +1 -1
- package/dist/src/providers/index.js +1 -0
- package/dist/src/providers/types.d.ts +25 -0
- package/dist/src/providers/types.d.ts.map +1 -1
- package/dist/src/routing/observation-recorder.test.js +1 -1
- package/dist/src/routing/observation-store.d.ts +15 -1
- package/dist/src/routing/observation-store.d.ts.map +1 -1
- package/dist/src/routing/observation-store.test.js +17 -11
- package/dist/src/routing/types.d.ts +1 -1
- package/dist/src/templates/adapters.d.ts +25 -0
- package/dist/src/templates/adapters.d.ts.map +1 -1
- package/dist/src/templates/adapters.js +70 -0
- package/dist/src/templates/adapters.test.js +49 -0
- package/dist/src/templates/index.d.ts +3 -1
- package/dist/src/templates/index.d.ts.map +1 -1
- package/dist/src/templates/index.js +1 -0
- package/dist/src/templates/registry.d.ts +31 -0
- package/dist/src/templates/registry.d.ts.map +1 -1
- package/dist/src/templates/registry.js +91 -0
- package/dist/src/templates/schema.d.ts +31 -0
- package/dist/src/templates/schema.d.ts.map +1 -0
- package/dist/src/templates/schema.js +139 -0
- package/dist/src/templates/schema.test.d.ts +2 -0
- package/dist/src/templates/schema.test.d.ts.map +1 -0
- package/dist/src/templates/schema.test.js +215 -0
- package/dist/src/templates/types.d.ts +22 -0
- package/dist/src/templates/types.d.ts.map +1 -1
- package/dist/src/templates/types.js +12 -0
- package/dist/src/tools/index.d.ts +2 -0
- package/dist/src/tools/index.d.ts.map +1 -1
- package/dist/src/tools/index.js +1 -0
- package/dist/src/tools/registry.d.ts +9 -1
- package/dist/src/tools/registry.d.ts.map +1 -1
- package/dist/src/tools/registry.js +13 -1
- package/dist/src/tools/stdio-server-entry.d.ts +25 -0
- package/dist/src/tools/stdio-server-entry.d.ts.map +1 -0
- package/dist/src/tools/stdio-server-entry.js +205 -0
- package/dist/src/tools/stdio-server.d.ts +87 -0
- package/dist/src/tools/stdio-server.d.ts.map +1 -0
- package/dist/src/tools/stdio-server.js +138 -0
- package/dist/src/workflow/workflow-types.d.ts +3 -3
- package/package.json +3 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quality-ratchet.test.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/quality-ratchet.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { loadQualityRatchet, checkQualityRatchet, updateQualityRatchet, initializeQualityRatchet, formatRatchetResult, } from './quality-ratchet.js';
|
|
3
|
+
vi.mock('node:fs', () => ({
|
|
4
|
+
readFileSync: vi.fn(),
|
|
5
|
+
writeFileSync: vi.fn(),
|
|
6
|
+
existsSync: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
9
|
+
const mockReadFileSync = vi.mocked(readFileSync);
|
|
10
|
+
const mockWriteFileSync = vi.mocked(writeFileSync);
|
|
11
|
+
const mockExistsSync = vi.mocked(existsSync);
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
vi.resetAllMocks();
|
|
14
|
+
});
|
|
15
|
+
function makeRatchet(overrides) {
|
|
16
|
+
return {
|
|
17
|
+
version: 1,
|
|
18
|
+
updatedAt: '2026-01-01T00:00:00Z',
|
|
19
|
+
updatedBy: 'SUP-100',
|
|
20
|
+
thresholds: {
|
|
21
|
+
testCount: { min: 100 },
|
|
22
|
+
testFailures: { max: 0 },
|
|
23
|
+
typecheckErrors: { max: 5 },
|
|
24
|
+
lintErrors: { max: 10 },
|
|
25
|
+
},
|
|
26
|
+
...overrides,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function makeBaseline(overrides) {
|
|
30
|
+
return {
|
|
31
|
+
timestamp: '2026-03-30T00:00:00Z',
|
|
32
|
+
commitSha: 'abc123',
|
|
33
|
+
tests: { total: 100, passed: 100, failed: 0, skipped: 0 },
|
|
34
|
+
typecheck: { errorCount: 5, exitCode: 0 },
|
|
35
|
+
lint: { errorCount: 10, warningCount: 20 },
|
|
36
|
+
...overrides,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// loadQualityRatchet
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
describe('loadQualityRatchet', () => {
|
|
43
|
+
it('loads and parses a valid ratchet file', () => {
|
|
44
|
+
const ratchet = makeRatchet();
|
|
45
|
+
mockExistsSync.mockReturnValue(true);
|
|
46
|
+
mockReadFileSync.mockReturnValue(JSON.stringify(ratchet));
|
|
47
|
+
const result = loadQualityRatchet('/repo');
|
|
48
|
+
expect(result).toEqual(ratchet);
|
|
49
|
+
});
|
|
50
|
+
it('returns null when file does not exist', () => {
|
|
51
|
+
mockExistsSync.mockReturnValue(false);
|
|
52
|
+
expect(loadQualityRatchet('/repo')).toBeNull();
|
|
53
|
+
});
|
|
54
|
+
it('throws on invalid JSON', () => {
|
|
55
|
+
mockExistsSync.mockReturnValue(true);
|
|
56
|
+
mockReadFileSync.mockReturnValue('not json');
|
|
57
|
+
expect(() => loadQualityRatchet('/repo')).toThrow();
|
|
58
|
+
});
|
|
59
|
+
it('throws on missing version', () => {
|
|
60
|
+
mockExistsSync.mockReturnValue(true);
|
|
61
|
+
mockReadFileSync.mockReturnValue(JSON.stringify({ thresholds: {} }));
|
|
62
|
+
expect(() => loadQualityRatchet('/repo')).toThrow('Invalid quality ratchet');
|
|
63
|
+
});
|
|
64
|
+
it('throws on missing thresholds', () => {
|
|
65
|
+
mockExistsSync.mockReturnValue(true);
|
|
66
|
+
mockReadFileSync.mockReturnValue(JSON.stringify({ version: 1 }));
|
|
67
|
+
expect(() => loadQualityRatchet('/repo')).toThrow('Invalid quality ratchet');
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// checkQualityRatchet
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
describe('checkQualityRatchet', () => {
|
|
74
|
+
it('passes when all metrics are within thresholds', () => {
|
|
75
|
+
const ratchet = makeRatchet();
|
|
76
|
+
const current = makeBaseline();
|
|
77
|
+
const result = checkQualityRatchet(ratchet, current);
|
|
78
|
+
expect(result.passed).toBe(true);
|
|
79
|
+
expect(result.violations).toHaveLength(0);
|
|
80
|
+
});
|
|
81
|
+
it('fails when test count drops below minimum', () => {
|
|
82
|
+
const ratchet = makeRatchet();
|
|
83
|
+
const current = makeBaseline({ tests: { total: 90, passed: 90, failed: 0, skipped: 0 } });
|
|
84
|
+
const result = checkQualityRatchet(ratchet, current);
|
|
85
|
+
expect(result.passed).toBe(false);
|
|
86
|
+
expect(result.violations).toHaveLength(1);
|
|
87
|
+
expect(result.violations[0].metric).toBe('testCount');
|
|
88
|
+
expect(result.violations[0].direction).toBe('below-min');
|
|
89
|
+
});
|
|
90
|
+
it('fails when test failures exceed maximum', () => {
|
|
91
|
+
const ratchet = makeRatchet();
|
|
92
|
+
const current = makeBaseline({ tests: { total: 100, passed: 97, failed: 3, skipped: 0 } });
|
|
93
|
+
const result = checkQualityRatchet(ratchet, current);
|
|
94
|
+
expect(result.passed).toBe(false);
|
|
95
|
+
expect(result.violations[0].metric).toBe('testFailures');
|
|
96
|
+
});
|
|
97
|
+
it('fails when typecheck errors exceed maximum', () => {
|
|
98
|
+
const ratchet = makeRatchet();
|
|
99
|
+
const current = makeBaseline({ typecheck: { errorCount: 8, exitCode: 1 } });
|
|
100
|
+
const result = checkQualityRatchet(ratchet, current);
|
|
101
|
+
expect(result.passed).toBe(false);
|
|
102
|
+
expect(result.violations[0].metric).toBe('typecheckErrors');
|
|
103
|
+
});
|
|
104
|
+
it('fails when lint errors exceed maximum', () => {
|
|
105
|
+
const ratchet = makeRatchet();
|
|
106
|
+
const current = makeBaseline({ lint: { errorCount: 15, warningCount: 20 } });
|
|
107
|
+
const result = checkQualityRatchet(ratchet, current);
|
|
108
|
+
expect(result.passed).toBe(false);
|
|
109
|
+
expect(result.violations[0].metric).toBe('lintErrors');
|
|
110
|
+
});
|
|
111
|
+
it('reports multiple violations', () => {
|
|
112
|
+
const ratchet = makeRatchet();
|
|
113
|
+
const current = makeBaseline({
|
|
114
|
+
tests: { total: 80, passed: 75, failed: 5, skipped: 0 },
|
|
115
|
+
typecheck: { errorCount: 10, exitCode: 1 },
|
|
116
|
+
});
|
|
117
|
+
const result = checkQualityRatchet(ratchet, current);
|
|
118
|
+
expect(result.passed).toBe(false);
|
|
119
|
+
expect(result.violations.length).toBeGreaterThanOrEqual(3);
|
|
120
|
+
});
|
|
121
|
+
it('passes when metrics exactly equal thresholds (boundary)', () => {
|
|
122
|
+
const ratchet = makeRatchet({
|
|
123
|
+
thresholds: {
|
|
124
|
+
testCount: { min: 100 },
|
|
125
|
+
testFailures: { max: 2 },
|
|
126
|
+
typecheckErrors: { max: 5 },
|
|
127
|
+
lintErrors: { max: 10 },
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
const current = makeBaseline({
|
|
131
|
+
tests: { total: 100, passed: 98, failed: 2, skipped: 0 },
|
|
132
|
+
typecheck: { errorCount: 5, exitCode: 0 },
|
|
133
|
+
lint: { errorCount: 10, warningCount: 0 },
|
|
134
|
+
});
|
|
135
|
+
const result = checkQualityRatchet(ratchet, current);
|
|
136
|
+
expect(result.passed).toBe(true);
|
|
137
|
+
expect(result.violations).toHaveLength(0);
|
|
138
|
+
});
|
|
139
|
+
it('fails when metric is one over the max threshold (boundary)', () => {
|
|
140
|
+
const ratchet = makeRatchet({
|
|
141
|
+
thresholds: {
|
|
142
|
+
testCount: { min: 100 },
|
|
143
|
+
testFailures: { max: 0 },
|
|
144
|
+
typecheckErrors: { max: 5 },
|
|
145
|
+
lintErrors: { max: 10 },
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
const current = makeBaseline({
|
|
149
|
+
tests: { total: 100, passed: 99, failed: 1, skipped: 0 },
|
|
150
|
+
});
|
|
151
|
+
const result = checkQualityRatchet(ratchet, current);
|
|
152
|
+
expect(result.passed).toBe(false);
|
|
153
|
+
expect(result.violations).toHaveLength(1);
|
|
154
|
+
expect(result.violations[0].metric).toBe('testFailures');
|
|
155
|
+
expect(result.violations[0].actual).toBe(1);
|
|
156
|
+
expect(result.violations[0].threshold).toBe(0);
|
|
157
|
+
});
|
|
158
|
+
it('fails when test count is one below the min threshold (boundary)', () => {
|
|
159
|
+
const ratchet = makeRatchet();
|
|
160
|
+
const current = makeBaseline({
|
|
161
|
+
tests: { total: 99, passed: 99, failed: 0, skipped: 0 },
|
|
162
|
+
});
|
|
163
|
+
const result = checkQualityRatchet(ratchet, current);
|
|
164
|
+
expect(result.passed).toBe(false);
|
|
165
|
+
expect(result.violations[0].metric).toBe('testCount');
|
|
166
|
+
expect(result.violations[0].actual).toBe(99);
|
|
167
|
+
});
|
|
168
|
+
it('passes with zero-threshold ratchet when metrics are zero', () => {
|
|
169
|
+
const ratchet = makeRatchet({
|
|
170
|
+
thresholds: {
|
|
171
|
+
testCount: { min: 0 },
|
|
172
|
+
testFailures: { max: 0 },
|
|
173
|
+
typecheckErrors: { max: 0 },
|
|
174
|
+
lintErrors: { max: 0 },
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
const current = makeBaseline({
|
|
178
|
+
tests: { total: 0, passed: 0, failed: 0, skipped: 0 },
|
|
179
|
+
typecheck: { errorCount: 0, exitCode: 0 },
|
|
180
|
+
lint: { errorCount: 0, warningCount: 0 },
|
|
181
|
+
});
|
|
182
|
+
const result = checkQualityRatchet(ratchet, current);
|
|
183
|
+
expect(result.passed).toBe(true);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
// updateQualityRatchet
|
|
188
|
+
// ---------------------------------------------------------------------------
|
|
189
|
+
describe('updateQualityRatchet', () => {
|
|
190
|
+
it('tightens thresholds when metrics improve', () => {
|
|
191
|
+
const ratchet = makeRatchet();
|
|
192
|
+
mockExistsSync.mockReturnValue(true);
|
|
193
|
+
mockReadFileSync.mockReturnValue(JSON.stringify(ratchet));
|
|
194
|
+
const improved = makeBaseline({
|
|
195
|
+
tests: { total: 110, passed: 110, failed: 0, skipped: 0 },
|
|
196
|
+
typecheck: { errorCount: 2, exitCode: 0 },
|
|
197
|
+
lint: { errorCount: 5, warningCount: 10 },
|
|
198
|
+
});
|
|
199
|
+
const updated = updateQualityRatchet('/repo', improved, 'SUP-200');
|
|
200
|
+
expect(updated).toBe(true);
|
|
201
|
+
expect(mockWriteFileSync).toHaveBeenCalled();
|
|
202
|
+
const written = JSON.parse(mockWriteFileSync.mock.calls[0][1]);
|
|
203
|
+
expect(written.thresholds.testCount.min).toBe(110);
|
|
204
|
+
expect(written.thresholds.typecheckErrors.max).toBe(2);
|
|
205
|
+
expect(written.thresholds.lintErrors.max).toBe(5);
|
|
206
|
+
expect(written.updatedBy).toBe('SUP-200');
|
|
207
|
+
});
|
|
208
|
+
it('does not update when metrics are worse or equal', () => {
|
|
209
|
+
const ratchet = makeRatchet();
|
|
210
|
+
mockExistsSync.mockReturnValue(true);
|
|
211
|
+
mockReadFileSync.mockReturnValue(JSON.stringify(ratchet));
|
|
212
|
+
const same = makeBaseline();
|
|
213
|
+
const updated = updateQualityRatchet('/repo', same, 'SUP-200');
|
|
214
|
+
expect(updated).toBe(false);
|
|
215
|
+
expect(mockWriteFileSync).not.toHaveBeenCalled();
|
|
216
|
+
});
|
|
217
|
+
it('returns false when ratchet file does not exist', () => {
|
|
218
|
+
mockExistsSync.mockReturnValue(false);
|
|
219
|
+
const updated = updateQualityRatchet('/repo', makeBaseline(), 'SUP-200');
|
|
220
|
+
expect(updated).toBe(false);
|
|
221
|
+
});
|
|
222
|
+
it('tightens only the metrics that improved (partial improvement)', () => {
|
|
223
|
+
const ratchet = makeRatchet({
|
|
224
|
+
thresholds: {
|
|
225
|
+
testCount: { min: 100 },
|
|
226
|
+
testFailures: { max: 5 },
|
|
227
|
+
typecheckErrors: { max: 10 },
|
|
228
|
+
lintErrors: { max: 20 },
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
mockExistsSync.mockReturnValue(true);
|
|
232
|
+
mockReadFileSync.mockReturnValue(JSON.stringify(ratchet));
|
|
233
|
+
// Only typecheck improved, rest is same or worse
|
|
234
|
+
const partial = makeBaseline({
|
|
235
|
+
tests: { total: 100, passed: 95, failed: 5, skipped: 0 },
|
|
236
|
+
typecheck: { errorCount: 3, exitCode: 0 },
|
|
237
|
+
lint: { errorCount: 20, warningCount: 0 },
|
|
238
|
+
});
|
|
239
|
+
const updated = updateQualityRatchet('/repo', partial, 'SUP-300');
|
|
240
|
+
expect(updated).toBe(true);
|
|
241
|
+
const written = JSON.parse(mockWriteFileSync.mock.calls[0][1]);
|
|
242
|
+
expect(written.thresholds.testCount.min).toBe(100); // unchanged
|
|
243
|
+
expect(written.thresholds.testFailures.max).toBe(5); // unchanged (equal)
|
|
244
|
+
expect(written.thresholds.typecheckErrors.max).toBe(3); // tightened
|
|
245
|
+
expect(written.thresholds.lintErrors.max).toBe(20); // unchanged (equal)
|
|
246
|
+
});
|
|
247
|
+
it('sets updatedAt to a recent timestamp', () => {
|
|
248
|
+
const ratchet = makeRatchet();
|
|
249
|
+
mockExistsSync.mockReturnValue(true);
|
|
250
|
+
mockReadFileSync.mockReturnValue(JSON.stringify(ratchet));
|
|
251
|
+
const improved = makeBaseline({
|
|
252
|
+
tests: { total: 200, passed: 200, failed: 0, skipped: 0 },
|
|
253
|
+
});
|
|
254
|
+
const before = new Date().toISOString();
|
|
255
|
+
updateQualityRatchet('/repo', improved, 'SUP-400');
|
|
256
|
+
const after = new Date().toISOString();
|
|
257
|
+
const written = JSON.parse(mockWriteFileSync.mock.calls[0][1]);
|
|
258
|
+
expect(written.updatedAt >= before).toBe(true);
|
|
259
|
+
expect(written.updatedAt <= after).toBe(true);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
// ---------------------------------------------------------------------------
|
|
263
|
+
// initializeQualityRatchet
|
|
264
|
+
// ---------------------------------------------------------------------------
|
|
265
|
+
describe('initializeQualityRatchet', () => {
|
|
266
|
+
it('creates ratchet file from baseline', () => {
|
|
267
|
+
const baseline = makeBaseline({
|
|
268
|
+
tests: { total: 50, passed: 48, failed: 2, skipped: 0 },
|
|
269
|
+
typecheck: { errorCount: 3, exitCode: 0 },
|
|
270
|
+
lint: { errorCount: 7, warningCount: 15 },
|
|
271
|
+
});
|
|
272
|
+
const ratchet = initializeQualityRatchet('/repo', baseline);
|
|
273
|
+
expect(ratchet.version).toBe(1);
|
|
274
|
+
expect(ratchet.thresholds.testCount.min).toBe(50);
|
|
275
|
+
expect(ratchet.thresholds.testFailures.max).toBe(2);
|
|
276
|
+
expect(ratchet.thresholds.typecheckErrors.max).toBe(3);
|
|
277
|
+
expect(ratchet.thresholds.lintErrors.max).toBe(7);
|
|
278
|
+
expect(mockWriteFileSync).toHaveBeenCalled();
|
|
279
|
+
});
|
|
280
|
+
it('initializes from a zero-metric baseline', () => {
|
|
281
|
+
const baseline = makeBaseline({
|
|
282
|
+
tests: { total: 0, passed: 0, failed: 0, skipped: 0 },
|
|
283
|
+
typecheck: { errorCount: 0, exitCode: 0 },
|
|
284
|
+
lint: { errorCount: 0, warningCount: 0 },
|
|
285
|
+
});
|
|
286
|
+
const ratchet = initializeQualityRatchet('/repo', baseline);
|
|
287
|
+
expect(ratchet.thresholds.testCount.min).toBe(0);
|
|
288
|
+
expect(ratchet.thresholds.testFailures.max).toBe(0);
|
|
289
|
+
expect(ratchet.thresholds.typecheckErrors.max).toBe(0);
|
|
290
|
+
expect(ratchet.thresholds.lintErrors.max).toBe(0);
|
|
291
|
+
});
|
|
292
|
+
it('sets updatedBy to "manual"', () => {
|
|
293
|
+
const ratchet = initializeQualityRatchet('/repo', makeBaseline());
|
|
294
|
+
expect(ratchet.updatedBy).toBe('manual');
|
|
295
|
+
});
|
|
296
|
+
it('writes JSON with trailing newline', () => {
|
|
297
|
+
initializeQualityRatchet('/repo', makeBaseline());
|
|
298
|
+
const written = mockWriteFileSync.mock.calls[0][1];
|
|
299
|
+
expect(written.endsWith('\n')).toBe(true);
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
// ---------------------------------------------------------------------------
|
|
303
|
+
// formatRatchetResult
|
|
304
|
+
// ---------------------------------------------------------------------------
|
|
305
|
+
describe('formatRatchetResult', () => {
|
|
306
|
+
it('formats a passing result', () => {
|
|
307
|
+
expect(formatRatchetResult({ passed: true, violations: [] })).toContain('passed');
|
|
308
|
+
});
|
|
309
|
+
it('formats a failing result with violations', () => {
|
|
310
|
+
const result = formatRatchetResult({
|
|
311
|
+
passed: false,
|
|
312
|
+
violations: [
|
|
313
|
+
{ metric: 'testCount', threshold: 100, actual: 90, direction: 'below-min' },
|
|
314
|
+
{ metric: 'typecheckErrors', threshold: 5, actual: 8, direction: 'above-max' },
|
|
315
|
+
],
|
|
316
|
+
});
|
|
317
|
+
expect(result).toContain('**FAILED**');
|
|
318
|
+
expect(result).toContain('testCount');
|
|
319
|
+
expect(result).toContain('below minimum');
|
|
320
|
+
expect(result).toContain('typecheckErrors');
|
|
321
|
+
expect(result).toContain('exceeds maximum');
|
|
322
|
+
});
|
|
323
|
+
it('formats a single violation', () => {
|
|
324
|
+
const result = formatRatchetResult({
|
|
325
|
+
passed: false,
|
|
326
|
+
violations: [
|
|
327
|
+
{ metric: 'lintErrors', threshold: 10, actual: 15, direction: 'above-max' },
|
|
328
|
+
],
|
|
329
|
+
});
|
|
330
|
+
expect(result).toContain('**FAILED**');
|
|
331
|
+
expect(result).toContain('lintErrors');
|
|
332
|
+
expect(result).toContain('15');
|
|
333
|
+
expect(result).toContain('10');
|
|
334
|
+
});
|
|
335
|
+
});
|
|
@@ -115,6 +115,8 @@ export interface OrchestratorConfig {
|
|
|
115
115
|
repository?: string;
|
|
116
116
|
/** Merge queue adapter for automated merge operations */
|
|
117
117
|
mergeQueueAdapter?: import('../merge-queue/types.js').MergeQueueAdapter;
|
|
118
|
+
/** Storage backend for the local merge queue adapter (required when provider is 'local') */
|
|
119
|
+
mergeQueueStorage?: import('../merge-queue/adapters/local.js').LocalMergeQueueStorage;
|
|
118
120
|
}
|
|
119
121
|
export interface OrchestratorIssue {
|
|
120
122
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAC5E,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAC7E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AACnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAEnD;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAA;AAE7D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,4DAA4D;IAC5D,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,kEAAkE;IAClE,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,kBAAkB;IACjC,sGAAsG;IACtG,QAAQ,CAAC,EAAE,aAAa,CAAA;IACxB,6CAA6C;IAC7C,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,qHAAqH;IACrH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAA;IACvC;;;OAGG;IACH,cAAc,CAAC,EAAE,sBAAsB,CAAA;IACvC;;;OAGG;IACH,WAAW,CAAC,EAAE,UAAU,EAAE,CAAA;IAC1B,8DAA8D;IAC9D,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;;;;OAOG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAA;IACjC;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,uDAAuD;IACvD,YAAY,CAAC,EAAE,wBAAwB,CAAA;IACvC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE;QAClB,iEAAiE;QACjE,OAAO,EAAE,MAAM,CAAA;QACf,4CAA4C;QAC5C,MAAM,EAAE,MAAM,CAAA;QACd,mCAAmC;QACnC,QAAQ,EAAE,MAAM,CAAA;KACjB,CAAA;IACD;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,qBAAqB,CAAC,CAAC,CAAA;IACxE;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,yDAAyD;IACzD,iBAAiB,CAAC,EAAE,OAAO,yBAAyB,EAAE,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-provider.d.ts","sourceRoot":"","sources":["../../../src/providers/claude-provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,OAAO,KAAK,EACV,aAAa,EACb,gBAAgB,EAChB,WAAW,EAEZ,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"claude-provider.d.ts","sourceRoot":"","sources":["../../../src/providers/claude-provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,OAAO,KAAK,EACV,aAAa,EACb,gBAAgB,EAChB,WAAW,EAEZ,MAAM,YAAY,CAAA;AA6FnB,qBAAa,cAAe,YAAW,aAAa;IAClD,QAAQ,CAAC,IAAI,EAAG,QAAQ,CAAS;IACjC,QAAQ,CAAC,YAAY;;;MAGX;IAEV,KAAK,CAAC,MAAM,EAAE,gBAAgB,GAAG,WAAW;IAI5C,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,GAAG,WAAW;IAIhE,OAAO,CAAC,YAAY;CA8KrB;AAsND;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,cAAc,CAErD"}
|
|
@@ -26,6 +26,17 @@ const autonomousCanUseTool = async (toolName, input) => {
|
|
|
26
26
|
if (['Write', 'Edit', 'NotebookEdit'].includes(toolName)) {
|
|
27
27
|
return { behavior: 'allow', updatedInput: input };
|
|
28
28
|
}
|
|
29
|
+
// Agent tool: always force foreground execution.
|
|
30
|
+
// Coordinators that spawn sub-agents with run_in_background=true exit
|
|
31
|
+
// before sub-agents finish, orphaning work. Strip the flag so the Agent
|
|
32
|
+
// tool blocks until the sub-agent completes.
|
|
33
|
+
if (toolName === 'Agent') {
|
|
34
|
+
if (input.run_in_background) {
|
|
35
|
+
const { run_in_background: _, ...rest } = input;
|
|
36
|
+
return { behavior: 'allow', updatedInput: rest };
|
|
37
|
+
}
|
|
38
|
+
return { behavior: 'allow', updatedInput: input };
|
|
39
|
+
}
|
|
29
40
|
// Task management and planning
|
|
30
41
|
if (['Task', 'TaskCreate', 'TaskUpdate', 'TaskGet', 'TaskList',
|
|
31
42
|
'EnterPlanMode', 'ExitPlanMode', 'Skill'].includes(toolName)) {
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex App Server Provider
|
|
3
|
+
*
|
|
4
|
+
* Manages a long-lived `codex app-server` process communicating via JSON-RPC 2.0
|
|
5
|
+
* over stdio. Supports multiple concurrent threads on a single process.
|
|
6
|
+
*
|
|
7
|
+
* Architecture:
|
|
8
|
+
* orchestrator → codex app-server (long-lived, one process)
|
|
9
|
+
* ├── thread_1 (agent session A)
|
|
10
|
+
* ├── thread_2 (agent session B)
|
|
11
|
+
* └── thread_3 (agent session C)
|
|
12
|
+
*
|
|
13
|
+
* Falls back to `codex exec` CLI mode when app-server is unavailable.
|
|
14
|
+
*
|
|
15
|
+
* JSON-RPC 2.0 protocol (over stdio JSONL):
|
|
16
|
+
* 1. initialize + initialized handshake
|
|
17
|
+
* 2. thread/start or thread/resume
|
|
18
|
+
* 3. turn/start with prompt
|
|
19
|
+
* 4. Stream item/*, turn/*, thread/* notifications
|
|
20
|
+
* 5. thread/unsubscribe on completion
|
|
21
|
+
*
|
|
22
|
+
* @see https://developers.openai.com/codex/app-server
|
|
23
|
+
*/
|
|
24
|
+
import type { AgentProvider, AgentSpawnConfig, AgentHandle, AgentEvent } from './types.js';
|
|
25
|
+
interface JsonRpcNotification {
|
|
26
|
+
method: string;
|
|
27
|
+
params?: Record<string, unknown>;
|
|
28
|
+
}
|
|
29
|
+
/** Status of a registered MCP server as reported by mcpServerStatus/list */
|
|
30
|
+
export interface McpServerStatusResult {
|
|
31
|
+
name: string;
|
|
32
|
+
status: 'connected' | 'connecting' | 'disconnected' | 'error';
|
|
33
|
+
toolCount?: number;
|
|
34
|
+
error?: string;
|
|
35
|
+
}
|
|
36
|
+
interface AppServerItem {
|
|
37
|
+
id: string;
|
|
38
|
+
type: string;
|
|
39
|
+
text?: string;
|
|
40
|
+
summary?: string;
|
|
41
|
+
content?: string;
|
|
42
|
+
command?: string;
|
|
43
|
+
cwd?: string;
|
|
44
|
+
status?: string;
|
|
45
|
+
exitCode?: number;
|
|
46
|
+
durationMs?: number;
|
|
47
|
+
changes?: Array<{
|
|
48
|
+
path: string;
|
|
49
|
+
kind: string;
|
|
50
|
+
}>;
|
|
51
|
+
server?: string;
|
|
52
|
+
tool?: string;
|
|
53
|
+
arguments?: unknown;
|
|
54
|
+
result?: {
|
|
55
|
+
content?: unknown[];
|
|
56
|
+
};
|
|
57
|
+
error?: {
|
|
58
|
+
message?: string;
|
|
59
|
+
};
|
|
60
|
+
items?: Array<{
|
|
61
|
+
text: string;
|
|
62
|
+
completed: boolean;
|
|
63
|
+
}>;
|
|
64
|
+
phase?: string;
|
|
65
|
+
}
|
|
66
|
+
interface AppServerTurn {
|
|
67
|
+
id: string;
|
|
68
|
+
status?: string;
|
|
69
|
+
usage?: {
|
|
70
|
+
input_tokens?: number;
|
|
71
|
+
output_tokens?: number;
|
|
72
|
+
cached_input_tokens?: number;
|
|
73
|
+
};
|
|
74
|
+
error?: {
|
|
75
|
+
message?: string;
|
|
76
|
+
codexErrorInfo?: string;
|
|
77
|
+
httpStatusCode?: number;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
interface AppServerThread {
|
|
81
|
+
id: string;
|
|
82
|
+
status?: string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Manages a single long-lived `codex app-server` process.
|
|
86
|
+
*
|
|
87
|
+
* Handles:
|
|
88
|
+
* - Process spawning and stdio communication
|
|
89
|
+
* - JSON-RPC 2.0 initialization handshake
|
|
90
|
+
* - Request/response correlation
|
|
91
|
+
* - Notification routing to thread subscribers
|
|
92
|
+
* - Health monitoring and graceful shutdown
|
|
93
|
+
*/
|
|
94
|
+
export declare class AppServerProcessManager {
|
|
95
|
+
private process;
|
|
96
|
+
private readline;
|
|
97
|
+
private nextId;
|
|
98
|
+
private pendingRequests;
|
|
99
|
+
private initialized;
|
|
100
|
+
private shutdownPromise;
|
|
101
|
+
/** Notification listeners keyed by threadId */
|
|
102
|
+
private threadListeners;
|
|
103
|
+
/** Global notification listeners (for notifications without a threadId) */
|
|
104
|
+
private globalListeners;
|
|
105
|
+
private readonly codexBin;
|
|
106
|
+
private readonly cwd;
|
|
107
|
+
private readonly env;
|
|
108
|
+
constructor(options: {
|
|
109
|
+
codexBin?: string;
|
|
110
|
+
cwd: string;
|
|
111
|
+
env?: Record<string, string>;
|
|
112
|
+
});
|
|
113
|
+
/**
|
|
114
|
+
* Start the app-server process and complete the initialization handshake.
|
|
115
|
+
* Idempotent — returns immediately if already initialized.
|
|
116
|
+
*/
|
|
117
|
+
start(): Promise<void>;
|
|
118
|
+
/**
|
|
119
|
+
* JSON-RPC 2.0 initialization handshake:
|
|
120
|
+
* 1. Send `initialize` request
|
|
121
|
+
* 2. Receive response
|
|
122
|
+
* 3. Send `initialized` notification
|
|
123
|
+
*/
|
|
124
|
+
private initialize;
|
|
125
|
+
/**
|
|
126
|
+
* Send a JSON-RPC request and wait for the response.
|
|
127
|
+
*/
|
|
128
|
+
request(method: string, params?: Record<string, unknown>, timeoutMs?: number): Promise<unknown>;
|
|
129
|
+
/**
|
|
130
|
+
* Send a JSON-RPC message (request or notification) to the app-server.
|
|
131
|
+
*/
|
|
132
|
+
private send;
|
|
133
|
+
/**
|
|
134
|
+
* Handle an incoming JSON-RPC response.
|
|
135
|
+
*/
|
|
136
|
+
private handleResponse;
|
|
137
|
+
/**
|
|
138
|
+
* Handle an incoming JSON-RPC notification.
|
|
139
|
+
* Routes to the appropriate thread listener based on threadId in params.
|
|
140
|
+
*/
|
|
141
|
+
private handleNotification;
|
|
142
|
+
/**
|
|
143
|
+
* Subscribe to notifications for a specific thread.
|
|
144
|
+
*/
|
|
145
|
+
subscribeThread(threadId: string, listener: (notification: JsonRpcNotification) => void): void;
|
|
146
|
+
/**
|
|
147
|
+
* Unsubscribe from notifications for a specific thread.
|
|
148
|
+
*/
|
|
149
|
+
unsubscribeThread(threadId: string): void;
|
|
150
|
+
/**
|
|
151
|
+
* Check if the app-server process is alive and initialized.
|
|
152
|
+
*/
|
|
153
|
+
isHealthy(): boolean;
|
|
154
|
+
/** Whether MCP servers have been configured on this process */
|
|
155
|
+
private mcpConfigured;
|
|
156
|
+
/**
|
|
157
|
+
* Register MCP servers with the app-server via config/batchWrite.
|
|
158
|
+
* Called once after initialization to tell Codex about the stdio
|
|
159
|
+
* MCP tool servers (af-linear, af-code-intelligence, etc.).
|
|
160
|
+
*
|
|
161
|
+
* Uses the Codex app-server `config/batchWrite` JSON-RPC method
|
|
162
|
+
* to register multiple MCP server configurations in a single call.
|
|
163
|
+
*/
|
|
164
|
+
configureMcpServers(servers: Array<{
|
|
165
|
+
name: string;
|
|
166
|
+
command: string;
|
|
167
|
+
args: string[];
|
|
168
|
+
env?: Record<string, string>;
|
|
169
|
+
}>): Promise<void>;
|
|
170
|
+
/**
|
|
171
|
+
* Query MCP server health via mcpServerStatus/list.
|
|
172
|
+
* Returns the status of all registered MCP servers.
|
|
173
|
+
*/
|
|
174
|
+
getMcpServerStatus(): Promise<McpServerStatusResult[]>;
|
|
175
|
+
/**
|
|
176
|
+
* Get the PID of the app-server process.
|
|
177
|
+
*/
|
|
178
|
+
get pid(): number | undefined;
|
|
179
|
+
/**
|
|
180
|
+
* Graceful shutdown: unsubscribe all threads, kill process.
|
|
181
|
+
*/
|
|
182
|
+
shutdown(): Promise<void>;
|
|
183
|
+
private performShutdown;
|
|
184
|
+
private rejectAllPending;
|
|
185
|
+
}
|
|
186
|
+
export interface AppServerEventMapperState {
|
|
187
|
+
sessionId: string | null;
|
|
188
|
+
totalInputTokens: number;
|
|
189
|
+
totalOutputTokens: number;
|
|
190
|
+
turnCount: number;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Map an App Server notification to normalized AgentEvents.
|
|
194
|
+
* Exported for unit testing.
|
|
195
|
+
*/
|
|
196
|
+
export declare function mapAppServerNotification(notification: JsonRpcNotification, state: AppServerEventMapperState): AgentEvent[];
|
|
197
|
+
/**
|
|
198
|
+
* Map item/started and item/completed notifications to AgentEvents.
|
|
199
|
+
* Exported for unit testing.
|
|
200
|
+
*/
|
|
201
|
+
export declare function mapAppServerItemEvent(method: string, params: Record<string, unknown>): AgentEvent[];
|
|
202
|
+
/**
|
|
203
|
+
* Normalize Codex MCP tool names to the `mcp__{server}__{tool}` format
|
|
204
|
+
* used by the orchestrator and Claude provider for consistent tool tracking.
|
|
205
|
+
*
|
|
206
|
+
* Codex reports MCP tools as `server` + `tool` (e.g., server='af-linear',
|
|
207
|
+
* tool='af_linear_get_issue'). We normalize to 'mcp__af-linear__af_linear_get_issue'.
|
|
208
|
+
*/
|
|
209
|
+
export declare function normalizeMcpToolName(server?: string, tool?: string): string;
|
|
210
|
+
/**
|
|
211
|
+
* Codex App Server Provider
|
|
212
|
+
*
|
|
213
|
+
* Uses a long-lived `codex app-server` process with JSON-RPC 2.0 over stdio.
|
|
214
|
+
* Falls back to `codex exec` CLI mode when CODEX_USE_APP_SERVER is not set
|
|
215
|
+
* or when the app-server binary is unavailable.
|
|
216
|
+
*/
|
|
217
|
+
export declare class CodexAppServerProvider implements AgentProvider {
|
|
218
|
+
readonly name: "codex";
|
|
219
|
+
readonly capabilities: {
|
|
220
|
+
readonly supportsMessageInjection: true;
|
|
221
|
+
readonly supportsSessionResume: true;
|
|
222
|
+
};
|
|
223
|
+
/** Shared process manager — one app-server process serves all threads */
|
|
224
|
+
private processManager;
|
|
225
|
+
spawn(config: AgentSpawnConfig): AgentHandle;
|
|
226
|
+
resume(sessionId: string, config: AgentSpawnConfig): AgentHandle;
|
|
227
|
+
/**
|
|
228
|
+
* Shut down the shared app-server process.
|
|
229
|
+
* Called when the orchestrator is done with this provider.
|
|
230
|
+
*/
|
|
231
|
+
shutdown(): Promise<void>;
|
|
232
|
+
private getOrCreateProcessManager;
|
|
233
|
+
}
|
|
234
|
+
export declare function createCodexAppServerProvider(): CodexAppServerProvider;
|
|
235
|
+
/** Exported for testing */
|
|
236
|
+
export type { JsonRpcNotification, AppServerItem, AppServerTurn, AppServerThread };
|
|
237
|
+
//# sourceMappingURL=codex-app-server-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-app-server-provider.d.ts","sourceRoot":"","sources":["../../../src/providers/codex-app-server-provider.ts"],"names":[],"mappings":"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"}
|