@engineeros/connector 0.9.0 → 0.9.1
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 +10 -0
- package/bin/engineeros-connector.mjs +3 -7
- package/package.json +4 -2
- package/src/agent-harness.mjs +85 -10
- package/src/runner.mjs +33 -10
package/README.md
CHANGED
|
@@ -92,3 +92,13 @@ npm install -g @openai/codex@latest
|
|
|
92
92
|
codex --version
|
|
93
93
|
npx @engineeros/connector start --workspace .
|
|
94
94
|
```
|
|
95
|
+
|
|
96
|
+
## Publish the connector
|
|
97
|
+
|
|
98
|
+
Run the release workflow from this package instead of calling `npm publish` directly:
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
npm run release:patch
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The workflow checks the versions already present on npm, keeps the current version when it is unpublished, or advances to the next unused patch version when necessary. It then runs the connector tests, syntax checks, and package dry run before publishing. A direct `npm publish` now stops early with the corrective command when its version already exists.
|
|
@@ -396,7 +396,7 @@ async function submitWorkspaceSnapshot() {
|
|
|
396
396
|
`Sent ${snapshot.file_count} safe file(s); ${snapshot.excluded_file_count} sensitive or generated path(s) excluded.`,
|
|
397
397
|
);
|
|
398
398
|
console.log(
|
|
399
|
-
`Workspace
|
|
399
|
+
`Workspace inventory registered as ${result.workspace_kind}. Assessment queued.`,
|
|
400
400
|
);
|
|
401
401
|
snapshotInFlight = false;
|
|
402
402
|
} catch (error) {
|
|
@@ -553,9 +553,7 @@ async function cancelPrompt(promptState) {
|
|
|
553
553
|
|
|
554
554
|
async function executeAssessment(assignment) {
|
|
555
555
|
const assessmentId = assignment.assessment_id;
|
|
556
|
-
console.log(
|
|
557
|
-
`Assessing workspace with ${codingAgent.name} (${assessmentId}).`,
|
|
558
|
-
);
|
|
556
|
+
console.log(`Agent is assessing the workspace (${assessmentId}).`);
|
|
559
557
|
let progress = 10;
|
|
560
558
|
const reportedMilestones = new Set();
|
|
561
559
|
const reportProgress = (message, { milestone = true } = {}) => {
|
|
@@ -607,9 +605,7 @@ async function executeAssessment(assignment) {
|
|
|
607
605
|
`EngineerOS rejected the assessment (${response.status}): ${await response.text()}`,
|
|
608
606
|
);
|
|
609
607
|
}
|
|
610
|
-
console.log(
|
|
611
|
-
`${codingAgent.name} workspace assessment is current in EngineerOS.`,
|
|
612
|
-
);
|
|
608
|
+
console.log("Workspace assessment is current in EngineerOS.");
|
|
613
609
|
} catch (error) {
|
|
614
610
|
if (socket.readyState === WebSocket.OPEN) {
|
|
615
611
|
socket.send(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@engineeros/connector",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Connect a local coding agent to EngineerOS, using ACP when supported.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -15,8 +15,10 @@
|
|
|
15
15
|
},
|
|
16
16
|
"scripts": {
|
|
17
17
|
"start": "node ./bin/engineeros-connector.mjs",
|
|
18
|
+
"prepublishOnly": "node ./scripts/release.mjs check",
|
|
19
|
+
"release:patch": "node ./scripts/release.mjs patch",
|
|
18
20
|
"test": "node --test --test-concurrency=1",
|
|
19
|
-
"type-check": "node --check ./bin/engineeros-connector.mjs && node --check ./src/acp-client.mjs && node --check ./src/agent-harness.mjs && node --check ./src/agent-registry.mjs && node --check ./src/capabilities.mjs && node --check ./src/cli-args.mjs && node --check ./src/config.mjs && node --check ./src/connection.mjs && node --check ./src/mcp-server.mjs && node --check ./src/runner.mjs"
|
|
21
|
+
"type-check": "node --check ./bin/engineeros-connector.mjs && node --check ./scripts/release.mjs && node --check ./src/acp-client.mjs && node --check ./src/agent-harness.mjs && node --check ./src/agent-registry.mjs && node --check ./src/capabilities.mjs && node --check ./src/cli-args.mjs && node --check ./src/config.mjs && node --check ./src/connection.mjs && node --check ./src/mcp-server.mjs && node --check ./src/runner.mjs"
|
|
20
22
|
},
|
|
21
23
|
"engines": {
|
|
22
24
|
"node": ">=22"
|
package/src/agent-harness.mjs
CHANGED
|
@@ -45,7 +45,12 @@ export function agentHarnessCapabilities() {
|
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
export function buildAgentHarnessPrompt({
|
|
48
|
+
export function buildAgentHarnessPrompt({
|
|
49
|
+
agentRole,
|
|
50
|
+
prompt,
|
|
51
|
+
sandboxMode,
|
|
52
|
+
requiredOutputHeading,
|
|
53
|
+
}) {
|
|
49
54
|
const role = ROLE_DEFINITIONS[agentRole];
|
|
50
55
|
if (!role) {
|
|
51
56
|
throw new Error(
|
|
@@ -61,8 +66,9 @@ export function buildAgentHarnessPrompt({ agentRole, prompt, sandboxMode }) {
|
|
|
61
66
|
if (!assignment) {
|
|
62
67
|
throw new Error("EngineerOS assignment is missing prompt_markdown.");
|
|
63
68
|
}
|
|
69
|
+
const outputHeading = normalizedOutputHeading(requiredOutputHeading);
|
|
64
70
|
|
|
65
|
-
|
|
71
|
+
const sections = [
|
|
66
72
|
"# EngineerOS Agent Assignment",
|
|
67
73
|
"",
|
|
68
74
|
"## Active Role",
|
|
@@ -71,13 +77,20 @@ export function buildAgentHarnessPrompt({ agentRole, prompt, sandboxMode }) {
|
|
|
71
77
|
`- Access: ${role.sandboxMode}`,
|
|
72
78
|
`- Responsibility: ${role.instruction}`,
|
|
73
79
|
"- User-facing language: refer to yourself neutrally as the Agent. Do not expose internal role, harness, provider, or coding-agent terminology unless the user asks.",
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
];
|
|
81
|
+
if (!outputHeading) {
|
|
82
|
+
sections.push(
|
|
83
|
+
"",
|
|
84
|
+
"## Interaction Contract",
|
|
85
|
+
"",
|
|
86
|
+
"- Infer the current project situation from the assignment and workspace evidence before responding.",
|
|
87
|
+
"- Lead with the useful answer or outcome. Do not narrate routine searches, tool calls, or internal work.",
|
|
88
|
+
"- When one next activity clearly follows and would help, offer exactly one short, concrete suggestion. Do not force a next step when none is useful.",
|
|
89
|
+
"- Ask a question only when a consequential choice cannot be resolved from available evidence.",
|
|
90
|
+
"- If the Assignment defines a response format, follow it exactly instead of adding conversational guidance.",
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
sections.push(
|
|
81
94
|
"",
|
|
82
95
|
"## Active Skill",
|
|
83
96
|
"",
|
|
@@ -86,7 +99,58 @@ export function buildAgentHarnessPrompt({ agentRole, prompt, sandboxMode }) {
|
|
|
86
99
|
"## Assignment",
|
|
87
100
|
"",
|
|
88
101
|
assignment,
|
|
89
|
-
|
|
102
|
+
);
|
|
103
|
+
if (outputHeading) {
|
|
104
|
+
sections.push(
|
|
105
|
+
"",
|
|
106
|
+
"## Final Response Contract",
|
|
107
|
+
"",
|
|
108
|
+
"- Return only the structured Markdown required by the Assignment.",
|
|
109
|
+
`- The first non-whitespace line must be exactly: ${outputHeading}`,
|
|
110
|
+
"- Do not add a preamble, status message, commentary, or code fence.",
|
|
111
|
+
"- Do not append a conversational summary or next activity.",
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
return sections.join("\n");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function normalizeAgentStructuredOutput(
|
|
118
|
+
response,
|
|
119
|
+
requiredOutputHeading,
|
|
120
|
+
) {
|
|
121
|
+
const outputHeading = normalizedOutputHeading(requiredOutputHeading);
|
|
122
|
+
if (!outputHeading) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
"EngineerOS structured output requires a heading contract.",
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
const content = String(response || "").trim();
|
|
128
|
+
if (!content) {
|
|
129
|
+
throw new Error("Agent completed without returning a response.");
|
|
130
|
+
}
|
|
131
|
+
const lines = content.split(/\r?\n/);
|
|
132
|
+
const headingIndex = lines.findIndex(
|
|
133
|
+
(line, index) => index < 20 && line.trim() === outputHeading,
|
|
134
|
+
);
|
|
135
|
+
const preamble =
|
|
136
|
+
headingIndex >= 0 ? lines.slice(0, headingIndex).join("\n") : content;
|
|
137
|
+
if (headingIndex < 0) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
`Agent response is missing the required heading '${outputHeading}'.`,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
if (preamble.length > 2_000) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
`Agent response contains too much text before the required heading '${outputHeading}'.`,
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
const normalized = lines.slice(headingIndex).join("\n").trim();
|
|
148
|
+
if (/```/.test(preamble) || /(?:^|\n)```\s*$/.test(normalized)) {
|
|
149
|
+
throw new Error(
|
|
150
|
+
`Agent response must return '${outputHeading}' as plain Markdown, without a code fence.`,
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
return normalized;
|
|
90
154
|
}
|
|
91
155
|
|
|
92
156
|
function bundledSkill(skillName) {
|
|
@@ -106,3 +170,14 @@ function bundledSkill(skillName) {
|
|
|
106
170
|
);
|
|
107
171
|
}
|
|
108
172
|
}
|
|
173
|
+
|
|
174
|
+
function normalizedOutputHeading(value) {
|
|
175
|
+
if (value === undefined || value === null) return null;
|
|
176
|
+
const heading = String(value).trim();
|
|
177
|
+
if (!/^# [^\r\n]+$/.test(heading)) {
|
|
178
|
+
throw new Error(
|
|
179
|
+
"EngineerOS requiredOutputHeading must be one level-one Markdown heading.",
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
return heading;
|
|
183
|
+
}
|
package/src/runner.mjs
CHANGED
|
@@ -12,7 +12,10 @@ import path from "node:path";
|
|
|
12
12
|
import { promisify } from "node:util";
|
|
13
13
|
import { deflateRaw } from "node:zlib";
|
|
14
14
|
import { launchAcpAgent } from "./acp-client.mjs";
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
buildAgentHarnessPrompt,
|
|
17
|
+
normalizeAgentStructuredOutput,
|
|
18
|
+
} from "./agent-harness.mjs";
|
|
16
19
|
|
|
17
20
|
const deflate = promisify(deflateRaw);
|
|
18
21
|
const MAX_ARCHIVE_BYTES = 25 * 1024 * 1024;
|
|
@@ -110,12 +113,13 @@ export async function executeAssignment(assignment, config, callbacks) {
|
|
|
110
113
|
await controller.completed;
|
|
111
114
|
const changedFiles = await changedFilePaths(runWorkspace);
|
|
112
115
|
if (!changedFiles.length)
|
|
113
|
-
throw new Error("
|
|
116
|
+
throw new Error("Agent completed without changing any files.");
|
|
114
117
|
const committed = await commitRunChange(
|
|
115
118
|
runWorkspace,
|
|
116
119
|
assignment.base_revision,
|
|
117
120
|
assignment.run_id,
|
|
118
121
|
);
|
|
122
|
+
const verificationHeading = "# Verification Report";
|
|
119
123
|
const verifier = launchAgentProcess(
|
|
120
124
|
runWorkspace,
|
|
121
125
|
buildAgentHarnessPrompt({
|
|
@@ -126,6 +130,7 @@ export async function executeAssignment(assignment, config, callbacks) {
|
|
|
126
130
|
committed.changedFiles,
|
|
127
131
|
),
|
|
128
132
|
sandboxMode: "read-only",
|
|
133
|
+
requiredOutputHeading: verificationHeading,
|
|
129
134
|
}),
|
|
130
135
|
"read-only",
|
|
131
136
|
config,
|
|
@@ -134,8 +139,12 @@ export async function executeAssignment(assignment, config, callbacks) {
|
|
|
134
139
|
);
|
|
135
140
|
callbacks.onProcess?.(verifier.child);
|
|
136
141
|
const verification = await verifier.completed;
|
|
137
|
-
const
|
|
142
|
+
const verificationReport = normalizeAgentStructuredOutput(
|
|
138
143
|
verification.finalMessage,
|
|
144
|
+
verificationHeading,
|
|
145
|
+
);
|
|
146
|
+
const proofEvidence = parseVerificationReport(
|
|
147
|
+
verificationReport,
|
|
139
148
|
assignment.proof_checks,
|
|
140
149
|
config.connector_id,
|
|
141
150
|
assignment.run_id,
|
|
@@ -147,7 +156,7 @@ export async function executeAssignment(assignment, config, callbacks) {
|
|
|
147
156
|
diff_patch: committed.diffPatch,
|
|
148
157
|
changed_files: committed.changedFiles,
|
|
149
158
|
proof_evidence: proofEvidence,
|
|
150
|
-
verification_report:
|
|
159
|
+
verification_report: verificationReport,
|
|
151
160
|
};
|
|
152
161
|
}
|
|
153
162
|
|
|
@@ -299,7 +308,7 @@ export async function executeWorkspaceAssessment(
|
|
|
299
308
|
config,
|
|
300
309
|
callbacks,
|
|
301
310
|
) {
|
|
302
|
-
const execution =
|
|
311
|
+
const execution = workspaceAssessmentExecution(assignment);
|
|
303
312
|
const startingRevision = await run(
|
|
304
313
|
"git",
|
|
305
314
|
["rev-parse", "HEAD"],
|
|
@@ -342,8 +351,10 @@ export async function executeWorkspaceAssessment(
|
|
|
342
351
|
);
|
|
343
352
|
callbacks.onProcess?.(controller.child);
|
|
344
353
|
const completed = await controller.completed;
|
|
345
|
-
const report =
|
|
346
|
-
|
|
354
|
+
const report = normalizeAgentStructuredOutput(
|
|
355
|
+
completed.finalMessage,
|
|
356
|
+
execution.requiredOutputHeading,
|
|
357
|
+
);
|
|
347
358
|
const endingRevision = await run(
|
|
348
359
|
"git",
|
|
349
360
|
["rev-parse", "HEAD"],
|
|
@@ -504,7 +515,7 @@ export async function executeConnectedPrompt(assignment, config, callbacks) {
|
|
|
504
515
|
const completed = await controller.completed;
|
|
505
516
|
const content = completed.finalMessage.trim();
|
|
506
517
|
if (!content) {
|
|
507
|
-
throw new Error("
|
|
518
|
+
throw new Error("Agent completed without returning a response.");
|
|
508
519
|
}
|
|
509
520
|
return {
|
|
510
521
|
content,
|
|
@@ -664,7 +675,18 @@ function assessmentCommandMilestone(command) {
|
|
|
664
675
|
return "Tracing architecture and code relationships";
|
|
665
676
|
}
|
|
666
677
|
|
|
667
|
-
export function
|
|
678
|
+
export function workspaceAssessmentExecution(assignment) {
|
|
679
|
+
const requiredOutputHeading =
|
|
680
|
+
assignment?.assessment_mode === "incremental"
|
|
681
|
+
? "# Workspace Assessment Delta"
|
|
682
|
+
: "# Workspace Assessment";
|
|
683
|
+
return {
|
|
684
|
+
...connectorExecution(assignment, { requiredOutputHeading }),
|
|
685
|
+
requiredOutputHeading,
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
export function connectorExecution(assignment, options = {}) {
|
|
668
690
|
const rawPrompt = assignment?.prompt_markdown;
|
|
669
691
|
if (typeof rawPrompt !== "string" || !rawPrompt.trim()) {
|
|
670
692
|
throw new Error("EngineerOS assignment is missing prompt_markdown.");
|
|
@@ -690,6 +712,7 @@ export function connectorExecution(assignment) {
|
|
|
690
712
|
agentRole,
|
|
691
713
|
prompt: rawPrompt,
|
|
692
714
|
sandboxMode,
|
|
715
|
+
requiredOutputHeading: options.requiredOutputHeading,
|
|
693
716
|
});
|
|
694
717
|
return {
|
|
695
718
|
prompt,
|
|
@@ -913,7 +936,7 @@ function launchCodexProcess(
|
|
|
913
936
|
else if (code === 0)
|
|
914
937
|
reject(
|
|
915
938
|
new Error(
|
|
916
|
-
"
|
|
939
|
+
"Agent completed without announcing a resumable session id.",
|
|
917
940
|
),
|
|
918
941
|
);
|
|
919
942
|
else reject(new Error(codexFailureMessage(output, code)));
|