@engineeros/connector 0.13.3 → 0.13.4
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 +1 -1
- package/bin/engineeros-connector.mjs +31 -16
- package/package.json +1 -1
- package/src/runner.mjs +16 -0
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ 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 Architecture, Capabilities, Quality, or any combination when only part of the workspace needs reassessment. EngineerOS stores every completed assessment stage
|
|
50
|
+
From **Project steering -> Workspace**, run the workspace assessment to use the connected agent subscription already authenticated on that computer. Select Architecture, Capabilities, Quality, or any combination when only part of the workspace needs reassessment. EngineerOS stores every completed assessment stage, resumes unfinished work after a connector restart, retry, or temporary connection loss, and then synthesizes the selected results into System State. After the capability catalog is accepted, the connector runs up to three isolated read-only capability workers concurrently; each result, live output, validation correction, and retry remains independently durable. 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 gives an incomplete report one bounded correction turn instead of repeatedly restarting it. Assessment cannot modify the workspace.
|
|
51
51
|
|
|
52
52
|
After onboarding, every project prompt is routed to this connection. Copilot, shaping, planning, architecture, and experience generation use the connected agent subscription and workspace context. Interactive prompts run independently from assessments and Goal scheduling. Prompt runs are read-only; only an explicitly registered Goal Run receives workspace-write access. If the connector is offline, EngineerOS asks the user to reconnect instead of silently switching models.
|
|
53
53
|
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
promptStreamEvent,
|
|
25
25
|
requeueInterruptedAssessment,
|
|
26
26
|
stopProcess,
|
|
27
|
+
takeWorkspaceAssessmentWave,
|
|
27
28
|
workspaceSnapshot,
|
|
28
29
|
} from "../src/runner.mjs";
|
|
29
30
|
import { disposeAcpRuntimes } from "../src/acp-client.mjs";
|
|
@@ -195,6 +196,8 @@ firstMessage.capabilities = capabilities;
|
|
|
195
196
|
|
|
196
197
|
let stopped = false;
|
|
197
198
|
let active = null;
|
|
199
|
+
const activeAssessments = new Map();
|
|
200
|
+
const ASSESSMENT_WORKER_LIMIT = 3;
|
|
198
201
|
const available = [];
|
|
199
202
|
const assessments = [];
|
|
200
203
|
const activePrompts = new Map();
|
|
@@ -212,7 +215,10 @@ process.on("SIGINT", async () => {
|
|
|
212
215
|
clearConnectionWatchdog?.();
|
|
213
216
|
clearTimeout(reconnectTimer);
|
|
214
217
|
clearInterval(pingTimer);
|
|
215
|
-
await
|
|
218
|
+
await Promise.all([
|
|
219
|
+
stopProcess(active?.child),
|
|
220
|
+
...[...activeAssessments.values()].map((state) => stopProcess(state.child)),
|
|
221
|
+
]);
|
|
216
222
|
await Promise.all([...activePrompts.values()].map(cancelPrompt));
|
|
217
223
|
await disposeAcpRuntimes();
|
|
218
224
|
socket?.close();
|
|
@@ -278,9 +284,10 @@ async function connect() {
|
|
|
278
284
|
return;
|
|
279
285
|
}
|
|
280
286
|
if (message.type === "workspace.assessment") {
|
|
287
|
+
const assessmentKey = `${message.assessment_id}:${message.stage}`;
|
|
281
288
|
const disposition = enqueueWorkspaceAssessment(
|
|
282
289
|
assessments,
|
|
283
|
-
|
|
290
|
+
activeAssessments.get(assessmentKey),
|
|
284
291
|
message,
|
|
285
292
|
);
|
|
286
293
|
if (disposition === "deferred") {
|
|
@@ -339,9 +346,9 @@ async function connect() {
|
|
|
339
346
|
clearInterval(pingTimer);
|
|
340
347
|
for (const promptState of activePrompts.values())
|
|
341
348
|
void cancelPrompt(promptState);
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
if (event.code === 4001) void stopProcess(
|
|
349
|
+
for (const assessmentState of activeAssessments.values()) {
|
|
350
|
+
assessmentState.transportInterrupted = true;
|
|
351
|
+
if (event.code === 4001) void stopProcess(assessmentState.child);
|
|
345
352
|
}
|
|
346
353
|
if (connectionRejected) {
|
|
347
354
|
stopped = true;
|
|
@@ -463,21 +470,27 @@ function sendPromptEvent(promptId, event) {
|
|
|
463
470
|
}
|
|
464
471
|
|
|
465
472
|
function pump() {
|
|
466
|
-
if (
|
|
467
|
-
const
|
|
468
|
-
|
|
473
|
+
if (socket.readyState !== WebSocket.OPEN || active) return;
|
|
474
|
+
const wave = takeWorkspaceAssessmentWave(
|
|
475
|
+
assessments,
|
|
476
|
+
activeAssessments.size,
|
|
477
|
+
ASSESSMENT_WORKER_LIMIT,
|
|
478
|
+
);
|
|
479
|
+
for (const assessment of wave) {
|
|
480
|
+
const assessmentKey = `${assessment.assessment_id}:${assessment.stage}`;
|
|
469
481
|
const assessmentState = {
|
|
470
482
|
kind: "assessment",
|
|
483
|
+
key: assessmentKey,
|
|
471
484
|
runId: assessment.assessment_id,
|
|
472
485
|
stage: assessment.stage,
|
|
473
486
|
child: null,
|
|
474
487
|
resumeAssignment: null,
|
|
475
488
|
transportInterrupted: false,
|
|
476
489
|
};
|
|
477
|
-
|
|
490
|
+
activeAssessments.set(assessmentKey, assessmentState);
|
|
478
491
|
void executeAssessment(assessment, assessmentState);
|
|
479
|
-
return;
|
|
480
492
|
}
|
|
493
|
+
if (activeAssessments.size > 0 || assessments.length > 0) return;
|
|
481
494
|
const runId = available.shift();
|
|
482
495
|
if (!runId) return;
|
|
483
496
|
active = { kind: "goal", runId, child: null, cancelled: false };
|
|
@@ -604,8 +617,10 @@ async function executeAssessment(assignment, assessmentState) {
|
|
|
604
617
|
let inactivityFailure = null;
|
|
605
618
|
let lastReportedMilestone = null;
|
|
606
619
|
const creditedMilestones = new Set();
|
|
620
|
+
const isActiveAssessment = () =>
|
|
621
|
+
activeAssessments.get(assessmentState.key) === assessmentState;
|
|
607
622
|
const reportProgress = (message, { milestone = true } = {}) => {
|
|
608
|
-
if (
|
|
623
|
+
if (!isActiveAssessment()) return;
|
|
609
624
|
if (milestone && lastReportedMilestone === message) return;
|
|
610
625
|
if (milestone) {
|
|
611
626
|
lastReportedMilestone = message;
|
|
@@ -635,7 +650,7 @@ async function executeAssessment(assignment, assessmentState) {
|
|
|
635
650
|
if (
|
|
636
651
|
!output ||
|
|
637
652
|
socket.readyState !== WebSocket.OPEN ||
|
|
638
|
-
|
|
653
|
+
!isActiveAssessment()
|
|
639
654
|
) {
|
|
640
655
|
return;
|
|
641
656
|
}
|
|
@@ -693,7 +708,7 @@ async function executeAssessment(assignment, assessmentState) {
|
|
|
693
708
|
}
|
|
694
709
|
let result = await executeWorkspaceAssessment(assignment, config, {
|
|
695
710
|
onProcess: (child) => {
|
|
696
|
-
if (
|
|
711
|
+
if (isActiveAssessment()) {
|
|
697
712
|
assessmentState.child = child;
|
|
698
713
|
agentProcessStarted = true;
|
|
699
714
|
lastAgentActivityAt = Date.now();
|
|
@@ -747,7 +762,7 @@ async function executeAssessment(assignment, assessmentState) {
|
|
|
747
762
|
const message = inactivityFailure || protocolFailureMessage(error);
|
|
748
763
|
if (
|
|
749
764
|
!assessmentState.transportInterrupted &&
|
|
750
|
-
|
|
765
|
+
isActiveAssessment() &&
|
|
751
766
|
socket.readyState === WebSocket.OPEN
|
|
752
767
|
) {
|
|
753
768
|
socket.send(
|
|
@@ -764,8 +779,8 @@ async function executeAssessment(assignment, assessmentState) {
|
|
|
764
779
|
} finally {
|
|
765
780
|
if (outputTimer) clearTimeout(outputTimer);
|
|
766
781
|
clearInterval(heartbeat);
|
|
767
|
-
if (
|
|
768
|
-
|
|
782
|
+
if (isActiveAssessment()) {
|
|
783
|
+
activeAssessments.delete(assessmentState.key);
|
|
769
784
|
const replayQueued = requeueInterruptedAssessment(
|
|
770
785
|
assessments,
|
|
771
786
|
assessmentState,
|
package/package.json
CHANGED
package/src/runner.mjs
CHANGED
|
@@ -1000,6 +1000,22 @@ export function enqueueWorkspaceAssessment(
|
|
|
1000
1000
|
return "queued";
|
|
1001
1001
|
}
|
|
1002
1002
|
|
|
1003
|
+
export function takeWorkspaceAssessmentWave(queue, activeCount, limit = 3) {
|
|
1004
|
+
const available = Math.max(0, limit - activeCount);
|
|
1005
|
+
if (!available || !queue.length) return [];
|
|
1006
|
+
if (!String(queue[0]?.stage || "").startsWith("capability:")) {
|
|
1007
|
+
return activeCount === 0 ? queue.splice(0, 1) : [];
|
|
1008
|
+
}
|
|
1009
|
+
const wave = [];
|
|
1010
|
+
while (
|
|
1011
|
+
wave.length < available &&
|
|
1012
|
+
String(queue[0]?.stage || "").startsWith("capability:")
|
|
1013
|
+
) {
|
|
1014
|
+
wave.push(queue.shift());
|
|
1015
|
+
}
|
|
1016
|
+
return wave;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1003
1019
|
export function requeueInterruptedAssessment(
|
|
1004
1020
|
queue,
|
|
1005
1021
|
assessmentState,
|