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.
- package/agentic/code/plugins/agent-loop/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/agent-persistence/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/agentic-installer/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/aiwg-dev/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/aiwg-evals/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/auto-memory/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/browser-control/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/color-palette/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/compound-memory/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/context-curator/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/daemon/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/doc-intelligence/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/droid-bridge/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/forensics/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/guided-implementation/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/hooks/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/knowledge-base/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/line-memory/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/llm-wiki/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/marketing/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/media-curator/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/nlp-prod/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/ops/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/prose-integration/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/research/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/rlm/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/sdlc/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/security-engineering/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/semantic-memory/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/skill-factory/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/star-prompt/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/testing-quality/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/twelve-factor/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/uat-mcp/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/utils/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/validation-complete/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/verbalized-sampling/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/voice/.claude-plugin/plugin.json +1 -1
- package/agentic/code/plugins/writing/.claude-plugin/plugin.json +1 -1
- package/dist/src/artifacts/cli.js +15 -3
- package/dist/src/artifacts/index-builder.js +3 -2
- package/dist/src/artifacts/index-preparation.js +91 -26
- package/dist/src/artifacts/index-refresh.js +17 -2
- package/dist/src/artifacts/index-worker.mjs +9 -7
- package/dist/src/cli/handlers/sessions.js +39 -6
- package/dist/src/sessions/adapters/codex.js +23 -10
- package/dist/src/sessions/batch-contracts.js +1 -1
- package/dist/src/sessions/batch-import.js +202 -2
- package/dist/src/sessions/contracts.js +10 -1
- package/dist/src/sessions/coverage-preflight.js +26 -1
- package/dist/src/sessions/policy.js +18 -7
- package/dist/src/sessions/refresh.js +2 -3
- package/dist/src/sessions/repository.js +4 -2
- package/dist/src/sessions/workspace-discovery.js +89 -22
- package/docs/cli/reference.md +11 -0
- package/docs/getting-started/session-history.md +6 -0
- package/docs/releases/v2026.10.5-announcement.md +49 -0
- package/docs/sessions/cli.md +39 -0
- package/package.json +1 -1
- package/prebuilt/fortemi-core/framework/aiwg-fortemi-index-v2.json +1 -1
- package/prebuilt/fortemi-core/framework/manifest.json +5 -5
- package/prebuilt/fortemi-core/framework/source-stats.json +1 -1
- package/prebuilt/framework-relations/manifest.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-persistence",
|
|
3
|
-
"version": "2026.10.
|
|
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.
|
|
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.
|
|
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": "browser-control",
|
|
3
|
-
"version": "2026.10.
|
|
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.
|
|
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": "forensics",
|
|
3
|
-
"version": "2026.10.
|
|
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": "knowledge-base",
|
|
3
|
-
"version": "2026.10.
|
|
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": "llm-wiki",
|
|
3
|
-
"version": "2026.10.
|
|
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": "media-curator",
|
|
3
|
-
"version": "2026.10.
|
|
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.
|
|
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.
|
|
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": "research",
|
|
3
|
-
"version": "2026.10.
|
|
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.
|
|
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": "security-engineering",
|
|
3
|
-
"version": "2026.10.
|
|
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.
|
|
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.
|
|
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": "uat-mcp",
|
|
3
|
-
"version": "2026.10.
|
|
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": "verbalized-sampling",
|
|
3
|
-
"version": "2026.10.
|
|
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",
|
|
@@ -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.
|
|
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 {
|
|
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
|
-
|
|
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
|
-
//
|
|
10
|
-
// Explicit
|
|
11
|
-
if (requested === undefined && (jobs < 1000 || !Number.isFinite(totalBytes) || totalBytes < 16 *
|
|
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 *
|
|
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
|
|
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
|
-
|
|
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 (
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
34
|
-
|
|
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.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
worker.on('
|
|
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
|
-
|
|
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 ===
|
|
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
|
-
|
|
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
|