@liy/agent-runner 0.1.0 → 0.2.0

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.
@@ -1,5 +1,115 @@
1
+ import type { AgentSessionEvent } from "@earendil-works/pi-coding-agent";
1
2
  import type { ResolvedAgentRunnerHarnessConfig } from "../config.js";
2
3
  import type { HarnessSpawn, TaskAgentHarnessDriver } from "./types.js";
4
+ /**
5
+ * Pi extension runtime error event emitted by RPC mode.
6
+ *
7
+ * @remarks
8
+ * Pi 0.74.0 emits this record from RPC mode but does not include it in the
9
+ * exported `AgentSessionEvent` union. Keep it structural so downstream Mote UI
10
+ * code can still switch on the upstream `extension_error` spelling.
11
+ *
12
+ * @public
13
+ */
14
+ export interface PiExtensionErrorEvent {
15
+ /**
16
+ * Upstream Pi event discriminator.
17
+ */
18
+ type: "extension_error";
19
+ /**
20
+ * Extension file path that raised the error.
21
+ */
22
+ extensionPath: string;
23
+ /**
24
+ * Extension event name being handled when the error occurred.
25
+ */
26
+ event: string;
27
+ /**
28
+ * Human-readable extension error text.
29
+ */
30
+ error: string;
31
+ }
32
+ /**
33
+ * Native Pi event forwarded by the Pi RPC harness driver.
34
+ *
35
+ * @public
36
+ */
37
+ export type PiEvent = AgentSessionEvent | PiExtensionErrorEvent;
38
+ /**
39
+ * Native Pi assistant-message stream event.
40
+ *
41
+ * @public
42
+ */
43
+ export type PiAssistantMessageEvent = Extract<PiEvent, {
44
+ type: "message_update";
45
+ }>["assistantMessageEvent"];
46
+ /**
47
+ * Structural copy of Pi RPC extension UI requests.
48
+ *
49
+ * @remarks
50
+ * The Pi package exports this type from an internal path in 0.74.0 but not from
51
+ * its public package root. Mote forwards the request opaquely and does not
52
+ * implement response handling in this pass.
53
+ *
54
+ * @public
55
+ */
56
+ export type PiExtensionUiRequest = {
57
+ type: "extension_ui_request";
58
+ id: string;
59
+ method: "select";
60
+ title: string;
61
+ options: string[];
62
+ timeout?: number;
63
+ } | {
64
+ type: "extension_ui_request";
65
+ id: string;
66
+ method: "confirm";
67
+ title: string;
68
+ message: string;
69
+ timeout?: number;
70
+ } | {
71
+ type: "extension_ui_request";
72
+ id: string;
73
+ method: "input";
74
+ title: string;
75
+ placeholder?: string;
76
+ timeout?: number;
77
+ } | {
78
+ type: "extension_ui_request";
79
+ id: string;
80
+ method: "editor";
81
+ title: string;
82
+ prefill?: string;
83
+ } | {
84
+ type: "extension_ui_request";
85
+ id: string;
86
+ method: "notify";
87
+ message: string;
88
+ notifyType?: "info" | "warning" | "error";
89
+ } | {
90
+ type: "extension_ui_request";
91
+ id: string;
92
+ method: "setStatus";
93
+ statusKey: string;
94
+ statusText: string | undefined;
95
+ } | {
96
+ type: "extension_ui_request";
97
+ id: string;
98
+ method: "setWidget";
99
+ widgetKey: string;
100
+ widgetLines: string[] | undefined;
101
+ widgetPlacement?: "aboveEditor" | "belowEditor";
102
+ } | {
103
+ type: "extension_ui_request";
104
+ id: string;
105
+ method: "setTitle";
106
+ title: string;
107
+ } | {
108
+ type: "extension_ui_request";
109
+ id: string;
110
+ method: "set_editor_text";
111
+ text: string;
112
+ };
3
113
  /**
4
114
  * Create the Pi RPC driver adapter.
5
115
  *
@@ -63,15 +63,16 @@ export declare class JsonlByteBufferParser {
63
63
  */
64
64
  export declare function forwardJsonlByteStream(stream: Readable, onLine: (line: string) => void): Promise<void>;
