@aexol/spectral 0.9.84 → 0.9.86

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.
@@ -23,6 +23,7 @@ export declare class BrowserService {
23
23
  private nextNetworkIndex;
24
24
  private pendingDialogs;
25
25
  private _recording;
26
+ private closePromise;
26
27
  constructor(config?: Partial<BrowserConfig>);
27
28
  static getInstance(config?: Partial<BrowserConfig>): BrowserService;
28
29
  static resetInstance(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"browser-service.d.ts","sourceRoot":"","sources":["../../../src/extensions/browser/browser-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"browser-service.d.ts","sourceRoot":"","sources":["../../../src/extensions/browser/browser-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,EAAkB,MAAM,YAAY,CAAC;AAChF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAsBjD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAQD,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,aAAa,CAA8D;IACnF,OAAO,CAAC,aAAa,CAA0C;IAC/D,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,cAAc,CAKd;IACR,OAAO,CAAC,UAAU,CAA0F;IAC5G,OAAO,CAAC,YAAY,CAA8B;gBACtC,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC;IAG3C,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,cAAc;IAsBnE,MAAM,CAAC,aAAa,IAAI,IAAI;IAO5B;;;;;;OAMG;WACU,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IActC,MAAM,CAAC,WAAW,IAAI,OAAO;IAQ7B,MAAM,CAAC,oBAAoB,IAAI,MAAM,GAAG,IAAI;IAUtC,aAAa,IAAI,OAAO,CAAC;QAC7B,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,cAAc,CAAC;QACxB,IAAI,EAAE,IAAI,CAAC;KACZ,CAAC;IA8CF,UAAU,IAAI,OAAO;IAIrB,gBAAgB,IAAI,cAAc;IAIlC,aAAa,IAAI,IAAI;IAKf,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAS9D,YAAY,IAAI,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAKvD,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAY1B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK1D,QAAQ,IAAI,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAO1D,SAAS,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IASvC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUtC,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAS1D,OAAO,CAAC,wBAAwB;IAchC,iBAAiB,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAGvD,YAAY,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAYzE,kBAAkB,CAChB,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,EAC9C,GAAG,UAAQ,GACV,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IASzD,oBAAoB,IAAI,IAAI;IAG5B,mBAAmB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAwBrC,kBAAkB,IAAI,cAAc,EAAE;IAGtC,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAG5D,oBAAoB,IAAI,IAAI;IAItB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA0BtB,cAAc,CAClB,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAC/C,OAAO,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IA6D/C,aAAa,IAAI,OAAO,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC;IAsC/E,eAAe,IAAI;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE;IASxG,YAAY,IAAI,IAAI;IAIpB,SAAS,IAAI,aAAa;CAG3B"}
@@ -25,6 +25,7 @@ let unregisterInstanceCleanup = null;
25
25
  // that closeAll() can still await it even though cleanupSessionResources()
26
26
  // cannot itself await (it is invoked synchronously from session.dispose()).
27
27
  let pendingClose = null;
28
+ let closeAllPromise = null;
28
29
  export class BrowserService {
29
30
  browser = null;
30
31
  context = null;
@@ -36,6 +37,7 @@ export class BrowserService {
36
37
  nextNetworkIndex = 0;
37
38
  pendingDialogs = [];
38
39
  _recording = null;
40
+ closePromise = null;
39
41
  constructor(config) {
40
42
  this.config = { ...getBrowserConfig(), ...config };
41
43
  }
@@ -68,6 +70,7 @@ export class BrowserService {
68
70
  unregisterInstanceCleanup = null;
69
71
  instance = null;
70
72
  pendingClose = null;
73
+ closeAllPromise = null;
71
74
  }
72
75
  /**
73
76
  * Close the active browser singleton (if any) and await completion. Unlike
@@ -77,17 +80,21 @@ export class BrowserService {
77
80
  * server shutdown so the Chromium process is actually torn down before exit.
78
81
  */
79
82
  static async closeAll() {
80
- const current = instance;
81
- instance = null;
82
- unregisterInstanceCleanup?.();
83
- unregisterInstanceCleanup = null;
84
- if (current) {
85
- await current.close().catch(() => { });
86
- return;
87
- }
88
- if (pendingClose) {
89
- await pendingClose;
90
- }
83
+ if (closeAllPromise)
84
+ return closeAllPromise;
85
+ closeAllPromise = (async () => {
86
+ const current = instance;
87
+ instance = null;
88
+ unregisterInstanceCleanup?.();
89
+ unregisterInstanceCleanup = null;
90
+ if (current)
91
+ await current.close().catch(() => { });
92
+ if (pendingClose)
93
+ await pendingClose;
94
+ })().finally(() => {
95
+ closeAllPromise = null;
96
+ });
97
+ return closeAllPromise;
91
98
  }
92
99
  static isAvailable() {
93
100
  try {
@@ -322,39 +329,39 @@ export class BrowserService {
322
329
  this.nextNetworkIndex = 0;
323
330
  }
324
331
  async close() {
325
- // Close each page (ignore errors if already closed).
326
- for (const page of this.pages) {
327
- try {
328
- await page.close();
329
- }
330
- catch { /* ignore */ }
331
- }
332
- this.pages = [];
333
- this.activePageIndex = 0;
334
- // Close the browser context.
335
- if (this.context) {
336
- try {
337
- await Promise.race([
338
- this.context.close(),
339
- new Promise((_, reject) => setTimeout(() => reject(new Error("context.close timed out")), 5_000)),
340
- ]);
332
+ // Share one close operation with concurrent callers; cleanup is fully awaited.
333
+ if (this.closePromise)
334
+ return this.closePromise;
335
+ this.closePromise = (async () => {
336
+ for (const page of this.pages) {
337
+ try {
338
+ await page.close();
339
+ }
340
+ catch { /* already closed */ }
341
341
  }
342
- catch { /* ignore */ }
342
+ this.pages = [];
343
+ this.activePageIndex = 0;
344
+ const context = this.context;
343
345
  this.context = null;
344
- }
345
- // Close the browser process (Playwright-managed Chromium).
346
- // Use a timeout so a hung close doesn't block server shutdown.
347
- if (this.browser) {
348
- try {
349
- await Promise.race([
350
- this.browser.close(),
351
- new Promise((_, reject) => setTimeout(() => reject(new Error("browser.close timed out")), 5_000)),
352
- ]);
346
+ if (context) {
347
+ try {
348
+ await context.close();
349
+ }
350
+ catch { /* already closed */ }
353
351
  }
354
- catch { /* ignore — process.exit will clean up orphans */ }
352
+ const browser = this.browser;
355
353
  this.browser = null;
356
- }
357
- this._recording = null;
354
+ if (browser) {
355
+ try {
356
+ await browser.close();
357
+ }
358
+ catch { /* already closed */ }
359
+ }
360
+ this._recording = null;
361
+ })().finally(() => {
362
+ this.closePromise = null;
363
+ });
364
+ return this.closePromise;
358
365
  }
359
366
  // ── Recording ──────────────────────────────────────────────
360
367
  async startRecording(opts) {
@@ -1 +1 @@
1
- {"version":3,"file":"scenario-tool.d.ts","sourceRoot":"","sources":["../../../../src/extensions/browser/scenario/scenario-tool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,oCAAoC,CAAC;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAsC5D,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,YAAY,EAAE,EAAE,EAAE,cAAc,GAAG,IAAI,CAuUjF"}
1
+ {"version":3,"file":"scenario-tool.d.ts","sourceRoot":"","sources":["../../../../src/extensions/browser/scenario/scenario-tool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,oCAAoC,CAAC;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAsC5D,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,YAAY,EAAE,EAAE,EAAE,cAAc,GAAG,IAAI,CA+VjF"}
@@ -75,6 +75,10 @@ export function registerScenarioTools(ext, bs) {
75
75
  type: "number",
76
76
  description: "Default pause (ms) after each step for readable video. Default: 800. Set 0 to disable.",
77
77
  },
78
+ keepBrowserOpen: {
79
+ type: "boolean",
80
+ description: "Keep the browser open after the scenario. Default: false.",
81
+ },
78
82
  },
79
83
  required: ["url"],
80
84
  },
@@ -128,207 +132,228 @@ export function registerScenarioTools(ext, bs) {
128
132
  const record = p.record === true;
129
133
  const stepDelay = typeof p.stepDelayMs === "number" ? p.stepDelayMs : 800;
130
134
  const results = [];
131
- // Optional recording start.
135
+ const keepBrowserOpen = p.keepBrowserOpen === true;
132
136
  let videoPath;
133
137
  let durationSeconds;
134
- if (record) {
135
- try {
136
- const rec = await bs.startRecording({ filename: p.filename });
137
- videoPath = rec.outputPath;
138
- }
139
- catch (err) {
140
- const msg = err instanceof Error ? err.message : String(err);
141
- return {
142
- content: [{ type: "text", text: `Failed to start recording: ${msg}` }],
143
- details: { isError: true },
144
- };
145
- }
146
- }
147
- // Navigate to the start URL.
148
138
  try {
149
- await bs.navigate(p.url);
150
- await sleep(stepDelay);
151
- }
152
- catch (err) {
153
- const msg = err instanceof Error ? err.message : String(err);
154
- // If we can't even navigate, fail everything.
155
- for (let i = 0; i < steps.length; i++) {
156
- results.push({
157
- step: i + 1,
158
- action: steps[i].action,
159
- status: "skipped",
160
- detail: `Blocked: navigation failed (${msg})`,
161
- durationMs: 0,
162
- });
163
- }
139
+ // Optional recording start.
164
140
  if (record) {
165
141
  try {
166
- const stop = await bs.stopRecording();
167
- videoPath = stop.outputPath;
168
- durationSeconds = stop.durationSeconds;
142
+ const rec = await bs.startRecording({ filename: p.filename });
143
+ videoPath = rec.outputPath;
144
+ }
145
+ catch (err) {
146
+ const msg = err instanceof Error ? err.message : String(err);
147
+ return {
148
+ content: [{ type: "text", text: `Failed to start recording: ${msg}` }],
149
+ details: { isError: true },
150
+ };
169
151
  }
170
- catch { /* ignore */ }
171
152
  }
172
- return finalize(results, videoPath, durationSeconds, false);
173
- }
174
- // Execute steps.
175
- for (let i = 0; i < steps.length; i++) {
176
- const step = steps[i];
177
- const start = Date.now();
178
- let status = "pass";
179
- let detail = "";
153
+ // Navigate to the start URL.
180
154
  try {
181
- const page = bs.getActivePage();
182
- switch (step.action) {
183
- case "navigate": {
184
- if (step.target) {
185
- await bs.navigate(step.target);
186
- detail = `Navigated to ${step.target}`;
187
- }
188
- else {
189
- status = "skipped";
190
- detail = "No target URL";
191
- }
192
- break;
193
- }
194
- case "click": {
195
- if (step.target) {
196
- await page.click(step.target, { timeout: bs.getConfig().actionTimeout || 5000 });
197
- detail = `Clicked ${step.target}`;
198
- }
199
- else {
200
- status = "skipped";
201
- detail = "No target";
202
- }
203
- break;
204
- }
205
- case "hover": {
206
- if (step.target) {
207
- await page.hover(step.target, { timeout: bs.getConfig().actionTimeout || 5000 });
208
- detail = `Hovered ${step.target}`;
209
- }
210
- else {
211
- status = "skipped";
212
- detail = "No target";
213
- }
214
- break;
215
- }
216
- case "type": {
217
- if (step.target && step.value !== undefined) {
218
- await page.fill(step.target, step.value, { timeout: bs.getConfig().actionTimeout || 5000 });
219
- detail = `Typed into ${step.target}`;
220
- }
221
- else {
222
- status = "skipped";
223
- detail = "Missing target or value";
224
- }
225
- break;
155
+ await bs.navigate(p.url);
156
+ await sleep(stepDelay);
157
+ }
158
+ catch (err) {
159
+ const msg = err instanceof Error ? err.message : String(err);
160
+ // If we can't even navigate, fail everything.
161
+ for (let i = 0; i < steps.length; i++) {
162
+ results.push({
163
+ step: i + 1,
164
+ action: steps[i].action,
165
+ status: "skipped",
166
+ detail: `Blocked: navigation failed (${msg})`,
167
+ durationMs: 0,
168
+ });
169
+ }
170
+ if (record) {
171
+ try {
172
+ const stop = await bs.stopRecording();
173
+ videoPath = stop.outputPath;
174
+ durationSeconds = stop.durationSeconds;
226
175
  }
227
- case "press": {
228
- const key = step.value ?? step.target;
229
- if (key) {
230
- await page.keyboard.press(key);
231
- detail = `Pressed ${key}`;
176
+ catch { /* ignore */ }
177
+ }
178
+ return finalize(results, videoPath, durationSeconds, false);
179
+ }
180
+ // Execute steps.
181
+ for (let i = 0; i < steps.length; i++) {
182
+ const step = steps[i];
183
+ const start = Date.now();
184
+ let status = "pass";
185
+ let detail = "";
186
+ try {
187
+ const page = bs.getActivePage();
188
+ switch (step.action) {
189
+ case "navigate": {
190
+ if (step.target) {
191
+ await bs.navigate(step.target);
192
+ detail = `Navigated to ${step.target}`;
193
+ }
194
+ else {
195
+ status = "skipped";
196
+ detail = "No target URL";
197
+ }
198
+ break;
232
199
  }
233
- else {
234
- status = "skipped";
235
- detail = "No key";
200
+ case "click": {
201
+ if (step.target) {
202
+ await page.click(step.target, { timeout: bs.getConfig().actionTimeout || 5000 });
203
+ detail = `Clicked ${step.target}`;
204
+ }
205
+ else {
206
+ status = "skipped";
207
+ detail = "No target";
208
+ }
209
+ break;
236
210
  }
237
- break;
238
- }
239
- case "select": {
240
- if (step.target && step.value !== undefined) {
241
- await page.selectOption(step.target, step.value);
242
- detail = `Selected ${step.value} in ${step.target}`;
211
+ case "hover": {
212
+ if (step.target) {
213
+ await page.hover(step.target, { timeout: bs.getConfig().actionTimeout || 5000 });
214
+ detail = `Hovered ${step.target}`;
215
+ }
216
+ else {
217
+ status = "skipped";
218
+ detail = "No target";
219
+ }
220
+ break;
243
221
  }
244
- else {
245
- status = "skipped";
246
- detail = "Missing target or value";
222
+ case "type": {
223
+ if (step.target && step.value !== undefined) {
224
+ await page.fill(step.target, step.value, { timeout: bs.getConfig().actionTimeout || 5000 });
225
+ detail = `Typed into ${step.target}`;
226
+ }
227
+ else {
228
+ status = "skipped";
229
+ detail = "Missing target or value";
230
+ }
231
+ break;
247
232
  }
248
- break;
249
- }
250
- case "wait": {
251
- const ms = step.waitMs ?? (step.value && /^\d+$/.test(step.value) ? Number(step.value) : null);
252
- if (ms !== null) {
253
- await page.waitForTimeout(ms);
254
- detail = `Waited ${ms}ms`;
233
+ case "press": {
234
+ const key = step.value ?? step.target;
235
+ if (key) {
236
+ await page.keyboard.press(key);
237
+ detail = `Pressed ${key}`;
238
+ }
239
+ else {
240
+ status = "skipped";
241
+ detail = "No key";
242
+ }
243
+ break;
255
244
  }
256
- else if (step.value) {
257
- await page.waitForSelector(`text=${step.value}`, { timeout: 30_000 });
258
- detail = `Waited for text "${step.value}"`;
245
+ case "select": {
246
+ if (step.target && step.value !== undefined) {
247
+ await page.selectOption(step.target, step.value);
248
+ detail = `Selected ${step.value} in ${step.target}`;
249
+ }
250
+ else {
251
+ status = "skipped";
252
+ detail = "Missing target or value";
253
+ }
254
+ break;
259
255
  }
260
- else {
261
- await page.waitForTimeout(1000);
262
- detail = "Waited 1000ms (default)";
256
+ case "wait": {
257
+ const ms = step.waitMs ?? (step.value && /^\d+$/.test(step.value) ? Number(step.value) : null);
258
+ if (ms !== null) {
259
+ await page.waitForTimeout(ms);
260
+ detail = `Waited ${ms}ms`;
261
+ }
262
+ else if (step.value) {
263
+ await page.waitForSelector(`text=${step.value}`, { timeout: 30_000 });
264
+ detail = `Waited for text "${step.value}"`;
265
+ }
266
+ else {
267
+ await page.waitForTimeout(1000);
268
+ detail = "Waited 1000ms (default)";
269
+ }
270
+ break;
263
271
  }
264
- break;
265
- }
266
- case "screenshot": {
267
- const config = bs.getConfig();
268
- const { mkdirSync } = await import("node:fs");
269
- mkdirSync(config.outputDir, { recursive: true });
270
- const ts = new Date().toISOString().replace(/[:.]/g, "-");
271
- const filepath = `${config.outputDir}/scenario-step-${i + 1}-${ts}.png`;
272
- await page.screenshot({ path: filepath });
273
- detail = `Screenshot: ${filepath}`;
274
- break;
275
- }
276
- case "evaluate": {
277
- const expr = step.target ?? step.value;
278
- if (expr) {
279
- const res = await page.evaluate(expr);
280
- detail = `Evaluated: ${expr}${res !== undefined ? ` → ${JSON.stringify(res).slice(0, 120)}` : ""}`;
272
+ case "screenshot": {
273
+ const config = bs.getConfig();
274
+ const { mkdirSync } = await import("node:fs");
275
+ mkdirSync(config.outputDir, { recursive: true });
276
+ const ts = new Date().toISOString().replace(/[:.]/g, "-");
277
+ const filepath = `${config.outputDir}/scenario-step-${i + 1}-${ts}.png`;
278
+ await page.screenshot({ path: filepath });
279
+ detail = `Screenshot: ${filepath}`;
280
+ break;
281
281
  }
282
- else {
283
- status = "skipped";
284
- detail = "No expression";
282
+ case "evaluate": {
283
+ const expr = step.target ?? step.value;
284
+ if (expr) {
285
+ const res = await page.evaluate(expr);
286
+ detail = `Evaluated: ${expr}${res !== undefined ? ` → ${JSON.stringify(res).slice(0, 120)}` : ""}`;
287
+ }
288
+ else {
289
+ status = "skipped";
290
+ detail = "No expression";
291
+ }
292
+ break;
285
293
  }
286
- break;
287
- }
288
- case "fill_form": {
289
- if (step.fields && step.fields.length > 0) {
290
- for (const f of step.fields) {
291
- await page.fill(f.name, f.value, { timeout: bs.getConfig().actionTimeout || 5000 });
294
+ case "fill_form": {
295
+ if (step.fields && step.fields.length > 0) {
296
+ for (const f of step.fields) {
297
+ await page.fill(f.name, f.value, { timeout: bs.getConfig().actionTimeout || 5000 });
298
+ }
299
+ detail = `Filled ${step.fields.length} field(s)`;
300
+ }
301
+ else {
302
+ status = "skipped";
303
+ detail = "No fields";
292
304
  }
293
- detail = `Filled ${step.fields.length} field(s)`;
305
+ break;
294
306
  }
295
- else {
296
- status = "skipped";
297
- detail = "No fields";
307
+ case "note":
308
+ default: {
309
+ detail = `Note: ${step.raw}`;
310
+ break;
298
311
  }
299
- break;
300
- }
301
- case "note":
302
- default: {
303
- detail = `Note: ${step.raw}`;
304
- break;
305
312
  }
306
313
  }
314
+ catch (err) {
315
+ status = "fail";
316
+ detail = err instanceof Error ? err.message : String(err);
317
+ }
318
+ const durationMs = Date.now() - start;
319
+ results.push({ step: i + 1, action: step.action, status, detail, durationMs });
320
+ // Per-step pause for readable video (skip for wait actions already paused).
321
+ if (step.action !== "wait") {
322
+ await sleep(step.waitMs ?? stepDelay);
323
+ }
307
324
  }
308
- catch (err) {
309
- status = "fail";
310
- detail = err instanceof Error ? err.message : String(err);
311
- }
312
- const durationMs = Date.now() - start;
313
- results.push({ step: i + 1, action: step.action, status, detail, durationMs });
314
- // Per-step pause for readable video (skip for wait actions already paused).
315
- if (step.action !== "wait") {
316
- await sleep(step.waitMs ?? stepDelay);
325
+ // Optional recording stop.
326
+ if (record) {
327
+ try {
328
+ const stop = await bs.stopRecording();
329
+ videoPath = stop.outputPath;
330
+ durationSeconds = stop.durationSeconds;
331
+ }
332
+ catch (err) {
333
+ const msg = err instanceof Error ? err.message : String(err);
334
+ return finalize(results, videoPath, durationSeconds, true, `Recording stop failed: ${msg}`);
335
+ }
317
336
  }
337
+ return finalize(results, videoPath, durationSeconds, true);
318
338
  }
319
- // Optional recording stop.
320
- if (record) {
321
- try {
322
- const stop = await bs.stopRecording();
323
- videoPath = stop.outputPath;
324
- durationSeconds = stop.durationSeconds;
325
- }
326
- catch (err) {
327
- const msg = err instanceof Error ? err.message : String(err);
328
- return finalize(results, videoPath, durationSeconds, true, `Recording stop failed: ${msg}`);
339
+ finally {
340
+ // Finalize recording before closing the browser so Playwright can flush
341
+ // the video. The explicit stop paths above handle normal recording;
342
+ // this also covers cancellation and unexpected step errors.
343
+ if (!keepBrowserOpen) {
344
+ if (record && bs.getRecordStatus().active) {
345
+ try {
346
+ const stop = await bs.stopRecording();
347
+ videoPath = stop.outputPath;
348
+ durationSeconds = stop.durationSeconds;
349
+ }
350
+ catch {
351
+ // Preserve the original scenario error/result while still closing.
352
+ }
353
+ }
354
+ await bs.close();
329
355
  }
330
356
  }
331
- return finalize(results, videoPath, durationSeconds, true);
332
357
  },
333
358
  };
334
359
  try {
@@ -1,11 +1,15 @@
1
1
  import { MODELS } from "./models.generated.js";
2
+ import { type OpenAICodexSubscriptionModelId } from "./openai-codex-models.js";
2
3
  import type { Api, KnownProvider, Model, ModelThinkingLevel, Usage } from "./types.js";
3
- type ModelApi<TProvider extends KnownProvider, TModelId extends keyof (typeof MODELS)[TProvider]> = (typeof MODELS)[TProvider][TModelId] extends {
4
+ type ModelsWithOverlays = typeof MODELS & {
5
+ "openai-codex": (typeof MODELS)["openai-codex"] & Record<OpenAICodexSubscriptionModelId, Model<"openai-codex-responses">>;
6
+ };
7
+ type ModelApi<TProvider extends KnownProvider, TModelId extends keyof ModelsWithOverlays[TProvider]> = ModelsWithOverlays[TProvider][TModelId] extends {
4
8
  api: infer TApi;
5
9
  } ? (TApi extends Api ? TApi : never) : never;
6
- export declare function getModel<TProvider extends KnownProvider, TModelId extends keyof (typeof MODELS)[TProvider]>(provider: TProvider, modelId: TModelId): Model<ModelApi<TProvider, TModelId>>;
10
+ export declare function getModel<TProvider extends KnownProvider, TModelId extends keyof ModelsWithOverlays[TProvider]>(provider: TProvider, modelId: TModelId): Model<ModelApi<TProvider, TModelId>>;
7
11
  export declare function getProviders(): KnownProvider[];
8
- export declare function getModels<TProvider extends KnownProvider>(provider: TProvider): Model<ModelApi<TProvider, keyof (typeof MODELS)[TProvider]>>[];
12
+ export declare function getModels<TProvider extends KnownProvider>(provider: TProvider): Model<ModelApi<TProvider, keyof ModelsWithOverlays[TProvider]>>[];
9
13
  export declare function calculateCost<TApi extends Api>(model: Model<TApi>, usage: Usage): Usage["cost"];
10
14
  export declare function getSupportedThinkingLevels<TApi extends Api>(model: Model<TApi>): ModelThinkingLevel[];
11
15
  export declare function clampThinkingLevel<TApi extends Api>(model: Model<TApi>, level: ModelThinkingLevel): ModelThinkingLevel;
@@ -1 +1 @@
1
- {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../../src/sdk/ai/models.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,KAAK,EAAE,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAavF,KAAK,QAAQ,CACZ,SAAS,SAAS,aAAa,EAC/B,QAAQ,SAAS,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,SAAS,CAAC,IAC9C,CAAC,OAAO,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,SAAS;IAAE,GAAG,EAAE,MAAM,IAAI,CAAA;CAAE,GAAG,CAAC,IAAI,SAAS,GAAG,GAAG,IAAI,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC;AAEjH,wBAAgB,QAAQ,CAAC,SAAS,SAAS,aAAa,EAAE,QAAQ,SAAS,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,SAAS,CAAC,EAC1G,QAAQ,EAAE,SAAS,EACnB,OAAO,EAAE,QAAQ,GACf,KAAK,CAAC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAGtC;AAED,wBAAgB,YAAY,IAAI,aAAa,EAAE,CAE9C;AAED,wBAAgB,SAAS,CAAC,SAAS,SAAS,aAAa,EACxD,QAAQ,EAAE,SAAS,GACjB,KAAK,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAGhE;AAED,wBAAgB,aAAa,CAAC,IAAI,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAO/F;AAID,wBAAgB,0BAA0B,CAAC,IAAI,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,kBAAkB,EAAE,CASrG;AAED,wBAAgB,kBAAkB,CAAC,IAAI,SAAS,GAAG,EAClD,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,KAAK,EAAE,kBAAkB,GACvB,kBAAkB,CAgBpB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,SAAS,GAAG,EAC9C,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,EACjC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,GAC/B,OAAO,CAGT"}
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../../src/sdk/ai/models.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAEN,KAAK,8BAA8B,EACnC,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAqBvF,KAAK,kBAAkB,GAAG,OAAO,MAAM,GAAG;IACzC,cAAc,EAAE,CAAC,OAAO,MAAM,CAAC,CAAC,cAAc,CAAC,GAC9C,MAAM,CAAC,8BAA8B,EAAE,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC;CACzE,CAAC;AAEF,KAAK,QAAQ,CACZ,SAAS,SAAS,aAAa,EAC/B,QAAQ,SAAS,MAAM,kBAAkB,CAAC,SAAS,CAAC,IACjD,kBAAkB,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,SAAS;IAAE,GAAG,EAAE,MAAM,IAAI,CAAA;CAAE,GAAG,CAAC,IAAI,SAAS,GAAG,GAAG,IAAI,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC;AAEpH,wBAAgB,QAAQ,CAAC,SAAS,SAAS,aAAa,EAAE,QAAQ,SAAS,MAAM,kBAAkB,CAAC,SAAS,CAAC,EAC7G,QAAQ,EAAE,SAAS,EACnB,OAAO,EAAE,QAAQ,GACf,KAAK,CAAC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAGtC;AAED,wBAAgB,YAAY,IAAI,aAAa,EAAE,CAE9C;AAED,wBAAgB,SAAS,CAAC,SAAS,SAAS,aAAa,EACxD,QAAQ,EAAE,SAAS,GACjB,KAAK,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAGnE;AAED,wBAAgB,aAAa,CAAC,IAAI,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAO/F;AAID,wBAAgB,0BAA0B,CAAC,IAAI,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,kBAAkB,EAAE,CASrG;AAED,wBAAgB,kBAAkB,CAAC,IAAI,SAAS,GAAG,EAClD,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,KAAK,EAAE,kBAAkB,GACvB,kBAAkB,CAgBpB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,SAAS,GAAG,EAC9C,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,EACjC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,SAAS,GAC/B,OAAO,CAGT"}
@@ -1,4 +1,5 @@
1
1
  import { MODELS } from "./models.generated.js";
2
+ import { OPENAI_CODEX_SUBSCRIPTION_MODELS, } from "./openai-codex-models.js";
2
3
  const modelRegistry = new Map();
3
4
  // Initialize registry from MODELS on module load
4
5
  for (const [provider, models] of Object.entries(MODELS)) {
@@ -8,6 +9,13 @@ for (const [provider, models] of Object.entries(MODELS)) {
8
9
  }
9
10
  modelRegistry.set(provider, providerModels);
10
11
  }
12
+ // Keep subscription-only Codex IDs outside the generated upstream catalog.
13
+ const openAICodexModels = modelRegistry.get("openai-codex");
14
+ if (openAICodexModels) {
15
+ for (const model of OPENAI_CODEX_SUBSCRIPTION_MODELS) {
16
+ openAICodexModels.set(model.id, model);
17
+ }
18
+ }
11
19
  export function getModel(provider, modelId) {
12
20
  const providerModels = modelRegistry.get(provider);
13
21
  return providerModels?.get(modelId);
@@ -0,0 +1,9 @@
1
+ import type { Model } from "./types.js";
2
+ /**
3
+ * Subscription model IDs that are not part of the generated upstream catalog.
4
+ * These are registered separately so regenerating models.generated.ts does not
5
+ * overwrite the subscription-only additions.
6
+ */
7
+ export type OpenAICodexSubscriptionModelId = "gpt-5.6" | "gpt-5.6-luna" | "gpt-5.6-terra" | "gpt-5.6-sol" | "gpt-5.6-luna-pro" | "gpt-5.6-terra-pro" | "gpt-5.6-sol-pro";
8
+ export declare const OPENAI_CODEX_SUBSCRIPTION_MODELS: Model<"openai-codex-responses">[];
9
+ //# sourceMappingURL=openai-codex-models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-codex-models.d.ts","sourceRoot":"","sources":["../../../src/sdk/ai/openai-codex-models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAIxC;;;;GAIG;AACH,MAAM,MAAM,8BAA8B,GACvC,SAAS,GACT,cAAc,GACd,eAAe,GACf,aAAa,GACb,kBAAkB,GAClB,mBAAmB,GACnB,iBAAiB,CAAC;AAErB,eAAO,MAAM,gCAAgC,EAAE,KAAK,CAAC,wBAAwB,CAAC,EAyB3E,CAAC"}
@@ -0,0 +1,27 @@
1
+ const OPENAI_CODEX_BASE_URL = "https://chatgpt.com/backend-api";
2
+ export const OPENAI_CODEX_SUBSCRIPTION_MODELS = [
3
+ "GPT-5.6",
4
+ "GPT-5.6 Luna",
5
+ "GPT-5.6 Terra",
6
+ "GPT-5.6 Sol",
7
+ "GPT-5.6 Luna Pro",
8
+ "GPT-5.6 Terra Pro",
9
+ "GPT-5.6 Sol Pro",
10
+ ].map((name) => ({
11
+ id: name.toLowerCase().replaceAll(" ", "-"),
12
+ name,
13
+ api: "openai-codex-responses",
14
+ provider: "openai-codex",
15
+ baseUrl: OPENAI_CODEX_BASE_URL,
16
+ reasoning: true,
17
+ thinkingLevelMap: { xhigh: "xhigh", minimal: "low" },
18
+ input: ["text", "image"],
19
+ cost: {
20
+ input: 5,
21
+ output: 30,
22
+ cacheRead: 0.5,
23
+ cacheWrite: 0,
24
+ },
25
+ contextWindow: 272000,
26
+ maxTokens: 128000,
27
+ }));
@@ -1 +1 @@
1
- {"version":3,"file":"agent-bridge.d.ts","sourceRoot":"","sources":["../../src/server/agent-bridge.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent-bridge.d.ts","sourceRoot":"","sources":["../../src/server/agent-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAaH,OAAO,EAQL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EAEtB,MAAM,8BAA8B,CAAC;AAYtC,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,8BAA8B,CAAC;AAYtC,OAAO,EACL,kBAAkB,IAAI,yBAAyB,EAEhD,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAG3E,UAAU,4BAA4B;IACpC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;CACpC;AAkBD,sEAAsE;AACtE,MAAM,MAAM,oBAAoB,GAAG,OAAO,yBAAyB,CAAC;AAmCpE,MAAM,WAAW,kBAAkB;IACjC,2EAA2E;IAC3E,GAAG,EAAE,MAAM,CAAC;IACZ,qFAAqF;IACrF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,IAAI,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACnC;;;;;OAKG;IACH,0BAA0B,EAAE,CAAC,GAAG,EAAE;QAChC,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;KACrB,KAAK,IAAI,CAAC;IACX;;;OAGG;IACH,yBAAyB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;IAC/B;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB;;;;OAIG;IACH,cAAc,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;IAC9C;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,oBAAoB,CAAC;IAC1C;;;;;OAKG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAsiBD,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAC,CAAe;IAC/B,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,WAAW,CAAC,CAAa;IACjC,OAAO,CAAC,OAAO,CAAC,CAA0B;IAC1C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;IAC1C;;;;;OAKG;IACH,OAAO,CAAC,aAAa,CAAC,CAAgB;IACtC;;;;OAIG;IACH,OAAO,CAAC,aAAa,CAAC,CAAiB;IACvC;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB,CAAC,CAAS;IACpC,sFAAsF;IACtF,OAAO,CAAC,iBAAiB,CAMT;IAChB,OAAO,CAAC,WAAW,CACuD;IAC1E,OAAO,CAAC,mBAAmB,CAA+B;IAC1D;;;;OAIG;IACH,OAAO,CAAC,cAAc,CAAuB;gBAEjC,IAAI,EAAE,kBAAkB;IAIpC;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA+U5B,OAAO,CAAC,4BAA4B;IAWpC;;;;;OAKG;YACW,qBAAqB;IAcnC;;;OAGG;IACG,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;YAI7B,oBAAoB;IAoBlC;;;;;;;;;;;;OAYG;IACH;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,0BAA0B;IA+GlC;;;;;;;;;;;;;;;OAeG;IACH;;;;;;;;OAQG;IACH,wBAAwB,IAAI,MAAM,GAAG,SAAS;IAI9C;;;;OAIG;IACH,eAAe,IACX;QAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GACxE,SAAS;IAIb,gBAAgB,IAAI,KAAK,CAAC;QACxB,IAAI,EAAE,MAAM,CAAC;QACb,EAAE,EAAE,MAAM,CAAC;QACX,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;IAgBF,iBAAiB,IAAI;QACnB,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,YAAY,GAAG,YAAY,GAAG,SAAS,CAAC;QACtE,QAAQ,EAAE;YACR,QAAQ,EAAE,OAAO,CAAC;YAClB,UAAU,EAAE,OAAO,CAAC;YACpB,UAAU,EAAE,OAAO,CAAC;YACpB,MAAM,EAAE,OAAO,CAAC;SACjB,CAAC;KACH;IAYD,OAAO,CAAC,iBAAiB;IAmCnB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC;IA2DpE;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,iCAAiC;IAkBzC;;;;;;OAMG;IACH,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAoBpD;;;;;;;;;;;OAWG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BrE;;;;;;;;OAQG;IACG,OAAO,CACX,OAAO,CAAC,EAAE,MAAM,GAAG,4BAA4B,GAC9C,OAAO,CAAC,gBAAgB,CAAC;IAY5B,OAAO,IAAI,IAAI;IA0Bf;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IAOrB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,sBAAsB;IAsD9B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,EAAE,iBAAiB,GAAG,IAAI;CAwczC"}
@@ -610,19 +610,14 @@ export class AgentBridge {
610
610
  observationalMemory(ext);
611
611
  },
612
612
  ];
613
- // Agent-behavior toggles. Loaded once at bridge construction so changes
614
- // take effect on the next session (not mid-turn).
615
- const agentSettings = this.opts.agentSettings ??
616
- loadAgentSettings(this.opts.cwd, this.opts.agentDir);
617
- // Only register the subagent tool when the toggle is enabled.
618
- if (agentSettings.subagentsEnabled) {
619
- extensionFactories.splice(4, 0, async (ext) => {
620
- subagentExt(ext, {
621
- disabledAgents: agentSettings.disabledSubagents,
622
- emitSubagentEvent: (event) => this.emitAndBuffer(event),
623
- });
613
+ // Always register subagent tool execute() checks settings from disk
614
+ // on every invocation, so toggling takes effect immediately (no restart).
615
+ extensionFactories.splice(4, 0, async (ext) => {
616
+ subagentExt(ext, {
617
+ disabledAgents: [],
618
+ emitSubagentEvent: (event) => this.emitAndBuffer(event),
624
619
  });
625
- }
620
+ });
626
621
  // Register the bundled MCP adapter. Static import is required so the
627
622
  // adapter is included in compiled standalone binaries (Bun build), where
628
623
  // filesystem-based discovery via jiti fails because the source files are
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aexol/spectral",
3
- "version": "0.9.84",
3
+ "version": "0.9.86",
4
4
  "description": "AI coding agent for Aexol with relay-based browser access.",
5
5
  "type": "module",
6
6
  "private": false,