@pi-unipi/unipi 2.6.0 → 2.6.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 (205) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/package.json +21 -22
  3. package/packages/ask-user/package.json +2 -2
  4. package/packages/ask-user/settings-tui.ts +18 -34
  5. package/packages/autocomplete/package.json +1 -1
  6. package/packages/autocomplete/src/constants.ts +2 -15
  7. package/packages/autocomplete/src/provider.ts +3 -17
  8. package/packages/btw/extensions/btw.ts +18 -45
  9. package/packages/btw/package.json +2 -2
  10. package/packages/compactor/README.md +2 -2
  11. package/packages/compactor/package.json +3 -3
  12. package/packages/compactor/skills/compactor/SKILL.md +1 -1
  13. package/packages/compactor/skills/compactor-detail/SKILL.md +3 -5
  14. package/packages/compactor/skills/compactor-doctor/SKILL.md +1 -1
  15. package/packages/compactor/skills/compactor-stats/SKILL.md +1 -1
  16. package/packages/compactor/src/commands/index.ts +47 -78
  17. package/packages/compactor/src/compaction/brief.ts +161 -90
  18. package/packages/compactor/src/compaction/build-sections.ts +3 -4
  19. package/packages/compactor/src/compaction/compact-args.ts +86 -0
  20. package/packages/compactor/src/compaction/cut.ts +270 -28
  21. package/packages/compactor/src/compaction/drill-down.ts +261 -0
  22. package/packages/compactor/src/compaction/format-recall.ts +96 -0
  23. package/packages/compactor/src/compaction/format.ts +8 -3
  24. package/packages/compactor/src/compaction/hooks.ts +248 -72
  25. package/packages/compactor/src/compaction/merge.ts +34 -4
  26. package/packages/compactor/src/compaction/rank.ts +270 -0
  27. package/packages/compactor/src/compaction/recall-scope.ts +28 -0
  28. package/packages/compactor/src/compaction/search-entries.ts +333 -96
  29. package/packages/compactor/src/compaction/skill-collapse.ts +35 -0
  30. package/packages/compactor/src/compaction/summarize.ts +37 -6
  31. package/packages/compactor/src/compaction/token-estimate.ts +104 -0
  32. package/packages/compactor/src/compaction/touched-files.ts +35 -0
  33. package/packages/compactor/src/config/manager.ts +2 -27
  34. package/packages/compactor/src/config/presets.ts +0 -2
  35. package/packages/compactor/src/config/schema.ts +2 -16
  36. package/packages/compactor/src/executor/executor.ts +6 -15
  37. package/packages/compactor/src/executor/runtime.ts +2 -12
  38. package/packages/compactor/src/index.ts +4 -121
  39. package/packages/compactor/src/info-screen.ts +3 -10
  40. package/packages/compactor/src/security/evaluator.ts +0 -53
  41. package/packages/compactor/src/security/policy.ts +7 -8
  42. package/packages/compactor/src/session/db.ts +0 -6
  43. package/packages/compactor/src/tools/ctx-execute-file.ts +0 -5
  44. package/packages/compactor/src/tools/register.ts +27 -50
  45. package/packages/compactor/src/tools/vcc-recall.ts +86 -48
  46. package/packages/compactor/src/tui/settings-overlay.ts +20 -40
  47. package/packages/compactor/src/types.ts +43 -100
  48. package/packages/core/constants.ts +1 -28
  49. package/packages/core/events.ts +1 -83
  50. package/packages/core/index.ts +1 -0
  51. package/packages/core/model-cache.ts +18 -12
  52. package/packages/core/package.json +3 -1
  53. package/packages/core/tui-overlay.ts +89 -0
  54. package/packages/core/tui-width.ts +18 -0
  55. package/packages/core/utils.ts +43 -0
  56. package/packages/footer/package.json +2 -2
  57. package/packages/footer/src/help.ts +2 -2
  58. package/packages/footer/src/index.ts +9 -10
  59. package/packages/footer/src/presets.ts +1 -27
  60. package/packages/footer/src/registry/index.ts +0 -23
  61. package/packages/footer/src/rendering/icons.ts +0 -13
  62. package/packages/footer/src/rendering/renderer.ts +5 -40
  63. package/packages/footer/src/rendering/separators.ts +0 -10
  64. package/packages/footer/src/rendering/theme.ts +1 -15
  65. package/packages/footer/src/segments/compactor.ts +7 -14
  66. package/packages/footer/src/segments/core.ts +15 -54
  67. package/packages/footer/src/segments/kanboard.ts +5 -5
  68. package/packages/footer/src/segments/mcp.ts +5 -5
  69. package/packages/footer/src/segments/memory.ts +4 -4
  70. package/packages/footer/src/segments/notify.ts +2 -2
  71. package/packages/footer/src/segments/ralph.ts +3 -3
  72. package/packages/footer/src/segments/status-ext.ts +1 -1
  73. package/packages/footer/src/segments/workflow.ts +3 -3
  74. package/packages/footer/src/tps-tracker.ts +0 -8
  75. package/packages/footer/src/tui/settings-tui.ts +14 -31
  76. package/packages/footer/src/types.ts +0 -15
  77. package/packages/image/package.json +2 -2
  78. package/packages/image/src/register-providers.ts +0 -11
  79. package/packages/image/src/tools.ts +0 -3
  80. package/packages/image/src/tui/model-selector.ts +36 -71
  81. package/packages/info-screen/config.ts +0 -16
  82. package/packages/info-screen/core-groups.ts +0 -10
  83. package/packages/info-screen/index.ts +22 -13
  84. package/packages/info-screen/package.json +2 -2
  85. package/packages/info-screen/registry.ts +1 -72
  86. package/packages/info-screen/tui/info-overlay.ts +85 -100
  87. package/packages/info-screen/usage-parser.ts +12 -47
  88. package/packages/input-shortcuts/package.json +2 -2
  89. package/packages/input-shortcuts/src/index.ts +3 -3
  90. package/packages/input-shortcuts/src/registers.ts +0 -8
  91. package/packages/input-shortcuts/src/settings-overlay.ts +4 -13
  92. package/packages/input-shortcuts/src/undo-redo.ts +0 -12
  93. package/packages/kanboard/package.json +2 -2
  94. package/packages/mcp/package.json +2 -2
  95. package/packages/mcp/src/bridge/client.ts +0 -15
  96. package/packages/mcp/src/config/manager.ts +0 -38
  97. package/packages/mcp/src/config/schema.ts +0 -27
  98. package/packages/mcp/src/config/sync.ts +0 -29
  99. package/packages/mcp/src/index.ts +1 -1
  100. package/packages/mcp/src/tui/add-overlay.ts +6 -6
  101. package/packages/mcp/src/types.ts +0 -6
  102. package/packages/memory/embedding.ts +0 -31
  103. package/packages/memory/mempalace.ts +0 -16
  104. package/packages/memory/package.json +4 -8
  105. package/packages/memory/storage.ts +56 -753
  106. package/packages/milestone/hooks.ts +1 -6
  107. package/packages/milestone/milestone.ts +2 -41
  108. package/packages/milestone/package.json +2 -2
  109. package/packages/notify/README.md +1 -0
  110. package/packages/notify/commands.ts +61 -3
  111. package/packages/notify/events.ts +0 -10
  112. package/packages/notify/package.json +2 -2
  113. package/packages/notify/settings.ts +0 -19
  114. package/packages/notify/tui/gotify-setup.ts +108 -143
  115. package/packages/notify/tui/ntfy-setup.ts +137 -172
  116. package/packages/notify/tui/recap-model-selector.ts +107 -101
  117. package/packages/notify/tui/settings-overlay.ts +94 -117
  118. package/packages/notify/tui/telegram-setup.ts +48 -79
  119. package/packages/notify/types.ts +0 -2
  120. package/packages/ralph/index.ts +11 -35
  121. package/packages/ralph/package.json +3 -3
  122. package/packages/ralph/ralph-loop.ts +3 -44
  123. package/packages/subagents/package.json +2 -1
  124. package/packages/subagents/src/agent-manager.ts +0 -51
  125. package/packages/subagents/src/agent-runner.ts +6 -80
  126. package/packages/subagents/src/conversation-viewer.ts +1 -19
  127. package/packages/subagents/src/core-compat.ts +8 -25
  128. package/packages/subagents/src/custom-agents.ts +0 -1
  129. package/packages/subagents/src/index.ts +3 -48
  130. package/packages/subagents/src/types.ts +0 -11
  131. package/packages/subagents/src/widget.ts +5 -5
  132. package/packages/unipi/bundled.js +4468 -7769
  133. package/packages/unipi/index.ts +6 -3
  134. package/packages/updater/package.json +2 -2
  135. package/packages/updater/src/changelog.ts +1 -16
  136. package/packages/updater/src/index.ts +0 -5
  137. package/packages/updater/src/installer.ts +4 -26
  138. package/packages/updater/src/markdown.ts +6 -152
  139. package/packages/updater/src/settings.ts +0 -15
  140. package/packages/updater/src/tui/changelog-overlay.ts +42 -234
  141. package/packages/updater/src/tui/list-detail-overlay.ts +260 -0
  142. package/packages/updater/src/tui/readme-overlay.ts +53 -210
  143. package/packages/updater/src/tui/settings-overlay.ts +13 -26
  144. package/packages/updater/src/version.ts +2 -31
  145. package/packages/utility/package.json +2 -4
  146. package/packages/utility/src/commands.ts +4 -5
  147. package/packages/utility/src/herdr-sync.ts +136 -0
  148. package/packages/utility/src/index.ts +11 -68
  149. package/packages/utility/src/lifecycle/cleanup.ts +2 -16
  150. package/packages/utility/src/{diff/settings.ts → settings.ts} +42 -80
  151. package/packages/utility/src/tools/env.ts +0 -0
  152. package/packages/utility/src/tui/name-badge-state.ts +18 -2
  153. package/packages/utility/src/tui/name-badge.ts +5 -4
  154. package/packages/utility/src/tui/util-settings-tui.ts +41 -224
  155. package/packages/web-api/package.json +2 -6
  156. package/packages/web-api/src/engine/dependencies.ts +13 -134
  157. package/packages/web-api/src/engine/extract.ts +9 -99
  158. package/packages/web-api/src/engine/format.ts +0 -75
  159. package/packages/web-api/src/engine/types.ts +0 -39
  160. package/packages/web-api/src/index.ts +1 -2
  161. package/packages/web-api/src/providers/base.ts +0 -10
  162. package/packages/web-api/src/providers/duckduckgo.ts +0 -1
  163. package/packages/web-api/src/providers/firecrawl.ts +0 -9
  164. package/packages/web-api/src/providers/jina-reader.ts +0 -9
  165. package/packages/web-api/src/providers/jina-search.ts +0 -9
  166. package/packages/web-api/src/providers/perplexity.ts +0 -9
  167. package/packages/web-api/src/providers/registry.ts +0 -80
  168. package/packages/web-api/src/providers/serpapi.ts +0 -9
  169. package/packages/web-api/src/providers/tavily.ts +0 -9
  170. package/packages/web-api/src/providers/wigolo-client.ts +0 -30
  171. package/packages/web-api/src/providers/wigolo.ts +0 -1
  172. package/packages/web-api/src/settings.ts +17 -77
  173. package/packages/web-api/src/tools.ts +21 -24
  174. package/packages/web-api/src/tui/settings-dialog.ts +0 -1
  175. package/packages/workflow/commands.ts +38 -122
  176. package/packages/workflow/index.ts +1 -5
  177. package/packages/workflow/package.json +2 -2
  178. package/packages/cocoindex/README.md +0 -94
  179. package/packages/cocoindex/bridge.ts +0 -842
  180. package/packages/cocoindex/commands.ts +0 -175
  181. package/packages/cocoindex/index.ts +0 -48
  182. package/packages/cocoindex/installer.ts +0 -397
  183. package/packages/cocoindex/package.json +0 -49
  184. package/packages/cocoindex/skills/cocoindex/SKILL.md +0 -88
  185. package/packages/cocoindex/tools.ts +0 -157
  186. package/packages/compactor/src/display/diff-renderer.ts +0 -281
  187. package/packages/compactor/src/display/line-width-safety.ts +0 -28
  188. package/packages/compactor/src/display/render-utils.ts +0 -52
  189. package/packages/compactor/src/display/thinking-label.ts +0 -18
  190. package/packages/compactor/src/display/tool-overrides.ts +0 -136
  191. package/packages/compactor/src/tools/compact.ts +0 -20
  192. package/packages/subagents/src/file-lock.ts +0 -102
  193. package/packages/utility/src/cache/ttl-cache.ts +0 -311
  194. package/packages/utility/src/diff/highlighter.ts +0 -352
  195. package/packages/utility/src/diff/parser.ts +0 -191
  196. package/packages/utility/src/diff/renderer.ts +0 -395
  197. package/packages/utility/src/diff/theme.ts +0 -316
  198. package/packages/utility/src/diff/wrapper.ts +0 -339
  199. package/packages/utility/src/display/capabilities.ts +0 -214
  200. package/packages/utility/src/display/width.ts +0 -226
  201. package/packages/utility/src/tools/batch.ts +0 -229
  202. package/packages/utility/src/tui/badge-settings-tui.ts +0 -388
  203. package/packages/utility/src/tui/badge-settings.ts +0 -67
  204. package/packages/utility/src/tui/settings-inspector.ts +0 -303
  205. package/packages/web-api/src/providers/llm-summarize.ts +0 -71
