@prestyj/cli 5.6.0 → 5.7.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.
Files changed (100) hide show
  1. package/dist/app-sidecar.js +127 -39
  2. package/dist/app-sidecar.js.map +1 -1
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +15 -0
  5. package/dist/cli.js.map +1 -1
  6. package/dist/core/agent-session-review-coverage.test.d.ts +2 -0
  7. package/dist/core/agent-session-review-coverage.test.d.ts.map +1 -0
  8. package/dist/core/agent-session-review-coverage.test.js +57 -0
  9. package/dist/core/agent-session-review-coverage.test.js.map +1 -0
  10. package/dist/core/agent-session.d.ts +11 -4
  11. package/dist/core/agent-session.d.ts.map +1 -1
  12. package/dist/core/agent-session.js +133 -22
  13. package/dist/core/agent-session.js.map +1 -1
  14. package/dist/core/compaction/compactor.test.js +1 -1
  15. package/dist/core/compaction/compactor.test.js.map +1 -1
  16. package/dist/core/event-bus.d.ts +2 -0
  17. package/dist/core/event-bus.d.ts.map +1 -1
  18. package/dist/core/event-bus.js.map +1 -1
  19. package/dist/core/ideal-review.d.ts +27 -0
  20. package/dist/core/ideal-review.d.ts.map +1 -1
  21. package/dist/core/ideal-review.js +64 -0
  22. package/dist/core/ideal-review.js.map +1 -1
  23. package/dist/core/ideal-review.test.js +34 -1
  24. package/dist/core/ideal-review.test.js.map +1 -1
  25. package/dist/core/lsp/client.d.ts +3 -0
  26. package/dist/core/lsp/client.d.ts.map +1 -1
  27. package/dist/core/lsp/client.js +20 -0
  28. package/dist/core/lsp/client.js.map +1 -1
  29. package/dist/core/lsp/manager.d.ts +32 -9
  30. package/dist/core/lsp/manager.d.ts.map +1 -1
  31. package/dist/core/lsp/manager.js +89 -37
  32. package/dist/core/lsp/manager.js.map +1 -1
  33. package/dist/core/lsp/manager.test.js +56 -4
  34. package/dist/core/lsp/manager.test.js.map +1 -1
  35. package/dist/core/run-lifecycle.d.ts +40 -0
  36. package/dist/core/run-lifecycle.d.ts.map +1 -0
  37. package/dist/core/run-lifecycle.js +95 -0
  38. package/dist/core/run-lifecycle.js.map +1 -0
  39. package/dist/core/run-lifecycle.test.d.ts +2 -0
  40. package/dist/core/run-lifecycle.test.d.ts.map +1 -0
  41. package/dist/core/run-lifecycle.test.js +64 -0
  42. package/dist/core/run-lifecycle.test.js.map +1 -0
  43. package/dist/core/session-manager.d.ts +25 -1
  44. package/dist/core/session-manager.d.ts.map +1 -1
  45. package/dist/core/session-manager.js +66 -1
  46. package/dist/core/session-manager.js.map +1 -1
  47. package/dist/core/session-manager.test.js +80 -2
  48. package/dist/core/session-manager.test.js.map +1 -1
  49. package/dist/core/subagent-manager.d.ts +40 -3
  50. package/dist/core/subagent-manager.d.ts.map +1 -1
  51. package/dist/core/subagent-manager.js +342 -30
  52. package/dist/core/subagent-manager.js.map +1 -1
  53. package/dist/core/subagent-manager.test.js +189 -3
  54. package/dist/core/subagent-manager.test.js.map +1 -1
  55. package/dist/core/subagent-store.d.ts +20 -0
  56. package/dist/core/subagent-store.d.ts.map +1 -0
  57. package/dist/core/subagent-store.js +177 -0
  58. package/dist/core/subagent-store.js.map +1 -0
  59. package/dist/core/subagent-store.test.d.ts +2 -0
  60. package/dist/core/subagent-store.test.d.ts.map +1 -0
  61. package/dist/core/subagent-store.test.js +94 -0
  62. package/dist/core/subagent-store.test.js.map +1 -0
  63. package/dist/modes/agent-home-mode.d.ts.map +1 -1
  64. package/dist/modes/agent-home-mode.js +10 -3
  65. package/dist/modes/agent-home-mode.js.map +1 -1
  66. package/dist/modes/serve-mode.d.ts.map +1 -1
  67. package/dist/modes/serve-mode.js +10 -3
  68. package/dist/modes/serve-mode.js.map +1 -1
  69. package/dist/modes/subagent-worker-mode.d.ts +2 -0
  70. package/dist/modes/subagent-worker-mode.d.ts.map +1 -1
  71. package/dist/modes/subagent-worker-mode.js +13 -3
  72. package/dist/modes/subagent-worker-mode.js.map +1 -1
  73. package/dist/tools/goals.d.ts +3 -3
  74. package/dist/tools/tasks.d.ts +2 -2
  75. package/dist/tools/web-fetch.d.ts +1 -1
  76. package/dist/ui/App.d.ts +7 -1
  77. package/dist/ui/App.d.ts.map +1 -1
  78. package/dist/ui/App.js +63 -17
  79. package/dist/ui/App.js.map +1 -1
  80. package/dist/ui/components/Footer.d.ts +1 -0
  81. package/dist/ui/components/Footer.d.ts.map +1 -1
  82. package/dist/ui/components/Footer.js +3 -3
  83. package/dist/ui/components/Footer.js.map +1 -1
  84. package/dist/ui/components/Footer.test.d.ts +2 -0
  85. package/dist/ui/components/Footer.test.d.ts.map +1 -0
  86. package/dist/ui/components/Footer.test.js +16 -0
  87. package/dist/ui/components/Footer.test.js.map +1 -0
  88. package/dist/ui/hooks/useAgentLoop.d.ts +8 -3
  89. package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
  90. package/dist/ui/hooks/useAgentLoop.js +63 -7
  91. package/dist/ui/hooks/useAgentLoop.js.map +1 -1
  92. package/dist/ui/hooks/useSessionPersistence.d.ts +4 -2
  93. package/dist/ui/hooks/useSessionPersistence.d.ts.map +1 -1
  94. package/dist/ui/hooks/useSessionPersistence.js +7 -1
  95. package/dist/ui/hooks/useSessionPersistence.js.map +1 -1
  96. package/dist/ui/render.d.ts +7 -0
  97. package/dist/ui/render.d.ts.map +1 -1
  98. package/dist/ui/render.js +4 -0
  99. package/dist/ui/render.js.map +1 -1
  100. package/package.json +5 -5
