archal 0.9.19 → 0.10.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 (83) hide show
  1. package/README.md +62 -154
  2. package/agents/openclaw/.archal.json +8 -0
  3. package/agents/openclaw/Dockerfile +97 -0
  4. package/agents/openclaw/README.md +113 -0
  5. package/agents/openclaw/drive.mjs +301 -0
  6. package/agents/openclaw/extract-openclaw-response-text.mjs +49 -0
  7. package/agents/openclaw/package.json +9 -0
  8. package/agents/openclaw/scenarios/acceptance/github-escalation-and-cleanup.md +43 -0
  9. package/agents/openclaw/scenarios/acceptance/jira-linear-release-triage.md +39 -0
  10. package/agents/openclaw/scenarios/acceptance/multi-clone-customer-risk-review.md +45 -0
  11. package/agents/openclaw/scenarios/acceptance/slack-incident-handoff.md +42 -0
  12. package/agents/openclaw/scenarios/acceptance/stripe-dunning-remediation.md +40 -0
  13. package/agents/openclaw/scenarios/close-stale-issues.md +35 -0
  14. package/agents/openclaw/scenarios/github-issue-triage-read-only.md +44 -0
  15. package/agents/openclaw/workspace/AGENTS.md +23 -0
  16. package/agents/openclaw/workspace/IDENTITY.md +8 -0
  17. package/agents/openclaw/workspace/SOUL.md +14 -0
  18. package/agents/openclaw/workspace/TOOLS.md +54 -0
  19. package/clone-assets/apify/tools.json +256 -22
  20. package/clone-assets/calcom/tools.json +2062 -0
  21. package/clone-assets/clickup/tools.json +3245 -0
  22. package/clone-assets/customerio/tools.json +2350 -0
  23. package/clone-assets/datadog/tools.json +734 -0
  24. package/clone-assets/github/tools.json +307 -27
  25. package/clone-assets/gitlab/tools.json +10688 -0
  26. package/clone-assets/google-workspace/tools.json +18 -6
  27. package/clone-assets/hubspot/tools.json +1604 -0
  28. package/clone-assets/jira/fidelity.json +1 -1
  29. package/clone-assets/jira/tools.json +266 -543
  30. package/clone-assets/linear/tools.json +278 -40
  31. package/clone-assets/ownerrez/tools.json +548 -0
  32. package/clone-assets/pricelabs/tools.json +343 -0
  33. package/clone-assets/sentry/tools.json +2821 -0
  34. package/clone-assets/slack/tools.json +1 -2
  35. package/clone-assets/stripe/tools.json +299 -46
  36. package/clone-assets/supabase/tools.json +437 -0
  37. package/clone-assets/unipile/tools.json +408 -0
  38. package/clone-assets/webflow/tools.json +2185 -0
  39. package/dist/autoloop-worker-types-CHaclqtD.d.cts +160 -0
  40. package/dist/cli.cjs +127896 -84811
  41. package/dist/{vitest/chunk-L36NXAU6.js → commands/autoloop-hosted-control-plane-client.cjs} +16344 -18845
  42. package/dist/commands/autoloop-hosted-control-plane-client.d.cts +133 -0
  43. package/dist/commands/autoloop-pr-verification.cjs +4312 -0
  44. package/dist/commands/autoloop-pr-verification.d.cts +19 -0
  45. package/dist/commands/autoloop-result-parser.cjs +27058 -0
  46. package/dist/commands/autoloop-result-parser.d.cts +196 -0
  47. package/dist/commands/autoloop-worker.cjs +38488 -0
  48. package/dist/commands/autoloop-worker.d.cts +102 -0
  49. package/dist/eval-shim.mjs +216 -0
  50. package/dist/index.cjs +15 -2
  51. package/dist/index.d.cts +3 -1
  52. package/dist/layer-1-ziaLpfLr.d.cts +62 -0
  53. package/dist/metafile-cjs.json +1 -0
  54. package/dist/reporter-entry.js +3 -0
  55. package/dist/sdk/index.cjs +51630 -0
  56. package/dist/sdk/index.d.cts +626 -0
  57. package/dist/vitest/chunk-2PDHTPZC.js +4867 -0
  58. package/dist/vitest/chunk-7L4BBB6M.js +2613 -0
  59. package/dist/vitest/index.cjs +6152 -75718
  60. package/dist/vitest/index.d.ts +22 -86
  61. package/dist/vitest/index.js +45 -414
  62. package/dist/vitest/runtime/hosted-session-reaper.cjs +682 -34399
  63. package/dist/vitest/runtime/hosted-session-reaper.js +1 -1
  64. package/dist/vitest/runtime/setup-files.js +2 -2
  65. package/manifest.json +9 -0
  66. package/package.json +20 -14
  67. package/skills/archal-agent/SKILL.md +86 -0
  68. package/skills/autoloop/SKILL.md +391 -0
  69. package/skills/autoloop/references/hosted-sources.md +94 -0
  70. package/skills/autoloop/references/trace-schema-mapping.md +104 -0
  71. package/skills/eval/SKILL.md +17 -15
  72. package/skills/free-account/SKILL.md +103 -0
  73. package/skills/install-agent/SKILL.md +202 -0
  74. package/skills/onboard/SKILL.md +29 -18
  75. package/skills/scenario/SKILL.md +36 -18
  76. package/skills/seed/SKILL.md +238 -0
  77. package/skills/vitest/SKILL.md +3 -2
  78. package/dist/harness.cjs +0 -62
  79. package/dist/harness.d.cts +0 -20
  80. package/dist/seed/dynamic-generator.cjs +0 -45687
  81. package/dist/seed/dynamic-generator.d.cts +0 -106
  82. package/dist/vitest/chunk-WZ7SA4CK.js +0 -47369
  83. package/skills/attach/SKILL.md +0 -402
