@liy/agent-runner 0.3.1 → 0.3.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.
@@ -0,0 +1,54 @@
1
+ /**
2
+ * One bounded Pi RPC trace record written to a task-local debug JSONL file.
3
+ *
4
+ * @public
5
+ */
6
+ export interface PiRpcTraceEvent {
7
+ /**
8
+ * Stable trace event name, such as `prompt.sent` or `stdout.event`.
9
+ */
10
+ kind: string;
11
+ /**
12
+ * Direction or process stream that produced the event.
13
+ */
14
+ stream?: "stdin" | "stdout" | "stderr" | "process";
15
+ /**
16
+ * Redacted trace payload. Large payloads are replaced with a preview before
17
+ * they are written.
18
+ */
19
+ payload?: unknown;
20
+ }
21
+ /**
22
+ * Append-only task-local Pi RPC trace writer.
23
+ *
24
+ * @public
25
+ */
26
+ export interface PiRpcTrace {
27
+ /**
28
+ * Absolute path to the trace file when it was opened successfully.
29
+ */
30
+ filePath?: string;
31
+ /**
32
+ * Append one redacted trace record.
33
+ *
34
+ * @param event - Trace event to serialize as one JSONL record.
35
+ */
36
+ write(event: PiRpcTraceEvent): void;
37
+ /**
38
+ * Flush and close the trace file.
39
+ */
40
+ close(): Promise<void>;
41
+ }
42
+ /**
43
+ * Create a Pi RPC trace writer under the task workspace debug directory.
44
+ *
45
+ * @param input - Task identity and workspace location.
46
+ * @returns Writable trace, or a no-op trace when the debug file cannot open.
47
+ *
48
+ * @public
49
+ */
50
+ export declare function createPiRpcTrace(input: {
51
+ taskId: string;
52
+ workspaceDir: string;
53
+ now?: () => Date;
54
+ }): PiRpcTrace;
package/dist/index.js CHANGED
@@ -15079,7 +15079,7 @@ var QuerySchema = external_exports.object({
15079
15079
  limit: external_exports.number().int().positive().max(1e4).default(1e3),
15080
15080
  accessLevel: AccessLevelSchema.default("serving")
15081
15081
  }).superRefine((query, ctx) => {
15082
- const surfaces = [query.from.surface, ...query.joins.map((join2) => join2.surface)];
15082
+ const surfaces = [query.from.surface, ...query.joins.map((join3) => join3.surface)];
15083
15083
  const knownSurfaces = /* @__PURE__ */ new Set();
15084
15084
  for (const [index, surface] of surfaces.entries()) {
15085
15085
  if (knownSurfaces.has(surface)) {
@@ -15114,8 +15114,8 @@ var QuerySchema = external_exports.object({
15114
15114
  path: ["time", "bucket", "fn"]
15115
15115
  });
15116
15116
  }
15117
- for (const [index, join2] of query.joins.entries()) {
15118
- visitConditionFields(join2.on, (field) => {
15117
+ for (const [index, join3] of query.joins.entries()) {
15118
+ visitConditionFields(join3.on, (field) => {
15119
15119
  validateFieldRef(field, ["joins", index, "on"]);
15120
15120
  });
15121
15121
  }
@@ -15632,6 +15632,92 @@ async function forwardHarnessLines(stream, handler) {
15632
15632
 
15633
15633
  // src/harness-drivers/pi-rpc.ts
15634
15634
  import { spawn as nodeSpawn2 } from "node:child_process";
15635
+
15636
+ // src/debug/pi-rpc-trace.ts
15637
+ import { once } from "node:events";
15638
+ import { createWriteStream, mkdirSync } from "node:fs";
15639
+ import { dirname as dirname2, join as join2 } from "node:path";
15640
+ var MAX_TRACE_LINE_BYTES = 16384;
15641
+ var MAX_TRACE_PREVIEW_CHARS = 8e3;
15642
+ function createPiRpcTrace(input) {
15643
+ const filePath = join2(input.workspaceDir, "debug", "pi-rpc.jsonl");
15644
+ try {
15645
+ mkdirSync(dirname2(filePath), { recursive: true });
15646
+ const stream = createWriteStream(filePath, {
15647
+ flags: "a",
15648
+ mode: 384
15649
+ });
15650
+ return createStreamTrace({
15651
+ filePath,
15652
+ stream,
15653
+ taskId: input.taskId,
15654
+ now: input.now ?? (() => /* @__PURE__ */ new Date())
15655
+ });
15656
+ } catch {
15657
+ return createNoopTrace();
15658
+ }
15659
+ }
15660
+ function createStreamTrace(input) {
15661
+ let closed = false;
15662
+ let failed = false;
15663
+ input.stream.on("error", () => {
15664
+ failed = true;
15665
+ });
15666
+ return {
15667
+ filePath: input.filePath,
15668
+ write(event) {
15669
+ if (closed || failed) {
15670
+ return;
15671
+ }
15672
+ input.stream.write(serializeTraceRecord({
15673
+ ts: input.now().toISOString(),
15674
+ taskId: input.taskId,
15675
+ ...event
15676
+ }));
15677
+ },
15678
+ async close() {
15679
+ if (closed) {
15680
+ return;
15681
+ }
15682
+ closed = true;
15683
+ if (failed) {
15684
+ return;
15685
+ }
15686
+ input.stream.end();
15687
+ await once(input.stream, "finish").then(() => void 0, () => void 0);
15688
+ }
15689
+ };
15690
+ }
15691
+ function createNoopTrace() {
15692
+ return {
15693
+ write() {
15694
+ },
15695
+ async close() {
15696
+ }
15697
+ };
15698
+ }
15699
+ function serializeTraceRecord(record2) {
15700
+ const line = `${JSON.stringify(record2)}
15701
+ `;
15702
+ if (Buffer.byteLength(line, "utf8") <= MAX_TRACE_LINE_BYTES) {
15703
+ return line;
15704
+ }
15705
+ return `${JSON.stringify({
15706
+ ...record2,
15707
+ truncated: true,
15708
+ payload: previewPayload(record2.payload)
15709
+ })}
15710
+ `;
15711
+ }
15712
+ function previewPayload(payload) {
15713
+ const text = typeof payload === "string" ? payload : JSON.stringify(payload) ?? "undefined";
15714
+ if (text.length <= MAX_TRACE_PREVIEW_CHARS) {
15715
+ return text;
15716
+ }
15717
+ return `${text.slice(0, MAX_TRACE_PREVIEW_CHARS)}...[truncated]`;
15718
+ }
15719
+
15720
+ // src/harness-drivers/pi-rpc.ts
15635
15721
  var driverOwnedPiFlags = /* @__PURE__ */ new Set([
15636
15722
  "--mode",
15637
15723
  "--provider",
@@ -15689,7 +15775,8 @@ function buildPiRpcArgs(harness) {
15689
15775
  }
15690
15776
  function startPiRpcHarness(input, spawnProcess) {
15691
15777
  const harness = input.harness;
15692
- const child = spawnProcess(harness.command, buildPiRpcArgs(harness), {
15778
+ const args = buildPiRpcArgs(harness);
15779
+ const child = spawnProcess(harness.command, args, {
15693
15780
  cwd: input.spec.workspaceDir,
15694
15781
  env: input.env,
15695
15782
  stdio: ["pipe", "pipe", "pipe"]
@@ -15697,6 +15784,19 @@ function startPiRpcHarness(input, spawnProcess) {
15697
15784
  const sanitizer = createHarnessOutputSanitizer(input.env, [
15698
15785
  ...input.harness.secretEnv ?? []
15699
15786
  ]);
15787
+ const trace = createPiRpcTrace({
15788
+ taskId: input.spec.taskId,
15789
+ workspaceDir: input.spec.workspaceDir
15790
+ });
15791
+ trace.write({
15792
+ kind: "process.started",
15793
+ stream: "process",
15794
+ payload: {
15795
+ command: harness.command,
15796
+ args,
15797
+ cwd: input.spec.workspaceDir
15798
+ }
15799
+ });
15700
15800
  let cleanupTermination = () => void 0;
15701
15801
  let agentEndReached = false;
15702
15802
  let resolveAgentEnd = () => void 0;
@@ -15713,6 +15813,7 @@ function startPiRpcHarness(input, spawnProcess) {
15713
15813
  input,
15714
15814
  line,
15715
15815
  sanitizer,
15816
+ trace,
15716
15817
  onAgentEnd() {
15717
15818
  agentEndReached = true;
15718
15819
  resolveAgentEnd("agent-end");
@@ -15723,20 +15824,36 @@ function startPiRpcHarness(input, spawnProcess) {
15723
15824
  });
15724
15825
  });
15725
15826
  const stderrDone = forwardJsonlByteStream(child.stderr, (line) => {
15726
- forwardPiStderrLine(input, line, sanitizer);
15827
+ forwardPiStderrLine(input, line, sanitizer, trace);
15727
15828
  });
15728
- writePiRpcCommand(child, {
15829
+ const promptCommand = {
15729
15830
  id: `mote-${input.spec.taskId}`,
15730
15831
  type: "prompt",
15731
15832
  message: input.prompt
15833
+ };
15834
+ writePiRpcCommand(child, promptCommand);
15835
+ trace.write({
15836
+ kind: "prompt.sent",
15837
+ stream: "stdin",
15838
+ payload: {
15839
+ id: promptCommand.id,
15840
+ type: promptCommand.type,
15841
+ messageLength: input.prompt.length
15842
+ }
15732
15843
  });
15733
15844
  const result = (async () => {
15734
15845
  let terminal;
15735
15846
  try {
15736
15847
  terminal = await Promise.race([closePromise, agentEndPromise]);
15737
15848
  } catch (error48) {
15849
+ trace.write({
15850
+ kind: "process.error",
15851
+ stream: "process",
15852
+ payload: formatTraceError(error48)
15853
+ });
15738
15854
  cleanupTermination();
15739
15855
  cleanupTermination = terminateChildProcess(child);
15856
+ await trace.close();
15740
15857
  throw error48;
15741
15858
  }
15742
15859
  if (terminal === "agent-end") {
@@ -15745,6 +15862,12 @@ function startPiRpcHarness(input, spawnProcess) {
15745
15862
  const closeResult = await closePromise;
15746
15863
  cleanupTermination();
15747
15864
  await Promise.all([stdoutDone, stderrDone]);
15865
+ trace.write({
15866
+ kind: "process.closed",
15867
+ stream: "process",
15868
+ payload: closeResult
15869
+ });
15870
+ await trace.close();
15748
15871
  return {
15749
15872
  exitCode: closeResult.exitCode,
15750
15873
  signal: closeResult.signal,
@@ -15753,6 +15876,12 @@ function startPiRpcHarness(input, spawnProcess) {
15753
15876
  }
15754
15877
  cleanupTermination();
15755
15878
  await Promise.all([stdoutDone, stderrDone]);
15879
+ trace.write({
15880
+ kind: "process.closed",
15881
+ stream: "process",
15882
+ payload: terminal
15883
+ });
15884
+ await trace.close();
15756
15885
  if (!agentEndReached && terminal.exitCode !== 0) {
15757
15886
  forwardPiHarnessStreamLine(
15758
15887
  input,
@@ -15780,20 +15909,44 @@ function handlePiRpcStdoutLine(input) {
15780
15909
  try {
15781
15910
  parsed = JSON.parse(trimmed);
15782
15911
  } catch {
15783
- forwardPiHarnessStreamLine(input.input, "stdout", input.sanitizer.redactText(trimmed));
15912
+ const message = input.sanitizer.redactText(trimmed);
15913
+ input.trace.write({
15914
+ kind: "stdout.text",
15915
+ stream: "stdout",
15916
+ payload: {
15917
+ message
15918
+ }
15919
+ });
15920
+ forwardPiHarnessStreamLine(input.input, "stdout", message);
15784
15921
  return;
15785
15922
  }
15923
+ const redacted = input.sanitizer.redactJson(parsed);
15786
15924
  if (isPiRpcResponse(parsed)) {
15925
+ input.trace.write({
15926
+ kind: "stdout.response",
15927
+ stream: "stdout",
15928
+ payload: redacted
15929
+ });
15787
15930
  if (parsed.command === "prompt" && parsed.success === false) {
15788
15931
  input.onPromptRejected(new Error(`Pi RPC prompt was rejected: ${formatPiRpcError(parsed, input.sanitizer.redactText)}`));
15789
15932
  }
15790
15933
  return;
15791
15934
  }
15792
15935
  if (isPiExtensionUiRequest(parsed)) {
15793
- forwardPiExtensionUiRequest(input.input, input.sanitizer.redactJson(parsed));
15936
+ input.trace.write({
15937
+ kind: "stdout.extension_ui_request",
15938
+ stream: "stdout",
15939
+ payload: redacted
15940
+ });
15941
+ forwardPiExtensionUiRequest(input.input, redacted);
15794
15942
  return;
15795
15943
  }
15796
- forwardPiEvent(input.input, input.sanitizer.redactJson(parsed));
15944
+ input.trace.write({
15945
+ kind: "stdout.event",
15946
+ stream: "stdout",
15947
+ payload: redacted
15948
+ });
15949
+ forwardPiEvent(input.input, redacted);
15797
15950
  if (parsed.type === "agent_end") {
15798
15951
  input.onAgentEnd();
15799
15952
  }
@@ -15828,8 +15981,16 @@ function forwardPiHarnessStreamLine(input, stream, message) {
15828
15981
  message: trimmed
15829
15982
  }));
15830
15983
  }
15831
- function forwardPiStderrLine(input, line, sanitizer) {
15832
- forwardPiHarnessStreamLine(input, "stderr", sanitizer.redactText(line));
15984
+ function forwardPiStderrLine(input, line, sanitizer, trace) {
15985
+ const message = sanitizer.redactText(line);
15986
+ trace.write({
15987
+ kind: "stderr.line",
15988
+ stream: "stderr",
15989
+ payload: {
15990
+ message
15991
+ }
15992
+ });
15993
+ forwardPiHarnessStreamLine(input, "stderr", message);
15833
15994
  }
15834
15995
  function writePiRpcCommand(child, command) {
15835
15996
  child.stdin.write(`${JSON.stringify(command)}
@@ -15868,6 +16029,17 @@ function formatPiRpcError(parsed, redactText) {
15868
16029
  }
15869
16030
  return "unknown error";
15870
16031
  }
16032
+ function formatTraceError(error48) {
16033
+ if (error48 instanceof Error) {
16034
+ return {
16035
+ name: error48.name,
16036
+ message: error48.message
16037
+ };
16038
+ }
16039
+ return {
16040
+ message: String(error48)
16041
+ };
16042
+ }
15871
16043
 
15872
16044
  // src/prompt.ts
15873
16045
  import { existsSync, readFileSync } from "node:fs";