@mduan/paseo-cli 0.11.0-fork.1 → 0.11.0-fork.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.
Files changed (44) hide show
  1. package/node_modules/@getpaseo/client/package.json +3 -3
  2. package/node_modules/@getpaseo/highlight/package.json +1 -1
  3. package/node_modules/@getpaseo/plugin/package.json +3 -3
  4. package/node_modules/@getpaseo/protocol/dist/agent-types.d.ts +2 -0
  5. package/node_modules/@getpaseo/protocol/dist/generated/validation/ws-outbound.aot.js +106 -1
  6. package/node_modules/@getpaseo/protocol/dist/messages.js +3 -0
  7. package/node_modules/@getpaseo/protocol/package.json +1 -1
  8. package/node_modules/@getpaseo/relay/package.json +1 -1
  9. package/node_modules/@getpaseo/server/dist/server/builtin-plugins/package.json +1 -1
  10. package/node_modules/@getpaseo/server/dist/server/server/agent/agent-manager.d.ts +2 -2
  11. package/node_modules/@getpaseo/server/dist/server/server/agent/agent-projections.js +3 -0
  12. package/node_modules/@getpaseo/server/dist/server/server/agent/agent-sdk-types.d.ts +3 -1
  13. package/node_modules/@getpaseo/server/dist/server/server/agent/agent-storage.d.ts +1 -0
  14. package/node_modules/@getpaseo/server/dist/server/server/agent/agent-storage.js +1 -0
  15. package/node_modules/@getpaseo/server/dist/server/server/agent/provider-subagents/subtitle-format.d.ts +5 -0
  16. package/node_modules/@getpaseo/server/dist/server/server/agent/provider-subagents/subtitle-format.js +16 -0
  17. package/node_modules/@getpaseo/server/dist/server/server/agent/providers/appended-lines-reader.d.ts +20 -0
  18. package/node_modules/@getpaseo/server/dist/server/server/agent/providers/appended-lines-reader.js +59 -0
  19. package/node_modules/@getpaseo/server/dist/server/server/agent/providers/claude/agent.js +61 -2
  20. package/node_modules/@getpaseo/server/dist/server/server/agent/providers/claude/subagents/live-source.d.ts +2 -0
  21. package/node_modules/@getpaseo/server/dist/server/server/agent/providers/claude/subagents/live-source.js +7 -0
  22. package/node_modules/@getpaseo/server/dist/server/server/agent/providers/claude/subagents/presentation.d.ts +2 -0
  23. package/node_modules/@getpaseo/server/dist/server/server/agent/providers/claude/subagents/presentation.js +3 -8
  24. package/node_modules/@getpaseo/server/dist/server/server/agent/providers/claude/subagents/transcript-cost.d.ts +18 -0
  25. package/node_modules/@getpaseo/server/dist/server/server/agent/providers/claude/subagents/transcript-cost.js +50 -0
  26. package/node_modules/@getpaseo/server/dist/server/server/agent/providers/codex/pricing.d.ts +15 -1
  27. package/node_modules/@getpaseo/server/dist/server/server/agent/providers/codex/pricing.js +32 -1
  28. package/node_modules/@getpaseo/server/dist/server/server/agent/providers/codex/rollout-cost.d.ts +4 -8
  29. package/node_modules/@getpaseo/server/dist/server/server/agent/providers/codex/rollout-cost.js +11 -51
  30. package/node_modules/@getpaseo/server/dist/server/server/agent/providers/codex-app-server-agent.d.ts +5 -1
  31. package/node_modules/@getpaseo/server/dist/server/server/agent/providers/codex-app-server-agent.js +82 -11
  32. package/node_modules/@getpaseo/server/dist/server/server/agent/turn-diffs/snapshot.d.ts +14 -0
  33. package/node_modules/@getpaseo/server/dist/server/server/agent/turn-diffs/snapshot.js +91 -4
  34. package/node_modules/@getpaseo/server/dist/server/server/agent/turn-diffs/store.d.ts +3 -3
  35. package/node_modules/@getpaseo/server/dist/server/server/agent/turn-diffs/store.js +24 -18
  36. package/node_modules/@getpaseo/server/dist/server/web-ui/_expo/static/js/web/{index-9489c4e1587c4252f0bd3b6e4b1abab3.js → index-23bdbee37d31a9422c12f2a801979611.js} +21 -21
  37. package/node_modules/@getpaseo/server/dist/server/web-ui/_expo/static/js/web/index-23bdbee37d31a9422c12f2a801979611.js.br +0 -0
  38. package/node_modules/@getpaseo/server/dist/server/web-ui/_expo/static/js/web/{index-9489c4e1587c4252f0bd3b6e4b1abab3.js.gz → index-23bdbee37d31a9422c12f2a801979611.js.gz} +0 -0
  39. package/node_modules/@getpaseo/server/dist/server/web-ui/index.html +1 -1
  40. package/node_modules/@getpaseo/server/dist/server/web-ui/index.html.br +0 -0
  41. package/node_modules/@getpaseo/server/dist/server/web-ui/index.html.gz +0 -0
  42. package/node_modules/@getpaseo/server/package.json +6 -6
  43. package/package.json +7 -7
  44. package/node_modules/@getpaseo/server/dist/server/web-ui/_expo/static/js/web/index-9489c4e1587c4252f0bd3b6e4b1abab3.js.br +0 -0
