@engineeros/connector 0.15.1 → 0.15.2
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/README.md +3 -1
- package/bin/engineeros-connector.mjs +91 -90
- package/package.json +1 -1
- package/src/runner.mjs +68 -22
package/README.md
CHANGED
|
@@ -47,7 +47,9 @@ npx --yes @engineeros/connector@latest pair PAIRING-CODE --url https://your-engi
|
|
|
47
47
|
|
|
48
48
|
The connector uploads a bounded ZIP snapshot for a safe file inventory, then stays online for deep assessments, rescans, and Goal Runs. Inventory never executes repository code. It excludes known secrets, dependency directories, build output, compiled binaries, files larger than 5 MB, agent-tool caches, Git metadata, and connector state before upload.
|
|
49
49
|
|
|
50
|
-
From **Project steering -> Workspace**, run the workspace assessment to use the connected agent subscription already authenticated on that computer. Select any combination of fifteen brownfield assessment domains, collectively covering 133 baseline checks from product and architecture through security, privacy, project-specific compliance, delivery, reliability, governance, and modernization planning. Every domain uses a detailed structured-Markdown contract. Connector `0.15.
|
|
50
|
+
From **Project steering -> Workspace**, run the workspace assessment to use the connected agent subscription already authenticated on that computer. Select any combination of fifteen brownfield assessment domains, collectively covering 133 baseline checks from product and architecture through security, privacy, project-specific compliance, delivery, reliability, governance, and modernization planning. Every domain uses a detailed structured-Markdown contract. Connector `0.15.1` uses half of the locally available CPU parallelism by default, capped at eight assessment workers, and supports an explicit `--assessment-workers` override from 1 to 32. It atomically spools each completed Markdown report under `.engineeros/assessments/<run-id>` before delivery. ACP agents such as OpenCode use isolated stage sessions within one persistent coding-agent process. The connector owns the worker pool and sends one aggregate heartbeat containing every active worker's phase, progress, last activity, and event count; report content is no longer streamed over the WebSocket or written into backend assessment JSON. The backend validates stage checkpoints so dependency fan-out remains authoritative, while the connector retains the reports and reuses them after reconnect without rerunning completed agents. Capability detail and project-specific compliance controls fan out after their catalogs. Final synthesis reads the connector-local Markdown files, then one completed bundle is revalidated, persisted, and published as canonical artifacts. The connector removes its spool only after EngineerOS acknowledges the complete bundle. Compliance findings describe repository-verifiable engineering readiness and missing external context, never legal compliance or certification. The connector prints each worker's safe activity and elapsed time every 30 seconds, stops a worker that produces no agent activity for ten minutes instead of waiting indefinitely, and applies separate bounded structure, semantic, and corrected-response formatting repairs before failing a stage. Assessment cannot modify tracked workspace source.
|
|
51
|
+
|
|
52
|
+
Every backend `422` content-validation response is returned to the stage agent with the exact failed item. Distinct validation failures continue through correction and resubmission until the complete stage is accepted. Three repetitions of the same unresolved validation error stop the automatic loop while preserving the latest local report for retry. Authentication, cancellation, source drift, and transport failures remain operational errors rather than agent-correction prompts.
|
|
51
53
|
|
|
52
54
|
Override the automatic assessment worker count when pairing (saved for that workspace) or starting the connector (for that process only):
|
|
53
55
|
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
requeueInterruptedAssessment,
|
|
28
28
|
stopProcess,
|
|
29
29
|
submitAssessmentResultWithRetry,
|
|
30
|
+
submitAssessmentWithValidationRepair,
|
|
30
31
|
takeWorkspaceAssessmentWave,
|
|
31
32
|
workspaceAssessmentWorkerLimit,
|
|
32
33
|
workspaceSnapshot,
|
|
@@ -721,6 +722,46 @@ async function executeAssessment(assignment, assessmentState) {
|
|
|
721
722
|
);
|
|
722
723
|
}
|
|
723
724
|
}, 15_000);
|
|
725
|
+
const assessmentAgentCallbacks = {
|
|
726
|
+
onController: (controller) => {
|
|
727
|
+
if (isActiveAssessment()) assessmentState.controller = controller;
|
|
728
|
+
},
|
|
729
|
+
onProcess: (child) => {
|
|
730
|
+
if (isActiveAssessment()) {
|
|
731
|
+
assessmentState.child = child;
|
|
732
|
+
agentProcessStarted = true;
|
|
733
|
+
if (assessmentState.phase !== "correcting") {
|
|
734
|
+
assessmentState.phase = "running";
|
|
735
|
+
reportProgress("Connected agent process started");
|
|
736
|
+
}
|
|
737
|
+
assessmentState.lastActivityAt = Date.now();
|
|
738
|
+
}
|
|
739
|
+
},
|
|
740
|
+
onEvent: (event) => {
|
|
741
|
+
assessmentState.lastActivityAt = Date.now();
|
|
742
|
+
assessmentState.eventCount += 1;
|
|
743
|
+
const message = assessmentProgressMessage(event);
|
|
744
|
+
if (message) reportProgress(message);
|
|
745
|
+
},
|
|
746
|
+
};
|
|
747
|
+
const attachStoredCorrection = (candidate, preparedAssignment) =>
|
|
748
|
+
attachAssessmentRejectionCorrection(
|
|
749
|
+
candidate,
|
|
750
|
+
preparedAssignment,
|
|
751
|
+
config,
|
|
752
|
+
assessmentAgentCallbacks,
|
|
753
|
+
{
|
|
754
|
+
draftPath: path
|
|
755
|
+
.join(
|
|
756
|
+
".engineeros",
|
|
757
|
+
"assessments",
|
|
758
|
+
String(assessmentId),
|
|
759
|
+
candidate.report_file,
|
|
760
|
+
)
|
|
761
|
+
.split(path.sep)
|
|
762
|
+
.join("/"),
|
|
763
|
+
},
|
|
764
|
+
);
|
|
724
765
|
try {
|
|
725
766
|
if (assignment.assessment_mode === "incremental") {
|
|
726
767
|
progress = 15;
|
|
@@ -736,63 +777,14 @@ async function executeAssessment(assignment, assessmentState) {
|
|
|
736
777
|
stage,
|
|
737
778
|
);
|
|
738
779
|
if (result) {
|
|
739
|
-
|
|
740
|
-
|
|
780
|
+
attachStoredCorrection(result, preparedAssignment);
|
|
781
|
+
reportProgress("Recovered completed stage report from connector storage");
|
|
782
|
+
} else {
|
|
783
|
+
result = await executeWorkspaceAssessment(
|
|
741
784
|
preparedAssignment,
|
|
742
785
|
config,
|
|
743
|
-
|
|
744
|
-
onController: (controller) => {
|
|
745
|
-
if (isActiveAssessment()) assessmentState.controller = controller;
|
|
746
|
-
},
|
|
747
|
-
onProcess: (child) => {
|
|
748
|
-
if (isActiveAssessment()) {
|
|
749
|
-
assessmentState.child = child;
|
|
750
|
-
agentProcessStarted = true;
|
|
751
|
-
assessmentState.phase = "correcting";
|
|
752
|
-
assessmentState.lastActivityAt = Date.now();
|
|
753
|
-
}
|
|
754
|
-
},
|
|
755
|
-
onEvent: (event) => {
|
|
756
|
-
assessmentState.lastActivityAt = Date.now();
|
|
757
|
-
assessmentState.eventCount += 1;
|
|
758
|
-
const message = assessmentProgressMessage(event);
|
|
759
|
-
if (message) reportProgress(message);
|
|
760
|
-
},
|
|
761
|
-
},
|
|
762
|
-
{
|
|
763
|
-
draftPath: path
|
|
764
|
-
.join(
|
|
765
|
-
".engineeros",
|
|
766
|
-
"assessments",
|
|
767
|
-
String(assessmentId),
|
|
768
|
-
result.report_file,
|
|
769
|
-
)
|
|
770
|
-
.split(path.sep)
|
|
771
|
-
.join("/"),
|
|
772
|
-
},
|
|
786
|
+
assessmentAgentCallbacks,
|
|
773
787
|
);
|
|
774
|
-
reportProgress("Recovered completed stage report from connector storage");
|
|
775
|
-
} else {
|
|
776
|
-
result = await executeWorkspaceAssessment(preparedAssignment, config, {
|
|
777
|
-
onController: (controller) => {
|
|
778
|
-
if (isActiveAssessment()) assessmentState.controller = controller;
|
|
779
|
-
},
|
|
780
|
-
onProcess: (child) => {
|
|
781
|
-
if (isActiveAssessment()) {
|
|
782
|
-
assessmentState.child = child;
|
|
783
|
-
agentProcessStarted = true;
|
|
784
|
-
assessmentState.phase = "running";
|
|
785
|
-
assessmentState.lastActivityAt = Date.now();
|
|
786
|
-
reportProgress("Connected agent process started");
|
|
787
|
-
}
|
|
788
|
-
},
|
|
789
|
-
onEvent: (event) => {
|
|
790
|
-
assessmentState.lastActivityAt = Date.now();
|
|
791
|
-
assessmentState.eventCount += 1;
|
|
792
|
-
const message = assessmentProgressMessage(event);
|
|
793
|
-
if (message) reportProgress(message);
|
|
794
|
-
},
|
|
795
|
-
});
|
|
796
788
|
await persistAssessmentStageResult(
|
|
797
789
|
config.workspace,
|
|
798
790
|
assessmentId,
|
|
@@ -837,44 +829,53 @@ async function executeAssessment(assignment, assessmentState) {
|
|
|
837
829
|
);
|
|
838
830
|
},
|
|
839
831
|
});
|
|
840
|
-
const
|
|
841
|
-
|
|
842
|
-
|
|
832
|
+
const repairedDelivery = await submitAssessmentWithValidationRepair(
|
|
833
|
+
result,
|
|
834
|
+
{
|
|
835
|
+
submit: deliverResult,
|
|
836
|
+
payloadFor: (candidate) =>
|
|
837
|
+
stage === "synthesis"
|
|
838
|
+
? assessmentCompletionBundle(
|
|
839
|
+
config.workspace,
|
|
840
|
+
assessmentId,
|
|
841
|
+
candidate,
|
|
842
|
+
)
|
|
843
|
+
: assessmentCheckpointPayload(candidate),
|
|
844
|
+
rejectionFor: (response) =>
|
|
845
|
+
describeRejectedResponse(response, "the assessment"),
|
|
846
|
+
correct: async (candidate, rejection) => {
|
|
847
|
+
agentCompleted = false;
|
|
848
|
+
assessmentState.phase = "correcting";
|
|
849
|
+
assessmentState.lastMessage = "Correcting a rejected stage result";
|
|
850
|
+
if (!candidate.correctAfterRejection) {
|
|
851
|
+
throw new Error(
|
|
852
|
+
`${rejection} The connector could not start the required agent correction.`,
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
return candidate.correctAfterRejection(rejection);
|
|
856
|
+
},
|
|
857
|
+
onCorrected: async (candidate) => {
|
|
858
|
+
await removeAssessmentStageResult(
|
|
843
859
|
config.workspace,
|
|
844
860
|
assessmentId,
|
|
845
|
-
|
|
846
|
-
)
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
result,
|
|
864
|
-
);
|
|
865
|
-
agentCompleted = true;
|
|
866
|
-
assessmentState.phase = "delivering";
|
|
867
|
-
assessmentState.lastMessage = "Delivering corrected stage result";
|
|
868
|
-
response = await deliverResult(
|
|
869
|
-
stage === "synthesis"
|
|
870
|
-
? await assessmentCompletionBundle(
|
|
871
|
-
config.workspace,
|
|
872
|
-
assessmentId,
|
|
873
|
-
result,
|
|
874
|
-
)
|
|
875
|
-
: assessmentCheckpointPayload(result),
|
|
876
|
-
);
|
|
877
|
-
}
|
|
861
|
+
stage,
|
|
862
|
+
);
|
|
863
|
+
const persisted = await persistAssessmentStageResult(
|
|
864
|
+
config.workspace,
|
|
865
|
+
assessmentId,
|
|
866
|
+
preparedAssignment,
|
|
867
|
+
candidate,
|
|
868
|
+
);
|
|
869
|
+
attachStoredCorrection(persisted, preparedAssignment);
|
|
870
|
+
agentCompleted = true;
|
|
871
|
+
assessmentState.phase = "delivering";
|
|
872
|
+
assessmentState.lastMessage = "Delivering corrected stage result";
|
|
873
|
+
return persisted;
|
|
874
|
+
},
|
|
875
|
+
},
|
|
876
|
+
);
|
|
877
|
+
result = repairedDelivery.result;
|
|
878
|
+
const response = repairedDelivery.response;
|
|
878
879
|
if (!response.ok) {
|
|
879
880
|
throw new Error(
|
|
880
881
|
await describeRejectedResponse(response, "the assessment"),
|
package/package.json
CHANGED
package/src/runner.mjs
CHANGED
|
@@ -33,6 +33,7 @@ const MAX_COMPRESSED_INVENTORY_BYTES = 10_000_000;
|
|
|
33
33
|
const EVIDENCE_POLICY_VERSION = "workspace-evidence-v1";
|
|
34
34
|
export const ASSESSMENT_INACTIVITY_TIMEOUT_MS = 10 * 60 * 1_000;
|
|
35
35
|
export const ASSESSMENT_RESULT_TIMEOUT_MS = 120_000;
|
|
36
|
+
export const MAX_REPEATED_ASSESSMENT_VALIDATION_FAILURES = 3;
|
|
36
37
|
export const MAX_ASSESSMENT_WORKERS = 32;
|
|
37
38
|
export const MAX_AUTOMATIC_ASSESSMENT_WORKERS = 8;
|
|
38
39
|
const EXCLUDED_DIRECTORIES = new Set([
|
|
@@ -466,12 +467,10 @@ export async function executeWorkspaceAssessment(
|
|
|
466
467
|
};
|
|
467
468
|
};
|
|
468
469
|
const result = await buildResult(completed, recovered.report);
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
});
|
|
474
|
-
}
|
|
470
|
+
attachAssessmentRejectionCorrection(result, assignment, config, callbacks, {
|
|
471
|
+
previousSessionId: completed.sessionId,
|
|
472
|
+
buildResult,
|
|
473
|
+
});
|
|
475
474
|
return result;
|
|
476
475
|
}
|
|
477
476
|
|
|
@@ -503,23 +502,40 @@ export function attachAssessmentRejectionCorrection(
|
|
|
503
502
|
`The authoritative draft is stored at \`${draftPath}\`. Read that file before making the requested correction.`,
|
|
504
503
|
].join("\n")
|
|
505
504
|
: "";
|
|
506
|
-
const
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
505
|
+
const correctionPrompt = `${assessmentRejectionCorrectionPrompt(validationMessage, execution.requiredOutputHeading)}${draftInstruction}`;
|
|
506
|
+
const launchCorrection = (prompt, sessionId) => {
|
|
507
|
+
const controller = launchAgentProcess(
|
|
508
|
+
config.workspace,
|
|
509
|
+
prompt,
|
|
510
|
+
execution.sandboxMode,
|
|
511
|
+
config,
|
|
512
|
+
callbacks,
|
|
513
|
+
execution.profile,
|
|
514
|
+
sessionId,
|
|
515
|
+
assessmentAcpContext,
|
|
516
|
+
);
|
|
517
|
+
callbacks.onController?.(controller);
|
|
518
|
+
callbacks.onProcess?.(controller.child);
|
|
519
|
+
return controller;
|
|
520
|
+
};
|
|
521
|
+
let corrected = await launchCorrection(
|
|
522
|
+
correctionPrompt,
|
|
513
523
|
previousSessionId,
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
524
|
+
).completed;
|
|
525
|
+
const recovered = await recoverAssessmentStageOutput({
|
|
526
|
+
completed: corrected,
|
|
527
|
+
prompt: correctionPrompt,
|
|
528
|
+
requiredOutputHeading: execution.requiredOutputHeading,
|
|
529
|
+
retry: async (formatPrompt, sessionId) => {
|
|
530
|
+
callbacks.onEvent?.({
|
|
531
|
+
type: "assessment.output_correction",
|
|
532
|
+
message: "The Agent is formatting the corrected stage report",
|
|
533
|
+
});
|
|
534
|
+
return launchCorrection(formatPrompt, sessionId).completed;
|
|
535
|
+
},
|
|
536
|
+
});
|
|
537
|
+
corrected = recovered.completed;
|
|
538
|
+
const correctedReport = recovered.report;
|
|
523
539
|
if (buildResult) {
|
|
524
540
|
return buildResult(
|
|
525
541
|
{
|
|
@@ -1174,6 +1190,36 @@ export async function submitAssessmentResultWithRetry(
|
|
|
1174
1190
|
);
|
|
1175
1191
|
}
|
|
1176
1192
|
|
|
1193
|
+
export async function submitAssessmentWithValidationRepair(
|
|
1194
|
+
initialResult,
|
|
1195
|
+
{
|
|
1196
|
+
submit,
|
|
1197
|
+
payloadFor,
|
|
1198
|
+
rejectionFor,
|
|
1199
|
+
correct,
|
|
1200
|
+
onCorrected = async () => {},
|
|
1201
|
+
maxRepeatedFailures = MAX_REPEATED_ASSESSMENT_VALIDATION_FAILURES,
|
|
1202
|
+
},
|
|
1203
|
+
) {
|
|
1204
|
+
let result = initialResult;
|
|
1205
|
+
let response = await submit(await payloadFor(result));
|
|
1206
|
+
const rejectionCounts = new Map();
|
|
1207
|
+
while (response.status === 422) {
|
|
1208
|
+
const rejection = await rejectionFor(response);
|
|
1209
|
+
const rejectionCount = (rejectionCounts.get(rejection) || 0) + 1;
|
|
1210
|
+
rejectionCounts.set(rejection, rejectionCount);
|
|
1211
|
+
if (rejectionCount > maxRepeatedFailures) {
|
|
1212
|
+
throw new Error(
|
|
1213
|
+
`${rejection} The Agent repeated this unresolved validation failure ${maxRepeatedFailures} times; the connector retained the latest local report for retry.`,
|
|
1214
|
+
);
|
|
1215
|
+
}
|
|
1216
|
+
result = await correct(result, rejection);
|
|
1217
|
+
result = (await onCorrected(result, rejection)) || result;
|
|
1218
|
+
response = await submit(await payloadFor(result));
|
|
1219
|
+
}
|
|
1220
|
+
return { result, response };
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1177
1223
|
function isTransientAssessmentResultResponse(response) {
|
|
1178
1224
|
return (
|
|
1179
1225
|
response.status === 408 ||
|