@@ -8,12 +8,10 @@ import {
8
8
  readArchalVitestConfig,
9
9
  resetArchalClones,
10
10
  resolveRuntimeModule
11
- } from "./chunk-L36NXAU6.js";
11
+ } from "./chunk-2PDHTPZC.js";
12
12
  import {
13
- encodeConfig,
14
- getCredentials,
15
- getSessionIdFilePath
16
- } from "./chunk-WZ7SA4CK.js";
13
+ encodeConfig
14
+ } from "./chunk-7L4BBB6M.js";
17
15
 
18
16
  // src/index.ts
19
17
  import { dirname, isAbsolute, resolve as resolve2 } from "path";
@@ -145,7 +143,7 @@ function installReporterLifecycleCheck(projectName) {
145
143
  return;
146
144
  }
147
145
  process.stderr.write(
148
- '\n\x1B[33m\x1B[1mArchal Vitest could not upload test results\n\x1B[0m\x1B[33mThe Archal reporter started, but Vitest did not call its final upload\nstep. Test results will not appear in the dashboard.\n\nCommon causes:\n - A Vitest upgrade changed per-project reporter lifecycle timing.\n - A consumer config filtered out reporters (look for reporter overrides).\n - The ArchalReporter threw before reaching collectAndReport.\n\nWhat to check:\n 1. Grep stderr for "[archal/reporter]". If any line appears, the\n reporter did run and this warning is a false positive.\n 2. If no [archal/reporter] lines exist at all, the reporter was\n installed but never called. Verify your vitest config wires it\n at the level Vitest actually honors (workspace root vs project).\n'
146
+ '\n\x1B[33m\x1B[1mArchal Vitest reporter did not finish lifecycle diagnostics\n\x1B[0m\x1B[33mThe Archal reporter started, but Vitest did not call its final\nlifecycle step. Benchmark Results are submitted by the scenario runner.\n\nCommon causes:\n - A Vitest upgrade changed per-project reporter lifecycle timing.\n - A consumer config filtered out reporters (look for reporter overrides).\n - The ArchalReporter threw before reaching collectAndReport.\n\nWhat to check:\n 1. Grep stderr for "[archal/reporter]". If any line appears, the\n reporter did run and this warning is a false positive.\n 2. If no [archal/reporter] lines exist at all, the reporter was\n installed but never called. Verify your vitest config wires it\n at the level Vitest actually honors (workspace root vs project).\n'
149
147
  );
150
148
  process.stderr.write("\x1B[0m\n");