65
65
  /**
66
- * Forward one generic harness output line as JSON progress or text log.
66
+ * Forward one generic harness output line as redacted subprocess stream text.
67
67
  *
68
68
  * @param channel - Connected control channel.
69
+ * @param taskId - Runtime task id for control-channel correlation.
70
+ * @param harness - Harness driver id that produced the line.
69
71
  * @param line - Raw harness output line.
70
72
  * @param streamName - Source stream name.
71
73
  * @param sanitizer - Output sanitizer.
72
- * @param driver - Optional driver id to include in forwarded payloads.
73
74
  */
74
- export declare function forwardHarnessLine(channel: ControlChannel, line: string, streamName: "stdout" | "stderr", sanitizer: HarnessOutputSanitizer, driver?: string): void;
75
+ export declare function forwardHarnessLine(channel: ControlChannel, taskId: string, harness: string, line: string, streamName: "stdout" | "stderr", sanitizer: HarnessOutputSanitizer): void;
75
76
  /**
76
77
  * Wait for one child process to close.
77
78
  *
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from "./config.js";
2
2
  export * from "./runtime.js";
3
3
  export * from "./runner.js";
4
+ export type { PiAssistantMessageEvent, PiEvent, PiExtensionErrorEvent, PiExtensionUiRequest, } from "./harness-drivers/pi-rpc.js";
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { createRequire as ___liy_agent_runnerCreateRequire } from "node:module";var require = ___liy_agent_runnerCreateRequire(import.meta.url);
1
2
  var __defProp = Object.defineProperty;
2
3
  var __export = (target, all) => {
3
4
  for (var name in all)
@@ -15511,25 +15512,17 @@ function forwardJsonlByteStream(stream, onLine) {
15511
15512
  });
15512
15513
  });
15513
15514
  }
15514
- function forwardHarnessLine(channel, line, streamName, sanitizer, driver) {
15515
+ function forwardHarnessLine(channel, taskId, harness, line, streamName, sanitizer) {
15515
15516
  const trimmed = sanitizer.redactText(line.trim());
15516
15517
  if (!trimmed) {
15517
15518
  return;
15518
15519
  }
15519
- try {
15520
- const parsed = JSON.parse(trimmed);
15521
- channel.send(createExecuteTaskRpcNotification("task.progress", {
15522
- ...driver ? { driver } : {},
15523
- stream: streamName,
15524
- event: sanitizer.redactJson(parsed)
15525
- }));
15526
- } catch {
15527
- channel.send(createExecuteTaskRpcNotification("task.log", {
15528
- ...driver ? { driver } : {},
15529
- stream: streamName,
15530
- message: trimmed
15531
- }));
15532
- }
15520
+ channel.send(createExecuteTaskRpcNotification(`harness.${streamName}`, {
15521
+ taskId,
15522
+ harness,
15523
+ stream: streamName,
15524
+ message: trimmed
15525
+ }));
15533
15526
  }
15534
15527
  function waitForChildClose(child) {
15535
15528
  return new Promise((resolve, reject) => {
@@ -15609,10 +15602,10 @@ function startCodexJsonlHarness(input, spawnProcess) {
15609
15602
  let cleanupTermination = () => void 0;
15610
15603
  child.stdin.end(input.prompt);
15611
15604
  const stdoutDone = forwardHarnessLines(child.stdout, (line) => {
15612
- forwardHarnessLine(input.channel, line, "stdout", sanitizer);
15605
+ forwardHarnessLine(input.channel, input.spec.taskId, "codex-jsonl", line, "stdout", sanitizer);
15613
15606
  });
15614
15607
  const stderrDone = forwardHarnessLines(child.stderr, (line) => {
15615
- forwardHarnessLine(input.channel, line, "stderr", sanitizer);
15608
+ forwardHarnessLine(input.channel, input.spec.taskId, "codex-jsonl", line, "stderr", sanitizer);
15616
15609
  });
15617
15610
  const result = waitForChildClose(child).then(async (closeResult) => {
15618
15611
  cleanupTermination();
@@ -15762,11 +15755,11 @@ function startPiRpcHarness(input, spawnProcess) {
15762
15755
  cleanupTermination();
15763
15756
  await Promise.all([stdoutDone, stderrDone]);
15764
15757
  if (!agentEndReached && terminal.exitCode !== 0) {
15765
- input.channel.send(createExecuteTaskRpcNotification("task.log", {
15766
- driver: "pi-rpc",
15767
- stream: "stderr",
15768
- message: `pi-rpc process closed before agent_end with ${formatHarnessExit(terminal)}`
15769
- }));
15758
+ forwardPiHarnessStreamLine(
15759
+ input,
15760
+ "stderr",
15761
+ `pi-rpc process closed before agent_end with ${formatHarnessExit(terminal)}`
15762
+ );
15770
15763
  }
15771
15764
  return terminal;
15772
15765
  })();
@@ -15788,37 +15781,57 @@ function handlePiRpcStdoutLine(input) {
15788
15781
  try {
15789
15782
  parsed = JSON.parse(trimmed);
15790
15783
  } catch {
15791
- input.input.channel.send(createExecuteTaskRpcNotification("task.log", {
15792
- driver: "pi-rpc",
15793
- stream: "stdout",
15794
- message: input.sanitizer.redactText(trimmed)
15795
- }));
15784
+ forwardPiHarnessStreamLine(input.input, "stdout", input.sanitizer.redactText(trimmed));
15796
15785
  return;
15797
15786
  }
15798
- input.input.channel.send(createExecuteTaskRpcNotification("task.progress", {
15799
- driver: "pi-rpc",
15800
- stream: "stdout",
15801
- event: input.sanitizer.redactJson(parsed)
15802
- }));
15803
- if (parsed.type === "response" && parsed.command === "prompt" && parsed.success === false) {
15804
- input.onPromptRejected(new Error(`Pi RPC prompt was rejected: ${formatPiRpcError(parsed, input.sanitizer.redactText)}`));
15787
+ if (isPiRpcResponse(parsed)) {
15788
+ if (parsed.command === "prompt" && parsed.success === false) {
15789
+ input.onPromptRejected(new Error(`Pi RPC prompt was rejected: ${formatPiRpcError(parsed, input.sanitizer.redactText)}`));
15790
+ }
15791
+ return;
15792
+ }
15793
+ if (isPiExtensionUiRequest(parsed)) {
15794
+ forwardPiExtensionUiRequest(input.input, input.sanitizer.redactJson(parsed));
15805
15795
  return;
15806
15796
  }
15797
+ forwardPiEvent(input.input, input.sanitizer.redactJson(parsed));
15807
15798
  if (parsed.type === "agent_end") {
15808
15799
  input.onAgentEnd();
15809
15800
  }
15810
15801
  }
15811
- function forwardPiStderrLine(input, line, sanitizer) {
15812
- const trimmed = sanitizer.redactText(line.trim());
15802
+ function forwardPiEvent(input, event) {
15803
+ input.channel.send(createExecuteTaskRpcNotification("pi.event", {
15804
+ taskId: input.spec.taskId,
15805
+ event
15806
+ }));
15807
+ }
15808
+ function forwardPiExtensionUiRequest(input, request) {
15809
+ input.channel.send(createExecuteTaskRpcNotification("pi.extension_ui_request", {
15810
+ taskId: input.spec.taskId,
15811
+ request
15812
+ }));
15813
+ }
15814
+ function isPiRpcResponse(parsed) {
15815
+ return parsed.type === "response" && typeof parsed.command === "string";
15816
+ }
15817
+ function isPiExtensionUiRequest(parsed) {
15818
+ return parsed.type === "extension_ui_request" && typeof parsed.id === "string";
15819
+ }
15820
+ function forwardPiHarnessStreamLine(input, stream, message) {
15821
+ const trimmed = message.trim();
15813
15822
  if (!trimmed) {
15814
15823
  return;
15815
15824
  }
15816
- input.channel.send(createExecuteTaskRpcNotification("task.log", {
15817
- driver: "pi-rpc",
15818
- stream: "stderr",
15825
+ input.channel.send(createExecuteTaskRpcNotification(`harness.${stream}`, {
15826
+ taskId: input.spec.taskId,
15827
+ harness: "pi-rpc",
15828
+ stream,
15819
15829
  message: trimmed
15820
15830
  }));
15821
15831
  }
15832
+ function forwardPiStderrLine(input, line, sanitizer) {
15833
+ forwardPiHarnessStreamLine(input, "stderr", sanitizer.redactText(line));
15834
+ }
15822
15835
  function writePiRpcCommand(child, command) {
15823
15836
  child.stdin.write(`${JSON.stringify(command)}
15824
15837
  `);
@@ -15876,30 +15889,40 @@ async function runTaskAgentRunner(spec, options = {}) {
15876
15889
  const harness = resolveAgentRunnerHarness(config2, spec.agentRunner);
15877
15890
  const channel = await openControlChannel(spec.rpcUrl);
15878
15891
  try {
15879
- channel.send(createExecuteTaskRpcNotification("task.started", {
15892
+ sendTaskStatus(channel, {
15880
15893
  taskId: spec.taskId,
15881
- workspaceDir: spec.workspaceDir
15882
- }));
15894
+ status: "running"
15895
+ });
15883
15896
  const completion = await runHarnessAndReadCompletion({
15884
15897
  spec,
15885
15898
  harness,
15886
15899
  channel,
15887
15900
  ...options.env ? { env: options.env } : {}
15888
15901
  });
15889
- channel.send(createExecuteTaskRpcNotification(`task.${completion.status}`, {
15902
+ sendTaskStatus(channel, {
15890
15903
  taskId: spec.taskId,
15891
15904
  ...completion
15892
- }));
15905
+ });
15893
15906
  return completion;
15907
+ } catch (error48) {
15908
+ const completion = createFailedCompletion(error48);
15909
+ await writeTaskCompletion({
15910
+ paths,
15911
+ completion
15912
+ }).catch(() => void 0);
15913
+ try {
15914
+ sendTaskStatus(channel, {
15915
+ taskId: spec.taskId,
15916
+ ...completion
15917
+ });
15918
+ } catch {
15919
+ }
15920
+ throw error48;
15894
15921
  } finally {
15895
15922
  channel.close();
15896
15923
  }
15897
15924
  } catch (error48) {
15898
- const completion = {
15899
- status: "failed",
15900
- summary: error48 instanceof Error ? error48.message : String(error48),
15901
- artifactIds: []
15902
- };
15925
+ const completion = createFailedCompletion(error48);
15903
15926
  await writeTaskCompletion({
15904
15927
  paths,
15905
15928
  completion
@@ -15907,6 +15930,16 @@ async function runTaskAgentRunner(spec, options = {}) {
15907
15930
  throw error48;
15908
15931
  }
15909
15932
  }
15933
+ function sendTaskStatus(channel, params) {
15934
+ channel.send(createExecuteTaskRpcNotification("task.status", params));
15935
+ }
15936
+ function createFailedCompletion(error48) {
15937
+ return {
15938
+ status: "failed",
15939
+ summary: error48 instanceof Error ? error48.message : String(error48),
15940
+ artifactIds: []
15941
+ };
15942
+ }
15910
15943
  function createHarnessEnvironment(input) {
15911
15944
  const baseEnv = input.env ?? process.env;
15912
15945
  const harnessEnv = {};
@@ -16014,7 +16047,14 @@ async function runHarnessAndReadCompletion(input) {
16014
16047
  });
16015
16048
  return completion2;
16016
16049
  }
16017
- const completion = await readTaskCompletion(paths);
16050
+ const completion = await readTaskCompletion(paths).catch(async (error48) => {
16051
+ const fallback = createMissingCompletionFailure(error48);
16052
+ await writeTaskCompletion({
16053
+ paths,
16054
+ completion: fallback
16055
+ });
16056
+ return fallback;
16057
+ });
16018
16058
  await validateTaskArtifactFiles({
16019
16059
  paths,
16020
16060
  completion
@@ -16024,6 +16064,14 @@ async function runHarnessAndReadCompletion(input) {
16024
16064
  }
16025
16065
  return completion;
16026
16066
  }
16067
+ function createMissingCompletionFailure(error48) {
16068
+ const detail = error48 instanceof Error ? error48.message : String(error48);
16069
+ return {
16070
+ status: "failed",
16071
+ summary: `harness ended without writing state/completion.json: ${detail}`,
16072
+ artifactIds: []
16073
+ };
16074
+ }
16027
16075
  function validateRunnerSpec(spec) {
16028
16076
  if ("harness" in spec) {
16029
16077
  throw new Error("task spec harness is no longer supported; use task spec agentRunner");