agentic-engineering-harness 0.6.8 → 0.6.10
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/agents/permissions.d.ts +28 -1
- package/dist/agents/permissions.js +188 -29
- package/dist/agents/permissions.js.map +1 -1
- package/dist/operations/completion.d.ts +26 -0
- package/dist/operations/completion.js +152 -0
- package/dist/operations/completion.js.map +1 -0
- package/dist/operations/controller.d.ts +3 -0
- package/dist/operations/controller.js +38 -9
- package/dist/operations/controller.js.map +1 -1
- package/dist/operations/mcp.js +23 -9
- package/dist/operations/mcp.js.map +1 -1
- package/dist/operations/state.d.ts +14 -0
- package/dist/operations/state.js +26 -0
- package/dist/operations/state.js.map +1 -1
- package/dist/paseo/launchSpec.d.ts +6 -0
- package/dist/paseo/launchSpec.js +25 -3
- package/dist/paseo/launchSpec.js.map +1 -1
- package/dist/paseo/modePreflight.d.ts +15 -0
- package/dist/paseo/modePreflight.js +118 -0
- package/dist/paseo/modePreflight.js.map +1 -0
- package/dist/paseo/native.d.ts +6 -2
- package/dist/paseo/native.js +26 -10
- package/dist/paseo/native.js.map +1 -1
- package/dist/paseo/runtime.d.ts +7 -2
- package/dist/paseo/runtime.js +75 -11
- package/dist/paseo/runtime.js.map +1 -1
- package/dist/paseo/sdk.d.ts +3 -0
- package/dist/paseo/sdk.js +122 -43
- package/dist/paseo/sdk.js.map +1 -1
- package/dist/paseo/start.d.ts +1 -1
- package/dist/paseo/start.js +4 -2
- package/dist/paseo/start.js.map +1 -1
- package/dist/workers/agentPrompt.js +232 -70
- package/dist/workers/agentPrompt.js.map +1 -1
- package/dist/workers/direct.js +85 -22
- package/dist/workers/direct.js.map +1 -1
- package/dist/workers/paseo.js +31 -7
- package/dist/workers/paseo.js.map +1 -1
- package/dist/workers/podman.js +97 -19
- package/dist/workers/podman.js.map +1 -1
- package/docs/PASEO_NATIVE.md +93 -4
- package/package.json +1 -1
- package/presets/agents/orchestration.jsonc +5 -0
- package/skills/engineering-workflow/SKILL.md +22 -12
- package/skills/paseo-orchestration/SKILL.md +19 -2
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
import type { HarnessProjectConfig } from "../core/types.js";
|
|
2
2
|
import type { AgentExecutionSelection } from "./types.js";
|
|
3
|
+
export type OpenCodeAgentBindingSource = "aeh-managed" | "explicit";
|
|
4
|
+
export interface OpenCodeAgentBinding {
|
|
5
|
+
agentId: string;
|
|
6
|
+
source: OpenCodeAgentBindingSource;
|
|
7
|
+
managed: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface OpenCodeRuntimeProjection {
|
|
10
|
+
binding: OpenCodeAgentBinding;
|
|
11
|
+
config: Record<string, unknown>;
|
|
12
|
+
env: Record<string, string>;
|
|
13
|
+
}
|
|
3
14
|
export declare function validateExecutionCapabilities(selection: AgentExecutionSelection, transport: string): string[];
|
|
4
15
|
export declare function permissionSummary(selection: AgentExecutionSelection): string;
|
|
5
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the concrete OpenCode execution identity for an AEH logical agent.
|
|
18
|
+
*
|
|
19
|
+
* Explicit nativeAgent is an externally-authored OpenCode primary/all agent and
|
|
20
|
+
* is preserved exactly. Otherwise AEH owns the native identity and injects a
|
|
21
|
+
* primary agent with a deterministic collision-resistant name into the
|
|
22
|
+
* session-local OpenCode config.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveOpenCodeAgentBinding(selection: AgentExecutionSelection): OpenCodeAgentBinding;
|
|
25
|
+
/**
|
|
26
|
+
* Compile the inline OpenCode configuration used by direct, Podman and Paseo
|
|
27
|
+
* transports. OPENCODE_CONFIG_CONTENT is loaded after ordinary user/project
|
|
28
|
+
* config, so the AEH-managed identity and policy are deterministic without
|
|
29
|
+
* mutating the user's global OpenCode configuration.
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildOpenCodeRuntimeConfig(selection: AgentExecutionSelection, config?: HarnessProjectConfig, binding?: OpenCodeAgentBinding): Record<string, unknown>;
|
|
32
|
+
export declare function compileOpenCodeRuntimeProjection(selection: AgentExecutionSelection, config?: HarnessProjectConfig): OpenCodeRuntimeProjection;
|
|
@@ -1,30 +1,189 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
issues
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
function
|
|
28
|
-
return
|
|
29
|
-
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
export function validateExecutionCapabilities(selection, transport) {
|
|
3
|
+
const issues = [];
|
|
4
|
+
if (selection.nativeAgent && selection.runtimeCapabilities.nativeAgent === false) {
|
|
5
|
+
issues.push(`Runtime ${selection.runtimeName} cannot select native agent ${selection.nativeAgent}.`);
|
|
6
|
+
}
|
|
7
|
+
const nativeAgentViaPaseo = selection.runtimeCapabilities.nativeAgentViaPaseo === true ||
|
|
8
|
+
(selection.runtimeAdapter === "opencode" && selection.paseoProvider === "opencode");
|
|
9
|
+
if (selection.nativeAgent && transport === "paseo" && !nativeAgentViaPaseo) {
|
|
10
|
+
issues.push(`Agent ${selection.logicalAgent} requires nativeAgent=${selection.nativeAgent}, but runtime ${selection.runtimeName} does not support that native agent through Paseo.`);
|
|
11
|
+
}
|
|
12
|
+
if (selection.variant && selection.runtimeCapabilities.variantSelection === false) {
|
|
13
|
+
issues.push(`Runtime ${selection.runtimeName} cannot select variant ${selection.variant}.`);
|
|
14
|
+
}
|
|
15
|
+
if (selection.role === "implementer" && selection.permissions.write === "deny") {
|
|
16
|
+
issues.push(`Implementer ${selection.logicalAgent} denies write permission.`);
|
|
17
|
+
}
|
|
18
|
+
if ((selection.role === "reviewer" || selection.role === "validator") &&
|
|
19
|
+
selection.permissions.write === "allow") {
|
|
20
|
+
issues.push(`${selection.role} ${selection.logicalAgent} explicitly allows writes; read-only roles should use write=deny unless intentionally mutating.`);
|
|
21
|
+
}
|
|
22
|
+
if (selection.role === "orchestrator" && selection.permissions.delegate === "deny") {
|
|
23
|
+
issues.push(`Orchestrator ${selection.logicalAgent} denies delegation.`);
|
|
24
|
+
}
|
|
25
|
+
return issues;
|
|
26
|
+
}
|
|
27
|
+
export function permissionSummary(selection) {
|
|
28
|
+
return (Object.entries(selection.permissions)
|
|
29
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
30
|
+
.join(", ") || "unspecified");
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the concrete OpenCode execution identity for an AEH logical agent.
|
|
34
|
+
*
|
|
35
|
+
* Explicit nativeAgent is an externally-authored OpenCode primary/all agent and
|
|
36
|
+
* is preserved exactly. Otherwise AEH owns the native identity and injects a
|
|
37
|
+
* primary agent with a deterministic collision-resistant name into the
|
|
38
|
+
* session-local OpenCode config.
|
|
39
|
+
*/
|
|
40
|
+
export function resolveOpenCodeAgentBinding(selection) {
|
|
41
|
+
const explicit = selection.nativeAgent?.trim();
|
|
42
|
+
if (explicit) {
|
|
43
|
+
return { agentId: explicit, source: "explicit", managed: false };
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
agentId: managedOpenCodeAgentId(selection.logicalAgent),
|
|
47
|
+
source: "aeh-managed",
|
|
48
|
+
managed: true
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Compile the inline OpenCode configuration used by direct, Podman and Paseo
|
|
53
|
+
* transports. OPENCODE_CONFIG_CONTENT is loaded after ordinary user/project
|
|
54
|
+
* config, so the AEH-managed identity and policy are deterministic without
|
|
55
|
+
* mutating the user's global OpenCode configuration.
|
|
56
|
+
*/
|
|
57
|
+
export function buildOpenCodeRuntimeConfig(selection, config, binding = resolveOpenCodeAgentBinding(selection)) {
|
|
58
|
+
const permission = buildOpenCodePermission(selection);
|
|
59
|
+
const mcp = {};
|
|
60
|
+
const tools = {};
|
|
61
|
+
const configured = config?.mcp?.servers ?? {};
|
|
62
|
+
for (const [name, server] of Object.entries(configured)) {
|
|
63
|
+
const selected = selection.mcps.includes(name) && server.enabled !== false;
|
|
64
|
+
tools[`${name}_*`] = selected;
|
|
65
|
+
if (selected)
|
|
66
|
+
mcp[name] = toOpenCodeMcp(server);
|
|
67
|
+
}
|
|
68
|
+
for (const name of selection.mcps) {
|
|
69
|
+
if (!configured[name])
|
|
70
|
+
tools[`${name}_*`] = true;
|
|
71
|
+
}
|
|
72
|
+
const managedAgent = binding.managed
|
|
73
|
+
? {
|
|
74
|
+
agent: {
|
|
75
|
+
[binding.agentId]: {
|
|
76
|
+
mode: "primary",
|
|
77
|
+
description: selection.description ??
|
|
78
|
+
`AEH-managed OpenCode execution identity for ${selection.logicalAgent}.`,
|
|
79
|
+
model: selection.modelId,
|
|
80
|
+
prompt: `AEH execution identity: ${selection.logicalAgent}. Follow the task-scoped AEH charter, frozen control-plane context, permissions and acceptance criteria supplied with each turn.`,
|
|
81
|
+
...(selection.variant ? { variant: selection.variant } : {}),
|
|
82
|
+
...(selection.temperature !== undefined
|
|
83
|
+
? { temperature: selection.temperature }
|
|
84
|
+
: {}),
|
|
85
|
+
...(Object.keys(permission).length ? { permission } : {})
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
default_agent: binding.agentId
|
|
89
|
+
}
|
|
90
|
+
: {};
|
|
91
|
+
return {
|
|
92
|
+
$schema: "https://opencode.ai/config.json",
|
|
93
|
+
permission,
|
|
94
|
+
...managedAgent,
|
|
95
|
+
...(Object.keys(mcp).length ? { mcp } : {}),
|
|
96
|
+
...(Object.keys(tools).length ? { tools } : {})
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
export function compileOpenCodeRuntimeProjection(selection, config) {
|
|
100
|
+
const binding = resolveOpenCodeAgentBinding(selection);
|
|
101
|
+
const runtimeConfig = buildOpenCodeRuntimeConfig(selection, config, binding);
|
|
102
|
+
return {
|
|
103
|
+
binding,
|
|
104
|
+
config: runtimeConfig,
|
|
105
|
+
env: {
|
|
106
|
+
OPENCODE_CONFIG_CONTENT: JSON.stringify(runtimeConfig)
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function buildOpenCodePermission(selection) {
|
|
111
|
+
const permission = {};
|
|
112
|
+
const p = selection.permissions;
|
|
113
|
+
if (p.read)
|
|
114
|
+
permission.read = p.read;
|
|
115
|
+
if (p.write)
|
|
116
|
+
permission.edit = p.write;
|
|
117
|
+
if (p.network) {
|
|
118
|
+
permission.webfetch = p.network;
|
|
119
|
+
permission.websearch = p.network;
|
|
120
|
+
}
|
|
121
|
+
if (p.delegate)
|
|
122
|
+
permission.task = p.delegate;
|
|
123
|
+
const bash = buildBashPermission(p.shell, p.gitWrite);
|
|
124
|
+
if (bash)
|
|
125
|
+
permission.bash = bash;
|
|
126
|
+
if (selection.skills.length && !selection.skills.includes("*")) {
|
|
127
|
+
permission.skill = Object.fromEntries([
|
|
128
|
+
["*", "deny"],
|
|
129
|
+
...selection.skills.map((skill) => [skill, "allow"])
|
|
130
|
+
]);
|
|
131
|
+
}
|
|
132
|
+
return permission;
|
|
133
|
+
}
|
|
134
|
+
function managedOpenCodeAgentId(logicalAgent) {
|
|
135
|
+
const original = logicalAgent.trim();
|
|
136
|
+
if (!original)
|
|
137
|
+
throw new Error("AEH logical agent name is required for OpenCode identity.");
|
|
138
|
+
const slug = original
|
|
139
|
+
.toLowerCase()
|
|
140
|
+
.replace(/[^a-z0-9._-]+/g, "-")
|
|
141
|
+
.replace(/^-+|-+$/g, "") || "agent";
|
|
142
|
+
const normalizedOriginal = original.toLowerCase();
|
|
143
|
+
if (slug === normalizedOriginal)
|
|
144
|
+
return `aeh-${slug}`;
|
|
145
|
+
const digest = createHash("sha256").update(original).digest("hex").slice(0, 8);
|
|
146
|
+
return `aeh-${slug}-${digest}`;
|
|
147
|
+
}
|
|
148
|
+
function toOpenCodeMcp(server) {
|
|
149
|
+
if (server.type === "local") {
|
|
150
|
+
return {
|
|
151
|
+
type: "local",
|
|
152
|
+
command: server.command,
|
|
153
|
+
enabled: true,
|
|
154
|
+
...(server.environment ? { environment: server.environment } : {}),
|
|
155
|
+
...(server.timeoutMs ? { timeout: server.timeoutMs } : {})
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
type: "remote",
|
|
160
|
+
url: server.url,
|
|
161
|
+
enabled: true,
|
|
162
|
+
...(server.headers ? { headers: server.headers } : {}),
|
|
163
|
+
...(server.oauth !== undefined ? { oauth: server.oauth } : {}),
|
|
164
|
+
...(server.timeoutMs ? { timeout: server.timeoutMs } : {})
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function buildBashPermission(shell, gitWrite) {
|
|
168
|
+
if (!gitWrite)
|
|
169
|
+
return shell;
|
|
170
|
+
const result = { "*": shell ?? "ask" };
|
|
171
|
+
for (const pattern of [
|
|
172
|
+
"git add *",
|
|
173
|
+
"git commit *",
|
|
174
|
+
"git push *",
|
|
175
|
+
"git tag *",
|
|
176
|
+
"git checkout *",
|
|
177
|
+
"git switch *",
|
|
178
|
+
"git reset *",
|
|
179
|
+
"git clean *",
|
|
180
|
+
"git restore *",
|
|
181
|
+
"git merge *",
|
|
182
|
+
"git rebase *",
|
|
183
|
+
"git cherry-pick *"
|
|
184
|
+
]) {
|
|
185
|
+
result[pattern] = gitWrite;
|
|
186
|
+
}
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
30
189
|
//# sourceMappingURL=permissions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../src/agents/permissions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../src/agents/permissions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { dispatchManagedPaseoAgent } from "../paseo/runtime.js";
|
|
2
|
+
import { recordPaseoTrace } from "../paseo/trace.js";
|
|
3
|
+
import type { OperationRecord } from "./state.js";
|
|
4
|
+
export type OperationCompletionStatus = "PENDING" | "SENT" | "FAILED" | "DISABLED";
|
|
5
|
+
export interface OperationCompletionTarget {
|
|
6
|
+
version: 1;
|
|
7
|
+
operationId: string;
|
|
8
|
+
agentId: string;
|
|
9
|
+
source?: string;
|
|
10
|
+
status: OperationCompletionStatus;
|
|
11
|
+
registeredAt: string;
|
|
12
|
+
attemptedAt?: string;
|
|
13
|
+
sentAt?: string;
|
|
14
|
+
failedAt?: string;
|
|
15
|
+
error?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface OperationCompletionDeps {
|
|
18
|
+
dispatch?: typeof dispatchManagedPaseoAgent;
|
|
19
|
+
trace?: typeof recordPaseoTrace;
|
|
20
|
+
}
|
|
21
|
+
export declare function operationCompletionFile(root: string, operationId: string): string;
|
|
22
|
+
export declare function registerOperationCompletionTarget(root: string, operationId: string, agentId: string, source?: string, trace?: typeof recordPaseoTrace): Promise<OperationCompletionTarget>;
|
|
23
|
+
export declare function disableOperationCompletionTarget(root: string, operationId: string, reason: string, trace?: typeof recordPaseoTrace): Promise<void>;
|
|
24
|
+
export declare function loadOperationCompletionTarget(root: string, operationId: string): Promise<OperationCompletionTarget | undefined>;
|
|
25
|
+
export declare function notifyOperationCompletion(root: string, operation: OperationRecord, deps?: OperationCompletionDeps): Promise<OperationCompletionTarget | undefined>;
|
|
26
|
+
export declare function completionPrompt(operation: OperationRecord): string;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { dispatchManagedPaseoAgent } from "../paseo/runtime.js";
|
|
4
|
+
import { recordPaseoTrace } from "../paseo/trace.js";
|
|
5
|
+
export function operationCompletionFile(root, operationId) {
|
|
6
|
+
return path.resolve(root, ".harness/operations", `${safeId(operationId)}.completion.json`);
|
|
7
|
+
}
|
|
8
|
+
export async function registerOperationCompletionTarget(root, operationId, agentId, source, trace = recordPaseoTrace) {
|
|
9
|
+
const target = {
|
|
10
|
+
version: 1,
|
|
11
|
+
operationId,
|
|
12
|
+
agentId: requiredId(agentId, "agent id"),
|
|
13
|
+
source,
|
|
14
|
+
status: "PENDING",
|
|
15
|
+
registeredAt: new Date().toISOString()
|
|
16
|
+
};
|
|
17
|
+
await persist(root, target);
|
|
18
|
+
await trace(root, "operation.callback.registered", {
|
|
19
|
+
operationId,
|
|
20
|
+
agentId: target.agentId,
|
|
21
|
+
source: source ?? "unknown"
|
|
22
|
+
});
|
|
23
|
+
return target;
|
|
24
|
+
}
|
|
25
|
+
export async function disableOperationCompletionTarget(root, operationId, reason, trace = recordPaseoTrace) {
|
|
26
|
+
const target = await loadOperationCompletionTarget(root, operationId);
|
|
27
|
+
if (!target || target.status === "SENT")
|
|
28
|
+
return;
|
|
29
|
+
const disabled = {
|
|
30
|
+
...target,
|
|
31
|
+
status: "DISABLED",
|
|
32
|
+
attemptedAt: new Date().toISOString(),
|
|
33
|
+
error: reason
|
|
34
|
+
};
|
|
35
|
+
await persist(root, disabled);
|
|
36
|
+
await trace(root, "operation.callback.disabled", {
|
|
37
|
+
operationId,
|
|
38
|
+
agentId: target.agentId,
|
|
39
|
+
reason
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
export async function loadOperationCompletionTarget(root, operationId) {
|
|
43
|
+
try {
|
|
44
|
+
const value = JSON.parse(await fs.readFile(operationCompletionFile(root, operationId), "utf8"));
|
|
45
|
+
if (value.version !== 1 ||
|
|
46
|
+
value.operationId !== operationId ||
|
|
47
|
+
!value.agentId ||
|
|
48
|
+
!["PENDING", "SENT", "FAILED", "DISABLED"].includes(value.status)) {
|
|
49
|
+
throw new Error("invalid completion target record");
|
|
50
|
+
}
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (isMissing(error))
|
|
55
|
+
return undefined;
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export async function notifyOperationCompletion(root, operation, deps = {}) {
|
|
60
|
+
const target = await loadOperationCompletionTarget(root, operation.id);
|
|
61
|
+
if (!target || target.status === "SENT" || target.status === "DISABLED")
|
|
62
|
+
return target;
|
|
63
|
+
const dispatch = deps.dispatch ?? dispatchManagedPaseoAgent;
|
|
64
|
+
const trace = deps.trace ?? recordPaseoTrace;
|
|
65
|
+
const attemptedAt = new Date().toISOString();
|
|
66
|
+
try {
|
|
67
|
+
const result = await dispatch(root, target.agentId, completionPrompt(operation), 60);
|
|
68
|
+
if (result.exitCode !== 0) {
|
|
69
|
+
throw new Error(result.stderr || result.stdout || `Paseo dispatch exited with code ${result.exitCode}`);
|
|
70
|
+
}
|
|
71
|
+
const sent = {
|
|
72
|
+
...target,
|
|
73
|
+
status: "SENT",
|
|
74
|
+
attemptedAt,
|
|
75
|
+
sentAt: new Date().toISOString(),
|
|
76
|
+
error: undefined
|
|
77
|
+
};
|
|
78
|
+
await persist(root, sent);
|
|
79
|
+
await trace(root, "operation.callback.sent", {
|
|
80
|
+
operationId: operation.id,
|
|
81
|
+
operationStatus: operation.status,
|
|
82
|
+
agentId: target.agentId,
|
|
83
|
+
transport: result.transport,
|
|
84
|
+
resultPath: resultPath(operation) ?? ""
|
|
85
|
+
});
|
|
86
|
+
return sent;
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
const failed = {
|
|
90
|
+
...target,
|
|
91
|
+
status: "FAILED",
|
|
92
|
+
attemptedAt,
|
|
93
|
+
failedAt: new Date().toISOString(),
|
|
94
|
+
error: error instanceof Error ? error.message : String(error)
|
|
95
|
+
};
|
|
96
|
+
await persist(root, failed);
|
|
97
|
+
await trace(root, "operation.callback.failed", {
|
|
98
|
+
operationId: operation.id,
|
|
99
|
+
operationStatus: operation.status,
|
|
100
|
+
agentId: target.agentId,
|
|
101
|
+
error: failed.error ?? "unknown"
|
|
102
|
+
});
|
|
103
|
+
return failed;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export function completionPrompt(operation) {
|
|
107
|
+
const artifact = resultPath(operation);
|
|
108
|
+
const result = operation.result ? JSON.stringify(operation.result) : "{}";
|
|
109
|
+
return [
|
|
110
|
+
"[AEH_OPERATION_COMPLETED]",
|
|
111
|
+
`Detached AEH operation ${operation.id} (${operation.kind}) reached terminal state ${operation.status}.`,
|
|
112
|
+
"This is an internal AEH controller completion callback for the user request that launched this operation; it is not a new user task.",
|
|
113
|
+
"Do not start a duplicate AUDIT/RUN and do not restart the completed operation.",
|
|
114
|
+
`Use aeh_operation_status for ${operation.id} only if you need to refresh durable metadata.${artifact ? ` Read the authoritative artifact ${artifact}.` : ""}`,
|
|
115
|
+
"Continue and finish the original pending user-facing request now, using the durable operation result and artifacts as the source of truth.",
|
|
116
|
+
operation.error ? `Operation error: ${operation.error.split("\n", 1)[0]}` : `Operation result: ${result}`
|
|
117
|
+
].join("\n");
|
|
118
|
+
}
|
|
119
|
+
function resultPath(operation) {
|
|
120
|
+
const report = operation.result?.report;
|
|
121
|
+
return typeof report === "string" && report.trim() ? report.trim() : undefined;
|
|
122
|
+
}
|
|
123
|
+
async function persist(root, target) {
|
|
124
|
+
const file = operationCompletionFile(root, target.operationId);
|
|
125
|
+
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
126
|
+
const temp = `${file}.${process.pid}.tmp`;
|
|
127
|
+
await fs.writeFile(temp, `${JSON.stringify(target, null, 2)}\n`);
|
|
128
|
+
try {
|
|
129
|
+
await fs.rename(temp, file);
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
await fs.rm(temp, { force: true }).catch(() => undefined);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function requiredId(value, name) {
|
|
136
|
+
const trimmed = value.trim();
|
|
137
|
+
if (!trimmed)
|
|
138
|
+
throw new Error(`${name} is required.`);
|
|
139
|
+
return trimmed;
|
|
140
|
+
}
|
|
141
|
+
function safeId(value) {
|
|
142
|
+
if (!/^[A-Za-z0-9._-]+$/.test(value))
|
|
143
|
+
throw new Error(`Invalid operation id '${value}'.`);
|
|
144
|
+
return value;
|
|
145
|
+
}
|
|
146
|
+
function isMissing(error) {
|
|
147
|
+
return Boolean(error &&
|
|
148
|
+
typeof error === "object" &&
|
|
149
|
+
"code" in error &&
|
|
150
|
+
error.code === "ENOENT");
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=completion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"completion.js","sourceRoot":"","sources":["../../src/operations/completion.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,10 +6,13 @@ export interface StartOperationOptions {
|
|
|
6
6
|
nodeExecutable: string;
|
|
7
7
|
entryFile: string;
|
|
8
8
|
spawnProcess?: typeof spawn;
|
|
9
|
+
completionAgentId?: string;
|
|
10
|
+
completionSource?: string;
|
|
9
11
|
}
|
|
10
12
|
export interface OperationControllerDeps {
|
|
11
13
|
run?: typeof runProcess;
|
|
12
14
|
trace?: typeof recordPaseoTrace;
|
|
15
|
+
notifyCompletion?: (root: string, operation: OperationRecord) => Promise<unknown>;
|
|
13
16
|
}
|
|
14
17
|
export declare function startDetachedOperation(root: string, kind: OperationKind, payload: OperationPayload, options: StartOperationOptions): Promise<OperationRecord>;
|
|
15
18
|
export declare function executeOperation(root: string, operationId: string, deps?: OperationControllerDeps): Promise<OperationRecord>;
|
|
@@ -8,7 +8,8 @@ import { runTask } from "../core/run.js";
|
|
|
8
8
|
import { listManagedPaseoAgents } from "../paseo/runtime.js";
|
|
9
9
|
import { recordPaseoTrace } from "../paseo/trace.js";
|
|
10
10
|
import { runProcess } from "../utils/process.js";
|
|
11
|
-
import {
|
|
11
|
+
import { disableOperationCompletionTarget, notifyOperationCompletion, registerOperationCompletionTarget } from "./completion.js";
|
|
12
|
+
import { loadOperation, patchOperation, saveOperation, transitionOperationToTerminal } from "./state.js";
|
|
12
13
|
export async function startDetachedOperation(root, kind, payload, options) {
|
|
13
14
|
const absoluteRoot = path.resolve(root);
|
|
14
15
|
const now = new Date().toISOString();
|
|
@@ -25,6 +26,10 @@ export async function startDetachedOperation(root, kind, payload, options) {
|
|
|
25
26
|
updatedAt: now
|
|
26
27
|
};
|
|
27
28
|
await saveOperation(absoluteRoot, record);
|
|
29
|
+
const completionAgentId = options.completionAgentId?.trim() || process.env.PASEO_AGENT_ID?.trim() || undefined;
|
|
30
|
+
if (completionAgentId) {
|
|
31
|
+
await registerOperationCompletionTarget(absoluteRoot, id, completionAgentId, options.completionSource ?? (options.completionAgentId ? "explicit" : "environment"));
|
|
32
|
+
}
|
|
28
33
|
const spawnProcess = options.spawnProcess ?? spawn;
|
|
29
34
|
let child;
|
|
30
35
|
try {
|
|
@@ -40,6 +45,9 @@ export async function startDetachedOperation(root, kind, payload, options) {
|
|
|
40
45
|
});
|
|
41
46
|
}
|
|
42
47
|
catch (error) {
|
|
48
|
+
if (completionAgentId) {
|
|
49
|
+
await disableOperationCompletionTarget(absoluteRoot, id, `Detached controller spawn failed before the initiating tool returned: ${String(error)}`).catch(() => undefined);
|
|
50
|
+
}
|
|
43
51
|
return patchOperation(absoluteRoot, id, {
|
|
44
52
|
status: "FAILED",
|
|
45
53
|
phase: "spawn-failed",
|
|
@@ -92,7 +100,7 @@ export async function executeOperation(root, operationId, deps = {}) {
|
|
|
92
100
|
const current = await loadOperation(absoluteRoot, operationId);
|
|
93
101
|
if (current.status === "CANCELLED")
|
|
94
102
|
return current;
|
|
95
|
-
return
|
|
103
|
+
return terminalizeOperation(absoluteRoot, operationId, {
|
|
96
104
|
status: "SUCCEEDED",
|
|
97
105
|
phase: "finished",
|
|
98
106
|
finishedAt: new Date().toISOString(),
|
|
@@ -102,7 +110,7 @@ export async function executeOperation(root, operationId, deps = {}) {
|
|
|
102
110
|
productionSafe: report.productionSafe,
|
|
103
111
|
report: `.harness/audits/${report.auditId}.json`
|
|
104
112
|
}
|
|
105
|
-
});
|
|
113
|
+
}, deps);
|
|
106
114
|
}
|
|
107
115
|
const payload = record.payload;
|
|
108
116
|
const contract = await loadTaskContract(absoluteRoot, payload.taskId, config);
|
|
@@ -112,7 +120,7 @@ export async function executeOperation(root, operationId, deps = {}) {
|
|
|
112
120
|
const current = await loadOperation(absoluteRoot, operationId);
|
|
113
121
|
if (current.status === "CANCELLED")
|
|
114
122
|
return current;
|
|
115
|
-
return
|
|
123
|
+
return terminalizeOperation(absoluteRoot, operationId, {
|
|
116
124
|
status: result.status === "PASS" ? "SUCCEEDED" : "FAILED",
|
|
117
125
|
phase: "finished",
|
|
118
126
|
finishedAt: new Date().toISOString(),
|
|
@@ -121,18 +129,18 @@ export async function executeOperation(root, operationId, deps = {}) {
|
|
|
121
129
|
status: result.status,
|
|
122
130
|
attempts: result.attempts
|
|
123
131
|
}
|
|
124
|
-
});
|
|
132
|
+
}, deps);
|
|
125
133
|
}
|
|
126
134
|
catch (error) {
|
|
127
135
|
const current = await loadOperation(absoluteRoot, operationId).catch(() => record);
|
|
128
136
|
if (current.status === "CANCELLED")
|
|
129
137
|
return current;
|
|
130
|
-
return
|
|
138
|
+
return terminalizeOperation(absoluteRoot, operationId, {
|
|
131
139
|
status: "FAILED",
|
|
132
140
|
phase: "failed",
|
|
133
141
|
error: error instanceof Error ? error.stack ?? error.message : String(error),
|
|
134
142
|
finishedAt: new Date().toISOString()
|
|
135
|
-
});
|
|
143
|
+
}, deps);
|
|
136
144
|
}
|
|
137
145
|
}
|
|
138
146
|
export async function waitForOperation(root, operationId, timeoutMs = 1_800_000, pollMs = 500) {
|
|
@@ -218,12 +226,12 @@ export async function cancelOperation(root, operationId, deps = {}) {
|
|
|
218
226
|
cleanupWarnings.push(`agent ${agentId}: ${stopped.stderr || stopped.stdout || `exit ${stopped.exitCode}`}`);
|
|
219
227
|
}
|
|
220
228
|
}
|
|
221
|
-
return
|
|
229
|
+
return terminalizeOperation(absoluteRoot, operationId, {
|
|
222
230
|
status: "CANCELLED",
|
|
223
231
|
phase: "cancelled",
|
|
224
232
|
finishedAt: new Date().toISOString(),
|
|
225
233
|
cleanupWarnings: cleanupWarnings.length ? cleanupWarnings : undefined
|
|
226
|
-
});
|
|
234
|
+
}, deps);
|
|
227
235
|
}
|
|
228
236
|
export function createOperationId(kind, seed) {
|
|
229
237
|
const stamp = new Date()
|
|
@@ -233,6 +241,27 @@ export function createOperationId(kind, seed) {
|
|
|
233
241
|
const hash = crypto.createHash("sha256").update(seed).digest("hex").slice(0, 8);
|
|
234
242
|
return `${kind.toUpperCase()}-${stamp}-${hash}`;
|
|
235
243
|
}
|
|
244
|
+
async function terminalizeOperation(root, operationId, patch, deps) {
|
|
245
|
+
const { record: terminal, transitioned } = await transitionOperationToTerminal(root, operationId, patch);
|
|
246
|
+
if (!transitioned)
|
|
247
|
+
return terminal;
|
|
248
|
+
const trace = deps.trace ?? recordPaseoTrace;
|
|
249
|
+
try {
|
|
250
|
+
if (deps.notifyCompletion)
|
|
251
|
+
await deps.notifyCompletion(root, terminal);
|
|
252
|
+
else
|
|
253
|
+
await notifyOperationCompletion(root, terminal, { trace });
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
await trace(root, "operation.callback.failed", {
|
|
257
|
+
operationId,
|
|
258
|
+
operationStatus: terminal.status,
|
|
259
|
+
error: error instanceof Error ? error.message : String(error),
|
|
260
|
+
boundary: "controller"
|
|
261
|
+
}).catch(() => undefined);
|
|
262
|
+
}
|
|
263
|
+
return terminal;
|
|
264
|
+
}
|
|
236
265
|
async function ensureOperationWorkspace(root, record, run, trace) {
|
|
237
266
|
const title = `AEH ${record.kind.toUpperCase()} · ${record.id}`;
|
|
238
267
|
const command = `paseo workspace create --isolation local --path ${quote(root)} --title ${quote(title)} --json`;
|