package/dist/ui/App.js CHANGED
@@ -13,6 +13,7 @@ import { useTaskBarStore, useTaskBarPolling, focusTaskBar, exitTaskBar, expandTa
13
13
  import { playNotificationSound } from "../utils/sound.js";
14
14
  import {} from "@prestyj/ai";
15
15
  import { downscaleForPreview, extractMediaPaths } from "../utils/image.js";
16
+ import { buildSubAgentCompletionFollowUp, } from "../core/subagent-manager.js";
16
17
  import { useAgentLoop } from "./hooks/useAgentLoop.js";
17
18
  import { useTranscriptHistory } from "./hooks/useTranscriptHistory.js";
18
19
  import { createWebSearchTool } from "../tools/web-search.js";
@@ -395,6 +396,7 @@ export function App(props) {
395
396
  const sessionManagerRef = useRef(props.sessionsDir ? new SessionManager(props.sessionsDir) : null);
396
397
  const sessionPathRef = useRef(props.sessionStore?.sessionPath ?? props.sessionPath);
397
398
  const persistedIndexRef = useRef(messagesRef.current.length);
399
+ const turnMetricsRef = useRef(props.sessionStore?.turnMetrics ?? []);
398
400
  const sessionStatsRef = useRef(props.sessionStore?.sessionStats ??
399
401
  createSessionStats({ sessionId: props.sessionStore?.sessionId ?? props.sessionId }));
400
402
  const [idealReviewEnabled, setIdealReviewEnabled] = useState(props.sessionStore?.idealReviewEnabled ?? props.idealReviewEnabled ?? true);
@@ -609,9 +611,9 @@ export function App(props) {
609
611
  .map((key) => props.credentialsByProvider?.[key])
610
612
  .find((c) => c !== undefined);
611
613
  const activeApiKey = currentCreds?.accessToken ?? props.apiKey;
612
- const activeAccountId = currentCreds?.accountId ?? props.accountId;
613
- const activeProjectId = currentCreds?.projectId ?? props.projectId;
614
- const activeBaseUrl = currentProvider === "gemini" ? undefined : (currentCreds?.baseUrl ?? props.baseUrl);
614
+ const activeAccountId = currentCreds ? currentCreds.accountId : props.accountId;
615
+ const activeProjectId = currentCreds ? currentCreds.projectId : props.projectId;
616
+ const activeBaseUrl = currentProvider === "gemini" ? undefined : currentCreds ? currentCreds.baseUrl : props.baseUrl;
615
617
  const contextWindowOptions = useMemo(() => ({ provider: currentProvider, accountId: activeAccountId }), [currentProvider, activeAccountId]);
616
618
  // Load git branch — re-runs whenever the displayed cwd changes (e.g. when
617
619
  // a pixel fix moves the agent into a different project root).
@@ -790,16 +792,19 @@ export function App(props) {
790
792
  useEffect(() => {
791
793
  void applyLanguageDetectionRef.current("initial");
792
794
  }, []);
795
+ const rebindSubagentsAfterCompaction = useCallback((sessionId) => props.subAgentManager?.rebindParentSession(sessionId) ?? Promise.resolve(), [props.subAgentManager]);
793
796
  const { persistCompactedSession, persistNewMessages } = useSessionPersistence({
794
797
  sessionManagerRef,
795
798
  sessionPathRef,
796
799
  sessionStatsRef,
797
800
  persistedIndexRef,
798
801
  messagesRef,
802
+ turnMetricsRef,
799
803
  cwdRef,
800
804
  currentProvider,
801
805
  currentModel,
802
806
  sessionStore,
807
+ onCompactedSession: rebindSubagentsAfterCompaction,
803
808
  });
804
809
  /**
805
810
  * Run the language detector against the current cwd. If the detected set is a
@@ -904,6 +909,8 @@ export function App(props) {
904
909
  projectId: activeProjectId,
905
910
  resolveCredentials,
906
911
  transformContext,
912
+ lspManager: props.lspManager,
913
+ reviewCoverageTracker: props.reviewCoverageTracker,
907
914
  getIdealReviewMessage: (stats, touchedFiles) => {
908
915
  if (!idealReviewEnabledRef.current)
909
916
  return null;
@@ -1490,14 +1497,37 @@ export function App(props) {
1490
1497
  return remaining;
1491
1498
  });
1492
1499
  }, [queueFlush]),
1493
- onTurnEnd: useCallback((turn, stopReason, usage) => {
1500
+ onTurnEnd: useCallback((turn, stopReason, usage, timing) => {
1494
1501
  recordTurnEnd(sessionStatsRef.current, usage);
1502
+ const metric = {
1503
+ version: 1,
1504
+ turn,
1505
+ provider: currentProvider,
1506
+ model: currentModel,
1507
+ stopReason,
1508
+ usage: { ...usage },
1509
+ timing: { ...timing },
1510
+ cost: {
1511
+ status: "unavailable",
1512
+ reason: "No authoritative effective-dated provider pricing is available",
1513
+ },
1514
+ };
1515
+ turnMetricsRef.current.push(metric);
1516
+ if (sessionStore)
1517
+ sessionStore.turnMetrics = [...turnMetricsRef.current];
1518
+ const metricSessionPath = sessionPathRef.current;
1519
+ const metricManager = sessionManagerRef.current;
1520
+ if (metricSessionPath && metricManager) {
1521
+ void metricManager.appendTurnMetric(metricSessionPath, metric);
1522
+ }
1495
1523
  log("INFO", "turn", `Turn ${turn} ended`, {
1496
1524
  stopReason,
1497
1525
  inputTokens: String(usage.inputTokens),
1498
1526
  outputTokens: String(usage.outputTokens),
1499
1527
  ...(usage.cacheRead != null && { cacheRead: String(usage.cacheRead) }),
1500
1528
  ...(usage.cacheWrite != null && { cacheWrite: String(usage.cacheWrite) }),
1529
+ providerDurationMs: String(timing.providerDurationMs),
1530
+ ...(timing.ttftMs != null && { ttftMs: String(timing.ttftMs) }),
1501
1531
  });
1502
1532
  // Track actual token count for compaction decisions.
1503
1533
  // Anthropic has separate input/output limits — only count input.
@@ -1515,7 +1545,7 @@ export function App(props) {
1515
1545
  }
1516
1546
  return remaining;
1517
1547
  });
1518
- }, [queueFlush]),
1548
+ }, [currentModel, currentProvider, queueFlush, sessionStore]),
1519
1549
  onDone: useCallback((durationMs, toolsUsed, runStats) => {
1520
1550
  log("INFO", "agent", `Agent done`, {
1521
1551
  duration: `${durationMs}ms`,
@@ -1688,6 +1718,9 @@ export function App(props) {
1688
1718
  // natural completion boundary regardless. The stuck-guard caps
1689
1719
  // nudges per step so a genuinely blocked agent surfaces.
1690
1720
  getFollowUpMessages: useCallback(() => {
1721
+ const childCompletionFollowUp = buildSubAgentCompletionFollowUp(props.subAgentManager);
1722
+ if (childCompletionFollowUp)
1723
+ return childCompletionFollowUp;
1691
1724
  const steps = planStepsRef.current;
1692
1725
  if (steps.length === 0 || !approvedPlanPathRef.current)
1693
1726
  return null;
@@ -1711,7 +1744,7 @@ export function App(props) {
1711
1744
  `or you genuinely need user input.`,
1712
1745
  },
1713
1746
  ];
1714
- }, []),
1747
+ }, [props.subAgentManager]),
1715
1748
  onRetry: useCallback(() => {
1716
1749
  // Roll back any pending progressive flushes from the aborted attempt.
1717
1750
  // Without this, a stall retry regenerates the preamble and the old
@@ -2786,6 +2819,10 @@ export function App(props) {
2786
2819
  try {
2787
2820
  const session = await sm.create(taskCwd, currentProvider, currentModel);
2788
2821
  newSessionPath = session.path;
2822
+ sessionStatsRef.current.sessionId = session.id;
2823
+ if (props.sessionStore)
2824
+ props.sessionStore.sessionId = session.id;
2825
+ await props.subAgentManager?.resetParentSession(session.id);
2789
2826
  log("INFO", "tasks", "New session for task", { path: session.path });
2790
2827
  }
2791
2828
  catch {
@@ -2811,22 +2848,27 @@ export function App(props) {
2811
2848
  agentLoop.reset();
2812
2849
  persistedIndexRef.current = messagesRef.current.length;
2813
2850
  const sm = sessionManagerRef.current;
2814
- if (sm) {
2815
- void sm.create(taskCwd, currentProvider, currentModel).then((session) => {
2816
- sessionPathRef.current = session.path;
2817
- log("INFO", "tasks", "New session for task", { path: session.path });
2818
- });
2819
- }
2820
2851
  const taskItem = { kind: "task", title, id: getId() };
2821
2852
  setLastUserMessage(title);
2822
2853
  setDoneStatus(null);
2823
2854
  setLiveItems([taskItem]);
2824
- void agentLoop.run(fullPrompt).catch((err) => {
2825
- if (agentLoop.isRunning) {
2826
- agentLoop.reset();
2855
+ void (async () => {
2856
+ try {
2857
+ if (sm) {
2858
+ const session = await sm.create(taskCwd, currentProvider, currentModel);
2859
+ sessionPathRef.current = session.path;
2860
+ sessionStatsRef.current.sessionId = session.id;
2861
+ await props.subAgentManager?.resetParentSession(session.id);
2862
+ log("INFO", "tasks", "New session for task", { path: session.path });
2863
+ }
2864
+ await agentLoop.run(fullPrompt);
2827
2865
  }
2828
- setLiveItems((prev) => [...prev, toErrorItem(err, getId())]);
2829
- });
2866
+ catch (err) {
2867
+ if (agentLoop.isRunning)
2868
+ agentLoop.reset();
2869
+ setLiveItems((prev) => [...prev, toErrorItem(err, getId())]);
2870
+ }
2871
+ })();
2830
2872
  }, [agentLoop, currentModel, currentProvider, props]);
2831
2873
  // Keep the ref in sync so stale closures (task picker, onDone run-all) can
2832
2874
  // start a task without being recreated each render.
@@ -3408,6 +3450,10 @@ export function App(props) {
3408
3450
  if (sm) {
3409
3451
  const s = await sm.create(props.cwd, currentProvider, currentModel);
3410
3452
  newSessionPath = s.path;
3453
+ sessionStatsRef.current.sessionId = s.id;
3454
+ if (props.sessionStore)
3455
+ props.sessionStore.sessionId = s.id;
3456
+ await props.subAgentManager?.resetParentSession(s.id);
3411
3457
  }
3412
3458
  if (props.resetUI && props.sessionStore) {
3413
3459
  // Clear the overlay so the new mount lands on the chat,