aiwg 2026.10.4 → 2026.10.5

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 (63) hide show
  1. package/agentic/code/plugins/agent-loop/.claude-plugin/plugin.json +1 -1
  2. package/agentic/code/plugins/agent-persistence/.claude-plugin/plugin.json +1 -1
  3. package/agentic/code/plugins/agentic-installer/.claude-plugin/plugin.json +1 -1
  4. package/agentic/code/plugins/aiwg-dev/.claude-plugin/plugin.json +1 -1
  5. package/agentic/code/plugins/aiwg-evals/.claude-plugin/plugin.json +1 -1
  6. package/agentic/code/plugins/auto-memory/.claude-plugin/plugin.json +1 -1
  7. package/agentic/code/plugins/browser-control/.claude-plugin/plugin.json +1 -1
  8. package/agentic/code/plugins/color-palette/.claude-plugin/plugin.json +1 -1
  9. package/agentic/code/plugins/compound-memory/.claude-plugin/plugin.json +1 -1
  10. package/agentic/code/plugins/context-curator/.claude-plugin/plugin.json +1 -1
  11. package/agentic/code/plugins/daemon/.claude-plugin/plugin.json +1 -1
  12. package/agentic/code/plugins/doc-intelligence/.claude-plugin/plugin.json +1 -1
  13. package/agentic/code/plugins/droid-bridge/.claude-plugin/plugin.json +1 -1
  14. package/agentic/code/plugins/forensics/.claude-plugin/plugin.json +1 -1
  15. package/agentic/code/plugins/guided-implementation/.claude-plugin/plugin.json +1 -1
  16. package/agentic/code/plugins/hooks/.claude-plugin/plugin.json +1 -1
  17. package/agentic/code/plugins/knowledge-base/.claude-plugin/plugin.json +1 -1
  18. package/agentic/code/plugins/line-memory/.claude-plugin/plugin.json +1 -1
  19. package/agentic/code/plugins/llm-wiki/.claude-plugin/plugin.json +1 -1
  20. package/agentic/code/plugins/marketing/.claude-plugin/plugin.json +1 -1
  21. package/agentic/code/plugins/media-curator/.claude-plugin/plugin.json +1 -1
  22. package/agentic/code/plugins/nlp-prod/.claude-plugin/plugin.json +1 -1
  23. package/agentic/code/plugins/ops/.claude-plugin/plugin.json +1 -1
  24. package/agentic/code/plugins/prose-integration/.claude-plugin/plugin.json +1 -1
  25. package/agentic/code/plugins/research/.claude-plugin/plugin.json +1 -1
  26. package/agentic/code/plugins/rlm/.claude-plugin/plugin.json +1 -1
  27. package/agentic/code/plugins/sdlc/.claude-plugin/plugin.json +1 -1
  28. package/agentic/code/plugins/security-engineering/.claude-plugin/plugin.json +1 -1
  29. package/agentic/code/plugins/semantic-memory/.claude-plugin/plugin.json +1 -1
  30. package/agentic/code/plugins/skill-factory/.claude-plugin/plugin.json +1 -1
  31. package/agentic/code/plugins/star-prompt/.claude-plugin/plugin.json +1 -1
  32. package/agentic/code/plugins/testing-quality/.claude-plugin/plugin.json +1 -1
  33. package/agentic/code/plugins/twelve-factor/.claude-plugin/plugin.json +1 -1
  34. package/agentic/code/plugins/uat-mcp/.claude-plugin/plugin.json +1 -1
  35. package/agentic/code/plugins/utils/.claude-plugin/plugin.json +1 -1
  36. package/agentic/code/plugins/validation-complete/.claude-plugin/plugin.json +1 -1
  37. package/agentic/code/plugins/verbalized-sampling/.claude-plugin/plugin.json +1 -1
  38. package/agentic/code/plugins/voice/.claude-plugin/plugin.json +1 -1
  39. package/agentic/code/plugins/writing/.claude-plugin/plugin.json +1 -1
  40. package/dist/src/artifacts/cli.js +15 -3
  41. package/dist/src/artifacts/index-builder.js +3 -2
  42. package/dist/src/artifacts/index-preparation.js +91 -26
  43. package/dist/src/artifacts/index-refresh.js +17 -2
  44. package/dist/src/artifacts/index-worker.mjs +9 -7
  45. package/dist/src/cli/handlers/sessions.js +39 -6
  46. package/dist/src/sessions/adapters/codex.js +23 -10
  47. package/dist/src/sessions/batch-contracts.js +1 -1
  48. package/dist/src/sessions/batch-import.js +202 -2
  49. package/dist/src/sessions/contracts.js +10 -1
  50. package/dist/src/sessions/coverage-preflight.js +26 -1
  51. package/dist/src/sessions/policy.js +18 -7
  52. package/dist/src/sessions/refresh.js +2 -3
  53. package/dist/src/sessions/repository.js +4 -2
  54. package/dist/src/sessions/workspace-discovery.js +89 -22
  55. package/docs/cli/reference.md +11 -0
  56. package/docs/getting-started/session-history.md +6 -0
  57. package/docs/releases/v2026.10.5-announcement.md +49 -0
  58. package/docs/sessions/cli.md +39 -0
  59. package/package.json +1 -1
  60. package/prebuilt/fortemi-core/framework/aiwg-fortemi-index-v2.json +1 -1
  61. package/prebuilt/fortemi-core/framework/manifest.json +5 -5
  62. package/prebuilt/fortemi-core/framework/source-stats.json +1 -1
  63. package/prebuilt/framework-relations/manifest.json +2 -2
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-loop",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Iterative AI coding loops with automatic recovery - iteration beats perfection",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-persistence",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Human-in-the-loop (HITL) gate definitions for agent operations. Provides reusable YAML gates for destructive-action authorization, false-positive override, and recovery escalation. Loaded by agent loops and orchestrators that need bounded human checkpoints.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-installer",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Structured YAML manifests for reproducible, agent-driven software installation across platforms. Scripts are the primary artifact; agentic instructions handle adaptation and recovery.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiwg-dev",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Developer tooling for building AIWG itself and AIWG addons/frameworks. Not for end users — install explicitly with `aiwg use aiwg-dev`.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiwg-evals",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Automated evaluation framework for agent quality, based on KAMI benchmark methodology and ReAct patterns",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auto-memory",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Automatic memory seed templates for AIWG projects",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-control",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Drive a real, user-installed Chromium-derived browser from an AIWG agent. Wraps @playwright/mcp with a setup wizard, health-check doctor, allow-list enforcement, and an opinionated agent persona that respects authorization and audit boundaries.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "color-palette",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Color palette selection and review tooling with color theory fundamentals, current trend research, and accessibility analysis.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compound-memory",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Governed orchestration across AIWG line memory, semantic memory, and llm-wiki.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-curator",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Context curation and distractor filtering for production-grade agent reliability",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "daemon",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Persistent daemon session capabilities: Concierge front-end, session memory, scheduler integration",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doc-intelligence",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Documentation intelligence addon for scraping, extraction, analysis, and generated-docs auditing.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "droid-bridge",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "MCP server bridging Claude Code to Factory Droid for batch operations and automated fixes",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forensics",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Digital forensics and incident response framework with 14 specialized agents covering target profiling, evidence acquisition, log/memory/container/cloud analysis, IOC extraction, and reporting.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guided-implementation",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Bounded iteration control for autonomous issue-to-code implementation",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hooks",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Claude Code hooks for workflow tracing and observability.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knowledge-base",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Knowledge-base / wiki framework with skills for ingestion, query, and lint. Build a structured, citable corpus with cross-references and concept pages.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "line-memory",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Bounded, plain-text project memory with least-recently-used retention.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llm-wiki",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "LLM-maintained markdown wiki that compounds as sources are ingested. Thin topology declaration on the semantic memory kernel — ships schema profiles and page templates, no custom mechanics.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "marketing",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Marketing automation framework with 37 specialized agents for campaign management.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "media-curator",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Media archive management framework with 7 specialized agents for source discovery, acquisition (yt-dlp / Internet Archive / Bandcamp), quality assessment, metadata tagging, and provenance tracking.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nlp-prod",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Tools, skills, and CLI extensions for designing and productionizing LLM inference pipelines. Pattern-guided, slim by default, eval-first.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ops",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Operational infrastructure framework with 12 specialized agents covering incident response, runbook execution, fleet inventory, certificate lifecycle, and disaster recovery planning.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prose-integration",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Tools for reading, parsing, validating, and executing OpenProse programs from AIWG workflows",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "research",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Research workflow framework with 9 specialized agents for discovery, acquisition, synthesis, citation management, GRADE quality assessment, and OAIS-compliant archival.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rlm",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "RLM-inspired recursive context decomposition for processing arbitrarily large codebases and document corpora through programmatic sub-agent delegation",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sdlc",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Complete SDLC framework with 180+ specialized agents for software development lifecycle management.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "security-engineering",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Applied security framework for cryptographic primitive selection, chain-of-trust design, authentication factor analysis, supply-chain trust, and physical-threat modeling. Complements OWASP-style application audits.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "semantic-memory",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Topology-agnostic semantic memory operations: ingest, lint, query-capture, and structured event logging. Consumers declare a memory topology in their manifest.json; kernel skills read that contract to parameterize behavior.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skill-factory",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Build, enhance, validate, and package Claude skills end-to-end. Coordinates skill-builder, skill-enhancer, quality-checker, and skill-packager via the skill-architect agent.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "star-prompt",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Tasteful prompt to encourage users to star the repository after successful command completion",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testing-quality",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Evidence-bound test conformance, platform protocols, normalization transactions and research-guided testing workflows",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "twelve-factor",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Design-time and audit-time workflows for Twelve-Factor and modern 12+ Factor cloud-native application practices.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uat-mcp",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Agent-executable acceptance testing via MCP connections — generate, execute, and report on UAT plans that validate MCP tool surfaces.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "utils",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Core AIWG utilities for context regeneration and workspace management.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "validation-complete",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Validation Complete for AIWG",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "verbalized-sampling",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Training-free prompting technique that improves output diversity by 1.6-2.1x through verbalized probability distributions, addressing RLHF mode collapse",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "voice",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Voice profile system for consistent, authentic writing.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "writing",
3
- "version": "2026.10.4",
3
+ "version": "2026.10.5",
4
4
  "description": "Writing quality validation and AI pattern detection.",
