pagegraph 0.8.0 → 0.8.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/dist/cli.js +88 -11
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -35710,7 +35710,7 @@ const runWith = (command, config) => {
|
|
|
35710
35710
|
};
|
|
35711
35711
|
//#endregion
|
|
35712
35712
|
//#region package.json
|
|
35713
|
-
var version = "0.8.
|
|
35713
|
+
var version = "0.8.2";
|
|
35714
35714
|
//#endregion
|
|
35715
35715
|
//#region src/core/projections.ts
|
|
35716
35716
|
const escapeXml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
@@ -43389,6 +43389,21 @@ const createWorkflowMutationPolicy = (options) => {
|
|
|
43389
43389
|
};
|
|
43390
43390
|
//#endregion
|
|
43391
43391
|
//#region src/workflows/opencode.ts
|
|
43392
|
+
const isActiveExecutorPlugin = (plugin) => JSON.stringify(plugin.source).toLowerCase().includes("executor") && plugin.state.status === "active";
|
|
43393
|
+
const waitForActiveExecutorPlugin = async (options) => {
|
|
43394
|
+
const timeoutMs = options.timeoutMs ?? 3e4;
|
|
43395
|
+
const pollMs = options.pollMs ?? 250;
|
|
43396
|
+
const now = options.now ?? Date.now;
|
|
43397
|
+
const sleep = options.sleep ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
|
|
43398
|
+
const deadline = now() + timeoutMs;
|
|
43399
|
+
do {
|
|
43400
|
+
if ((await options.list()).some(isActiveExecutorPlugin)) return true;
|
|
43401
|
+
const remaining = deadline - now();
|
|
43402
|
+
if (remaining <= 0) return false;
|
|
43403
|
+
await sleep(Math.min(pollMs, remaining));
|
|
43404
|
+
if (now() >= deadline) return false;
|
|
43405
|
+
} while (true);
|
|
43406
|
+
};
|
|
43392
43407
|
const assertWorkflowSkillsAvailable = (configDirectory, skills) => {
|
|
43393
43408
|
const missing = skills.flatMap((skill) => [resolve(configDirectory, "skills", skill, "SKILL.md"), resolve(configDirectory, "skills", skill, "references", "executor.md")].filter((file) => !existsSync(file)));
|
|
43394
43409
|
if (missing.length === 0) return;
|
|
@@ -43418,7 +43433,7 @@ const TranscriptSchema = Struct({ messages: ArraySchema(Unknown) });
|
|
|
43418
43433
|
const decodeTextPart = decodeUnknownOption(TextPartSchema);
|
|
43419
43434
|
const decodeAssistantMessage = decodeUnknownOption(AssistantMessageSchema);
|
|
43420
43435
|
const decodeTranscript = decodeUnknownSync(TranscriptSchema);
|
|
43421
|
-
const
|
|
43436
|
+
const parseWorkflowState = (transcript) => {
|
|
43422
43437
|
const candidates = decodeTranscript(transcript).messages.flatMap((message) => {
|
|
43423
43438
|
const assistant = decodeAssistantMessage(message);
|
|
43424
43439
|
if (isNone(assistant)) return [];
|
|
@@ -43437,6 +43452,25 @@ const parseState = (transcript) => {
|
|
|
43437
43452
|
}
|
|
43438
43453
|
throw new Error("The SEO agent did not return a valid workflow JSON object.");
|
|
43439
43454
|
};
|
|
43455
|
+
const parseWorkflowStateWithRepair = async (transcript, repair) => {
|
|
43456
|
+
try {
|
|
43457
|
+
return {
|
|
43458
|
+
state: parseWorkflowState(transcript),
|
|
43459
|
+
transcript
|
|
43460
|
+
};
|
|
43461
|
+
} catch {
|
|
43462
|
+
const repaired = await repair();
|
|
43463
|
+
return {
|
|
43464
|
+
state: parseWorkflowState(repaired),
|
|
43465
|
+
transcript: repaired
|
|
43466
|
+
};
|
|
43467
|
+
}
|
|
43468
|
+
};
|
|
43469
|
+
const STATE_REPAIR_PROMPT = [
|
|
43470
|
+
"Your previous turn did not end with the workflow JSON object requested by the original prompt.",
|
|
43471
|
+
"Do not call tools, add commentary, or wrap the response in Markdown.",
|
|
43472
|
+
"Return only one valid JSON object matching the original JSON Schema, using the evidence already collected."
|
|
43473
|
+
].join(" ");
|
|
43440
43474
|
const decodeToolPart = decodeUnknownOption(Struct({
|
|
43441
43475
|
type: Literal("tool"),
|
|
43442
43476
|
id: String$6,
|
|
@@ -43500,14 +43534,14 @@ const interactionEventError = (event, sessionId) => {
|
|
|
43500
43534
|
if (form !== null && typeof form === "object" && form["sessionID"] === sessionId) return /* @__PURE__ */ new Error("OpenCode requested a form; embedded PageGraph workflows are noninteractive.");
|
|
43501
43535
|
}
|
|
43502
43536
|
};
|
|
43503
|
-
const waitForIdle = async (host, sessionId,
|
|
43537
|
+
const waitForIdle = async (host, sessionId, options) => {
|
|
43504
43538
|
const controller = new AbortController();
|
|
43505
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
43539
|
+
const timer = setTimeout(() => controller.abort(), options.timeoutMs);
|
|
43506
43540
|
try {
|
|
43507
43541
|
for await (const event of host.sessions.log({
|
|
43508
43542
|
sessionID: sessionId,
|
|
43509
43543
|
follow: true,
|
|
43510
|
-
after
|
|
43544
|
+
after: options.after
|
|
43511
43545
|
}, { signal: controller.signal })) {
|
|
43512
43546
|
const interactionError = interactionEventError(event, sessionId);
|
|
43513
43547
|
if (interactionError !== void 0) throw interactionError;
|
|
@@ -43515,6 +43549,9 @@ const waitForIdle = async (host, sessionId, timeoutMs, after) => {
|
|
|
43515
43549
|
if (event.type === "session.execution.failed" && event.data.sessionID === sessionId) throw new Error(`OpenCode session failed: ${JSON.stringify(event.data.error)}`);
|
|
43516
43550
|
if (event.type === "session.execution.interrupted" && event.data.sessionID === sessionId) throw new Error(`OpenCode session was interrupted: ${event.data.reason}.`);
|
|
43517
43551
|
}
|
|
43552
|
+
} catch (cause) {
|
|
43553
|
+
if (controller.signal.aborted) throw new Error(`OpenCode session did not complete within ${options.configuredTimeoutMs ?? options.timeoutMs}ms.`, { cause });
|
|
43554
|
+
throw cause;
|
|
43518
43555
|
} finally {
|
|
43519
43556
|
clearTimeout(timer);
|
|
43520
43557
|
}
|
|
@@ -43533,7 +43570,7 @@ const acquireWorkflowHost = async (options) => {
|
|
|
43533
43570
|
});
|
|
43534
43571
|
try {
|
|
43535
43572
|
const location = { directory: options.root };
|
|
43536
|
-
if ((await host.plugin.list({ location })).data
|
|
43573
|
+
if (!await waitForActiveExecutorPlugin({ list: async () => (await host.plugin.list({ location })).data })) throw new Error(`No active Executor plugin was discovered in ${configDirectory}; agentic SEO workflows require Executor.`);
|
|
43537
43574
|
const cursors = /* @__PURE__ */ new Map();
|
|
43538
43575
|
const complete = async (sessionId, prompt, workflowOptions) => {
|
|
43539
43576
|
assertWorkflowSkillsAvailable(configDirectory, workflowOptions.skills);
|
|
@@ -43541,22 +43578,62 @@ const acquireWorkflowHost = async (options) => {
|
|
|
43541
43578
|
sessionID: sessionId,
|
|
43542
43579
|
permissions: [...workflowOptions.permissions]
|
|
43543
43580
|
});
|
|
43581
|
+
const configuredTimeoutMs = options.timeoutMs ?? 18e4;
|
|
43582
|
+
const deadline = Date.now() + configuredTimeoutMs;
|
|
43583
|
+
const remaining = () => Math.max(0, deadline - Date.now());
|
|
43544
43584
|
await host.sessions.prompt({
|
|
43545
43585
|
sessionID: sessionId,
|
|
43546
43586
|
text: prompt,
|
|
43547
43587
|
skills: workflowOptions.skills.map((id) => ({ id }))
|
|
43548
43588
|
});
|
|
43549
|
-
|
|
43589
|
+
let cursor = await waitForIdle(host, sessionId, {
|
|
43590
|
+
timeoutMs: remaining(),
|
|
43591
|
+
configuredTimeoutMs,
|
|
43592
|
+
after: cursors.get(sessionId)
|
|
43593
|
+
});
|
|
43550
43594
|
cursors.set(sessionId, cursor);
|
|
43551
|
-
|
|
43595
|
+
let transcript = await host.sessions.export({
|
|
43552
43596
|
sessionID: sessionId,
|
|
43553
43597
|
sanitize: false
|
|
43554
43598
|
});
|
|
43599
|
+
const parsed = await parseWorkflowStateWithRepair(transcript, async () => {
|
|
43600
|
+
const priorPermissions = [...workflowOptions.permissions ?? []];
|
|
43601
|
+
await host.sessions.update({
|
|
43602
|
+
sessionID: sessionId,
|
|
43603
|
+
permissions: [{
|
|
43604
|
+
action: "*",
|
|
43605
|
+
resource: "*",
|
|
43606
|
+
effect: "deny"
|
|
43607
|
+
}]
|
|
43608
|
+
});
|
|
43609
|
+
try {
|
|
43610
|
+
await host.sessions.prompt({
|
|
43611
|
+
sessionID: sessionId,
|
|
43612
|
+
text: STATE_REPAIR_PROMPT
|
|
43613
|
+
});
|
|
43614
|
+
cursor = await waitForIdle(host, sessionId, {
|
|
43615
|
+
timeoutMs: remaining(),
|
|
43616
|
+
configuredTimeoutMs,
|
|
43617
|
+
after: cursor
|
|
43618
|
+
});
|
|
43619
|
+
cursors.set(sessionId, cursor);
|
|
43620
|
+
transcript = await host.sessions.export({
|
|
43621
|
+
sessionID: sessionId,
|
|
43622
|
+
sanitize: false
|
|
43623
|
+
});
|
|
43624
|
+
return transcript;
|
|
43625
|
+
} finally {
|
|
43626
|
+
await host.sessions.update({
|
|
43627
|
+
sessionID: sessionId,
|
|
43628
|
+
permissions: priorPermissions
|
|
43629
|
+
});
|
|
43630
|
+
}
|
|
43631
|
+
});
|
|
43555
43632
|
return {
|
|
43556
|
-
state:
|
|
43633
|
+
state: parsed.state,
|
|
43557
43634
|
sessionId,
|
|
43558
|
-
transcript,
|
|
43559
|
-
executor: collectExecutorEvidence(transcript)
|
|
43635
|
+
transcript: parsed.transcript,
|
|
43636
|
+
executor: collectExecutorEvidence(parsed.transcript)
|
|
43560
43637
|
};
|
|
43561
43638
|
};
|
|
43562
43639
|
return {
|