@@ -15,10 +15,14 @@ async function git(args, options) {
15
15
  }
16
16
  async function resolveRepo(cwd) {
17
17
  try {
18
- const [repoRoot, indexPath] = (await git(["rev-parse", "--show-toplevel", "--git-path", "index"], { cwd }))
18
+ const [repoRoot, indexPath, headLogPath] = (await git(["rev-parse", "--show-toplevel", "--git-path", "index", "--git-path", "logs/HEAD"], { cwd }))
19
19
  .trim()
20
20
  .split("\n");
21
- return { repoRoot, indexPath: path.resolve(cwd, indexPath) };
21
+ return {
22
+ repoRoot,
23
+ indexPath: path.resolve(cwd, indexPath),
24
+ headLogPath: path.resolve(cwd, headLogPath),
25
+ };
22
26
  }
23
27
  catch {
24
28
  // Not a git repository (or git is missing): turn diffs are git-only.
@@ -43,6 +47,19 @@ export async function snapshotWorkingTree(cwd) {
43
47
  const repo = await resolveRepo(cwd);
44
48
  if (!repo)
45
49
  return null;
50
+ return { repoRoot: repo.repoRoot, tree: await writeWorkingTree(repo) };
51
+ }
52
+ /** A working tree snapshot plus where HEAD and its reflog stood, to separate out branch moves. */
53
+ export async function snapshotTurnStart(cwd) {
54
+ const repo = await resolveRepo(cwd);
55
+ if (!repo)
56
+ return null;
57
+ const headLogSize = await fs.stat(repo.headLogPath).then((stat) => stat.size, () => 0);
58
+ const head = await readHead(repo.repoRoot);
59
+ const tree = await writeWorkingTree(repo);
60
+ return { repoRoot: repo.repoRoot, tree, head, headLogPath: repo.headLogPath, headLogSize };
61
+ }
62
+ async function writeWorkingTree(repo) {
46
63
  const tempIndex = path.join(os.tmpdir(), `paseo-turn-index-${randomUUID()}`);
47
64
  try {
48
65
  const stat = await fs.stat(repo.indexPath).catch(() => null);
@@ -61,13 +78,83 @@ export async function snapshotWorkingTree(cwd) {
61
78
  input: untracked.map((file) => `${file}\0`).join(""),
62
79
  });
63
80
  }
64
- const tree = (await git(["write-tree"], { cwd: repo.repoRoot, env })).trim();
65
- return { repoRoot: repo.repoRoot, tree };
81
+ return (await git(["write-tree"], { cwd: repo.repoRoot, env })).trim();
66
82
  }
67
83
  finally {
68
84
  await fs.rm(tempIndex, { force: true });
69
85
  }
70
86
  }