5
5
  "author": {
6
6
  "name": "AIWG Contributors",
@@ -20,6 +20,7 @@ import path from 'node:path';
20
20
  import { readFileSync } from 'node:fs';
21
21
  import { fileURLToPath } from 'node:url';
22
22
  import { GRAPH_CONFIGS, OPERATIONAL_DISCOVERY_TYPES, OPERATIONAL_SHOW_TYPES, isOperationalShowType, loadUserGraphConfigs, loadGlobalGraphConfigs, orderedGraphEntries, } from './types.js';
23
+ import { resolveIndexWorkers } from './index-preparation.js';
23
24
  import { SUPPORTED_VIEWS } from './corpus-views/renderers.js';
24
25
  import { parseResourceSelector, readVerifiedRegularFile, } from '../resources/web-release.js';
25
26
  import { findPackageRoot } from '../cli/find-package-root.js';
@@ -332,6 +333,7 @@ export function printIndexUsage() {
332
333
  console.log(' Session import defaults: 67108864 bytes per record, 1073741824 bytes (1 GiB) per source; source 0 is unlimited.');
333
334
  console.log(' --exclude <id-or-glob> (repeatable): skip session IDs or absolute path globs; adds to sessions.import.exclude and AIWG_SESSIONS_IMPORT_EXCLUDE (comma-separated).');
334
335
  console.log(' Import limits: CLI flag > AIWG_SESSIONS_IMPORT_MAX_RECORD_BYTES / AIWG_SESSIONS_IMPORT_MAX_SOURCE_BYTES > workspace config > default.');
336
+ console.log(' build/refresh --index-workers <n>: preparation processes (0 = serial); flag > AIWG_INDEX_WORKERS > automatic CPU/memory limits.');
335
337
  console.log(' sync [--backend fortemi-core] [--graph <name> | --all] [--repo <name>]');
336
338
  console.log(' [--privacy private|sanitized|public] [--generated-at <iso>] [--json]');
337
339
  console.log(' --graph <name> Target a specific graph (framework, project, codebase, source, user, or user-defined)');
@@ -503,6 +505,7 @@ async function handleBuild(args) {
503
505
  console.log('Usage: aiwg index build [options]');
504
506
  console.log('');
505
507
  console.log('Options:');
508
+ console.log(' --index-workers <n> Preparation processes (0 = serial); overrides AIWG_INDEX_WORKERS');
506
509
  console.log(' --force Full rebuild (ignore checksums, re-index everything)');
507
510
  console.log(' --verbose Show detailed progress during indexing');
508
511
  console.log(' --all Build all known graphs (including user-defined)');
@@ -530,6 +533,15 @@ async function handleBuild(args) {
530
533
  console.log(' aiwg index sync --all');
531
534
  return;
532
535
  }
536
+ let workers;
537
+ try {
538
+ workers = resolveIndexWorkers(parseFlagValue(args, '--index-workers', 'Error: --index-workers requires a value'));
539
+ }
540
+ catch (error) {
541
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
542
+ process.exitCode = 1;
543
+ return;
544
+ }
533
545
  // Dynamic import to keep the CLI router lightweight
534
546
  const { buildIndex } = await import('./index-builder.js');
535
547
  const cwd = process.cwd();
@@ -554,7 +566,7 @@ async function handleBuild(args) {
554
566
  // --graph X: build the JSON graph if X is one; otherwise X may be a
555
567
  // research-corpus markdown view, rendered below.
556
568
  if (graph in GRAPH_CONFIGS) {
557
- await buildIndex(cwd, { force, verbose, scope, includeIgnored, graph, explicit: true });
569
+ await buildIndex(cwd, { force, verbose, scope, includeIgnored, workers, graph, explicit: true });
558
570
  jsonBuilt = true;
559
571
  builtGraphs.add(graph);
560
572
  }
@@ -562,7 +574,7 @@ async function handleBuild(args) {
562
574
  else if (all) {
563
575
  // Build all known graphs — user asked for everything, but don't hard-error on missing dirs
564
576
  for (const [name] of orderedGraphEntries(Object.entries(GRAPH_CONFIGS))) {
565
- await buildIndex(cwd, { force, verbose, includeIgnored, graph: name, explicit: false });
577
+ await buildIndex(cwd, { force, verbose, includeIgnored, workers, graph: name, explicit: false });
566
578
  builtGraphs.add(name);
567
579
  }
568
580
  jsonBuilt = true;
@@ -571,7 +583,7 @@ async function handleBuild(args) {
571
583
  // Default: build graphs with defaultBuild=true; skip gracefully if their dirs don't exist
572
584
  for (const [name, config] of orderedGraphEntries(Object.entries(GRAPH_CONFIGS))) {
573
585
  if (config.defaultBuild) {
574
- await buildIndex(cwd, { force, verbose, includeIgnored, scope: name === Object.keys(GRAPH_CONFIGS)[0] ? scope : undefined, graph: name, explicit: false });
586
+ await buildIndex(cwd, { force, verbose, includeIgnored, workers, scope: name === Object.keys(GRAPH_CONFIGS)[0] ? scope : undefined, graph: name, explicit: false });
575
587
  builtGraphs.add(name);
576
588
  }
577
589
  }
@@ -24,7 +24,7 @@ import { FRAMEWORK_RELATIONS_REVISION } from './framework-source-digest.js';
24
24
  import { normalizeOperationalState } from './operational-state.js';
25
25
  import { DEFAULT_PROJECT_AIWG_DIR, resolveProjectAiwgDir, } from '../config/project-artifacts.js';
26
26
  import { normalizeStateTransferProjection } from './state-transfer.js';
27
- import { runIndexPreparation } from './index-preparation.js';
27
+ import { resolveIndexWorkers, runIndexPreparation } from './index-preparation.js';
28
28
  import { collectGraphIndexFiles, findArtifactFiles, indexPathFor } from './index-files.js';
29
29
  function absoluteEntryPath(cwd, entryPath, graph) {
30
30
  if ((!graph || graph === 'project') && entryPath.startsWith(`${DEFAULT_PROJECT_AIWG_DIR}/`)) {
@@ -911,6 +911,7 @@ export function prepareIndexArtifact(job) {
911
911
  }
912
912
  export async function buildIndex(cwd, options = {}) {
913
913
  const { force = false, verbose = false, scope, includeIgnored = false, outputDir, graph, explicit = true } = options;
914
+ const workers = resolveIndexWorkers(options.workers);
914
915
  const startTime = Date.now();
915
916
  const log = options.quiet ? (..._args) => { } : console.log;
916
917
  // Ensure user-defined graphs are loaded
@@ -1119,7 +1120,7 @@ export async function buildIndex(cwd, options = {}) {
1119
1120
  jobs.push({ fullPath, relativePath, mtimeMs: stat.mtimeMs, birthtimeMs: stat.birthtimeMs, sizeBytes: stat.size,
1120
1121
  ...(!force && existingEntries[relativePath] ? { existingChecksum: existingEntries[relativePath].checksum } : {}) });
1121
1122
  }
1122
- const preparation = await runIndexPreparation(jobs, prepareIndexArtifact, import.meta.url, options.workers);
1123
+ const preparation = await runIndexPreparation(jobs, prepareIndexArtifact, import.meta.url, workers, options.onDiagnostic ?? (message => console.warn(`Index preparation: ${message}`)));
1123
1124
  const preparedArtifacts = new Map(jobs.map((job, index) => [job.fullPath, preparation.results[index]]));
1124
1125
  for (const fullPath of files) {
1125
1126
  const relativePath = indexPathFor(cwd, fullPath, graph);
@@ -1,65 +1,130 @@
1
1
  /** Bounded artifact-local preparation; ordered graph reduction stays in index-builder. */
2
2
  import os from 'node:os';
3
- import { Worker } from 'node:worker_threads';
3
+ import { fork } from 'node:child_process';
4
+ import { signalChild } from '../process/signal-child.mjs';
5
+ const MIB = 1024 ** 2;
6
+ const WORKER_HEAP_MB = 256;
7
+ // Leave room for V8's young generation, executable code, IPC and native allocations.
8
+ const WORKER_BUDGET_BYTES = 512 * MIB;
9
+ // Parsing and metadata extraction amplify input size; do large files only after the pool exits.
10
+ const MAX_WORKER_ARTIFACT_BYTES = MIB;
11
+ export function resolveIndexWorkers(requested, env = process.env) {
12
+ const value = requested ?? env.AIWG_INDEX_WORKERS;
13
+ if (requested === undefined && (value === undefined || value === ''))
14
+ return undefined;
15
+ if ((typeof value === 'string' && !/^\d+$/.test(value))
16
+ || !Number.isSafeInteger(Number(value)) || Number(value) < 0) {
17
+ throw new Error(`${requested === undefined ? 'AIWG_INDEX_WORKERS' : '--index-workers'} must be a nonnegative safe integer (0 = serial)`);
18
+ }
19
+ return Number(value);
20
+ }
4
21
  export function indexPreparationWorkers(jobs, requested, cores = os.availableParallelism(), memory = Math.min(os.freemem(), process.availableMemory?.() ?? Infinity), totalBytes = 0) {
5
- if (requested !== undefined && (!Number.isInteger(requested) || requested < 0))
6
- throw new Error('Index workers must be a nonnegative integer');
22
+ requested = resolveIndexWorkers(requested);
7
23
  if (jobs < 64 || cores < 3 || requested === 0 || requested === 1)
8
24
  return 0;
9
- // Thread startup costs dominate lightweight scans even at thousands of files.
10
- // Explicit thread requests bypass only this conservative automatic workload gate.
11
- if (requested === undefined && (jobs < 1000 || !Number.isFinite(totalBytes) || totalBytes < 16 * 1024 ** 2))
25
+ // Process startup costs dominate lightweight scans even at thousands of files.
26
+ // Explicit requests bypass only this conservative automatic workload gate.
27
+ if (requested === undefined && (jobs < 1000 || !Number.isFinite(totalBytes) || totalBytes < 16 * MIB))
12
28
  return 0;
13
- const byMemory = Math.floor((memory - 512 * 1024 ** 2) / (256 * 1024 ** 2));
29
+ const byMemory = Math.floor((memory - 512 * MIB) / WORKER_BUDGET_BYTES);
14
30
  const bounded = Math.max(0, Math.min(requested ?? 4, 4, cores - 1, byMemory, jobs));
15
31
  return bounded >= 2 ? bounded : 0;
16
32
  }
17
- export async function runIndexPreparation(jobs, serial, moduleUrl, requested) {
18
- const workerCount = indexPreparationWorkers(jobs.length, requested, undefined, undefined, jobs.reduce((total, job) => total + job.sizeBytes, 0));
33
+ export async function runIndexPreparation(jobs, serial, moduleUrl, requested, onDiagnostic) {
34
+ const diagnostics = [];
35
+ const diagnose = (message) => { diagnostics.push(message); onDiagnostic?.(message); };
36
+ const eligible = jobs.map((job, index) => ({ job, index })).filter(({ job }) => Number.isFinite(job.sizeBytes) && job.sizeBytes >= 0 && job.sizeBytes <= MAX_WORKER_ARTIFACT_BYTES);
37
+ const workerCount = indexPreparationWorkers(eligible.length, requested, undefined, undefined, eligible.reduce((total, { job }) => total + job.sizeBytes, 0));
19
38
  if (!workerCount)
20
- return { results: jobs.map(serial), workerCount: 0 };
39
+ return { results: jobs.map(serial), workerCount: 0, diagnostics };
21
40
  const results = new Array(jobs.length), workers = [];
41
+ const closed = [];
22
42
  let next = 0, completed = 0, stopped = false;
23
43
  try {
24
44
  await new Promise((resolve, reject) => {
25
- const fail = (error) => { stopped = true; reject(error); };
45
+ // Stop dispatch on a process failure. Reap the entire pool before retrying
46
+ // unfinished work in the main isolate, avoiding overlapping memory peaks.
47
+ const recover = (message) => {
48
+ if (stopped)
49
+ return;
50
+ stopped = true;
51
+ diagnose(`${message}; retrying unfinished preparation serially`);
52
+ resolve();
53
+ };
26
54
  const dispatch = (worker) => {
27
- if (!stopped && next < jobs.length) {
28
- const index = next++;
29
- worker.postMessage({ index, job: jobs[index] });
55
+ if (stopped || next >= eligible.length)
56
+ return;
57
+ const { index, job } = eligible[next++];
58
+ try {
59
+ worker.send({ index, job }, error => { if (error)
60
+ recover(`Index preparation IPC failed for ${job.relativePath}: ${error.message}`); });
61
+ }
62
+ catch (error) {
63
+ recover(`Index preparation IPC failed for ${job.relativePath}: ${error instanceof Error ? error.message : String(error)}`);
30
64
  }
31
65
  };
32
- for (let index = 0; index < workerCount; index++) {
33
- const worker = new Worker(new URL('./index-worker.mjs', import.meta.url), {
34
- workerData: { moduleUrl }, execArgv: [], resourceLimits: { maxOldGenerationSizeMb: 256 },
66
+ for (let index = 0; index < workerCount && !stopped; index++) {
67
+ // A worker_threads resourceLimit is not a process boundary: V8 can abort
68
+ // Node during allocation before its isolate-limit error is delivered.
69
+ // A child heap fatal/SIGABRT is instead an ordinary, recoverable exit.
70
+ const worker = fork(new URL('./index-worker.mjs', import.meta.url), [moduleUrl], {
71
+ execArgv: [`--max-old-space-size=${WORKER_HEAP_MB}`, '--max-semi-space-size=16'],
72
+ stdio: ['ignore', 'ignore', 'pipe', 'ipc'], serialization: 'advanced',
35
73
  });
36
74
  workers.push(worker);
37
- worker.on('error', fail);
38
- worker.on('exit', code => { if (!stopped && completed < jobs.length)
39
- fail(new Error(`Index preparation worker exited early (${code})`)); });
40
- worker.on('message', message => {
75
+ closed.push(new Promise(done => worker.once('close', () => done())));
76
+ let stderr = '';
77
+ let activePath = '<startup>';
78
+ worker.stderr?.on('data', chunk => { stderr = (stderr + String(chunk)).slice(-8192); });
79
+ worker.on('error', error => recover(`Index preparation worker failed for ${activePath}: ${error.message}`));
80
+ worker.on('exit', (code, signal) => {
81
+ const oom = /heap out of memory|ERR_WORKER_OUT_OF_MEMORY/i.test(stderr) ? ' (heap out of memory)' : '';
82
+ recover(`Index preparation worker exited early for ${activePath} (${signal ?? code})${oom}`);
83
+ });
84
+ worker.on('message', (message) => {
41
85
  if (stopped)
42
86
  return;
43
87
  if (message.error) {
44
- fail(new Error(message.error));
88
+ stopped = true;
89
+ reject(new Error(message.error));
45
90
  return;
46
91
  }
47
92
  results[message.index] = message.result;
48
93
  completed++;
49
- if (completed === jobs.length) {
94
+ if (completed === eligible.length) {
50
95
  stopped = true;
51
96
  resolve();
52
97
  }
53
- else
98
+ else {
99
+ activePath = eligible[next]?.job.relativePath ?? '<idle>';
54
100
  dispatch(worker);
101
+ }
55
102
  });
103
+ activePath = eligible[next]?.job.relativePath ?? '<idle>';
56
104
  dispatch(worker);
57
105
  }
58
106
  });
59
- return { results, workerCount };
60
107
  }
61
108
  finally {
62
- await Promise.all(workers.map(worker => worker.terminate()));
109
+ stopped = true;
110
+ for (const worker of workers)
111
+ if (worker.exitCode === null && worker.signalCode === null)
112
+ signalChild(worker, 'SIGKILL');
113
+ await Promise.all(closed);
114
+ }
115
+ const largeCount = jobs.length - eligible.length;
116
+ if (largeCount)
117
+ diagnose(`${largeCount} artifact(s) above the worker size threshold prepared serially`);
118
+ for (let index = 0; index < jobs.length; index++) {
119
+ if (!results[index]) {
120
+ try {
121
+ results[index] = serial(jobs[index]);
122
+ }
123
+ catch (error) {
124
+ throw new Error(`${jobs[index].relativePath}: ${error instanceof Error ? error.message : String(error)}`);
125
+ }
126
+ }
63
127
  }
128
+ return { results, workerCount, diagnostics };
64
129
  }
65
130
  //# sourceMappingURL=index-preparation.js.map