@pi-agents/orchid 0.1.0-beta.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 (163) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/LICENSE +21 -0
  3. package/README.md +246 -0
  4. package/agents/AGENTS-MANIFEST.md +42 -0
  5. package/agents/brain.md +42 -0
  6. package/agents/context-builder.md +46 -0
  7. package/agents/delegate.md +12 -0
  8. package/agents/dev-1.md +42 -0
  9. package/agents/oracle.md +73 -0
  10. package/agents/planner.md +55 -0
  11. package/agents/researcher.md +52 -0
  12. package/agents/reviewer.md +79 -0
  13. package/agents/scout.md +50 -0
  14. package/agents/tester.md +45 -0
  15. package/agents/worker.md +55 -0
  16. package/extensions/ralph.ts +1 -0
  17. package/extensions/reviewer-extension.ts +125 -0
  18. package/extensions/task-orchestrator.ts +28 -0
  19. package/package.json +63 -0
  20. package/prompts/gather-context-and-clarify.md +13 -0
  21. package/prompts/parallel-cleanup.md +59 -0
  22. package/prompts/parallel-context-build.md +53 -0
  23. package/prompts/parallel-handoff-plan.md +59 -0
  24. package/prompts/parallel-research.md +50 -0
  25. package/prompts/parallel-review.md +54 -0
  26. package/prompts/review-loop.md +41 -0
  27. package/skills/orchid/SKILL.md +214 -0
  28. package/skills/orchid/orchid-cleanup/SKILL.md +122 -0
  29. package/skills/orchid/orchid-converge/SKILL.md +124 -0
  30. package/skills/orchid/orchid-decompose/SKILL.md +201 -0
  31. package/skills/orchid/orchid-doctor/SKILL.md +162 -0
  32. package/skills/orchid/orchid-investigate/SKILL.md +102 -0
  33. package/skills/orchid/orchid-launch/SKILL.md +147 -0
  34. package/skills/ralph/SKILL.md +73 -0
  35. package/skills/subagents/pi-subagents/SKILL.md +813 -0
  36. package/src/index.ts +7 -0
  37. package/src/orchestrator/abort.ts +534 -0
  38. package/src/orchestrator/agent-bridge-extension.ts +1020 -0
  39. package/src/orchestrator/agent-host.ts +954 -0
  40. package/src/orchestrator/cleanup.ts +776 -0
  41. package/src/orchestrator/config-loader.ts +1412 -0
  42. package/src/orchestrator/config-schema.ts +690 -0
  43. package/src/orchestrator/config.ts +81 -0
  44. package/src/orchestrator/context-window.ts +66 -0
  45. package/src/orchestrator/diagnostic-reports.ts +475 -0
  46. package/src/orchestrator/diagnostics.ts +394 -0
  47. package/src/orchestrator/discovery.ts +1833 -0
  48. package/src/orchestrator/engine-worker.ts +415 -0
  49. package/src/orchestrator/engine.ts +5940 -0
  50. package/src/orchestrator/execution.ts +3104 -0
  51. package/src/orchestrator/extension.ts +5934 -0
  52. package/src/orchestrator/formatting.ts +785 -0
  53. package/src/orchestrator/git.ts +88 -0
  54. package/src/orchestrator/index.ts +28 -0
  55. package/src/orchestrator/lane-runner.ts +1787 -0
  56. package/src/orchestrator/mailbox.ts +780 -0
  57. package/src/orchestrator/merge.ts +3414 -0
  58. package/src/orchestrator/messages.ts +1062 -0
  59. package/src/orchestrator/migrations.ts +278 -0
  60. package/src/orchestrator/naming.ts +117 -0
  61. package/src/orchestrator/path-resolver.ts +275 -0
  62. package/src/orchestrator/persistence.ts +2625 -0
  63. package/src/orchestrator/process-registry.ts +452 -0
  64. package/src/orchestrator/quality-gate.ts +1085 -0
  65. package/src/orchestrator/resume.ts +3488 -0
  66. package/src/orchestrator/sessions.ts +57 -0
  67. package/src/orchestrator/settings-loader.ts +136 -0
  68. package/src/orchestrator/settings-tui.ts +2208 -0
  69. package/src/orchestrator/sidecar-telemetry.ts +267 -0
  70. package/src/orchestrator/supervisor.ts +4548 -0
  71. package/src/orchestrator/task-executor-core.ts +675 -0
  72. package/src/orchestrator/tmux-compat.ts +37 -0
  73. package/src/orchestrator/tool-allowlist-constants.ts +37 -0
  74. package/src/orchestrator/types.ts +4465 -0
  75. package/src/orchestrator/verification.ts +547 -0
  76. package/src/orchestrator/waves.ts +1564 -0
  77. package/src/orchestrator/workspace.ts +707 -0
  78. package/src/orchestrator/worktree.ts +2725 -0
  79. package/src/ralph/index.ts +825 -0
  80. package/src/subagents/agents/agent-management.ts +648 -0
  81. package/src/subagents/agents/agent-scope.ts +6 -0
  82. package/src/subagents/agents/agent-selection.ts +23 -0
  83. package/src/subagents/agents/agent-serializer.ts +86 -0
  84. package/src/subagents/agents/agents.ts +832 -0
  85. package/src/subagents/agents/chain-serializer.ts +137 -0
  86. package/src/subagents/agents/frontmatter.ts +29 -0
  87. package/src/subagents/agents/identity.ts +30 -0
  88. package/src/subagents/agents/skills.ts +632 -0
  89. package/src/subagents/extension/config.ts +16 -0
  90. package/src/subagents/extension/control-notices.ts +92 -0
  91. package/src/subagents/extension/doctor.ts +199 -0
  92. package/src/subagents/extension/fanout-child.ts +170 -0
  93. package/src/subagents/extension/index.ts +573 -0
  94. package/src/subagents/extension/schemas.ts +168 -0
  95. package/src/subagents/intercom/intercom-bridge.ts +379 -0
  96. package/src/subagents/intercom/result-intercom.ts +377 -0
  97. package/src/subagents/runs/background/async-execution.ts +712 -0
  98. package/src/subagents/runs/background/async-job-tracker.ts +310 -0
  99. package/src/subagents/runs/background/async-resume.ts +345 -0
  100. package/src/subagents/runs/background/async-status.ts +325 -0
  101. package/src/subagents/runs/background/completion-dedupe.ts +63 -0
  102. package/src/subagents/runs/background/notify.ts +108 -0
  103. package/src/subagents/runs/background/parallel-groups.ts +45 -0
  104. package/src/subagents/runs/background/result-watcher.ts +307 -0
  105. package/src/subagents/runs/background/run-id-resolver.ts +83 -0
  106. package/src/subagents/runs/background/run-status.ts +269 -0
  107. package/src/subagents/runs/background/stale-run-reconciler.ts +336 -0
  108. package/src/subagents/runs/background/subagent-runner.ts +1808 -0
  109. package/src/subagents/runs/background/top-level-async.ts +13 -0
  110. package/src/subagents/runs/foreground/chain-clarify.ts +1333 -0
  111. package/src/subagents/runs/foreground/chain-execution.ts +938 -0
  112. package/src/subagents/runs/foreground/execution.ts +918 -0
  113. package/src/subagents/runs/foreground/subagent-executor.ts +2527 -0
  114. package/src/subagents/runs/shared/completion-guard.ts +147 -0
  115. package/src/subagents/runs/shared/long-running-guard.ts +175 -0
  116. package/src/subagents/runs/shared/mcp-direct-tool-allowlist.ts +365 -0
  117. package/src/subagents/runs/shared/model-fallback.ts +103 -0
  118. package/src/subagents/runs/shared/nested-events.ts +819 -0
  119. package/src/subagents/runs/shared/nested-path.ts +52 -0
  120. package/src/subagents/runs/shared/nested-render.ts +115 -0
  121. package/src/subagents/runs/shared/parallel-utils.ts +109 -0
  122. package/src/subagents/runs/shared/pi-args.ts +220 -0
  123. package/src/subagents/runs/shared/pi-spawn.ts +115 -0
  124. package/src/subagents/runs/shared/run-history.ts +60 -0
  125. package/src/subagents/runs/shared/single-output.ts +164 -0
  126. package/src/subagents/runs/shared/subagent-control.ts +226 -0
  127. package/src/subagents/runs/shared/subagent-prompt-runtime.ts +170 -0
  128. package/src/subagents/runs/shared/worktree.ts +577 -0
  129. package/src/subagents/shared/artifacts.ts +98 -0
  130. package/src/subagents/shared/atomic-json.ts +16 -0
  131. package/src/subagents/shared/file-coalescer.ts +40 -0
  132. package/src/subagents/shared/fork-context.ts +76 -0
  133. package/src/subagents/shared/formatters.ts +133 -0
  134. package/src/subagents/shared/jsonl-writer.ts +81 -0
  135. package/src/subagents/shared/model-info.ts +78 -0
  136. package/src/subagents/shared/post-exit-stdio-guard.ts +85 -0
  137. package/src/subagents/shared/session-identity.ts +10 -0
  138. package/src/subagents/shared/session-tokens.ts +44 -0
  139. package/src/subagents/shared/settings.ts +397 -0
  140. package/src/subagents/shared/status-format.ts +49 -0
  141. package/src/subagents/shared/types.ts +822 -0
  142. package/src/subagents/shared/utils.ts +450 -0
  143. package/src/subagents/slash/prompt-template-bridge.ts +397 -0
  144. package/src/subagents/slash/slash-bridge.ts +174 -0
  145. package/src/subagents/slash/slash-commands.ts +528 -0
  146. package/src/subagents/slash/slash-live-state.ts +292 -0
  147. package/src/subagents/tui/render-helpers.ts +80 -0
  148. package/src/subagents/tui/render.ts +1358 -0
  149. package/templates/agents/local/supervisor.md +33 -0
  150. package/templates/agents/local/task-merger.md +27 -0
  151. package/templates/agents/local/task-reviewer.md +30 -0
  152. package/templates/agents/local/task-worker.md +34 -0
  153. package/templates/agents/supervisor-routing.md +92 -0
  154. package/templates/agents/supervisor.md +229 -0
  155. package/templates/agents/task-merger.md +214 -0
  156. package/templates/agents/task-reviewer.md +260 -0
  157. package/templates/agents/task-worker-segment.md +44 -0
  158. package/templates/agents/task-worker.md +557 -0
  159. package/templates/tasks/CONTEXT.md +30 -0
  160. package/templates/tasks/EXAMPLE-001-hello-world/PROMPT.md +98 -0
  161. package/templates/tasks/EXAMPLE-001-hello-world/STATUS.md +73 -0
  162. package/templates/tasks/EXAMPLE-002-parallel-smoke/PROMPT.md +97 -0
  163. package/templates/tasks/EXAMPLE-002-parallel-smoke/STATUS.md +73 -0
