sinapse-ai 1.6.1 → 1.8.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.
- package/.claude/CLAUDE.md +5 -11
- package/.claude/hooks/README.md +14 -1
- package/.claude/hooks/code-intel-pretool.cjs +115 -0
- package/.claude/hooks/enforce-delegation.cjs +31 -3
- package/.claude/hooks/enforce-framework-boundary.cjs +324 -0
- package/.claude/hooks/enforce-permission-mode.cjs +249 -0
- package/.claude/hooks/secret-scanning.cjs +34 -43
- package/.claude/hooks/synapse-engine.cjs +23 -23
- package/.claude/hooks/telemetry-post-tool.cjs +128 -0
- package/.claude/hooks/telemetry-stop.cjs +132 -0
- package/.claude/hooks/verify-packages.cjs +9 -2
- package/.claude/rules/documentation-first.md +1 -1
- package/.claude/rules/hook-governance.md +2 -0
- package/.sinapse-ai/cli/commands/health/index.js +24 -0
- package/.sinapse-ai/core/README.md +11 -0
- package/.sinapse-ai/core/config/config-loader.js +19 -0
- package/.sinapse-ai/core/config/merge-utils.js +8 -0
- package/.sinapse-ai/core/errors/constants.js +147 -0
- package/.sinapse-ai/core/errors/error-registry.js +176 -0
- package/.sinapse-ai/core/errors/index.js +50 -0
- package/.sinapse-ai/core/errors/serializer.js +147 -0
- package/.sinapse-ai/core/errors/sinapse-error.js +144 -0
- package/.sinapse-ai/core/errors/utils.js +187 -0
- package/.sinapse-ai/core/execution/build-orchestrator.js +47 -49
- package/.sinapse-ai/core/execution/build-state-manager.js +183 -31
- package/.sinapse-ai/core/execution/parallel-executor.js +7 -1
- package/.sinapse-ai/core/execution/semantic-merge-engine.js +26 -14
- package/.sinapse-ai/core/execution/subagent-dispatcher.js +201 -60
- package/.sinapse-ai/core/execution/wave-executor.js +4 -1
- package/.sinapse-ai/core/grounding/README.md +71 -11
- package/.sinapse-ai/core/health-check/checks/project/framework-config.js +38 -2
- package/.sinapse-ai/core/health-check/checks/project/package-json.js +47 -3
- package/.sinapse-ai/core/health-check/checks/services/gemini-cli.js +117 -0
- package/.sinapse-ai/core/health-check/checks/services/index.js +2 -0
- package/.sinapse-ai/core/health-check/healers/index.js +40 -3
- package/.sinapse-ai/core/ideation/ideation-engine.js +212 -107
- package/.sinapse-ai/core/ids/gate-evaluator.js +318 -0
- package/.sinapse-ai/core/ids/gates/g5-semantic-handshake.js +190 -0
- package/.sinapse-ai/core/ids/gates/g6-ci-integrity.js +162 -0
- package/.sinapse-ai/core/ids/index.js +30 -0
- package/.sinapse-ai/core/memory/__tests__/active-modules.verify.js +11 -0
- package/.sinapse-ai/core/memory/gotchas-memory.js +37 -2
- package/.sinapse-ai/core/orchestration/agent-invoker.js +29 -6
- package/.sinapse-ai/core/orchestration/brownfield-handler.js +36 -3
- package/.sinapse-ai/core/orchestration/condition-evaluator.js +57 -0
- package/.sinapse-ai/core/orchestration/executors/epic-3-executor.js +76 -5
- package/.sinapse-ai/core/orchestration/executors/epic-4-executor.js +63 -17
- package/.sinapse-ai/core/orchestration/executors/epic-6-executor.js +153 -41
- package/.sinapse-ai/core/orchestration/executors/epic-executor.js +40 -0
- package/.sinapse-ai/core/orchestration/greenfield-handler.js +87 -3
- package/.sinapse-ai/core/orchestration/master-orchestrator.js +150 -10
- package/.sinapse-ai/core/orchestration/parallel-executor.js +6 -1
- package/.sinapse-ai/core/orchestration/recovery-handler.js +81 -8
- package/.sinapse-ai/core/orchestration/workflow-executor.js +41 -0
- package/.sinapse-ai/core/registry/registry-loader.js +71 -5
- package/.sinapse-ai/core/registry/squad-agent-resolver.js +253 -0
- package/.sinapse-ai/core/synapse/context/context-tracker.js +104 -9
- package/.sinapse-ai/core/synapse/context/index.js +19 -0
- package/.sinapse-ai/core/synapse/context/semantic-handshake-engine.js +555 -0
- package/.sinapse-ai/core/synapse/diagnostics/collectors/pipeline-collector.js +4 -2
- package/.sinapse-ai/core/synapse/engine.js +43 -3
- package/.sinapse-ai/core/telemetry/ids-sink.js +188 -0
- package/.sinapse-ai/core/utils/output-formatter.js +8 -290
- package/.sinapse-ai/core/utils/spawn-safe.js +186 -0
- package/.sinapse-ai/core-config.yaml +68 -1
- package/.sinapse-ai/data/entity-registry.yaml +15082 -13618
- package/.sinapse-ai/data/registry-update-log.jsonl +143 -0
- package/.sinapse-ai/development/agents/developer.md +2 -0
- package/.sinapse-ai/development/agents/devops.md +9 -0
- package/.sinapse-ai/development/external-executors/README.md +18 -0
- package/.sinapse-ai/development/external-executors/codex.md +56 -0
- package/.sinapse-ai/development/scripts/populate-entity-registry.js +65 -9
- package/.sinapse-ai/development/scripts/squad/squad-downloader.js +169 -14
- package/.sinapse-ai/development/tasks/delegate-to-external-executor.md +152 -0
- package/.sinapse-ai/development/tasks/github-devops-pre-push-quality-gate.md +46 -29
- package/.sinapse-ai/development/tasks/update-sinapse.md +3 -3
- package/.sinapse-ai/hooks/sinapse-brand-grounding.cjs +4 -7
- package/.sinapse-ai/hooks/sinapse-ds-grounding.cjs +5 -8
- package/.sinapse-ai/hooks/sinapse-vault-grounding.cjs +6 -9
- package/.sinapse-ai/infrastructure/integrations/ai-providers/ai-provider-factory.js +4 -1
- package/.sinapse-ai/infrastructure/integrations/ai-providers/claude-provider.js +57 -55
- package/.sinapse-ai/infrastructure/integrations/pm-adapters/github-adapter.js +9 -7
- package/.sinapse-ai/infrastructure/scripts/ide-sync/gemini-commands.js +298 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/index.js +127 -6
- package/.sinapse-ai/infrastructure/scripts/ide-sync/persona-renderer.js +97 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/antigravity.js +121 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/cursor.js +119 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/github-copilot.js +191 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/kimi.js +448 -0
- package/.sinapse-ai/install-manifest.yaml +218 -114
- package/.sinapse-ai/product/templates/engine/renderer.js +20 -1
- package/.sinapse-ai/scripts/pm.sh +18 -6
- package/bin/cli.js +17 -0
- package/bin/commands/agents.js +96 -0
- package/bin/commands/doctor.js +15 -0
- package/bin/commands/ideate.js +129 -0
- package/bin/commands/uninstall.js +40 -0
- package/bin/postinstall.js +50 -4
- package/bin/sinapse.js +146 -2
- package/bin/utils/secret-scanner-core.js +253 -0
- package/bin/utils/staged-secret-scan.js +106 -40
- package/docs/framework/collaboration-autonomy-plan.md +18 -18
- package/docs/guides/parallel-workflow.md +6 -6
- package/package.json +22 -5
- package/packages/installer/src/installer/git-hooks-installer.js +384 -0
- package/packages/installer/src/installer/sinapse-ai-installer.js +16 -0
- package/packages/installer/src/wizard/ide-config-generator.js +23 -0
- package/packages/installer/src/wizard/validators.js +38 -1
- package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +5 -1
- package/packages/installer/tests/unit/doctor/doctor-checks.test.js +44 -22
- package/packages/installer/tests/unit/git-hooks-installer.test.js +262 -0
- package/scripts/eval-runner.js +422 -0
- package/scripts/generate-install-manifest.js +13 -9
- package/scripts/generate-synapse-runtime.js +51 -0
- package/scripts/regenerate-orqx-stubs.ps1 +6 -5
- package/scripts/validate-all.js +1 -0
- package/scripts/validate-evals.js +466 -0
- package/scripts/validate-schemas.js +539 -0
- package/scripts/validate-squad-orqx.js +9 -2
- package/squads/claude-code-mastery/knowledge-base/memory-systems-reference.md +1 -1
- package/squads/squad-brand/templates/client-delivery-template.md +1 -1
- package/squads/squad-content/knowledge-base/social-compression-framework.md +1 -1
- package/squads/squad-council/knowledge-base/brand-strategy-models.md +1 -1
- package/.sinapse-ai/development/scripts/elicitation-engine.js +0 -385
- package/.sinapse-ai/development/scripts/elicitation-session-manager.js +0 -300
- package/.sinapse-ai/development/tasks/test-validation-task.md +0 -172
- package/docs/chrome-brain-upgrade-plan.md +0 -624
- package/docs/constitution-compliance.md +0 -87
- package/docs/mega-upgrade-orchestration-plan.md +0 -71
- package/docs/research-synthesis-for-upgrade.md +0 -511
- package/docs/security-audit-report.md +0 -306
|
@@ -23,6 +23,11 @@
|
|
|
23
23
|
const fs = require('fs');
|
|
24
24
|
const path = require('path');
|
|
25
25
|
const crypto = require('crypto');
|
|
26
|
+
const {
|
|
27
|
+
normalizeError,
|
|
28
|
+
sanitizeValue: sanitizeErrorValue,
|
|
29
|
+
serializeError,
|
|
30
|
+
} = require('../errors');
|
|
26
31
|
|
|
27
32
|
// Optional dependencies with graceful fallback
|
|
28
33
|
let chalk;
|
|
@@ -87,6 +92,66 @@ const NotificationType = {
|
|
|
87
92
|
ABANDONED: 'abandoned',
|
|
88
93
|
};
|
|
89
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Converts arbitrary values into stable, JSON-safe log data.
|
|
97
|
+
*
|
|
98
|
+
* @param {*} value - Value to sanitize.
|
|
99
|
+
* @param {WeakSet<object>} [seen=new WeakSet()] - Objects in the current path.
|
|
100
|
+
* @returns {*} Data-only representation that JSON.stringify can serialize.
|
|
101
|
+
*/
|
|
102
|
+
function sanitizeLogValue(value, seen = new WeakSet()) {
|
|
103
|
+
return sanitizeErrorValue(value, seen, {
|
|
104
|
+
includeStack: shouldExposeLogErrorStack(),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Checks whether persisted attempt logs may include raw error stack traces.
|
|
110
|
+
*
|
|
111
|
+
* @returns {boolean} True when stack trace logging is explicitly enabled.
|
|
112
|
+
*/
|
|
113
|
+
function shouldExposeLogErrorStack() {
|
|
114
|
+
const stackFlag = process.env.DEBUG_ERROR_STACKS || process.env.DEBUG_STACKS || '';
|
|
115
|
+
return ['1', 'true', 'yes', 'on'].includes(String(stackFlag).toLowerCase());
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Stringifies attempt log details without allowing log formatting to throw.
|
|
120
|
+
*
|
|
121
|
+
* @param {*} value - Value to stringify.
|
|
122
|
+
* @returns {string} JSON string or a fallback marker.
|
|
123
|
+
*/
|
|
124
|
+
function stringifyLogDetails(value) {
|
|
125
|
+
try {
|
|
126
|
+
return JSON.stringify(sanitizeLogValue(value));
|
|
127
|
+
} catch (error) {
|
|
128
|
+
return JSON.stringify(`[Unserializable: ${error.message}]`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Normalize a failed build attempt into legacy message plus canonical details.
|
|
134
|
+
*
|
|
135
|
+
* @param {*} error - Raw failure error/value.
|
|
136
|
+
* @param {object} context - Build/subtask context for metadata.
|
|
137
|
+
* @returns {{ message: string, details: object }} Normalized failure payload.
|
|
138
|
+
*/
|
|
139
|
+
function normalizeFailureError(error, context = {}) {
|
|
140
|
+
const normalized = normalizeError(error || 'Unknown error', {
|
|
141
|
+
code: 'SNPS_EXECUTION_FAILED',
|
|
142
|
+
metadata: {
|
|
143
|
+
buildState: context,
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
message: normalized.message,
|
|
149
|
+
details: serializeError(normalized, {
|
|
150
|
+
includeStack: shouldExposeLogErrorStack(),
|
|
151
|
+
}),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
90
155
|
// ═══════════════════════════════════════════════════════════════════════════════════
|
|
91
156
|
// SCHEMA VALIDATION
|
|
92
157
|
// ═══════════════════════════════════════════════════════════════════════════════════
|
|
@@ -185,6 +250,8 @@ class BuildStateManager {
|
|
|
185
250
|
// Internal state
|
|
186
251
|
this._state = null;
|
|
187
252
|
this._logBuffer = [];
|
|
253
|
+
this._persistenceAvailable = true;
|
|
254
|
+
this._persistenceError = null;
|
|
188
255
|
}
|
|
189
256
|
|
|
190
257
|
// ─────────────────────────────────────────────────────────────────────────────────
|
|
@@ -244,14 +311,24 @@ class BuildStateManager {
|
|
|
244
311
|
* @returns {Object|null} Loaded state or null if not exists
|
|
245
312
|
*/
|
|
246
313
|
loadState() {
|
|
314
|
+
if (!this._persistenceAvailable) {
|
|
315
|
+
return this._state;
|
|
316
|
+
}
|
|
317
|
+
|
|
247
318
|
if (!fs.existsSync(this.stateFilePath)) {
|
|
248
319
|
return null;
|
|
249
320
|
}
|
|
250
321
|
|
|
322
|
+
let content;
|
|
251
323
|
try {
|
|
252
|
-
|
|
253
|
-
|
|
324
|
+
content = fs.readFileSync(this.stateFilePath, 'utf-8');
|
|
325
|
+
} catch (error) {
|
|
326
|
+
this._markPersistenceUnavailable(error);
|
|
327
|
+
return this._state;
|
|
328
|
+
}
|
|
254
329
|
|
|
330
|
+
try {
|
|
331
|
+
const state = JSON.parse(content);
|
|
255
332
|
// Validate
|
|
256
333
|
const validation = validateBuildState(state);
|
|
257
334
|
if (!validation.valid) {
|
|
@@ -277,11 +354,6 @@ class BuildStateManager {
|
|
|
277
354
|
throw new Error('No state to save. Call createState() or loadState() first.');
|
|
278
355
|
}
|
|
279
356
|
|
|
280
|
-
// Ensure directory exists
|
|
281
|
-
if (!fs.existsSync(this.planDir)) {
|
|
282
|
-
fs.mkdirSync(this.planDir, { recursive: true });
|
|
283
|
-
}
|
|
284
|
-
|
|
285
357
|
// Only update timestamp if explicitly requested (via saveCheckpoint)
|
|
286
358
|
if (options.updateCheckpoint) {
|
|
287
359
|
this._state.lastCheckpoint = new Date().toISOString();
|
|
@@ -293,11 +365,24 @@ class BuildStateManager {
|
|
|
293
365
|
throw new Error(`Invalid state: ${validation.errors.join(', ')}`);
|
|
294
366
|
}
|
|
295
367
|
|
|
296
|
-
|
|
297
|
-
|
|
368
|
+
if (!this._persistenceAvailable) {
|
|
369
|
+
return this._state;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
try {
|
|
373
|
+
// Ensure directory exists
|
|
374
|
+
if (!fs.existsSync(this.planDir)) {
|
|
375
|
+
fs.mkdirSync(this.planDir, { recursive: true });
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Write state file
|
|
379
|
+
fs.writeFileSync(this.stateFilePath, JSON.stringify(this._state, null, 2), 'utf-8');
|
|
298
380
|
|
|
299
|
-
|
|
300
|
-
|
|
381
|
+
// Flush log buffer
|
|
382
|
+
this._flushLogBuffer();
|
|
383
|
+
} catch (error) {
|
|
384
|
+
this._markPersistenceUnavailable(error);
|
|
385
|
+
}
|
|
301
386
|
|
|
302
387
|
return this._state;
|
|
303
388
|
}
|
|
@@ -341,11 +426,6 @@ class BuildStateManager {
|
|
|
341
426
|
throw new Error('No state loaded');
|
|
342
427
|
}
|
|
343
428
|
|
|
344
|
-
// Ensure checkpoint directory exists
|
|
345
|
-
if (!fs.existsSync(this.checkpointDir)) {
|
|
346
|
-
fs.mkdirSync(this.checkpointDir, { recursive: true });
|
|
347
|
-
}
|
|
348
|
-
|
|
349
429
|
const checkpointId = this._generateCheckpointId();
|
|
350
430
|
const now = new Date().toISOString();
|
|
351
431
|
|
|
@@ -376,8 +456,19 @@ class BuildStateManager {
|
|
|
376
456
|
this._updateMetrics(checkpoint);
|
|
377
457
|
|
|
378
458
|
// Save checkpoint file
|
|
379
|
-
|
|
380
|
-
|
|
459
|
+
if (this._persistenceAvailable) {
|
|
460
|
+
try {
|
|
461
|
+
// Ensure checkpoint directory exists
|
|
462
|
+
if (!fs.existsSync(this.checkpointDir)) {
|
|
463
|
+
fs.mkdirSync(this.checkpointDir, { recursive: true });
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const checkpointPath = path.join(this.checkpointDir, `${checkpointId}.json`);
|
|
467
|
+
fs.writeFileSync(checkpointPath, JSON.stringify(checkpoint, null, 2), 'utf-8');
|
|
468
|
+
} catch (error) {
|
|
469
|
+
this._markPersistenceUnavailable(error);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
381
472
|
|
|
382
473
|
// Save main state with checkpoint timestamp update
|
|
383
474
|
this.saveState({ updateCheckpoint: true });
|
|
@@ -781,12 +872,21 @@ class BuildStateManager {
|
|
|
781
872
|
throw new Error('No state loaded');
|
|
782
873
|
}
|
|
783
874
|
|
|
875
|
+
const attempt =
|
|
876
|
+
options.attempt ||
|
|
877
|
+
this._state.failedAttempts.filter((f) => f.subtaskId === subtaskId).length + 1;
|
|
878
|
+
|
|
879
|
+
const normalizedFailure = normalizeFailureError(options.error, {
|
|
880
|
+
storyId: this.storyId,
|
|
881
|
+
subtaskId,
|
|
882
|
+
attempt,
|
|
883
|
+
});
|
|
884
|
+
|
|
784
885
|
const failure = {
|
|
785
886
|
subtaskId,
|
|
786
|
-
attempt
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
error: options.error || 'Unknown error',
|
|
887
|
+
attempt,
|
|
888
|
+
error: normalizedFailure.message,
|
|
889
|
+
errorDetails: normalizedFailure.details,
|
|
790
890
|
timestamp: new Date().toISOString(),
|
|
791
891
|
approach: options.approach || null,
|
|
792
892
|
duration: options.duration || null,
|
|
@@ -808,6 +908,7 @@ class BuildStateManager {
|
|
|
808
908
|
this._logAttempt(subtaskId, 'failure', {
|
|
809
909
|
attempt: failure.attempt,
|
|
810
910
|
error: failure.error,
|
|
911
|
+
errorDetails: failure.errorDetails,
|
|
811
912
|
isStuck: isStuck.stuck,
|
|
812
913
|
});
|
|
813
914
|
|
|
@@ -918,6 +1019,8 @@ class BuildStateManager {
|
|
|
918
1019
|
* @private
|
|
919
1020
|
*/
|
|
920
1021
|
_logAttempt(subtaskId, action, details = {}) {
|
|
1022
|
+
if (!this._persistenceAvailable) return;
|
|
1023
|
+
|
|
921
1024
|
const entry = {
|
|
922
1025
|
timestamp: new Date().toISOString(),
|
|
923
1026
|
storyId: this.storyId,
|
|
@@ -927,7 +1030,7 @@ class BuildStateManager {
|
|
|
927
1030
|
};
|
|
928
1031
|
|
|
929
1032
|
// Format log line
|
|
930
|
-
const logLine = `[${entry.timestamp}] [${this.storyId}] [${subtaskId}] ${action}: ${
|
|
1033
|
+
const logLine = `[${entry.timestamp}] [${this.storyId}] [${subtaskId}] ${action}: ${stringifyLogDetails(details)}\n`;
|
|
931
1034
|
|
|
932
1035
|
this._logBuffer.push(logLine);
|
|
933
1036
|
|
|
@@ -943,15 +1046,20 @@ class BuildStateManager {
|
|
|
943
1046
|
*/
|
|
944
1047
|
_flushLogBuffer() {
|
|
945
1048
|
if (this._logBuffer.length === 0) return;
|
|
1049
|
+
if (!this._persistenceAvailable) return;
|
|
946
1050
|
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
fs.
|
|
950
|
-
|
|
1051
|
+
try {
|
|
1052
|
+
// Ensure directory exists
|
|
1053
|
+
if (!fs.existsSync(this.planDir)) {
|
|
1054
|
+
fs.mkdirSync(this.planDir, { recursive: true });
|
|
1055
|
+
}
|
|
951
1056
|
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1057
|
+
// Append to log file
|
|
1058
|
+
fs.appendFileSync(this.logFilePath, this._logBuffer.join(''), 'utf-8');
|
|
1059
|
+
this._logBuffer = [];
|
|
1060
|
+
} catch (error) {
|
|
1061
|
+
this._markPersistenceUnavailable(error);
|
|
1062
|
+
}
|
|
955
1063
|
}
|
|
956
1064
|
|
|
957
1065
|
/**
|
|
@@ -961,11 +1069,22 @@ class BuildStateManager {
|
|
|
961
1069
|
* @returns {string[]} Log lines
|
|
962
1070
|
*/
|
|
963
1071
|
getAttemptLog(options = {}) {
|
|
1072
|
+
if (!this._persistenceAvailable) {
|
|
1073
|
+
return [];
|
|
1074
|
+
}
|
|
1075
|
+
|
|
964
1076
|
if (!fs.existsSync(this.logFilePath)) {
|
|
965
1077
|
return [];
|
|
966
1078
|
}
|
|
967
1079
|
|
|
968
|
-
|
|
1080
|
+
let content;
|
|
1081
|
+
try {
|
|
1082
|
+
content = fs.readFileSync(this.logFilePath, 'utf-8');
|
|
1083
|
+
} catch (error) {
|
|
1084
|
+
this._markPersistenceUnavailable(error);
|
|
1085
|
+
return [];
|
|
1086
|
+
}
|
|
1087
|
+
|
|
969
1088
|
let lines = content.split('\n').filter((l) => l.trim());
|
|
970
1089
|
|
|
971
1090
|
// Filter by subtask if specified
|
|
@@ -1143,6 +1262,39 @@ class BuildStateManager {
|
|
|
1143
1262
|
// Silent by default - can be overridden
|
|
1144
1263
|
}
|
|
1145
1264
|
|
|
1265
|
+
/**
|
|
1266
|
+
* Check if file-backed persistence is still available.
|
|
1267
|
+
*
|
|
1268
|
+
* @returns {boolean} True when state/checkpoint/log writes are available.
|
|
1269
|
+
*/
|
|
1270
|
+
isPersistenceAvailable() {
|
|
1271
|
+
return this._persistenceAvailable;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
/**
|
|
1275
|
+
* Get the first persistence error that disabled file-backed writes.
|
|
1276
|
+
*
|
|
1277
|
+
* @returns {Error|null} Persistence error or null when persistence is available.
|
|
1278
|
+
*/
|
|
1279
|
+
getPersistenceError() {
|
|
1280
|
+
return this._persistenceError;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
/**
|
|
1284
|
+
* Disable file-backed persistence after an I/O failure.
|
|
1285
|
+
*
|
|
1286
|
+
* @param {Error} error - Underlying persistence error.
|
|
1287
|
+
* @private
|
|
1288
|
+
*/
|
|
1289
|
+
_markPersistenceUnavailable(error) {
|
|
1290
|
+
if (!this._persistenceAvailable) return;
|
|
1291
|
+
|
|
1292
|
+
this._persistenceAvailable = false;
|
|
1293
|
+
this._persistenceError = error instanceof Error ? error : new Error(String(error));
|
|
1294
|
+
this._logBuffer = [];
|
|
1295
|
+
this._log(`Persistence unavailable: ${this._persistenceError.message}`);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1146
1298
|
// ─────────────────────────────────────────────────────────────────────────────────
|
|
1147
1299
|
// CLI FORMATTING
|
|
1148
1300
|
// ─────────────────────────────────────────────────────────────────────────────────
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Parallel Executor
|
|
2
|
+
* Parallel Executor (PROVIDER-level)
|
|
3
3
|
* Story GEMINI-INT.17 - Multi-Agent Parallel Execution
|
|
4
4
|
*
|
|
5
5
|
* Executes Claude and Gemini in parallel for improved quality and reliability.
|
|
6
|
+
*
|
|
7
|
+
* NOT a duplicate of `core/orchestration/parallel-executor.js` (audit 2026-06-11
|
|
8
|
+
* flagged them as duplicate — they are NOT). This one races/merges AI PROVIDERS
|
|
9
|
+
* (RACE/CONSENSUS/BEST_OF/MERGE/FALLBACK). The orchestration one runs independent
|
|
10
|
+
* WORKFLOW PHASES concurrently. Different concerns; do not merge (a merge would
|
|
11
|
+
* lose one capability).
|
|
6
12
|
*/
|
|
7
13
|
|
|
8
14
|
const EventEmitter = require('events');
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* Based on Auto-Claude's merge system architecture.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
const { execSync } = require('child_process');
|
|
19
|
+
const { execSync, execFileSync } = require('child_process');
|
|
20
20
|
const fs = require('fs');
|
|
21
21
|
const path = require('path');
|
|
22
22
|
const EventEmitter = require('events');
|
|
@@ -180,17 +180,29 @@ class SemanticAnalyzer {
|
|
|
180
180
|
}));
|
|
181
181
|
|
|
182
182
|
// Extract functions
|
|
183
|
+
// DATA-003: the jsFunction regex can degrade to quadratic on pathological
|
|
184
|
+
// inputs (very large files with lots of whitespace). Cap the analyzed size
|
|
185
|
+
// and the iteration count so a single oversized file can't stall the engine.
|
|
186
|
+
// Normal files (well under the limits) are unaffected.
|
|
187
|
+
const MAX_FUNC_SCAN_BYTES = 200_000;
|
|
188
|
+
const MAX_FUNC_MATCHES = 10_000;
|
|
183
189
|
let match;
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
190
|
+
if (content.length <= MAX_FUNC_SCAN_BYTES) {
|
|
191
|
+
const funcRegex = new RegExp(this.patterns.jsFunction.source, 'gm');
|
|
192
|
+
let matchCount = 0;
|
|
193
|
+
while ((match = funcRegex.exec(content)) !== null) {
|
|
194
|
+
if (++matchCount > MAX_FUNC_MATCHES) break;
|
|
195
|
+
const name = match[1] || match[2] || match[3];
|
|
196
|
+
if (name) {
|
|
197
|
+
elements.functions.push({
|
|
198
|
+
type: 'function',
|
|
199
|
+
name,
|
|
200
|
+
content: this.extractFunctionBody(content, match.index),
|
|
201
|
+
location: this.getLocation(content, match.index),
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
// Guard against zero-width matches causing an infinite loop.
|
|
205
|
+
if (match.index === funcRegex.lastIndex) funcRegex.lastIndex++;
|
|
194
206
|
}
|
|
195
207
|
}
|
|
196
208
|
|
|
@@ -1521,7 +1533,7 @@ class SemanticMergeEngine extends EventEmitter {
|
|
|
1521
1533
|
|
|
1522
1534
|
try {
|
|
1523
1535
|
// Get list of files
|
|
1524
|
-
const fileList =
|
|
1536
|
+
const fileList = execFileSync('git', ['ls-tree', '-r', '--name-only', branch], {
|
|
1525
1537
|
cwd: this.rootPath,
|
|
1526
1538
|
encoding: 'utf8',
|
|
1527
1539
|
})
|
|
@@ -1531,7 +1543,7 @@ class SemanticMergeEngine extends EventEmitter {
|
|
|
1531
1543
|
for (const filePath of fileList) {
|
|
1532
1544
|
if (this.shouldProcessFile(filePath)) {
|
|
1533
1545
|
try {
|
|
1534
|
-
const content =
|
|
1546
|
+
const content = execFileSync('git', ['show', `${branch}:${filePath}`], {
|
|
1535
1547
|
cwd: this.rootPath,
|
|
1536
1548
|
encoding: 'utf8',
|
|
1537
1549
|
});
|
|
@@ -1564,7 +1576,7 @@ class SemanticMergeEngine extends EventEmitter {
|
|
|
1564
1576
|
|
|
1565
1577
|
try {
|
|
1566
1578
|
// Get modified files in this task
|
|
1567
|
-
const diffOutput =
|
|
1579
|
+
const diffOutput = execFileSync('git', ['diff', '--name-only', `${branch || 'main'}...HEAD`], {
|
|
1568
1580
|
cwd: workDir,
|
|
1569
1581
|
encoding: 'utf8',
|
|
1570
1582
|
}).trim();
|