@emiliosp/pi-maestro 0.9.2 → 0.9.3
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/ROADMAP.md +19 -10
- package/extensions/maestro.ts +1 -1
- package/package.json +1 -1
- package/src/maestro/checks/assertEnvironment.ts +98 -3
package/ROADMAP.md
CHANGED
|
@@ -76,6 +76,25 @@ All 5 `acceptance criteria` passed through independent inspection of the written
|
|
|
76
76
|
|
|
77
77
|
## Now
|
|
78
78
|
|
|
79
|
+
### R-06: Check subagent extensions at activation
|
|
80
|
+
|
|
81
|
+
#### Objective
|
|
82
|
+
|
|
83
|
+
Make `pi-subagents` availability problems visible before a `builder` or `verifier` `run` starts.
|
|
84
|
+
|
|
85
|
+
#### Implementation status
|
|
86
|
+
|
|
87
|
+
Activation now requires a successful version 1 `ping` reply from the parent `pi-subagents` extension within 2000 ms. The check does not start an agent. All 7 `acceptance criteria` passed with no `verifier` `findings`; the workflow reached `candidate-ready`.
|
|
88
|
+
|
|
89
|
+
A later integration test requested by the `owner` directly checks the environment check's failed-reply error. This test addition is outside the completed independent verification.
|
|
90
|
+
|
|
91
|
+
[Specification](.specs/20261013-194407-check-pi-subagents-availability-with-ping-at-activation/spec.md).
|
|
92
|
+
|
|
93
|
+
#### Completed when
|
|
94
|
+
|
|
95
|
+
Unavailable `pi-subagents` prevents activation. The error identifies the problem.
|
|
96
|
+
|
|
97
|
+
|
|
79
98
|
## Next
|
|
80
99
|
|
|
81
100
|
## Later
|
|
@@ -104,16 +123,6 @@ When criteria are removed during `spec` preparation or revision, renumber the re
|
|
|
104
123
|
|
|
105
124
|
The `spec` and its current references use consistent numbering.
|
|
106
125
|
|
|
107
|
-
### R-06: Check subagent extensions at activation
|
|
108
|
-
|
|
109
|
-
#### Objective
|
|
110
|
-
|
|
111
|
-
Make extension availability problems visible before a `builder` or `verifier` `run` starts. Activation already checks agent launch requirements, so first identify any missing extension checks.
|
|
112
|
-
|
|
113
|
-
#### Completed when
|
|
114
|
-
|
|
115
|
-
Unavailable required subagent extensions prevent activation. The error identifies the problem.
|
|
116
|
-
|
|
117
126
|
### R-07: Guide initial configuration
|
|
118
127
|
|
|
119
128
|
#### Objective
|
package/extensions/maestro.ts
CHANGED
package/package.json
CHANGED
|
@@ -3,12 +3,26 @@
|
|
|
3
3
|
* Used: When the owner activates Maestro mode.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { randomUUID } from 'node:crypto';
|
|
6
7
|
import { realpath } from 'node:fs/promises';
|
|
7
|
-
import type {
|
|
8
|
+
import type {
|
|
9
|
+
ExtensionAPI,
|
|
10
|
+
ExtensionContext,
|
|
11
|
+
} from '@earendil-works/pi-coding-agent';
|
|
8
12
|
import { resolveSubagentLaunchContract } from 'pi-subagents/preflight';
|
|
9
13
|
import { loadConfiguration } from '#config/loadConfiguration.ts';
|
|
10
14
|
import { AGENTS } from '#config/schema.ts';
|
|
11
15
|
|
|
16
|
+
export const SUBAGENT_PING = {
|
|
17
|
+
REQUEST_EVENT: 'subagents:rpc:v1:request',
|
|
18
|
+
REPLY_EVENT_PREFIX: 'subagents:rpc:v1:reply:',
|
|
19
|
+
VERSION: 1,
|
|
20
|
+
METHOD: 'ping',
|
|
21
|
+
TIMEOUT_MS: 2000,
|
|
22
|
+
INSTALLATION_ERROR:
|
|
23
|
+
'Maestro flow requires pi-subagents installed in the Pi environment.',
|
|
24
|
+
} as const;
|
|
25
|
+
|
|
12
26
|
const getErrorMessage = (error: unknown): string =>
|
|
13
27
|
error instanceof Error ? error.message : String(error);
|
|
14
28
|
|
|
@@ -76,9 +90,81 @@ async function assertAgentsAvailable(projectRoot: string): Promise<void> {
|
|
|
76
90
|
}
|
|
77
91
|
}
|
|
78
92
|
|
|
79
|
-
|
|
80
|
-
|
|
93
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
94
|
+
typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
95
|
+
|
|
96
|
+
const isReplyWithRequestId = (
|
|
97
|
+
reply: unknown,
|
|
98
|
+
): reply is Record<string, unknown> & { requestId: string } =>
|
|
99
|
+
isRecord(reply) && typeof reply.requestId === 'string';
|
|
100
|
+
|
|
101
|
+
type SuccessfulPingReply = {
|
|
102
|
+
version: typeof SUBAGENT_PING.VERSION;
|
|
103
|
+
requestId: string;
|
|
104
|
+
method: typeof SUBAGENT_PING.METHOD;
|
|
105
|
+
success: true;
|
|
106
|
+
data: { version: typeof SUBAGENT_PING.VERSION };
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
function assertSuccessfulPingReply(
|
|
110
|
+
reply: unknown,
|
|
111
|
+
): asserts reply is SuccessfulPingReply {
|
|
112
|
+
if (
|
|
113
|
+
!isReplyWithRequestId(reply) ||
|
|
114
|
+
reply.version !== SUBAGENT_PING.VERSION ||
|
|
115
|
+
reply.method !== SUBAGENT_PING.METHOD ||
|
|
116
|
+
reply.success !== true ||
|
|
117
|
+
!isRecord(reply.data) ||
|
|
118
|
+
reply.data.version !== SUBAGENT_PING.VERSION
|
|
119
|
+
) {
|
|
120
|
+
throw new Error('Unsuccessful or invalid pi-subagents ping reply.');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function assertSubagentsRespond(
|
|
125
|
+
piEventsBus: ExtensionAPI['events'],
|
|
81
126
|
): Promise<void> {
|
|
127
|
+
const requestId = randomUUID();
|
|
128
|
+
const { promise, resolve, reject } = Promise.withResolvers<unknown>();
|
|
129
|
+
|
|
130
|
+
const unsubscribe = piEventsBus.on(
|
|
131
|
+
`${SUBAGENT_PING.REPLY_EVENT_PREFIX}${requestId}`,
|
|
132
|
+
(reply) => {
|
|
133
|
+
if (isReplyWithRequestId(reply) && reply.requestId !== requestId) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
resolve(reply);
|
|
138
|
+
},
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const timeout = setTimeout(
|
|
142
|
+
() => reject(new Error('Pi-subagents ping timed out after 2000 ms.')),
|
|
143
|
+
SUBAGENT_PING.TIMEOUT_MS,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
piEventsBus.emit(SUBAGENT_PING.REQUEST_EVENT, {
|
|
148
|
+
version: SUBAGENT_PING.VERSION,
|
|
149
|
+
requestId,
|
|
150
|
+
method: SUBAGENT_PING.METHOD,
|
|
151
|
+
});
|
|
152
|
+
assertSuccessfulPingReply(await promise);
|
|
153
|
+
} finally {
|
|
154
|
+
clearTimeout(timeout);
|
|
155
|
+
unsubscribe();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
type AssertEnvironmentInput = {
|
|
160
|
+
context: ExtensionContext;
|
|
161
|
+
piEventsBus: ExtensionAPI['events'];
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export async function assertEnvironment({
|
|
165
|
+
context,
|
|
166
|
+
piEventsBus,
|
|
167
|
+
}: AssertEnvironmentInput): Promise<void> {
|
|
82
168
|
const projectRoot = await getProjectRoot(context);
|
|
83
169
|
|
|
84
170
|
try {
|
|
@@ -92,4 +178,13 @@ export async function assertEnvironment(
|
|
|
92
178
|
const config = await getActivationConfiguration(projectRoot);
|
|
93
179
|
|
|
94
180
|
assertModelsAvailable({ context, config });
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
await assertSubagentsRespond(piEventsBus);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
throw new Error(
|
|
186
|
+
`${SUBAGENT_PING.INSTALLATION_ERROR} ${getErrorMessage(error)}`,
|
|
187
|
+
{ cause: error },
|
|
188
|
+
);
|
|
189
|
+
}
|
|
95
190
|
}
|