151
149
  for (const survivor of survivors) {
@@ -193,7 +191,7 @@ function installWorkspaceReporterCheck(projectNames) {
193
191
  return;
194
192
  }
195
193
  process.stderr.write(
196
- "\n\x1B[33m\x1B[1mArchal Vitest did not upload workspace test results\n\x1B[0m\x1B[33marchalVitestWorkspace() configured hosted clone projects, but no\nroot Archal reporter was registered. Requests were routed to the\nhosted clones, but dashboard upload was skipped.\n\nFix: add a root vitest.config.ts alongside vitest.workspace.ts:\n\n import { defineConfig } from 'vitest/config';\n import { archalVitestRootConfig } from 'archal/vitest';\n\n export default defineConfig(archalVitestRootConfig());\n\nIf you do not need workspace mode, use withArchal() instead.\n\x1B[0m\n"
194
+ "\n\x1B[33m\x1B[1mArchal Vitest did not run workspace reporter diagnostics\n\x1B[0m\x1B[33marchalVitestWorkspace() configured hosted clone projects, but no\nroot Archal reporter was registered. Requests were routed to the\nhosted clones, but reporter lifecycle diagnostics were skipped.\n\nFix: add a root vitest.config.ts alongside vitest.workspace.ts:\n\n import { defineConfig } from 'vitest/config';\n import { archalVitestRootConfig } from 'archal/vitest';\n\n export default defineConfig(archalVitestRootConfig());\n\nIf you do not need workspace mode, use withArchal() instead.\n\x1B[0m\n"
197
195
  );
198
196
  for (const survivor of survivors) {
199
197
  try {
@@ -254,7 +252,7 @@ function installImportSentinel() {
254
252
  if (process.env[ARCHAL_VITEST_CONFIG_ENV2]) return;
255
253
  warningWritten = true;
256
254
  process.stderr.write(
257
- "\n\x1B[33m\x1B[1mArchal Vitest was imported but not configured\n\x1B[0m\x1B[33mSomething in your test run imported `archal/vitest`, but no\nArchal Vitest helper was called (`withArchal`, `archalVitestProject`,\n`archalVitestRootConfig`). Test results will not appear in the dashboard.\n\nFix: in your vitest.config.ts, wrap your test block with withArchal():\n\n import { defineConfig } from 'vitest/config';\n import { withArchal } from 'archal/vitest';\n export default defineConfig({\n test: withArchal({}, { services: {} }),\n });\n\nOr run `archal init` from your project root and we will wire it for you.\n\x1B[0m\n"
255
+ "\n\x1B[33m\x1B[1mArchal Vitest was imported but not configured\n\x1B[0m\x1B[33mSomething in your test run imported `archal/vitest`, but no\nArchal Vitest helper was called (`withArchal`, `archalVitestProject`,\n`archalVitestRootConfig`). Benchmark Results are submitted by the scenario runner.\n\nFix: in your vitest.config.ts, wrap your test block with withArchal():\n\n import { defineConfig } from 'vitest/config';\n import { withArchal } from 'archal/vitest';\n export default defineConfig({\n test: withArchal({}, { services: {} }),\n });\n\nOr run `archal init` from your project root and we will wire it for you.\n\x1B[0m\n"
258
256
  );
259
257
  };
260
258
  const sigintHandler = () => {
@@ -274,14 +272,8 @@ function installImportSentinel() {
274
272
  }
275
273
 
276
274
  // src/reporter.ts
277
- import picomatch from "picomatch";
278
275
  var LOG_PREFIX = "[archal/reporter]";
279
276
  var STRICT_ENV = "ARCHAL_REPORTER_STRICT";
280
- var REPORTER_TOKEN_ENV = "ARCHAL_REPORTER_TOKEN";
281
- var REPORTER_AUTH_URL_ENV = "ARCHAL_REPORTER_AUTH_URL";
282
- var REPORTER_AUTH_BASE_URL_ENV = "ARCHAL_REPORTER_AUTH_BASE_URL";
283
- var REPORTER_BYPASS_SECRET_ENV = "ARCHAL_REPORTER_VERCEL_AUTOMATION_BYPASS_SECRET";
284
- var REPORTER_WORKSPACE_ENV = "ARCHAL_REPORTER_WORKSPACE_ID";
285
277
  var WORKSPACE_REPORTER_MARKER_ENV = "ARCHAL_VITEST_WORKSPACE_REPORTER_MARKER";
286
278
  function isStrict() {
287
279
  return process.env[STRICT_ENV] === "1";
@@ -294,112 +286,43 @@ function warn(message) {
294
286
  }
295
287
  process.stderr.write(line);
296
288
  }
297
- function info(message) {
289
+ function diagnostic(message) {
298
290
  process.stderr.write(`${LOG_PREFIX} ${message}
299
291
  `);
300
292
  }
301
- async function resolveReporterToken() {
302
- const explicitReporterToken = process.env[REPORTER_TOKEN_ENV]?.trim();
303
- if (explicitReporterToken) {
304
- return explicitReporterToken;
305
- }
306
- try {
307
- const creds = getCredentials();
308
- return creds?.token ?? process.env["ARCHAL_TOKEN"]?.trim();
309
- } catch (authErr) {
310
- warn(`auth resolution failed: ${authErr instanceof Error ? authErr.message : String(authErr)}`);
311
- return void 0;
312
- }
313
- }
314
- function resolveReporterBaseUrl() {
315
- return (process.env[REPORTER_AUTH_URL_ENV]?.trim() || process.env[REPORTER_AUTH_BASE_URL_ENV]?.trim() || process.env["ARCHAL_AUTH_URL"]?.trim() || process.env["ARCHAL_AUTH_BASE_URL"]?.trim() || "https://www.archal.ai").replace(/\/+$/, "");
316
- }
317
- function buildReporterHeaders(token) {
318
- const headers = new Headers({
319
- "content-type": "application/json",
320
- authorization: `Bearer ${token}`
321
- });
322
- const bypassSecret = process.env[REPORTER_BYPASS_SECRET_ENV]?.trim();
323
- if (bypassSecret) {
324
- headers.set("x-vercel-protection-bypass", bypassSecret);
325
- }
326
- const workspaceId = resolveReporterWorkspaceId();
327
- if (workspaceId) {
328
- headers.set("x-archal-workspace", workspaceId);
329
- }
330
- return headers;
331
- }
332
- function resolveReporterWorkspaceId() {
333
- const reporterWorkspaceId = process.env[REPORTER_WORKSPACE_ENV]?.trim();
334
- if (reporterWorkspaceId) {
335
- return reporterWorkspaceId;
336
- }
337
- const cliWorkspaceId = process.env["ARCHAL_WORKSPACE_ID"]?.trim();
338
- return cliWorkspaceId || void 0;
339
- }
340
- function normalizePathForMatch(path) {
341
- return path.replaceAll("\\", "/");
342
- }
343
- function buildIncludeMatcher(pattern) {
344
- const normalizedPattern = normalizePathForMatch(pattern).replace(/^\.?\//, "");
345
- const patterns = normalizedPattern.startsWith("**/") ? [normalizedPattern] : [normalizedPattern, `**/${normalizedPattern}`];
346
- const matchers = patterns.map((candidate) => picomatch(candidate, { dot: true }));
347
- return (filePath) => {
348
- const normalizedFile = normalizePathForMatch(filePath);
349
- return matchers.some((matcher) => matcher(normalizedFile));
350
- };
351
- }
352
293
  function readEncodedConfigFromTarget(target) {
353
294
  const env = target?.config?.env;
354
295
  return env?.[ARCHAL_VITEST_CONFIG_ENV];
355
296
  }
356
- function readProjectIncludePatterns(project) {
357
- const config = project?.config;
358
- const include = Array.isArray(config?.include) ? config.include : Array.isArray(config?.test?.include) ? config.test.include : [];
359
- return include.filter(
360
- (pattern) => typeof pattern === "string" && pattern.length > 0
361
- );
362
- }
363
297
  var ArchalReporter = class {
364
- startTime = 0;
365
298
  ctx = null;
366
299
  lifecycleMarkerPath = "";
367
- workspaceReporterMarkerPath = "";
368
- projects = [];
369
300
  /**
370
- * In-flight upload promise for the current run. Vitest v3+ fires both
371
- * `onFinished` and `onTestRunEnd` in parallel via `Promise.all` (see
372
- * vitest@3.x packages/vitest/src/node/test-run.ts), so without this guard
373
- * the reporter uploads results twice and the dashboard shows duplicate
374
- * rows. Reset at the start of each run (onTestRunStart / onWatcherRerun)
375
- * so watch-mode reruns still upload.
301
+ * In-flight lifecycle promise for the current run. Vitest v3+ fires both
302
+ * `onFinished` and `onTestRunEnd` in parallel via `Promise.all`, so without
303
+ * this guard the reporter emits duplicate diagnostics. Reset at the start
304
+ * of each run (onTestRunStart / onWatcherRerun) so watch-mode reruns still
305
+ * report.
376
306
  */
377
- activeUpload = null;
307
+ activeLifecycle = null;
378
308
  onInit(ctx) {
379
- this.startTime = Date.now();
380
309
  this.ctx = ctx;
381
- this.projects = this.resolveProjects();
382
- const projectName = this.projects[0]?.config.projectName ?? "vitest";
310
+ const projectName = this.resolveProjectName();
383
311
  this.lifecycleMarkerPath = installReporterLifecycleCheck(projectName);
384
- this.workspaceReporterMarkerPath = process.env[WORKSPACE_REPORTER_MARKER_ENV] ?? "";
385
- markWorkspaceReporterInstalled(this.workspaceReporterMarkerPath);
386
- this.workspaceReporterMarkerPath = "";
312
+ const workspaceReporterMarkerPath = process.env[WORKSPACE_REPORTER_MARKER_ENV] ?? "";
313
+ markWorkspaceReporterInstalled(workspaceReporterMarkerPath);
387
314
  }
388
- /**
389
- * v3+ canonical start-of-run hook. Resetting the upload guard here lets
390
- * watch-mode reruns upload their own results.
391
- */
392
315
  onTestRunStart(_specifications) {
393
- this.activeUpload = null;
394
- this.startTime = Date.now();
316
+ this.activeLifecycle = null;
395
317
  }
396
- /**
397
- * v2 watch-mode start-of-rerun hook. Same purpose as onTestRunStart for
398
- * v3+. Vitest v2 has no onTestRunStart, so we reset here instead.
399
- */
400
318
  onWatcherRerun(_files, _trigger) {
401
- this.activeUpload = null;
402
- this.startTime = Date.now();
319
+ this.activeLifecycle = null;
320
+ }
321
+ async onFinished(files, _errors) {
322
+ await this.runLifecycleOnce(files?.length ?? 0, "files");
323
+ }
324
+ async onTestRunEnd(testModules, _unhandledErrors, _reason) {
325
+ await this.runLifecycleOnce(testModules?.length ?? 0, "test modules");
403
326
  }
404
327
  decodeConfig(encoded) {
405
328
  try {
@@ -408,320 +331,34 @@ var ArchalReporter = class {
408
331
  return null;
409
332
  }
410
333
  }
411
- buildProjectContext(config, includePatterns) {
412
- return {
413
- config,
414
- includePatterns,
415
- includeMatchers: includePatterns.map(buildIncludeMatcher)
416
- };
417
- }
418
- resolveProjects() {
419
- const resolvedProjects = (this.ctx?.projects ?? []).map((project) => {
334
+ resolveProjectName() {
335
+ for (const project of this.ctx?.projects ?? []) {
420
336
  const config = this.decodeConfig(readEncodedConfigFromTarget(project));
421
- return config ? this.buildProjectContext(config, readProjectIncludePatterns(project)) : null;
422
- }).filter((project) => project !== null);
423
- if (resolvedProjects.length > 0) {
424
- return resolvedProjects;
337
+ if (config?.projectName) {
338
+ return config.projectName;
339
+ }
425
340
  }
426
341
  const rootConfig = this.decodeConfig(
427
342
  readEncodedConfigFromTarget(this.ctx) ?? process.env[ARCHAL_VITEST_CONFIG_ENV]
428
343
  );
429
- return rootConfig ? [this.buildProjectContext(rootConfig, [])] : [];
430
- }
431
- /**
432
- * vitest v2 calls onFinished with an array of File objects (from @vitest/runner).
433
- * Each File extends Suite which has `tasks: Task[]`, and each Task has
434
- * `.type` ('test' | 'suite'), `.name`, `.result?.state`, `.result?.duration`.
435
- */
436
- async onFinished(files, _errors) {
437
- await this.runUploadOnce(() => this.collectAndReport(files ?? [], "v2"));
344
+ return rootConfig?.projectName ?? "vitest";
438
345
  }
439
- /**
440
- * vitest v3+ calls onTestRunEnd alongside onFinished (in parallel via
441
- * Promise.all). v4 drops onFinished entirely. The argument is an array of
442
- * test modules, each with nested children.
443
- */
444
- async onTestRunEnd(testModules) {
445
- await this.runUploadOnce(() => this.collectAndReport(testModules ?? [], "v4"));
446
- }
447
- /**
448
- * Dedupe upload work when Vitest fires multiple end-of-run hooks. In
449
- * Vitest v3+ both `onFinished` and `onTestRunEnd` fire from the same
450
- * `Promise.all` in TestRun.end(); the first hook to enter wins and the
451
- * second awaits the same in-flight upload instead of POSTing a duplicate
452
- * row to /api/test-results. The resolved promise is kept on the instance
453
- * so any late hook still no-ops — the guard is only cleared at the start
454
- * of a new run (onTestRunStart / onWatcherRerun).
455
- */
456
- async runUploadOnce(work) {
457
- if (!this.activeUpload) {
458
- this.activeUpload = work();
346
+ async runLifecycleOnce(itemCount, itemLabel) {
347
+ if (!this.activeLifecycle) {
348
+ this.activeLifecycle = this.reportLifecycleComplete(itemCount, itemLabel);
459
349
  }
460
- await this.activeUpload;
350
+ await this.activeLifecycle;
461
351
  }
462
- async collectAndReport(items, version) {
352
+ async reportLifecycleComplete(itemCount, itemLabel) {
463
353
  markReporterFired(this.lifecycleMarkerPath);
464
354
  this.lifecycleMarkerPath = "";
465
- if (this.projects.length === 0) {
466
- this.projects = this.resolveProjects();
467
- }
468
- if (this.projects.length === 0) {
469
- warn(
470
- "Archal Vitest config was unreadable. Using the default project name. If this persists, make sure withArchal() or archalVitestProject() wraps your test config."
471
- );
472
- this.projects = [
473
- this.buildProjectContext(
474
- {
475
- projectName: "vitest",
476
- services: {}
477
- },
478
- []
479
- )
480
- ];
481
- }
482
- if (items.length === 0) {
483
- warn(`No ${version === "v2" ? "files" : "test modules"} received. Nothing to upload.`);
484
- return;
485
- }
486
- const results = version === "v2" ? this.collectV2Files(items) : this.collectV4Modules(items);
487
- if (results.length === 0) {
488
- warn("Collected 0 tests after reading the test tree. Nothing to upload.");
489
- return;
490
- }
491
- const buckets = this.partitionResults(results);
492
- if (buckets.length === 0) {
493
- warn("collected tests but could not map them to an Archal Vitest project.");
494
- return;
495
- }
496
- try {
497
- for (const bucket of buckets) {
498
- await this.sendResults(bucket.project, bucket.results);
499
- }
500
- } catch (err) {
501
- warn(`failed to upload test results: ${err instanceof Error ? err.message : String(err)}`);
502
- }
503
- }
504
- /**
505
- * vitest v2: File extends Suite { tasks: Task[] }.
506
- * Task.type is 'test' | 'suite' | 'custom'. Suite tasks recurse.
507
- */
508
- collectV2Files(files) {
509
- const results = [];
510
- const collectTasks = (tasks, fileName) => {
511
- for (const task of tasks) {
512
- if (task.type === "test" || task.type === "custom") {
513
- const state = task.result?.state;
514
- results.push({
515
- name: task.name ?? "unnamed",
516
- file: fileName,
517
- status: state === "pass" ? "passed" : state === "fail" ? "failed" : "skipped",
518
- duration: task.result?.duration ?? 0,
519
- error: state === "fail" && task.result?.errors?.[0] ? task.result.errors[0].message ?? String(task.result.errors[0]) : void 0
520
- });
521
- } else if (task.type === "suite" && Array.isArray(task.tasks)) {
522
- collectTasks(task.tasks, fileName);
523
- }
524
- }
525
- };
526
- for (const file of files) {
527
- const f = file;
528
- const fileName = f.filepath ?? f.name ?? "unknown";
529
- if (Array.isArray(f.tasks)) {
530
- collectTasks(f.tasks, fileName);
531
- }
532
- }
533
- return results;
534
- }
535
- /**
536
- * vitest v4: TestModule.children() → TestSuite[].children() → TestCase[]
537
- * All accessors are methods (not properties) in v4.
538
- */
539
- collectV4Modules(testModules) {
540
- const results = [];
541
- const collectTests = (node, fileName) => {
542
- const children = typeof node.children === "function" ? node.children() : node.children ?? [];
543
- for (const child of children) {
544
- if (child.type === "test") {
545
- const result = typeof child.result === "function" ? child.result() : child.result;
546
- const diag = typeof child.diagnostic === "function" ? child.diagnostic() : child.diagnostic;
547
- const state = result?.state;
548
- results.push({
549
- name: child.name ?? "unnamed",
550
- file: fileName,
551
- status: state === "passed" ? "passed" : state === "failed" ? "failed" : "skipped",
552
- duration: diag?.duration ?? result?.duration ?? 0,
553
- error: state === "failed" && result?.errors?.[0] ? result.errors[0].message ?? String(result.errors[0]) : void 0
554
- });
555
- } else {
556
- collectTests(child, fileName);
557
- }
558
- }
559
- };
560
- for (const mod of testModules) {
561
- const m = mod;
562
- const fileName = m.moduleId ?? m.id ?? "unknown";
563
- collectTests(m, fileName);
564
- }
565
- return results;
566
- }
567
- resolveProjectForFile(fileName) {
568
- const normalizedFile = normalizePathForMatch(fileName);
569
- const onlyProject = this.projects[0] ?? null;
570
- if (this.projects.length === 1) {
571
- if (!onlyProject) {
572
- return null;
573
- }
574
- if (onlyProject.includeMatchers.length === 0) {
575
- return onlyProject;
576
- }
577
- return onlyProject.includeMatchers.some((matcher) => matcher(normalizedFile)) ? onlyProject : null;
578
- }
579
- let bestMatch = null;
580
- let bestSpecificity = -1;
581
- for (const project of this.projects) {
582
- for (const [index, matcher] of project.includeMatchers.entries()) {
583
- if (!matcher(normalizedFile)) {
584
- continue;
585
- }
586
- const specificity = project.includePatterns[index]?.length ?? 0;
587
- if (specificity > bestSpecificity) {
588
- bestMatch = project;
589
- bestSpecificity = specificity;
590
- }
591
- }
592
- }
593
- return bestMatch;
594
- }
595
- partitionResults(results) {
596
- const buckets = /* @__PURE__ */ new Map();
597
- for (const result of results) {
598
- const project = this.resolveProjectForFile(result.file);
599
- if (!project) {
600
- warn(
601
- `Could not map ${result.file} to a Vitest project config. Skipping reporter upload for that file.`
602
- );
603
- continue;
604
- }
605
- const key = project.config.projectName;
606
- const bucket = buckets.get(key);
607
- if (bucket) {
608
- bucket.results.push(result);
609
- continue;
610
- }
611
- buckets.set(key, {
612
- project,
613
- results: [result]
614
- });
615
- }
616
- return [...buckets.values()];
617
- }
618
- async sendResults(project, results) {
619
- const summary = {
620
- total: results.length,
621
- passed: results.filter((result) => result.status === "passed").length,
622
- failed: results.filter((result) => result.status === "failed").length,
623
- skipped: results.filter((result) => result.status === "skipped").length,
624
- duration: Date.now() - this.startTime
625
- };
626
- const token = await resolveReporterToken();
627
- if (!token) {
628
- warn(
629
- `skipped upload (no auth token found). Set ${REPORTER_TOKEN_ENV} or ARCHAL_TOKEN, or run \`archal login\` to populate ~/.archal/credentials.json.`
630
- );
355
+ if (itemCount === 0) {
356
+ warn(`No ${itemLabel} received. Nothing to upload.`);
631
357
  return;
632
358
  }
633
- const baseUrl = resolveReporterBaseUrl();
634
- const sessionId = await this.readSessionId(project.config.sessionKey);
635
- const payload = {
636
- sessionId,
637
- projectName: project.config.projectName,
638
- services: Object.keys(project.config.services),
639
- results,
640
- summary,
641
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
642
- };
643
- const res = await fetch(`${baseUrl}/api/test-results`, {
644
- method: "POST",
645
- headers: buildReporterHeaders(token),
646
- body: JSON.stringify(payload)
647
- });
648
- if (res.ok) {
649
- info(`Uploaded ${summary.passed}/${summary.total} tests to Archal Tests.`);
650
- if (sessionId) {
651
- try {
652
- const syncRes = await fetch(`${baseUrl}/api/sessions/sync`, {
653
- method: "POST",
654
- headers: buildReporterHeaders(token),
655
- body: JSON.stringify({
656
- sessionId,
657
- twinIds: Object.keys(project.config.services),
658
- status: summary.failed > 0 ? "failed" : "ended",
659
- endedAt: (/* @__PURE__ */ new Date()).toISOString(),
660
- runtimeStartedAt: new Date(Date.now() - summary.duration).toISOString(),
661
- source: "test",
662
- evidence: {
663
- reportSummary: {
664
- scenarioTitle: project.config.projectName,
665
- satisfactionScore: summary.total > 0 ? Math.round(summary.passed / summary.total * 100) : null,
666
- runCount: 1
667
- }
668
- }
669
- })
670
- });
671
- if (!syncRes.ok) {
672
- const body = await syncRes.text().catch(() => "");
673
- warn(`Session sync failed (${syncRes.status}): ${body.slice(0, 200)}`);
674
- }
675
- } catch (syncErr) {
676
- warn(
677
- `Session sync failed: ${syncErr instanceof Error ? syncErr.message : String(syncErr)}`
678
- );
679
- }
680
- } else {
681
- warn(
682
- "Uploaded test results, but could not link them to a session. The run may not appear in Activity."
683
- );
684
- }
685
- } else {
686
- const body = await res.text().catch(() => "");
687
- warn(`Upload failed (${res.status}): ${body.slice(0, 200)}`);
688
- }
689
- }
690
- async readSessionId(sessionKey) {
691
- const { readFileSync: readFileSync2, readdirSync, statSync } = await import("fs");
692
- const { dirname: dirname2, join: join2 } = await import("path");
693
- if (sessionKey?.trim()) {
694
- try {
695
- const keyed = readFileSync2(getSessionIdFilePath(sessionKey), "utf-8").trim();
696
- if (keyed) return keyed;
697
- } catch {
698
- }
699
- }
700
- if (sessionKey?.trim()) {
701
- return void 0;
702
- }
703
- try {
704
- const sessionsDir = dirname2(getSessionIdFilePath("any"));
705
- const entries = readdirSync(sessionsDir);
706
- let newest;
707
- for (const name of entries) {
708
- if (!name.endsWith(".session-id")) continue;
709
- const path = join2(sessionsDir, name);
710
- try {
711
- const st = statSync(path);
712
- if (!newest || st.mtimeMs > newest.mtimeMs) {
713
- newest = { path, mtimeMs: st.mtimeMs };
714
- }
715
- } catch {
716
- }
717
- }
718
- if (newest) {
719
- const content = readFileSync2(newest.path, "utf-8").trim();
720
- if (content) return content;
721
- }
722
- } catch {
723
- }
724
- return void 0;
359
+ diagnostic(
360
+ "skipped legacy test-result upload. Benchmark Results ingestion requires a Benchmark Run payload and no longer writes /api/test-results."
361
+ );
725
362
  }
726
363
  };
727
364
 
@@ -735,16 +372,16 @@ function classifySeed(value) {
735
372
  if (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.startsWith("/")) {
736
373
  const ext = extname(trimmed).toLowerCase();
737
374
  if (ext === ".json") return { type: "file", path: trimmed, format: "json" };
738
- if (ext === ".md") return { type: "file", path: trimmed, format: "markdown" };
375
+ if (ext === ".sql") return { type: "file", path: trimmed, format: "sql" };
739
376
  throw new Error(
740
- `Seed file must be .json or .md, got "${ext || "(no extension)"}": ${trimmed}
377
+ `Seed file must be .json or .sql, got "${ext || "(no extension)"}": ${trimmed}
741
378
  .json \u2192 exact clone state loaded directly
742
- .md \u2192 plain-English description, Archal generates the state`
379
+ .sql \u2192 SQL state loaded through the clone state boundary`
743
380
  );
744
381
  }
745
- if (trimmed.endsWith(".json") || trimmed.endsWith(".md")) {
382
+ if (trimmed.endsWith(".json") || trimmed.endsWith(".sql")) {
746
383
  const ext = extname(trimmed).toLowerCase();
747
- const format = ext === ".json" ? "json" : "markdown";
384
+ const format = ext === ".json" ? "json" : "sql";
748
385
  return { type: "file", path: trimmed, format };
749
386
  }
750
387
  return { type: "named", name: trimmed };
@@ -926,11 +563,6 @@ function archalVitestProject(options, testOptions = {}) {
926
563
  const test = buildTestConfig(options, testOptions);
927
564
  return defineProject({ test });
928
565
  }
929
- function archalVitestConfig(options, testOptions = {}) {
930
- const built = buildTestConfig(options, testOptions);
931
- built.reporters = ["default", new ArchalReporter()];
932
- return built;
933
- }
934
566
  function archalVitestRootConfig() {
935
567
  markHelperCalled();
936
568
  return {
@@ -991,7 +623,6 @@ function archalVitestWorkspace(projects) {
991
623
  }
992
624
  export {
993
625
  ArchalReporter,
994
- archalVitestConfig,
995
626
  archalVitestProject,
996
627
  archalVitestRootConfig,
997
628
  archalVitestWorkspace,