@@ -0,0 +1,267 @@
1
+ /**
2
+ * Sidecar Telemetry Utilities
3
+ *
4
+ * Canonical home for sidecar JSONL tailing and telemetry delta parsing.
5
+ *
6
+ * These utilities are used by the orchestrator to poll live worker telemetry
7
+ * (token usage, tool calls, retry state) from sidecar JSONL files written by
8
+ * the pi coding agent during task execution.
9
+ *
10
+ * @since TP-161
11
+ */
12
+
13
+ import { existsSync, mkdirSync, statSync, openSync, readSync, closeSync } from "fs";
14
+ import { join, dirname } from "path";
15
+
16
+ // ── Sidecar Directory Resolution ──────────────────────────────────────
17
+
18
+ /**
19
+ * Returns the .pi directory path for sidecar files (lane state, conversation logs).
20
+ * In orchestrated mode, the orchestrator passes ORCH_SIDECAR_DIR pointing to the
21
+ * MAIN repo's .pi/ directory (not the worktree's).
22
+ */
23
+ export function getSidecarDir(): string {
24
+ // Orchestrator provides the main repo .pi path
25
+ const orchDir = process.env.ORCH_SIDECAR_DIR;
26
+ if (orchDir) {
27
+ if (!existsSync(orchDir)) mkdirSync(orchDir, { recursive: true });
28
+ return orchDir;
29
+ }
30
+ // Fallback: walk up from cwd
31
+ let dir = process.cwd();
32
+ for (let i = 0; i < 10; i++) {
33
+ const piDir = join(dir, ".pi");
34
+ if (existsSync(piDir)) return piDir;
35
+ const parent = dirname(dir);
36
+ if (parent === dir) break;
37
+ dir = parent;
38
+ }
39
+ const piDir = join(process.cwd(), ".pi");
40
+ if (!existsSync(piDir)) mkdirSync(piDir, { recursive: true });
41
+ return piDir;
42
+ }
43
+
44
+ // ── Sidecar Tail State ────────────────────────────────────────────────
45
+
46
+ /**
47
+ * Mutable state for incremental byte-offset sidecar JSONL reading.
48
+ * One instance per sidecar file, persists across poll ticks within a session.
49
+ */
50
+ export interface SidecarTailState {
51
+ /** Byte offset of the next unread position in the sidecar file */
52
+ offset: number;
53
+ /** Partial trailing line from the last read (incomplete JSONL line) */
54
+ partial: string;
55
+ /** Whether a retry is currently active (persisted across ticks) */
56
+ retryActive: boolean;
57
+ }
58
+
59
+ export function createSidecarTailState(): SidecarTailState {
60
+ return { offset: 0, partial: "", retryActive: false };
61
+ }
62
+
63
+ // ── Sidecar Telemetry Delta ───────────────────────────────────────────
64
+
65
+ /**
66
+ * Parsed telemetry accumulated from sidecar JSONL events.
67
+ * Returned by tailSidecarJsonl() on each tick.
68
+ */
69
+ export interface SidecarTelemetryDelta {
70
+ /** Per-turn input tokens (sum of new message_end events in this tick) */
71
+ inputTokens: number;
72
+ outputTokens: number;
73
+ cacheReadTokens: number;
74
+ cacheWriteTokens: number;
75
+ /** Incremental cost from new message_end events */
76
+ cost: number;
77
+ /** Most recent totalTokens from message_end usage (cumulative, for context %) */
78
+ latestTotalTokens: number;
79
+ /** Tool calls observed in this tick */
80
+ toolCalls: number;
81
+ /** Last tool description from tool_execution_start */
82
+ lastTool: string;
83
+ /** Whether a retry is currently active (persisted across ticks via SidecarTailState) */
84
+ retryActive: boolean;
85
+ /** Total retries started in this tick */
86
+ retriesStarted: number;
87
+ /** Error message from the most recent auto_retry_start */
88
+ lastRetryError: string;
89
+ /** Whether any sidecar events were parsed in this tick (used for callback gating) */
90
+ hadEvents: boolean;
91
+ /** Authoritative context usage from pi get_session_stats (pi ≥ 0.63.0, null if unavailable) */
92
+ contextUsage: { percent: number; totalTokens: number; maxTokens: number } | null;
93
+ /** True when a get_session_stats response was seen but lacked contextUsage (older pi) */
94
+ sawStatsResponseWithoutContextUsage: boolean;
95
+ }
96
+
97
+ // ── Incremental JSONL Tailing ─────────────────────────────────────────
98
+
99
+ /**
100
+ * Incrementally read new lines from a sidecar JSONL file and parse telemetry events.
101
+ *
102
+ * O(new) per call — only reads bytes after the previous offset. Handles:
103
+ * - File not yet created (returns zero delta)
104
+ * - Empty reads (no new data since last tick)
105
+ * - Partial trailing lines (buffered for next call)
106
+ * - Malformed JSON lines (skipped with stderr warning, does not break iteration)
107
+ *
108
+ * The caller (poll loop) accumulates the returned deltas into TaskState.
109
+ */
110
+ export function tailSidecarJsonl(
111
+ filePath: string,
112
+ tailState: SidecarTailState,
113
+ ): SidecarTelemetryDelta {
114
+ const delta: SidecarTelemetryDelta = {
115
+ inputTokens: 0,
116
+ outputTokens: 0,
117
+ cacheReadTokens: 0,
118
+ cacheWriteTokens: 0,
119
+ cost: 0,
120
+ latestTotalTokens: 0,
121
+ toolCalls: 0,
122
+ lastTool: "",
123
+ retryActive: tailState.retryActive,
124
+ retriesStarted: 0,
125
+ lastRetryError: "",
126
+ hadEvents: false,
127
+ contextUsage: null,
128
+ sawStatsResponseWithoutContextUsage: false,
129
+ };
130
+
131
+ // Gracefully handle missing file (wrapper hasn't written yet)
132
+ let fileSize: number;
133
+ try {
134
+ fileSize = statSync(filePath).size;
135
+ } catch {
136
+ return delta; // File doesn't exist yet — no-op
137
+ }
138
+
139
+ if (fileSize <= tailState.offset) {
140
+ return delta; // No new data
141
+ }
142
+
143
+ // Read new bytes from offset to end of file
144
+ const bytesToRead = fileSize - tailState.offset;
145
+ const buf = Buffer.alloc(bytesToRead);
146
+ let fd: number;
147
+ try {
148
+ fd = openSync(filePath, "r");
149
+ } catch {
150
+ return delta; // File became inaccessible between stat and open
151
+ }
152
+ try {
153
+ readSync(fd, buf, 0, bytesToRead, tailState.offset);
154
+ } catch {
155
+ closeSync(fd);
156
+ return delta; // Read error — try again next tick
157
+ }
158
+ closeSync(fd);
159
+ tailState.offset = fileSize;
160
+
161
+ // Split into lines, preserving any partial trailing line
162
+ const chunk = tailState.partial + buf.toString("utf-8");
163
+ const lines = chunk.split("\n");
164
+ // Last element is either "" (if chunk ended with \n) or a partial line
165
+ tailState.partial = lines.pop() || "";
166
+
167
+ for (const line of lines) {
168
+ const trimmed = line.trim();
169
+ if (!trimmed) continue;
170
+
171
+ let event: any;
172
+ try {
173
+ event = JSON.parse(trimmed);
174
+ } catch {
175
+ // Malformed JSON — skip silently (concurrent write race, truncated line)
176
+ continue;
177
+ }
178
+
179
+ if (!event || !event.type) continue;
180
+
181
+ delta.hadEvents = true;
182
+
183
+ switch (event.type) {
184
+ case "message_end": {
185
+ const usage = event.message?.usage;
186
+ if (usage) {
187
+ delta.inputTokens += usage.input || 0;
188
+ delta.outputTokens += usage.output || 0;
189
+ delta.cacheReadTokens += usage.cacheRead || 0;
190
+ delta.cacheWriteTokens += usage.cacheWrite || 0;
191
+ if (usage.cost) {
192
+ delta.cost +=
193
+ typeof usage.cost === "object"
194
+ ? usage.cost.total || 0
195
+ : typeof usage.cost === "number"
196
+ ? usage.cost
197
+ : 0;
198
+ }
199
+ // totalTokens is cumulative (grows each turn) — use latest value.
200
+ // Include cacheRead tokens: pi's totalTokens and the
201
+ // input+output fallback both exclude cache reads, but cached
202
+ // tokens still consume context window capacity.
203
+ const rawTotal = usage.totalTokens || (usage.input || 0) + (usage.output || 0);
204
+ const totalTokens = rawTotal + (usage.cacheRead || 0);
205
+ if (totalTokens > delta.latestTotalTokens) {
206
+ delta.latestTotalTokens = totalTokens;
207
+ }
208
+ }
209
+ break;
210
+ }
211
+
212
+ case "tool_execution_start": {
213
+ delta.toolCalls++;
214
+ const toolDesc = event.toolName || "unknown";
215
+ let argPreview = "";
216
+ if (event.args) {
217
+ if (typeof event.args === "string") {
218
+ argPreview = event.args.slice(0, 80);
219
+ } else if (typeof event.args === "object") {
220
+ const firstVal = Object.values(event.args)[0];
221
+ if (typeof firstVal === "string") {
222
+ argPreview = (firstVal as string).slice(0, 80);
223
+ }
224
+ }
225
+ }
226
+ delta.lastTool = argPreview ? `${toolDesc} ${argPreview}` : toolDesc;
227
+ break;
228
+ }
229
+
230
+ case "auto_retry_start": {
231
+ delta.retriesStarted++;
232
+ delta.lastRetryError = event.errorMessage || event.error || "unknown";
233
+ tailState.retryActive = true;
234
+ break;
235
+ }
236
+
237
+ case "auto_retry_end": {
238
+ tailState.retryActive = false;
239
+ break;
240
+ }
241
+
242
+ case "response": {
243
+ // get_session_stats response from pi ≥ 0.63.0 — authoritative context usage
244
+ if (event.success === true && event.data?.contextUsage) {
245
+ const cu = event.data.contextUsage;
246
+ // pi sends `percent` (pi ≥ 0.63.0); accept `percentUsed` as legacy fallback
247
+ const pctValue = cu.percent ?? cu.percentUsed;
248
+ if (typeof pctValue === "number") {
249
+ delta.contextUsage = {
250
+ percent: pctValue,
251
+ totalTokens: cu.totalTokens || 0,
252
+ maxTokens: cu.maxTokens || 0,
253
+ };
254
+ }
255
+ } else if (event.success === true && event.data && !event.data.contextUsage) {
256
+ // Successful get_session_stats response but no contextUsage — older pi
257
+ delta.sawStatsResponseWithoutContextUsage = true;
258
+ }
259
+ break;
260
+ }
261
+ }
262
+ }
263
+
264
+ // Reflect persisted retry state into the delta for the caller
265
+ delta.retryActive = tailState.retryActive;
266
+ return delta;
267
+ }