geotechcli 0.4.76 → 0.4.78
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/dist/commands/ai.d.ts.map +1 -1
- package/dist/commands/ai.js +275 -7
- package/dist/commands/ai.js.map +1 -1
- package/dist/commands/fem.d.ts.map +1 -1
- package/dist/commands/fem.js +45 -2
- package/dist/commands/fem.js.map +1 -1
- package/dist/commands/ingest.d.ts.map +1 -1
- package/dist/commands/ingest.js +31 -3
- package/dist/commands/ingest.js.map +1 -1
- package/dist/commands/signal.d.ts +3 -0
- package/dist/commands/signal.d.ts.map +1 -0
- package/dist/commands/signal.js +236 -0
- package/dist/commands/signal.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai.d.ts","sourceRoot":"","sources":["../../src/commands/ai.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"ai.d.ts","sourceRoot":"","sources":["../../src/commands/ai.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAw3DpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmU5D;AAMD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAwChE;AAMD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgDzD;AA4GD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAuX3D;AAMD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA0J1D;AAMD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAuF5D"}
|
package/dist/commands/ai.js
CHANGED
|
@@ -3,7 +3,7 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } fr
|
|
|
3
3
|
import { join, relative, resolve } from 'node:path';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import ora from 'ora';
|
|
6
|
-
import { buildLLMConfig, DEFAULT_LLM_VISION_MODEL, analyzeCoreBox, classifyRMRFromImage, classifySoilFromDescription, ingestBoreholeLogDocument, inspectPdfDocument, queryGBRDocument, interpretSensorImage, runAgent, runSwarm, AgentConversation, loadProject, getProjectAgentContext, addAgentSession, addArtifact, addNote, saveNamedDataset, saveDerivedParameter, setActiveAnalysisContext, generateReport, generateReportFromCaseFile, buildProjectWorkflowReport, renderReportAsPdf, renderReportAsDocx, buildSwarmSessionProjectRecord, persistSwarmCaseFile, persistCaseFileEvidence, analyzeWorkspace, resolveWorkspaceRoot, buildProjectWorkflowRouterPrompt, generateText, routeProjectWorkflowRequest, runProjectWorkflow, } from '@geotechcli/core';
|
|
6
|
+
import { buildLLMConfig, DEFAULT_LLM_VISION_MODEL, analyzeCoreBox, classifyRMRFromImage, classifySoilFromDescription, ingestBoreholeLogDocument, inspectPdfDocument, queryGBRDocument, interpretSensorImage, runAgent, runSwarm, AgentConversation, loadProject, getProjectAgentContext, addAgentSession, addArtifact, addNote, saveNamedDataset, saveDerivedParameter, setActiveAnalysisContext, generateReport, generateReportFromCaseFile, buildProjectWorkflowReport, renderReportAsPdf, renderReportAsDocx, buildSwarmSessionProjectRecord, persistSwarmCaseFile, persistCaseFileEvidence, analyzeWorkspace, resolveWorkspaceRoot, buildProjectWorkflowRouterPrompt, generateText, routeProjectWorkflowRequest, runProjectWorkflow, analyzeSignalFile, } from '@geotechcli/core';
|
|
7
7
|
import { heading, keyValue, renderJSON, renderRichText, success, error, warn, renderTable, info } from '../ui/terminal.js';
|
|
8
8
|
import { addGlobalFlags, getGlobalFlags } from '../util/flags.js';
|
|
9
9
|
import { estimateHostedBetaVisionBodyBytes, formatByteSize, HOSTED_BETA_REQUEST_LIMIT_BYTES, readVisionInput, readVisionPdfPageInputs, resolveStructuredOutputTarget, HOSTED_BETA_REQUEST_SAFE_BYTES, } from '../util/vision-output.js';
|
|
@@ -29,6 +29,8 @@ function summarizeWorkspaceManifestForAgent(manifest) {
|
|
|
29
29
|
'Calculation/verifier pre-check:',
|
|
30
30
|
`- Status: ${manifest.verifier.status}`,
|
|
31
31
|
`- Findings: ${manifest.verifier.summary.blocking} blocking, ${manifest.verifier.summary.review} review, ${manifest.verifier.summary.info} info`,
|
|
32
|
+
`- Calculation routes: ${manifest.verifier.calculationReadiness.summary.ready} ready, ${manifest.verifier.calculationReadiness.summary.readyWithAssumptions} assumption-bound, ${manifest.verifier.calculationReadiness.summary.blocked} blocked`,
|
|
33
|
+
...manifest.verifier.calculationReadiness.workflows.slice(0, 8).map((workflow) => `- ${workflow.workflow}: ${workflow.status}; missing=${workflow.missing.join(', ') || 'none'}; command=${workflow.commandTemplate}`),
|
|
32
34
|
...manifest.verifier.findings.slice(0, 8).map((finding) => `- ${finding.severity}/${finding.code}: ${finding.message}`),
|
|
33
35
|
].join('\n')
|
|
34
36
|
: '';
|
|
@@ -79,6 +81,11 @@ const PROJECT_AGENT_TASKS = [
|
|
|
79
81
|
label: 'Ground-model interpretation',
|
|
80
82
|
prompt: 'Interpret the evidence-bound GroundModel from the attached workspace manifest. Summarize strata, groundwater, uncertainty, and what engineering workflows are ready.',
|
|
81
83
|
},
|
|
84
|
+
{
|
|
85
|
+
task: 'calculation-readiness',
|
|
86
|
+
label: 'Calculation readiness and draft routing',
|
|
87
|
+
prompt: 'Summarize calculation readiness from the attached workspace manifest. Separate ready, assumption-bound, and blocked bearing, settlement, pile, liquefaction, slope, and FEM draft workflows. Recommend only validated deterministic GeotechCLI commands and missing user inputs.',
|
|
88
|
+
},
|
|
82
89
|
{
|
|
83
90
|
task: 'risk-analysis',
|
|
84
91
|
label: 'Risk analysis',
|
|
@@ -94,6 +101,11 @@ const PROJECT_AGENT_TASKS = [
|
|
|
94
101
|
label: 'Preliminary recommendations',
|
|
95
102
|
prompt: 'Prepare preliminary geotechnical recommendations from the attached workspace manifest. Clearly mark what is evidence-backed, assumption-bound, or blocked by missing inputs.',
|
|
96
103
|
},
|
|
104
|
+
{
|
|
105
|
+
task: 'signal-analysis',
|
|
106
|
+
label: 'Signal analysis',
|
|
107
|
+
prompt: 'Route monitoring, instrumentation, settlement, piezometer, inclinometer, vibration, and load-test files into deterministic geotech signal analyze commands. Summarize available sources, missing threshold assumptions, and review gates without inventing signal metrics.',
|
|
108
|
+
},
|
|
97
109
|
{
|
|
98
110
|
task: 'visualization',
|
|
99
111
|
label: 'Visualizations and maps',
|
|
@@ -138,6 +150,46 @@ function normalizeProjectTask(value) {
|
|
|
138
150
|
case 'interpretation':
|
|
139
151
|
case 'ground-model-interpretation':
|
|
140
152
|
return 'ground-model';
|
|
153
|
+
case 'calculation':
|
|
154
|
+
case 'calculations':
|
|
155
|
+
case 'calculation-readiness':
|
|
156
|
+
case 'calculation-routing':
|
|
157
|
+
case 'calc-readiness':
|
|
158
|
+
case 'design-readiness':
|
|
159
|
+
case 'bearing':
|
|
160
|
+
case 'bearing-capacity':
|
|
161
|
+
case 'settlement':
|
|
162
|
+
case 'pile':
|
|
163
|
+
case 'pile-capacity':
|
|
164
|
+
case 'liquefaction':
|
|
165
|
+
case 'slope':
|
|
166
|
+
case 'slope-stability':
|
|
167
|
+
case 'fem-readiness':
|
|
168
|
+
case 'fem-foundation':
|
|
169
|
+
case 'fem-foundation-settlement':
|
|
170
|
+
case 'fem-excavation':
|
|
171
|
+
case 'fem-excavation-deformation':
|
|
172
|
+
case 'fem-tunnel':
|
|
173
|
+
case 'fem-tunnel-settlement':
|
|
174
|
+
case 'fem-tunnel-volume-loss-settlement':
|
|
175
|
+
case 'fem-shaft':
|
|
176
|
+
case 'fem-shaft-deformation':
|
|
177
|
+
case 'fem-pile-group':
|
|
178
|
+
case 'fem-pile-group-elastic-interaction':
|
|
179
|
+
case 'fem-slope':
|
|
180
|
+
case 'fem-slope-embankment':
|
|
181
|
+
case 'fem-slope-embankment-deformation':
|
|
182
|
+
case 'fem-embankment':
|
|
183
|
+
case 'fem-retaining-wall':
|
|
184
|
+
case 'fem-retaining-wall-excavation-support':
|
|
185
|
+
case 'fem-excavation-support':
|
|
186
|
+
case 'fem-seepage':
|
|
187
|
+
case 'fem-seepage-groundwater-coupling':
|
|
188
|
+
case 'fem-groundwater-coupling':
|
|
189
|
+
case 'fem-staged-settlement':
|
|
190
|
+
case 'fem-staged-settlement-consolidation':
|
|
191
|
+
case 'fem-consolidation':
|
|
192
|
+
return 'calculation-readiness';
|
|
141
193
|
case 'risk':
|
|
142
194
|
case 'risk-analysis':
|
|
143
195
|
return 'risk-analysis';
|
|
@@ -149,6 +201,24 @@ function normalizeProjectTask(value) {
|
|
|
149
201
|
case 'recommendations':
|
|
150
202
|
case 'foundation-recommendations':
|
|
151
203
|
return 'recommendations';
|
|
204
|
+
case 'signal':
|
|
205
|
+
case 'signals':
|
|
206
|
+
case 'signal-analysis':
|
|
207
|
+
case 'signal-analytics':
|
|
208
|
+
case 'monitoring':
|
|
209
|
+
case 'monitoring-analysis':
|
|
210
|
+
case 'time-series':
|
|
211
|
+
case 'timeseries':
|
|
212
|
+
case 'instrumentation':
|
|
213
|
+
case 'piezometer':
|
|
214
|
+
case 'piezometers':
|
|
215
|
+
case 'inclinometer':
|
|
216
|
+
case 'inclinometers':
|
|
217
|
+
case 'vibration':
|
|
218
|
+
case 'load-test':
|
|
219
|
+
case 'load-tests':
|
|
220
|
+
case 'pile-load-test':
|
|
221
|
+
return 'signal-analysis';
|
|
152
222
|
case 'viz':
|
|
153
223
|
case 'visualize':
|
|
154
224
|
case 'visualization':
|
|
@@ -162,7 +232,9 @@ function normalizeProjectTask(value) {
|
|
|
162
232
|
}
|
|
163
233
|
}
|
|
164
234
|
function projectTaskPrompt(task, customPrompt) {
|
|
165
|
-
const taskDef = PROJECT_AGENT_TASKS.find((item) => item.task === task)
|
|
235
|
+
const taskDef = PROJECT_AGENT_TASKS.find((item) => item.task === task)
|
|
236
|
+
?? PROJECT_AGENT_TASKS.find((item) => item.task === 'custom-question')
|
|
237
|
+
?? PROJECT_AGENT_TASKS[0];
|
|
166
238
|
if (task === 'custom-question' && customPrompt?.trim()) {
|
|
167
239
|
return `Project-aware custom question: ${customPrompt.trim()}`;
|
|
168
240
|
}
|
|
@@ -198,9 +270,11 @@ function inferProjectIntent(rawPrompt, selectedTask) {
|
|
|
198
270
|
};
|
|
199
271
|
add('data-quality', /\b(?:data quality|inventory|missing data|quality report|duplicate)\b/);
|
|
200
272
|
add('ground-model', /\b(?:ground model|interpret|strata|stratigraphy|lithology|hydrogeology)\b/);
|
|
273
|
+
add('calculation-readiness', /\b(?:calculation readiness|calculation route|calculation routing|design readiness|design route|bearing(?: capacity| calculation| readiness)?|settlement(?: calculation| readiness| design| route| workflow)|pile(?: capacity| calculation| readiness)?|liquefaction(?: calculation| readiness)?|slope(?: stability| calculation| readiness)?|fem(?: draft| readiness| foundation settlement| excavation deformation)?|ready for calculation|ready for design)\b/);
|
|
201
274
|
add('risk-analysis', /\b(?:risk|hazard|limitation|uncertainty|mitigation)\b/);
|
|
202
275
|
add('anomaly-detection', /\b(?:anomal\w*|conflict|outlier|inconsistent|inconsistency)\b/);
|
|
203
276
|
add('recommendations', /\b(?:recommend|foundation option|advice|next action)\b/);
|
|
277
|
+
add('signal-analysis', /\b(?:signal analysis|signal analytics|monitoring analysis|time[-\s]?series|instrumentation|piezometer|pore pressure|inclinometer|vibration|accelerometer|settlement monitoring|monitoring trend|threshold|trigger level|load[-\s]?test)\b/);
|
|
204
278
|
add('visualization', /\b(?:visual\w*|map|plot|chart|section|profile|strip log)\b/);
|
|
205
279
|
const uniqueTasks = [...new Set(tasks)];
|
|
206
280
|
if (uniqueTasks.length === 0) {
|
|
@@ -224,6 +298,9 @@ function projectReadinessFromManifest(manifest) {
|
|
|
224
298
|
const hasGroundModel = Boolean(manifest.groundModel && manifest.groundModel.stats.evidenceRefs > 0);
|
|
225
299
|
const hasCoordinates = Boolean(manifest.groundModel?.map?.points?.length);
|
|
226
300
|
const hasVerifier = Boolean(manifest.verifier);
|
|
301
|
+
const signalSources = (manifest.summary.datasetTypes['monitoring-time-series'] ?? 0)
|
|
302
|
+
+ (manifest.summary.datasetTypes['signal-record'] ?? 0)
|
|
303
|
+
+ (manifest.summary.datasetTypes['pile-load-test'] ?? 0);
|
|
227
304
|
const calculationWorkflows = manifest.verifier?.calculationReadiness.workflows ?? [];
|
|
228
305
|
const readyWorkflowCount = calculationWorkflows.filter((workflow) => workflow.status !== 'blocked').length;
|
|
229
306
|
const verifierMissing = calculationWorkflows.flatMap((workflow) => workflow.missing).slice(0, 8);
|
|
@@ -256,6 +333,15 @@ function projectReadinessFromManifest(manifest) {
|
|
|
256
333
|
: ['Risk analysis needs GroundModel verification output.'],
|
|
257
334
|
missing: hasVerifier ? verifierMissing : ['GroundModel verifier output'],
|
|
258
335
|
},
|
|
336
|
+
{
|
|
337
|
+
task: 'calculation-readiness',
|
|
338
|
+
label: 'Calculation readiness and draft routing',
|
|
339
|
+
status: readyWorkflowCount > 0 ? 'partially_ready' : hasVerifier ? 'blocked' : 'blocked',
|
|
340
|
+
reasons: hasVerifier
|
|
341
|
+
? [`${readyWorkflowCount} calculation workflow(s) are ready or assumption-bound; ${calculationWorkflows.filter((workflow) => workflow.status === 'blocked').length} blocked.`]
|
|
342
|
+
: ['Calculation readiness needs GroundModel verifier output.'],
|
|
343
|
+
missing: hasVerifier ? verifierMissing : ['GroundModel verifier output'],
|
|
344
|
+
},
|
|
259
345
|
{
|
|
260
346
|
task: 'anomaly-detection',
|
|
261
347
|
label: 'Anomaly detection / data conflicts',
|
|
@@ -274,6 +360,15 @@ function projectReadinessFromManifest(manifest) {
|
|
|
274
360
|
: ['No downstream calculation workflow is ready yet.'],
|
|
275
361
|
missing: verifierMissing,
|
|
276
362
|
},
|
|
363
|
+
{
|
|
364
|
+
task: 'signal-analysis',
|
|
365
|
+
label: 'Signal analysis',
|
|
366
|
+
status: signalSources > 0 ? 'partially_ready' : 'blocked',
|
|
367
|
+
reasons: signalSources > 0
|
|
368
|
+
? [`${signalSources} monitoring/signal/load-test source(s) can be routed into deterministic signal analysis.`]
|
|
369
|
+
: ['Signal analysis needs settlement, piezometer, inclinometer, vibration, load-test, or time-series data.'],
|
|
370
|
+
missing: signalSources > 0 ? ['project-specific thresholds / trigger levels'] : ['monitoring/signal CSV, TSV, or XLSX files'],
|
|
371
|
+
},
|
|
277
372
|
{
|
|
278
373
|
task: 'visualization',
|
|
279
374
|
label: 'Visualizations and maps',
|
|
@@ -490,6 +585,177 @@ function relativeArtifactPath(rootPath, filePath) {
|
|
|
490
585
|
const rel = relative(rootPath, filePath);
|
|
491
586
|
return rel && !rel.startsWith('..') ? rel : filePath;
|
|
492
587
|
}
|
|
588
|
+
const SIGNAL_WORKFLOW_DATASET_TYPES = new Set(['monitoring-time-series', 'signal-record', 'pile-load-test']);
|
|
589
|
+
function detectSignalWorkflowSources(manifest) {
|
|
590
|
+
const sources = [];
|
|
591
|
+
const seen = new Set();
|
|
592
|
+
for (const file of manifest.files) {
|
|
593
|
+
const schemas = file.schemas ?? [];
|
|
594
|
+
const matchingSchemas = schemas.filter((schema) => SIGNAL_WORKFLOW_DATASET_TYPES.has(schema.datasetType));
|
|
595
|
+
if (matchingSchemas.length > 0) {
|
|
596
|
+
for (const schema of matchingSchemas) {
|
|
597
|
+
const key = `${file.absolutePath}#${schema.sheetName ?? ''}`;
|
|
598
|
+
if (seen.has(key))
|
|
599
|
+
continue;
|
|
600
|
+
seen.add(key);
|
|
601
|
+
sources.push({
|
|
602
|
+
file,
|
|
603
|
+
label: schema.sheetName ? `${file.path}#${schema.sheetName}` : file.path,
|
|
604
|
+
signalType: inferSignalWorkflowType(file, schema),
|
|
605
|
+
sheetName: schema.sheetName,
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
if (SIGNAL_WORKFLOW_DATASET_TYPES.has(file.classification.datasetType)) {
|
|
611
|
+
const key = `${file.absolutePath}#`;
|
|
612
|
+
if (seen.has(key))
|
|
613
|
+
continue;
|
|
614
|
+
seen.add(key);
|
|
615
|
+
sources.push({
|
|
616
|
+
file,
|
|
617
|
+
label: file.path,
|
|
618
|
+
signalType: inferSignalWorkflowType(file),
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return sources;
|
|
623
|
+
}
|
|
624
|
+
function inferSignalWorkflowType(file, schema) {
|
|
625
|
+
if (schema?.datasetType === 'pile-load-test' || file.classification.datasetType === 'pile-load-test')
|
|
626
|
+
return 'load-test';
|
|
627
|
+
const roles = new Set(schema?.columns.flatMap((column) => column.roles) ?? []);
|
|
628
|
+
if (roles.has('settlement'))
|
|
629
|
+
return 'settlement';
|
|
630
|
+
if (roles.has('pore_pressure'))
|
|
631
|
+
return 'piezometer';
|
|
632
|
+
if (roles.has('inclination'))
|
|
633
|
+
return 'inclinometer';
|
|
634
|
+
if (roles.has('vibration'))
|
|
635
|
+
return 'vibration';
|
|
636
|
+
const text = [file.path, file.classification.datasetType, ...file.classification.signals].join(' ').toLowerCase();
|
|
637
|
+
if (/\b(load[-_\s]?test|pile[-_\s]?load)\b/.test(text))
|
|
638
|
+
return 'load-test';
|
|
639
|
+
if (/\b(settlement|heave|subsidence)\b/.test(text))
|
|
640
|
+
return 'settlement';
|
|
641
|
+
if (/\b(piezometer|pore[-_\s]?pressure|groundwater|water[-_\s]?level)\b/.test(text))
|
|
642
|
+
return 'piezometer';
|
|
643
|
+
if (/\b(inclinometer|inclination|tilt|deflection)\b/.test(text))
|
|
644
|
+
return 'inclinometer';
|
|
645
|
+
if (/\b(vibration|accelerometer|seismic|fft|psd)\b/.test(text))
|
|
646
|
+
return 'vibration';
|
|
647
|
+
return 'unknown';
|
|
648
|
+
}
|
|
649
|
+
function signalArtifactSlug(value) {
|
|
650
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 64) || 'signal';
|
|
651
|
+
}
|
|
652
|
+
async function persistSignalAnalysisArtifacts(options) {
|
|
653
|
+
if (options.workflowRun.task !== 'signal-analysis')
|
|
654
|
+
return;
|
|
655
|
+
const sources = detectSignalWorkflowSources(options.manifest).slice(0, 12);
|
|
656
|
+
if (sources.length === 0)
|
|
657
|
+
return;
|
|
658
|
+
const signalDir = join(options.runDir, 'signals');
|
|
659
|
+
mkdirSync(signalDir, { recursive: true });
|
|
660
|
+
const indexEntries = [];
|
|
661
|
+
let successCount = 0;
|
|
662
|
+
let failureCount = 0;
|
|
663
|
+
for (const [index, source] of sources.entries()) {
|
|
664
|
+
const artifactPath = join(signalDir, `${String(index + 1).padStart(2, '0')}-${signalArtifactSlug(source.label)}.analysis.json`);
|
|
665
|
+
const analyzeOptions = {
|
|
666
|
+
...(source.signalType !== 'unknown' ? { type: source.signalType } : {}),
|
|
667
|
+
...(source.sheetName ? { sheetName: source.sheetName } : {}),
|
|
668
|
+
maxRows: 5000,
|
|
669
|
+
};
|
|
670
|
+
try {
|
|
671
|
+
const result = await analyzeSignalFile(source.file.absolutePath, analyzeOptions);
|
|
672
|
+
writeFileSync(artifactPath, JSON.stringify(result, null, 2), 'utf-8');
|
|
673
|
+
successCount += 1;
|
|
674
|
+
const relativePath = relativeArtifactPath(options.plan.workspace.rootPath, artifactPath);
|
|
675
|
+
const status = result.warnings.length > 0 ? 'review' : 'pass';
|
|
676
|
+
indexEntries.push({
|
|
677
|
+
source: source.file.path,
|
|
678
|
+
absolutePath: source.file.absolutePath,
|
|
679
|
+
sheetName: source.sheetName,
|
|
680
|
+
status,
|
|
681
|
+
signalType: result.signalType,
|
|
682
|
+
rowsAnalyzed: result.source.rowsAnalyzed,
|
|
683
|
+
rowsRejected: result.source.rowsRejected,
|
|
684
|
+
series: result.series.length,
|
|
685
|
+
thresholdFlags: result.thresholdFlags.length,
|
|
686
|
+
missingIntervals: result.missingIntervals.length,
|
|
687
|
+
warnings: result.warnings,
|
|
688
|
+
artifact: relativePath,
|
|
689
|
+
});
|
|
690
|
+
options.workflowRun.artifacts.push({
|
|
691
|
+
kind: 'json',
|
|
692
|
+
path: relativePath,
|
|
693
|
+
description: `Deterministic signal analysis for ${source.label}`,
|
|
694
|
+
});
|
|
695
|
+
options.workflowRun.trace.steps.push({
|
|
696
|
+
type: 'tool_call',
|
|
697
|
+
name: 'signal.analyze_file',
|
|
698
|
+
status,
|
|
699
|
+
detail: `Analyzed ${source.label} into ${relativePath}.`,
|
|
700
|
+
});
|
|
701
|
+
options.workflowRun.toolCalls.push({
|
|
702
|
+
type: 'tool_call',
|
|
703
|
+
tool: 'signal.analyze_file',
|
|
704
|
+
status,
|
|
705
|
+
summary: `Analyzed ${source.label}: ${result.source.rowsAnalyzed} rows, ${result.series.length} series, ${result.thresholdFlags.length} threshold flag(s), ${result.missingIntervals.length} missing interval(s).`,
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
catch (err) {
|
|
709
|
+
failureCount += 1;
|
|
710
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
711
|
+
indexEntries.push({
|
|
712
|
+
source: source.file.path,
|
|
713
|
+
absolutePath: source.file.absolutePath,
|
|
714
|
+
sheetName: source.sheetName,
|
|
715
|
+
status: 'blocked',
|
|
716
|
+
signalType: source.signalType,
|
|
717
|
+
error: message,
|
|
718
|
+
});
|
|
719
|
+
options.workflowRun.trace.steps.push({
|
|
720
|
+
type: 'tool_call',
|
|
721
|
+
name: 'signal.analyze_file',
|
|
722
|
+
status: 'blocked',
|
|
723
|
+
detail: `Could not analyze ${source.label}: ${message}`,
|
|
724
|
+
});
|
|
725
|
+
options.workflowRun.toolCalls.push({
|
|
726
|
+
type: 'tool_call',
|
|
727
|
+
tool: 'signal.analyze_file',
|
|
728
|
+
status: 'blocked',
|
|
729
|
+
summary: `Could not analyze ${source.label}: ${message}`,
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
if (failureCount > 0) {
|
|
734
|
+
options.workflowRun.status = successCount > 0 && options.workflowRun.status !== 'blocked' ? 'review' : 'blocked';
|
|
735
|
+
}
|
|
736
|
+
const indexPath = join(signalDir, 'index.json');
|
|
737
|
+
const indexPayload = {
|
|
738
|
+
schemaVersion: 'geotech.signal-analysis-artifact-index.v1',
|
|
739
|
+
generatedAt: options.workflowRun.generatedAt,
|
|
740
|
+
runId: options.workflowRun.runId,
|
|
741
|
+
task: options.workflowRun.task,
|
|
742
|
+
sources: indexEntries,
|
|
743
|
+
};
|
|
744
|
+
writeFileSync(indexPath, JSON.stringify(indexPayload, null, 2), 'utf-8');
|
|
745
|
+
const relativeIndexPath = relativeArtifactPath(options.plan.workspace.rootPath, indexPath);
|
|
746
|
+
options.workflowRun.artifacts.push({
|
|
747
|
+
kind: 'json',
|
|
748
|
+
path: relativeIndexPath,
|
|
749
|
+
description: 'Deterministic signal analysis artifact index',
|
|
750
|
+
});
|
|
751
|
+
options.workflowRun.summary.push(`Signal analyses persisted: ${successCount}/${sources.length} source(s) under ${relativeArtifactPath(options.plan.workspace.rootPath, signalDir)}.`);
|
|
752
|
+
options.workflowRun.toolCalls.push({
|
|
753
|
+
type: 'tool_call',
|
|
754
|
+
tool: 'signal.analysis_artifacts',
|
|
755
|
+
status: failureCount > 0 ? 'review' : 'pass',
|
|
756
|
+
summary: `Persisted ${successCount}/${sources.length} deterministic signal analysis artifact(s).`,
|
|
757
|
+
});
|
|
758
|
+
}
|
|
493
759
|
async function requestProjectWorkflowRouteProposal(options) {
|
|
494
760
|
const routePrompt = buildProjectWorkflowRouterPrompt({
|
|
495
761
|
prompt: options.prompt,
|
|
@@ -573,7 +839,7 @@ function renderProjectAwarePlan(plan, flags) {
|
|
|
573
839
|
}
|
|
574
840
|
console.log('');
|
|
575
841
|
warn('No model-heavy workflow has run yet. Choose a task with --task, or ask a project question with --workspace.');
|
|
576
|
-
console.log(chalk.gray(' Next actions: geotech agent --task data-quality | --task ground-model | --task risk-analysis | --task anomaly-detection | --task recommendations | --task visualization'));
|
|
842
|
+
console.log(chalk.gray(' Next actions: geotech agent --task data-quality | --task ground-model | --task calculation-readiness | --task risk-analysis | --task anomaly-detection | --task recommendations | --task signal-analysis | --task visualization'));
|
|
577
843
|
success(`Project state written to ${relativeArtifactPath(plan.workspace.rootPath, join(plan.workspace.rootPath, '.geotech'))}`);
|
|
578
844
|
}
|
|
579
845
|
async function renderAndPersistProjectWorkflow(plan, manifest, task, flags) {
|
|
@@ -583,11 +849,12 @@ async function renderAndPersistProjectWorkflow(plan, manifest, task, flags) {
|
|
|
583
849
|
runId: plan.runId,
|
|
584
850
|
now: plan.project.generatedAt,
|
|
585
851
|
});
|
|
586
|
-
const report = buildProjectWorkflowReport(workflowRun);
|
|
587
852
|
const runDir = join(plan.workspace.rootPath, '.geotech', 'runs', plan.runId);
|
|
588
853
|
const resultPath = join(runDir, 'workflow_result.json');
|
|
589
854
|
const reportPath = join(runDir, 'workflow_report.md');
|
|
590
855
|
const tracePath = join(runDir, 'workflow_trace.json');
|
|
856
|
+
await persistSignalAnalysisArtifacts({ plan, manifest, workflowRun, runDir });
|
|
857
|
+
const report = buildProjectWorkflowReport(workflowRun);
|
|
591
858
|
writeFileSync(resultPath, JSON.stringify(workflowRun, null, 2), 'utf-8');
|
|
592
859
|
writeFileSync(reportPath, report.fullMarkdown, 'utf-8');
|
|
593
860
|
writeFileSync(tracePath, JSON.stringify(workflowRun.trace, null, 2), 'utf-8');
|
|
@@ -660,12 +927,13 @@ async function renderAndPersistProjectWorkflowRoute(plan, manifest, route, flags
|
|
|
660
927
|
runId: workflowRunId,
|
|
661
928
|
now: plan.project.generatedAt,
|
|
662
929
|
});
|
|
663
|
-
const report = buildProjectWorkflowReport(workflowRun);
|
|
664
930
|
const runDir = join(plan.workspace.rootPath, '.geotech', 'runs', workflowRun.runId);
|
|
665
931
|
mkdirSync(runDir, { recursive: true });
|
|
666
932
|
const resultPath = join(runDir, 'workflow_result.json');
|
|
667
933
|
const reportPath = join(runDir, 'workflow_report.md');
|
|
668
934
|
const tracePath = join(runDir, 'workflow_trace.json');
|
|
935
|
+
await persistSignalAnalysisArtifacts({ plan, manifest, workflowRun, runDir });
|
|
936
|
+
const report = buildProjectWorkflowReport(workflowRun);
|
|
669
937
|
writeFileSync(resultPath, JSON.stringify(workflowRun, null, 2), 'utf-8');
|
|
670
938
|
writeFileSync(reportPath, report.fullMarkdown, 'utf-8');
|
|
671
939
|
writeFileSync(tracePath, JSON.stringify(workflowRun.trace, null, 2), 'utf-8');
|
|
@@ -1791,14 +2059,14 @@ function renderSwarmStep(step, json, quiet = false) {
|
|
|
1791
2059
|
}
|
|
1792
2060
|
export function registerAgentCommand(program) {
|
|
1793
2061
|
const cmd = new Command('agent')
|
|
1794
|
-
.description('Agentic AI -
|
|
2062
|
+
.description('Agentic AI - routes evidence into deterministic tools; FEM execution remains human-invoked and experimental')
|
|
1795
2063
|
.argument('[task...]', 'Engineering task in natural language')
|
|
1796
2064
|
.option('--swarm', 'Use the role-based multi-agent swarm planner and specialist review loop')
|
|
1797
2065
|
.option('--skills', 'Enable installed skill tools for this session')
|
|
1798
2066
|
.option('--project <id>', 'Load and persist context to a stored project')
|
|
1799
2067
|
.option('--workspace <dir>', 'Scan a local workspace and attach its manifest summary to the agent task')
|
|
1800
2068
|
.option('--no-workspace', 'Disable automatic project-aware workspace discovery')
|
|
1801
|
-
.option('--task <task>', 'Run a project-aware task: data-quality, ground-model, risk-analysis, anomaly-detection, recommendations, visualization')
|
|
2069
|
+
.option('--task <task>', 'Run a project-aware task: data-quality, ground-model, calculation-readiness, risk-analysis, anomaly-detection, recommendations, signal-analysis, visualization')
|
|
1802
2070
|
.option('--route-with-model', 'Let the configured LLM propose a validated workflow route when deterministic routing needs selection')
|
|
1803
2071
|
.option('--plan-only', 'Scan the workspace, write .geotech project state, and show workflow readiness without calling an LLM')
|
|
1804
2072
|
.option('--refresh', 'Refresh the deterministic workspace manifest and .geotech project state')
|