87
+ async function readHead(repoRoot) {
88
+ try {
89
+ return (await git(["rev-parse", "--verify", "-q", "HEAD"], { cwd: repoRoot })).trim();
90
+ }
91
+ catch {
92
+ return undefined;
93
+ }
94
+ }
95
+ async function readFileFrom(filePath, offset) {
96
+ const handle = await fs.open(filePath, "r").catch(() => null);
97
+ if (!handle)
98
+ return undefined;
99
+ try {
100
+ const { size } = await handle.stat();
101
+ if (size < offset)
102
+ return undefined;
103
+ const buffer = Buffer.alloc(size - offset);
104
+ await handle.read(buffer, 0, buffer.length, offset);
105
+ return buffer.toString("utf8");
106
+ }
107
+ finally {
108
+ await handle.close();
109
+ }
110
+ }
111
+ const OWN_COMMIT_MESSAGE = /^commit( \((initial|amend)\))?: /;
112
+ /**
113
+ * The commit HEAD landed on after the turn's last branch move (checkout, reset, pull, merge,
114
+ * rebase, cherry-pick), read from the reflog lines the turn appended. Undefined when HEAD only
115
+ * moved by the agent's own commits; null when the reflog can't tell.
116
+ */
117
+ async function findLastBranchMove(start) {
118
+ const appended = await readFileFrom(start.headLogPath, start.headLogSize);
119
+ if (appended === undefined)
120
+ return null;
121
+ let landing;
122
+ for (const line of appended.split("\n")) {
123
+ // <old> <new> <identity> <time> <tz>\t<message>
124
+ const [header, message = ""] = line.split("\t", 2);
125
+ const [oldHead, newHead] = header.split(" ");
126
+ if (!newHead || oldHead === newHead || OWN_COMMIT_MESSAGE.test(message))
127
+ continue;
128
+ landing = newHead;
129
+ }
130
+ return landing;
131
+ }
132
+ /**
133
+ * The tree to diff a turn from. When the turn moved HEAD to another commit, the start snapshot's
134
+ * uncommitted changes are replayed onto where HEAD landed, so the diff holds the agent's edits and
135
+ * commits and none of the content the move brought in. Undefined when that can't be worked out
136
+ * (no reflog, unborn branch, replay conflict); the turn then gets no diff.
137
+ */
138
+ export async function resolveTurnFromTree(start) {
139
+ const head = await readHead(start.repoRoot);
140
+ if (head === start.head)
141
+ return start.tree;
142
+ if (!head || !start.head)
143
+ return undefined;
144
+ const landing = await findLastBranchMove(start);
145
+ if (landing === null)
146
+ return undefined;
147
+ if (landing === undefined || landing === start.head)
148
+ return start.tree;
149
+ try {
150
+ const output = await git(["merge-tree", "--write-tree", `--merge-base=${start.head}`, landing, start.tree], { cwd: start.repoRoot });
151
+ return output.split("\n")[0];
152
+ }
153
+ catch {
154
+ // Exit code 1: the start's uncommitted changes conflict with where HEAD landed.
155
+ return undefined;
156
+ }
157
+ }
71
158
  export async function diffTreeStats(params) {
72
159
  if (params.fromTree === params.toTree)
73
160
  return [];
@@ -1,6 +1,6 @@
1
1
  import type { Logger } from "pino";
2
2
  import { type AgentTurnDiffTarget, type TurnDiffSummary } from "../../messages.js";
3
- import { type WorkingTreeSnapshot } from "./snapshot.js";
3
+ import { type TurnStartSnapshot } from "./snapshot.js";
4
4
  export interface TurnDiffRange {
5
5
  repoRoot: string;
6
6
  fromTree: string;
@@ -8,7 +8,7 @@ export interface TurnDiffRange {
8
8
  }
9
9
  export interface RecordTurnInput {
10
10
  agentId: string;
11
- start: WorkingTreeSnapshot;
11
+ start: TurnStartSnapshot;
12
12
  turnId: string;
13
13
  userMessageIds: string[];
14
14
  }
@@ -21,7 +21,7 @@ export declare class TurnDiffStore {
21
21
  private readonly logger;
22
22
  private readonly queues;
23
23
  constructor(dir: string, logger: Logger);
24
- captureStart(cwd: string): Promise<WorkingTreeSnapshot | null>;
24
+ captureStart(cwd: string): Promise<TurnStartSnapshot | null>;
25
25
  recordTurn(input: RecordTurnInput): Promise<void>;
26
26
  list(agentId: string): Promise<TurnDiffSummary[]>;
27
27
  resolveRange(agentId: string, target: AgentTurnDiffTarget): Promise<TurnDiffRange | null>;
@@ -4,7 +4,7 @@ import { z } from "zod";
4
4
  import { withTimeout } from "../../../utils/promise-timeout.js";
5
5
  import { writeJsonFileAtomic } from "../../atomic-file.js";
6
6
  import { TurnDiffFileStatSchema, } from "../../messages.js";
7
- import { diffTreeStats, snapshotWorkingTree, syncTreeRefs, } from "./snapshot.js";
7
+ import { diffTreeStats, resolveTurnFromTree, snapshotTurnStart, snapshotWorkingTree, syncTreeRefs, } from "./snapshot.js";
8
8
  // The prompt waits for the start snapshot; past this, the turn goes without a diff.
9
9
  const START_SNAPSHOT_TIMEOUT_MS = 2000;
10
10
  const MAX_STORED_TURNS = 200;
@@ -17,15 +17,18 @@ const StoredTurnSchema = TreeRangeSchema.extend({
17
17
  });
18
18
  const TurnDiffRecordSchema = z.object({
19
19
  repoRoot: z.string(),
20
- // First snapshot of the chat; "Chat Session" diffs from here. Never trimmed.
21
- sessionFromTree: z.string(),
22
- // Most recent finished turn, kept even when it changed nothing.
23
- lastTurn: TreeRangeSchema,
20
+ // Most recent finished turn, kept even when it changed nothing. Absent when the turn moved HEAD
21
+ // in a way its diff couldn't be separated from.
22
+ lastTurn: TreeRangeSchema.optional(),
24
23
  // Turns that changed files, oldest first.
25
24
  turns: z.array(StoredTurnSchema),
26
25
  });
27
26
  function referencedTrees(record) {
28
- const trees = new Set([record.sessionFromTree, record.lastTurn.fromTree, record.lastTurn.toTree]);
27
+ const trees = new Set();
28
+ if (record.lastTurn) {
29
+ trees.add(record.lastTurn.fromTree);
30
+ trees.add(record.lastTurn.toTree);
31
+ }
29
32
  for (const turn of record.turns) {
30
33
  trees.add(turn.fromTree);
31
34
  trees.add(turn.toTree);
@@ -53,7 +56,7 @@ export class TurnDiffStore {
53
56
  async captureStart(cwd) {
54
57
  try {
55
58
  return await withTimeout({
56
- promise: snapshotWorkingTree(cwd),
59
+ promise: snapshotTurnStart(cwd),
57
60
  timeoutMs: START_SNAPSHOT_TIMEOUT_MS,
58
61
  label: "Turn start snapshot",
59
62
  });
@@ -77,9 +80,11 @@ export class TurnDiffStore {
77
80
  const { repoRoot } = record;
78
81
  switch (target.kind) {
79
82
  case "last_turn":
80
- return { repoRoot, ...record.lastTurn };
83
+ return record.lastTurn ? { repoRoot, ...record.lastTurn } : null;
84
+ // COMPAT(turnDiffSession): added in v0.11.0-beta.3, remove after 2027-01-04. Older clients
85
+ // can still ask for the removed Chat Session diff; it is always unavailable.
81
86
  case "session":
82
- return { repoRoot, fromTree: record.sessionFromTree, toTree: record.lastTurn.toTree };
87
+ return null;
83
88
  case "turn": {
84
89
  const turn = record.turns.find((candidate) => candidate.turnId === target.turnId);
85
90
  return turn ? { repoRoot, fromTree: turn.fromTree, toTree: turn.toTree } : null;
@@ -99,26 +104,27 @@ export class TurnDiffStore {
99
104
  const end = await snapshotWorkingTree(start.repoRoot);
100
105
  if (!end)
101
106
  return;
102
- const files = await diffTreeStats({
103
- repoRoot: start.repoRoot,
104
- fromTree: start.tree,
105
- toTree: end.tree,
106
- });
107
+ const fromTree = await resolveTurnFromTree(start);
108
+ if (!fromTree) {
109
+ this.logger.info({ agentId, turnId }, "Turn moved HEAD in a way its diff can't separate");
110
+ }
111
+ const files = fromTree
112
+ ? await diffTreeStats({ repoRoot: start.repoRoot, fromTree, toTree: end.tree })
113
+ : [];
107
114
  const previous = await this.read(agentId);
108
115
  const isSameRepo = previous?.repoRoot === start.repoRoot;
109
116
  const record = {
110
117
  repoRoot: start.repoRoot,
111
- sessionFromTree: isSameRepo ? previous.sessionFromTree : start.tree,
112
- lastTurn: { fromTree: start.tree, toTree: end.tree },
118
+ lastTurn: fromTree ? { fromTree, toTree: end.tree } : undefined,
113
119
  turns: isSameRepo ? previous.turns : [],
114
120
  };
115
- if (files.length > 0) {
121
+ if (fromTree && files.length > 0) {
116
122
  const completedAt = new Date().toISOString();
117
123
  const turn = {
118
124
  turnId,
119
125
  userMessageIds,
120
126
  completedAt,
121
- fromTree: start.tree,
127
+ fromTree,
122
128
  toTree: end.tree,
123
129
  files,
124
130
  };