@@ -57,23 +57,6 @@ Pause and reflect on your progress:
57
57
 
58
58
  Update the task file with your reflection, then continue working.`;
59
59
 
60
- /** Default task template */
61
- export const DEFAULT_TEMPLATE = `# Task
62
-
63
- Describe your task here.
64
-
65
- ## Goals
66
- - Goal 1
67
- - Goal 2
68
-
69
- ## Checklist
70
- - [ ] Item 1
71
- - [ ] Item 2
72
-
73
- ## Notes
74
- (Update this as you work)
75
- `;
76
-
77
60
  /** Ralph loop manager */
78
61
  export class RalphLoopManager {
79
62
  private currentLoop: string | null = null;
@@ -144,7 +127,7 @@ export class RalphLoopManager {
144
127
  if (message && this.ctx.hasUI) this.ctx.ui.notify(message, "info");
145
128
  }
146
129
 
147
- completeLoop(state: LoopState, banner: string): void {
130
+ completeLoop(state: LoopState, reason: "completed" | "cancelled" = "completed", message?: string): void {
148
131
  state.status = "completed";
149
132
  state.completedAt = now();
150
133
  state.active = false;
@@ -156,26 +139,7 @@ export class RalphLoopManager {
156
139
  iteration: state.iteration,
157
140
  maxIterations: state.maxIterations,
158
141
  status: "completed",
159
- reason: "completed",
160
- } satisfies UnipiRalphLoopEvent);
161
-
162
- this.currentLoop = null;
163
- this.updateUI();
164
- }
165
-
166
- stopLoop(state: LoopState, message?: string): void {
167
- state.status = "completed";
168
- state.completedAt = now();
169
- state.active = false;
170
- this.saveState(state);
171
-
172
- // Emit event
173
- this.emitFn(UNIPI_EVENTS.RALPH_LOOP_END, {
174
- name: state.name,
175
- iteration: state.iteration,
176
- maxIterations: state.maxIterations,
177
- status: "completed",
178
- reason: "cancelled",
142
+ reason,
179
143
  } satisfies UnipiRalphLoopEvent);
180
144
 
181
145
  this.currentLoop = null;
@@ -365,12 +329,7 @@ export class RalphLoopManager {
365
329
 
366
330
  // Check max iterations
367
331
  if (state.maxIterations > 0 && state.iteration > state.maxIterations) {
368
- this.completeLoop(
369
- state,
370
- `───────────────────────────────────────────────────────────────────────
371
- ⚠️ RALPH LOOP STOPPED: ${state.name} | Max iterations (${state.maxIterations}) reached
372
- ───────────────────────────────────────────────────────────────────────`,
373
- );
332
+ this.completeLoop(state);
374
333
  return null;
375
334
  }
376
335
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/subagents",
3
- "version": "2.6.0",
3
+ "version": "2.6.1",
4
4
  "description": "Subagents for UniPi — parallel execution, file locking, workflow integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -18,6 +18,7 @@
18
18
  "test": "npx tsx --test src/__tests__/*.test.ts"
19
19
  },
20
20
  "dependencies": {
21
+ "@pi-unipi/core": "2.6.1",
21
22
  "@earendil-works/pi-agent-core": "^0.80.0"
22
23
  },
23
24
  "devDependencies": {
@@ -17,7 +17,6 @@ import { loadCustomAgents } from "./custom-agents.js";
17
17
  function compareCodeUnits(a: string, b: string): number {
18
18
  return a < b ? -1 : a > b ? 1 : 0;
19
19
  }
20
- import { FileLock } from "./file-lock.js";
21
20
 
22
21
  export type OnAgentComplete = (record: AgentRecord) => void;
23
22
  export type OnAgentStart = (record: AgentRecord) => void;
@@ -41,7 +40,6 @@ interface SpawnOptions {
41
40
  thinkingLevel?: ThinkingLevel;
42
41
  maxTurns?: number;
43
42
  isolated?: boolean;
44
- inheritContext?: boolean;
45
43
  isBackground?: boolean;
46
44
  onToolActivity?: (activity: ToolActivity) => void;
47
45
  onTextDelta?: (delta: string, fullText: string) => void;
@@ -58,9 +56,6 @@ export class AgentManager {
58
56
  private customAgents: Map<string, AgentConfig>;
59
57
  private typeSettings: SubagentsConfig["types"];
60
58
 
61
- /** Per-file transparent locking for write agents. */
62
- readonly fileLock = new FileLock();
63
-
64
59
  /** Queue of background agents waiting to start. */
65
60
  private queue: { id: string; args: SpawnArgs }[] = [];
66
61
  /** Number of currently running background agents. */
@@ -107,11 +102,6 @@ export class AgentManager {
107
102
  }
108
103
  }
109
104
 
110
- setMaxConcurrent(n: number) {
111
- this.maxConcurrent = Math.max(1, n);
112
- this.drainQueue();
113
- }
114
-
115
105
  getMaxConcurrent(): number {
116
106
  return this.maxConcurrent;
117
107
  }
@@ -140,7 +130,6 @@ export class AgentManager {
140
130
  toolUses: 0,
141
131
  startedAt: Date.now(),
142
132
  abortController,
143
- lockedFiles: new Set(),
144
133
  };
145
134
  this.agents.set(id, record);
146
135
 
@@ -189,7 +178,6 @@ export class AgentManager {
189
178
  agentConfig,
190
179
  maxTurns: options.maxTurns ?? agentConfig?.maxTurns,
191
180
  isolated: options.isolated ?? agentConfig?.isolated,
192
- inheritContext: options.inheritContext ?? agentConfig?.inheritContext,
193
181
  thinkingLevel: options.thinkingLevel ?? agentConfig?.thinking,
194
182
  signal: record.abortController!.signal,
195
183
  onToolActivity: (activity) => {
@@ -211,10 +199,6 @@ export class AgentManager {
211
199
  record.session = session;
212
200
  record.completedAt ??= Date.now();
213
201
 
214
- // Release any held file locks
215
- this.fileLock.releaseAll(id);
216
- record.lockedFiles.clear();
217
-
218
202
  if (options.isBackground) {
219
203
  this.runningBackground--;
220
204
  this.onComplete?.(record);
@@ -229,10 +213,6 @@ export class AgentManager {
229
213
  record.error = err instanceof Error ? err.message : String(err);
230
214
  record.completedAt ??= Date.now();
231
215
 
232
- // Release any held file locks
233
- this.fileLock.releaseAll(id);
234
- record.lockedFiles.clear();
235
-
236
216
  if (options.isBackground) {
237
217
  this.runningBackground--;
238
218
  this.onComplete?.(record);
@@ -293,8 +273,6 @@ export class AgentManager {
293
273
  record.abortController?.abort();
294
274
  record.status = "stopped";
295
275
  record.completedAt = Date.now();
296
- this.fileLock.releaseAll(id);
297
- record.lockedFiles.clear();
298
276
  return true;
299
277
  }
300
278
 
@@ -318,38 +296,9 @@ export class AgentManager {
318
296
  count++;
319
297
  }
320
298
  }
321
- this.fileLock.clear();
322
299
  return count;
323
300
  }
324
301
 
325
- /** Wait for all agents. */
326
- async waitForAll(): Promise<void> {
327
- while (true) {
328
- this.drainQueue();
329
- const pending = [...this.agents.values()]
330
- .filter((r) => r.status === "running" || r.status === "queued")
331
- .map((r) => r.promise)
332
- .filter(Boolean);
333
- if (pending.length === 0) break;
334
- await Promise.allSettled(pending);
335
- }
336
- }
337
-
338
- /** Whether any agents running or queued. */
339
- hasRunning(): boolean {
340
- return [...this.agents.values()].some((r) => r.status === "running" || r.status === "queued");
341
- }
342
-
343
- /** Remove completed records. */
344
- clearCompleted(): void {
345
- for (const [id, record] of this.agents) {
346
- if (record.status === "running" || record.status === "queued") continue;
347
- record.session?.dispose?.();
348
- record.session = undefined;
349
- this.agents.delete(id);
350
- }
351
- }
352
-
353
302
  private cleanup() {
354
303
  const cutoff = Date.now() - 10 * 60_000;
355
304
  for (const [id, record] of this.agents) {
@@ -25,27 +25,8 @@ const EXCLUDED_TOOL_NAMES = ["spawn_helper", "get_helper_result", "Agent", "get_
25
25
  /** All known built-in tool names. */
26
26
  const BUILTIN_TOOL_NAMES = ["read", "bash", "edit", "write", "grep", "find", "ls"];
27
27
 
28
- /** Default max turns. undefined = unlimited. */
29
- let defaultMaxTurns: number | undefined;
30
-
31
- export function getDefaultMaxTurns(): number | undefined {
32
- return defaultMaxTurns;
33
- }
34
-
35
- export function setDefaultMaxTurns(n: number | undefined): void {
36
- defaultMaxTurns = n == null || n === 0 ? undefined : Math.max(1, n);
37
- }
38
-
39
- /** Grace turns after soft limit. */
40
- let graceTurns = 5;
41
-
42
- export function getGraceTurns(): number {
43
- return graceTurns;
44
- }
45
-
46
- export function setGraceTurns(n: number): void {
47
- graceTurns = Math.max(1, n);
48
- }
28
+ /** Grace turns after the soft turn limit before aborting. */
29
+ const GRACE_TURNS = 5;
49
30
 
50
31
  /** Tool activity info. */
51
32
  export interface ToolActivity {
@@ -61,7 +42,6 @@ export interface RunOptions {
61
42
  maxTurns?: number;
62
43
  signal?: AbortSignal;
63
44
  isolated?: boolean;
64
- inheritContext?: boolean;
65
45
  thinkingLevel?: ThinkingLevel;
66
46
  cwd?: string;
67
47
  onToolActivity?: (activity: ToolActivity) => void;
@@ -123,8 +103,7 @@ function getToolNamesForType(type: AgentType, config?: AgentConfig): string[] {
123
103
  return [...BUILTIN_TOOL_NAMES];
124
104
  }
125
105
 
126
- /**
127
- * Resolve agent config for a type.
106
+ /** Resolve agent config for a type.
128
107
  * Priority: explicit config > builtin config > default
129
108
  */
130
109
  function resolveAgentConfig(type: AgentType, explicitConfig?: AgentConfig): AgentConfig | undefined {
@@ -132,15 +111,6 @@ function resolveAgentConfig(type: AgentType, explicitConfig?: AgentConfig): Agen
132
111
  return BUILTIN_CONFIGS[type];
133
112
  }
134
113
 
135
- /** Resolve model from config. */
136
- function resolveDefaultModel(
137
- parentModel: Model<any> | undefined,
138
- configModel?: string,
139
- ): Model<any> | undefined {
140
- // For now, just use parent model. Full model resolution requires registry.
141
- return parentModel;
142
- }
143
-
144
114
  /**
145
115
  * Run an agent session.
146
116
  */
@@ -191,7 +161,7 @@ export async function runAgent(
191
161
  await loader.reload();
192
162
 
193
163
  // Resolve model
194
- const model = options.model ?? resolveDefaultModel(ctx.model);
164
+ const model = options.model ?? ctx.model;
195
165
 
196
166
  // Create session
197
167
  const sessionOpts: Parameters<typeof createAgentSession>[0] = {
@@ -235,7 +205,7 @@ export async function runAgent(
235
205
 
236
206
  // Track turns
237
207
  let turnCount = 0;
238
- const maxTurns = options.maxTurns ?? defaultMaxTurns;
208
+ const maxTurns = options.maxTurns;
239
209
  let softLimitReached = false;
240
210
  let aborted = false;
241
211
 
@@ -248,7 +218,7 @@ export async function runAgent(
248
218
  if (!softLimitReached && turnCount >= maxTurns) {
249
219
  softLimitReached = true;
250
220
  session.steer("You have reached your turn limit. Wrap up immediately — provide your final answer now.");
251
- } else if (softLimitReached && turnCount >= maxTurns + graceTurns) {
221
+ } else if (softLimitReached && turnCount >= maxTurns + GRACE_TURNS) {
252
222
  aborted = true;
253
223
  session.abort();
254
224
  }
@@ -283,47 +253,3 @@ export async function runAgent(
283
253
  const responseText = collector.getText().trim() || getLastAssistantText(session);
284
254
  return { responseText, session, aborted, steered: softLimitReached };
285
255
  }
286
-
287
- /**
288
- * Get conversation text from a session.
289
- */
290
- export function getAgentConversation(session: AgentSession): string {
291
- const parts: string[] = [];
292
-
293
- for (const msg of session.messages) {
294
- if (msg.role === "user") {
295
- const content = msg.content;
296
- const text = typeof content === "string"
297
- ? content
298
- : content
299
- .filter((c): c is { type: "text"; text: string } => c.type === "text")
300
- .map((c) => c.text)
301
- .join("");
302
- if (text.trim()) parts.push(`[User]: ${text.trim()}`);
303
- } else if (msg.role === "assistant") {
304
- const textParts: string[] = [];
305
- const toolCalls: string[] = [];
306
- const content = msg.content;
307
- if (typeof content !== "string") {
308
- for (const c of content) {
309
- if (c.type === "text" && c.text) textParts.push(c.text);
310
- else if (c.type === "toolCall") toolCalls.push(` Tool: ${(c as any).name ?? "unknown"}`);
311
- }
312
- }
313
- if (textParts.length > 0) parts.push(`[Assistant]: ${textParts.join("\n")}`);
314
- if (toolCalls.length > 0) parts.push(`[Tool Calls]:\n${toolCalls.join("\n")}`);
315
- } else if (msg.role === "toolResult") {
316
- const content = msg.content;
317
- const text = typeof content === "string"
318
- ? content
319
- : content
320
- .filter((c): c is { type: "text"; text: string } => c.type === "text")
321
- .map((c) => c.text)
322
- .join("");
323
- const truncated = text.length > 200 ? text.slice(0, 200) + "..." : text;
324
- parts.push(`[Tool Result (${msg.toolName})]: ${truncated}`);
325
- }
326
- }
327
-
328
- return parts.join("\n\n");
329
- }
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import type { AgentSession } from "@earendil-works/pi-coding-agent";
10
+ import { formatMs, describeActivity } from "./widget.js";
10
11
  import {
11
12
  type Component,
12
13
  matchesKey,
@@ -30,13 +31,6 @@ function extractText(content: string | Array<{ type: string; text?: string }>):
30
31
  .join("");
31
32
  }
32
33
 
33
- /** Format duration. */
34
- function formatMs(ms: number): string {
35
- if (ms >= 60_000) return `${(ms / 60_000).toFixed(1)}m`;
36
- if (ms >= 1_000) return `${(ms / 1_000).toFixed(1)}s`;
37
- return `${ms}ms`;
38
- }
39
-
40
34
  /** Format tokens compactly. */
41
35
  function formatTokens(count: number): string {
42
36
  if (count >= 1_000_000) return `${(count / 1_000_000).toFixed(1)}M token`;
@@ -45,18 +39,6 @@ function formatTokens(count: number): string {
45
39
  }
46
40
 
47
41
  /** Describe current activity from active tools. */
48
- function describeActivity(activeTools: Map<string, string>, responseText?: string): string {
49
- if (activeTools.size > 0) {
50
- const names = [...new Set(activeTools.values())];
51
- return names.join(", ") + "…";
52
- }
53
- if (responseText && responseText.trim().length > 0) {
54
- const lastLine = responseText.split("\n").find((l) => l.trim())?.trim() ?? "";
55
- if (lastLine.length > 60) return lastLine.slice(0, 60) + "…";
56
- if (lastLine.length > 0) return lastLine;
57
- }
58
- return "thinking…";
59
- }
60
42
 
61
43
  interface ViewerRecord {
62
44
  type: string;
@@ -1,33 +1,17 @@
1
+ /**
2
+ * @pi-unipi/subagents — Local helpers (not in @pi-unipi/core)
3
+ *
4
+ * boundHelperOutput: bounded output with artifact-to-disk fallback.
5
+ * withHerdrBlocked: wraps a fn with herdr:blocked events.
6
+ */
7
+
8
+ import { emitEvent } from "@pi-unipi/core";
1
9
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
10
  import { chmodSync, lstatSync, mkdirSync, writeFileSync } from "node:fs";
3
11
  import { homedir } from "node:os";
4
12
  import { join } from "node:path";
5
13
  import { randomUUID } from "node:crypto";
6
14
 
7
- export const MODULES = { SUBAGENTS: "subagents" } as const;
8
-
9
- export const UNIPI_EVENTS = {
10
- MODULE_READY: "unipi:module:ready",
11
- BADGE_GENERATE_REQUEST: "unipi:badge:generate:request",
12
- } as const;
13
-
14
- export interface UnipiBadgeGenerateRequestEvent {
15
- conversationSummary?: string;
16
- }
17
-
18
- export function emitEvent(
19
- pi: Pick<ExtensionAPI, "events">,
20
- eventName: string,
21
- payload: unknown,
22
- ): boolean {
23
- try {
24
- pi.events.emit(eventName, payload);
25
- return true;
26
- } catch {
27
- return false;
28
- }
29
- }
30
-
31
15
  export interface BoundedHelperOutput {
32
16
  text: string;
33
17
  truncated: boolean;
@@ -35,7 +19,6 @@ export interface BoundedHelperOutput {
35
19
  artifactPath?: string;
36
20
  }
37
21
 
38
- /** Standalone-package twin of core's bounded-output policy. */
39
22
  const MAX_RAW_HELPER_ARTIFACT_BYTES = 16 * 1024 * 1024;
40
23
 
41
24
  export function boundHelperOutput(
@@ -74,7 +74,6 @@ function loadAgentFromFile(filePath: string, source: "project" | "global"): Agen
74
74
  maxTurns: (frontmatter as any).max_turns as number | undefined,
75
75
  systemPrompt: body.trim(),
76
76
  promptMode: ((frontmatter as any).prompt_mode as "replace" | "append") ?? "replace",
77
- inheritContext: (frontmatter as any).inherit_context as boolean | undefined,
78
77
  runInBackground: (frontmatter as any).run_in_background as boolean | undefined,
79
78
  isolated: (frontmatter as any).isolated as boolean | undefined,
80
79
  enabled: (frontmatter as any).enabled !== false,
@@ -12,12 +12,13 @@ import { Type } from "typebox";
12
12
  import { existsSync, readdirSync } from "node:fs";
13
13
  import { join } from "node:path";
14
14
  import { homedir } from "node:os";
15
- import { boundHelperOutput, emitEvent, MODULES, UNIPI_EVENTS, withHerdrBlocked, type UnipiBadgeGenerateRequestEvent } from "./core-compat.js";
15
+ import { MODULES, UNIPI_EVENTS, emitEvent, type UnipiBadgeGenerateRequestEvent } from "@pi-unipi/core";
16
+ import { boundHelperOutput, withHerdrBlocked } from "./core-compat.js";
16
17
  import { AgentManager } from "./agent-manager.js";
17
18
  import { initConfig } from "./config.js";
18
19
  import { type AgentActivity, type NotificationDetails, BUILTIN_TYPES } from "./types.js";
19
20
  import { ConversationViewer } from "./conversation-viewer.js";
20
- import { AgentWidget } from "./widget.js";
21
+ import { AgentWidget, SPINNER, TOOL_DISPLAY, formatMs, formatTurns, describeActivity } from "./widget.js";
21
22
 
22
23
  /** Get info registry from global */
23
24
  function getInfoRegistry() {
@@ -26,18 +27,8 @@ function getInfoRegistry() {
26
27
 
27
28
  // ---- Formatting helpers (shared between renderers and inline text) ----
28
29
 
29
- const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
30
30
 
31
31
  /** Tool name → human-readable action. */
32
- const TOOL_DISPLAY: Record<string, string> = {
33
- read: "reading",
34
- bash: "running command",
35
- edit: "editing",
36
- write: "writing",
37
- grep: "searching",
38
- find: "finding files",
39
- ls: "listing",
40
- };
41
32
 
42
33
  function formatTokens(count: number): string {
43
34
  if (count >= 1_000_000) return `${(count / 1_000_000).toFixed(1)}M token`;
@@ -45,42 +36,6 @@ function formatTokens(count: number): string {
45
36
  return `${count} token`;
46
37
  }
47
38
 
48
- function formatTurns(turn: number, max?: number | null): string {
49
- return max != null ? `⟳${turn}≤${max}` : `⟳${turn}`;
50
- }
51
-
52
- function formatMs(ms: number): string {
53
- if (ms >= 60_000) return `${(ms / 60_000).toFixed(1)}m`;
54
- if (ms >= 1_000) return `${(ms / 1_000).toFixed(1)}s`;
55
- return `${ms}ms`;
56
- }
57
-
58
- /** Build activity description from active tools. */
59
- function describeActivity(activeTools: Map<string, string>, responseText?: string): string {
60
- if (activeTools.size > 0) {
61
- const groups = new Map<string, number>();
62
- for (const toolName of activeTools.values()) {
63
- const action = TOOL_DISPLAY[toolName] ?? toolName;
64
- groups.set(action, (groups.get(action) ?? 0) + 1);
65
- }
66
- const parts: string[] = [];
67
- for (const [action, count] of groups) {
68
- if (count > 1) {
69
- parts.push(`${action} ${count} ${action === "searching" ? "patterns" : "files"}`);
70
- } else {
71
- parts.push(action);
72
- }
73
- }
74
- return parts.join(", ") + "…";
75
- }
76
- if (responseText && responseText.trim().length > 0) {
77
- const line = responseText.split("\n").find((l) => l.trim())?.trim() ?? "";
78
- if (line.length > 60) return line.slice(0, 60) + "…";
79
- if (line.length > 0) return line;
80
- }
81
- return "thinking…";
82
- }
83
-
84
39
  /** Format tokens safely from session. */
85
40
  function safeFormatTokens(session: any): string {
86
41
  if (!session) return "";
@@ -74,7 +74,6 @@ export interface AgentConfig {
74
74
  maxTurns?: number;
75
75
  systemPrompt: string;
76
76
  promptMode: "replace" | "append";
77
- inheritContext?: boolean;
78
77
  runInBackground?: boolean;
79
78
  isolated?: boolean;
80
79
  memory?: MemoryScope;
@@ -101,16 +100,6 @@ export interface AgentRecord {
101
100
  promise?: Promise<string>;
102
101
  /** Set when result consumed via get_result — suppresses notification. */
103
102
  resultConsumed?: boolean;
104
- /** Files locked by this agent. */
105
- lockedFiles: Set<string>;
106
- }
107
-
108
- /** File lock entry. */
109
- export interface FileLockEntry {
110
- agentId: string;
111
- filePath: string;
112
- promise: Promise<void>;
113
- release: () => void;
114
103
  }
115
104
 
116
105
  /** Extension config. */
@@ -21,13 +21,13 @@ import type { AgentActivity } from "./types.js";
21
21
  const MAX_WIDGET_LINES = 12;
22
22
 
23
23
  /** Braille spinner frames. */
24
- const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
24
+ export const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
25
25
 
26
26
  /** Statuses that indicate error/non-success (for linger behavior). */
27
27
  const ERROR_STATUSES = new Set(["error", "aborted", "stopped"]);
28
28
 
29
29
  /** Tool name → human-readable action for activity descriptions. */
30
- const TOOL_DISPLAY: Record<string, string> = {
30
+ export const TOOL_DISPLAY: Record<string, string> = {
31
31
  read: "reading",
32
32
  bash: "running command",
33
33
  edit: "editing",
@@ -40,14 +40,14 @@ const TOOL_DISPLAY: Record<string, string> = {
40
40
  // ---- Formatting helpers ----
41
41
 
42
42
  /** Format duration. */
43
- function formatMs(ms: number): string {
43
+ export function formatMs(ms: number): string {
44
44
  if (ms >= 60_000) return `${(ms / 60_000).toFixed(1)}m`;
45
45
  if (ms >= 1_000) return `${(ms / 1_000).toFixed(1)}s`;
46
46
  return `${ms}ms`;
47
47
  }
48
48
 
49
49
  /** Format turns with optional max limit: "⟳5≤30" or "⟳5". */
50
- function formatTurns(turn: number, max?: number): string {
50
+ export function formatTurns(turn: number, max?: number): string {
51
51
  return max != null ? `⟳${turn}≤${max}` : `⟳${turn}`;
52
52
  }
53
53
 
@@ -55,7 +55,7 @@ function formatTurns(turn: number, max?: number): string {
55
55
  * Build a human-readable activity string from currently-running tools.
56
56
  * Groups by tool type with counts: "reading 3 files, searching 2 patterns".
57
57
  */
58
- function describeActivity(activeTools: Map<string, string>, responseText?: string): string {
58
+ export function describeActivity(activeTools: Map<string, string>, responseText?: string): string {
59
59
  if (activeTools.size > 0) {
60
60
  const groups = new Map<string, number>();
61
61
  for (const toolName of activeTools.values()) {