@crouton-kit/humanloop 0.3.39 → 0.4.1

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.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  export { mountPanel, validateInput, launchTui } from './tui/app.js';
2
- export { defaultGenerateVisual } from './visuals/generate.js';
3
2
  export { launchReview } from './editor/review.js';
4
3
  export { launchReview as review } from './editor/review.js';
5
4
  export type { ReviewOptions } from './editor/review.js';
@@ -15,6 +14,8 @@ export type { SubmitDeckOptions, SubmitReviewOptions } from './inbox/tickets.js'
15
14
  export { dispatchCompletion, reconcileCompletions } from './inbox/completion.js';
16
15
  export { requestFollowUp, cancelFollowUp, submitFollowUpResult, readFollowUp } from './inbox/followup.js';
17
16
  export type { FollowUpRequestEvent, FollowUpRequest, FollowUpResult } from './inbox/followup.js';
17
+ export { VISUAL_CAPABILITY, canonicalizeInteraction, canonicalInteractionJson, parseVisualRequestEvent, readVisualRequest, readVisualResult, readVisualRequestForEvent, startVisualRequest, submitVisualResult, cancelVisualRequest, cancelVisualRequestsForTicket, dispatchVisualCleanup, listVisualCleanupObligations, listVisualCleanupObligationsForRoot, reconcileVisualRequestsForTicket, reconcileStaleVisualRequestsForRoot, } from './inbox/visual.js';
18
+ export type { VisualClaimIdentity, VisualCleanupObligation, VisualProtocolRequest, VisualRequestEvent, VisualProtocolResult, VisualResultSubmission, StartVisualRequestOptions, StartedVisualRequest, VisualStartDeliveryResult, VisualCleanupDeliveryResult, VisualCleanupTask, VisualRequestReconciliation, } from './inbox/visual.js';
18
19
  export { claimTicket, heartbeatClaim, releaseClaim, readTicketClaim } from './inbox/claim.js';
19
20
  export type { TicketClaim, ClaimOptions } from './inbox/claim.js';
20
21
  export { renderMarkdown, checkMarkdown, ensureRenderer, isRendererReady, } from './render/termrender.js';
@@ -23,6 +24,5 @@ export { notifyDeck } from './inbox/deck-factories.js';
23
24
  export type { NotifyDeckOpts } from './inbox/deck-factories.js';
24
25
  export { deckPath, reviewPath, responsePath, progressPath, visualsDir, interactionState, isResolved, isClaimed, atomicWriteJson, readJson, writeResponse, writeProgress, clearProgress, } from './inbox/convention.js';
25
26
  export type { InteractionState } from './inbox/convention.js';
26
- export type { Interaction, InteractionOption, InteractionResponse, InteractionKind, Deck, DeckSource, MountedPanel, MountedPanelOpts, GenerateVisual, VisualBlock, FollowUpState, FeedbackComment, FeedbackResult, ResolutionEnvelope, InboxItem, DisplayOpts, ClaimSummary, TicketSummary, DeckTicketSummary, ReviewTicketSummary, ReviewDescriptor, DeckTicketResult, ReviewTicketResult, CanceledTicketResult, TicketResult, CompletionEvent, } from './types.js';
27
+ export type { Interaction, InteractionOption, InteractionResponse, InteractionKind, Deck, DeckSource, CanonicalInteractionOption, CanonicalInteraction, MountedPanel, MountedPanelOpts, VisualProvider, VisualRequest, VisualHandle, VisualResult, VisualBlock, FollowUpState, FeedbackComment, FeedbackResult, ResolutionEnvelope, InboxItem, DisplayOpts, ClaimSummary, TicketSummary, DeckTicketSummary, ReviewTicketSummary, ReviewDescriptor, DeckTicketResult, ReviewTicketResult, CanceledTicketResult, TicketResult, CompletionEvent, FocusEvent, } from './types.js';
27
28
  export type { Key } from './tui/terminal.js';
28
- export type { ConversationMessage } from './conversation/reader.js';
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  export { mountPanel, validateInput, launchTui } from './tui/app.js';
2
- export { defaultGenerateVisual } from './visuals/generate.js';
3
2
  export { launchReview } from './editor/review.js';
4
3
  export { launchReview as review } from './editor/review.js';
5
4
  // Interaction-layer surface (SDK).
@@ -12,6 +11,7 @@ export { registerInboxRoot, unregisterInboxRoot, listInboxRoots, managedInboxRoo
12
11
  export { submitDeck, submitReview, readTicketResult, finalizeDeck, finalizeReview, completeDeck, completeReview, cancelTicket, cancelTicketResult, } from './inbox/tickets.js';
13
12
  export { dispatchCompletion, reconcileCompletions } from './inbox/completion.js';
14
13
  export { requestFollowUp, cancelFollowUp, submitFollowUpResult, readFollowUp } from './inbox/followup.js';
14
+ export { VISUAL_CAPABILITY, canonicalizeInteraction, canonicalInteractionJson, parseVisualRequestEvent, readVisualRequest, readVisualResult, readVisualRequestForEvent, startVisualRequest, submitVisualResult, cancelVisualRequest, cancelVisualRequestsForTicket, dispatchVisualCleanup, listVisualCleanupObligations, listVisualCleanupObligationsForRoot, reconcileVisualRequestsForTicket, reconcileStaleVisualRequestsForRoot, } from './inbox/visual.js';
15
15
  export { claimTicket, heartbeatClaim, releaseClaim, readTicketClaim } from './inbox/claim.js';
16
16
  // Renderer binding — the sole org-wide termrender caller. Consumers
17
17
  // (sisyphus md-render / ask-schema) route markdown through these.
@@ -1,2 +1,2 @@
1
1
  /** Run the inbox controller in a popup-owned TTY and accept graceful close requests. */
2
- export declare function openInboxPopup(controlSocket?: string, roots?: string[]): Promise<void>;
2
+ export declare function openInboxPopup(controlSocket?: string, roots?: string[], targetPane?: string): Promise<void>;
@@ -2,8 +2,8 @@ import { createServer } from 'node:net';
2
2
  import { rmSync } from 'node:fs';
3
3
  import { InboxController } from '../inbox/controller.js';
4
4
  /** Run the inbox controller in a popup-owned TTY and accept graceful close requests. */
5
- export async function openInboxPopup(controlSocket, roots) {
6
- const controller = new InboxController({ roots });
5
+ export async function openInboxPopup(controlSocket, roots, targetPane) {
6
+ const controller = new InboxController({ roots, targetPane });
7
7
  const server = controlSocket === undefined ? undefined : createServer((connection) => {
8
8
  connection.once('data', (data) => {
9
9
  if (data.toString('utf8').trim() === 'close')
package/dist/tui/app.d.ts CHANGED
@@ -1,14 +1,11 @@
1
- import type { Deck, InteractionResponse, MountedPanel, MountedPanelOpts, GenerateVisual } from '../types.js';
1
+ import type { Deck, InteractionResponse, MountedPanel, MountedPanelOpts, VisualProvider } from '../types.js';
2
2
  /** Validate an arbitrary parsed value as a Deck. Delegates to the canonical
3
3
  * Zod validator in `inbox/deck-schema.ts` (the single source of truth shared
4
4
  * with sisyphus). Kept exported for back-compat. */
5
5
  export declare function validateInput(parsed: unknown): Deck;
6
6
  export declare function mountPanel(opts: MountedPanelOpts): MountedPanel;
7
7
  export interface ResolveDirOpts {
8
- /** Originating provider session id → per-interaction visual context from history. */
9
- sessionId?: string;
10
- /** Explicit visual generator; overrides the sessionId default. */
11
- generateVisual?: GenerateVisual;
8
+ visualProvider?: VisualProvider;
12
9
  cols?: number;
13
10
  rows?: number;
14
11
  }
@@ -31,7 +28,7 @@ export declare function resolveInteractionDir(dir: string, deck: Deck, opts?: Re
31
28
  responsePath: string;
32
29
  deck: Deck;
33
30
  }>;
34
- export declare function launchTui(decisionsPath: string, sessionId?: string): Promise<{
31
+ export declare function launchTui(decisionsPath: string, opts?: ResolveDirOpts): Promise<{
35
32
  responses: InteractionResponse[];
36
33
  completedAt: string;
37
34
  }>;
package/dist/tui/app.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import { readFileSync, existsSync, writeFileSync, renameSync, unlinkSync, statSync } from 'fs';
2
+ import { randomUUID } from 'node:crypto';
2
3
  import { dirname, resolve as resolvePath } from 'node:path';
3
4
  import { setupTerminal, restoreTerminal, parseKeypress, getTerminalSize } from './terminal.js';
4
5
  import { diffFrame, renderOverview, renderItemReview, renderFinal, renderHandoff, clampItemReviewScroll } from './render.js';
5
6
  import { handleKeypress, assignShortcuts } from './input.js';
6
- import { visualGeneratorForConversationSession } from '../visuals/conversation.js';
7
- import { visualRenderWidth } from '../visuals/generate.js';
8
7
  import { renderMarkdown } from '../render/termrender.js';
9
8
  import { editBufferInEditor } from '../editor/roundtrip.js';
10
9
  import { validateDeck } from '../inbox/deck-schema.js';
10
+ import { canonicalizeInteraction } from '../inbox/visual.js';
11
11
  import { progressPath as progressPathFor, deckPath as deckPathFor, writeResponse, clearProgress } from '../inbox/convention.js';
12
12
  import { startWebServer } from '../browser/server.js';
13
13
  import { openBrowser } from '../browser/open.js';
@@ -107,10 +107,13 @@ function rebindPersist(internals) {
107
107
  internals.callbacks.onProgress?.(responses);
108
108
  };
109
109
  }
110
+ function visualRenderWidth(cols) {
111
+ return Math.max(1, Math.min(cols - 4, 76));
112
+ }
110
113
  function renderVisualMarkdown(markdown, cols) {
111
114
  return renderMarkdown(markdown, visualRenderWidth(cols)).join('\n');
112
115
  }
113
- /** Reflow ready visual context locally; resizing must never invoke the model. */
116
+ /** Reflow ready Visual Markdown locally without issuing another request. */
114
117
  function rerenderVisuals(internals) {
115
118
  for (const [id, visual] of internals.state.visuals) {
116
119
  if (visual.status !== 'ready' || visual.markdown === undefined)
@@ -118,27 +121,58 @@ function rerenderVisuals(internals) {
118
121
  internals.state.visuals.set(id, { ...visual, content: renderVisualMarkdown(visual.markdown, internals.cols) });
119
122
  }
120
123
  }
124
+ function visualRequestInteraction(interaction) {
125
+ return canonicalizeInteraction(interaction);
126
+ }
127
+ function retireVisuals(internals) {
128
+ internals.visualGeneration += 1;
129
+ const handles = [...internals.visualHandles.values()];
130
+ internals.visualHandles.clear();
131
+ internals.state.visuals.clear();
132
+ for (const handle of handles) {
133
+ try {
134
+ handle.cancel();
135
+ }
136
+ catch { /* stale UI currency remains authoritative even if a host cancel fails */ }
137
+ }
138
+ }
121
139
  function fireVisuals(internals, interactions) {
122
- if (internals.generateVisual === undefined)
140
+ const provider = internals.visualProvider;
141
+ if (provider === undefined)
123
142
  return;
124
- const gen = internals.generateVisual;
125
- const generation = ++internals.visualGeneration;
143
+ const generation = internals.visualGeneration;
144
+ const generationId = randomUUID();
126
145
  for (const interaction of interactions) {
127
- const generationCols = internals.cols;
146
+ const requestId = randomUUID();
128
147
  internals.state.visuals.set(interaction.id, { questionId: interaction.id, content: '', status: 'loading' });
129
- gen(interaction, generationCols).then((r) => {
148
+ let handle;
149
+ try {
150
+ handle = provider({ requestId, generationId, interaction: visualRequestInteraction(interaction) });
151
+ }
152
+ catch {
153
+ internals.state.visuals.set(interaction.id, { questionId: interaction.id, content: '', status: 'error' });
154
+ continue;
155
+ }
156
+ internals.visualHandles.set(requestId, handle);
157
+ handle.result.then((result) => {
158
+ if (internals.visualHandles.get(requestId) !== handle)
159
+ return;
160
+ internals.visualHandles.delete(requestId);
130
161
  if (!internals.mounted || generation !== internals.visualGeneration)
131
162
  return;
132
- if (!internals.state.interactions.some((x) => x.id === interaction.id))
163
+ if (!internals.state.interactions.some((candidate) => candidate.id === interaction.id))
133
164
  return;
134
- internals.state.visuals.set(interaction.id, r.ok
135
- ? { questionId: interaction.id, content: internals.cols === generationCols ? r.ansi : renderVisualMarkdown(r.markdown, internals.cols), markdown: r.markdown, status: 'ready' }
165
+ internals.state.visuals.set(interaction.id, result.status === 'ready' && typeof result.markdown === 'string'
166
+ ? { questionId: interaction.id, content: renderVisualMarkdown(result.markdown, internals.cols), markdown: result.markdown, status: 'ready' }
136
167
  : { questionId: interaction.id, content: '', status: 'error' });
137
168
  internals.callbacks.onDirty?.();
138
169
  }).catch(() => {
170
+ if (internals.visualHandles.get(requestId) !== handle)
171
+ return;
172
+ internals.visualHandles.delete(requestId);
139
173
  if (!internals.mounted || generation !== internals.visualGeneration)
140
174
  return;
141
- if (!internals.state.interactions.some((x) => x.id === interaction.id))
175
+ if (!internals.state.interactions.some((candidate) => candidate.id === interaction.id))
142
176
  return;
143
177
  internals.state.visuals.set(interaction.id, { questionId: interaction.id, content: '', status: 'error' });
144
178
  internals.callbacks.onDirty?.();
@@ -154,8 +188,9 @@ export function mountPanel(opts) {
154
188
  cols: opts.cols,
155
189
  rows: opts.rows,
156
190
  mounted: true,
157
- generateVisual: opts.generateVisual,
191
+ visualProvider: opts.visualProvider,
158
192
  visualGeneration: 0,
193
+ visualHandles: new Map(),
159
194
  progressPath: opts.progressPath,
160
195
  followUpAvailable,
161
196
  callbacks: {
@@ -230,14 +265,21 @@ export function mountPanel(opts) {
230
265
  return renderLines();
231
266
  },
232
267
  unmount() {
268
+ if (!internals.mounted)
269
+ return;
233
270
  internals.mounted = false;
234
- internals.state.visuals.clear();
271
+ retireVisuals(internals);
235
272
  internals.state.persist = undefined;
236
273
  },
237
274
  loadDeck(deck, loadOpts) {
238
275
  if (!internals.mounted)
239
276
  return;
240
277
  const prior = collectResponses(internals.state);
278
+ // Retire before replacing capability: old handles become non-current,
279
+ // then the next generation uses only the freshly derived provider.
280
+ retireVisuals(internals);
281
+ if (loadOpts !== undefined && 'visualProvider' in loadOpts)
282
+ internals.visualProvider = loadOpts.visualProvider;
241
283
  internals.state = buildInitialState(deck, internals.callbacks.onEditorRequest !== undefined, internals.followUpAvailable);
242
284
  if (loadOpts !== undefined && loadOpts.progressPath !== undefined) {
243
285
  internals.progressPath = loadOpts.progressPath;
@@ -321,7 +363,6 @@ export async function resolveInteractionDir(dir, deck, opts = {}) {
321
363
  const term = getTerminalSize();
322
364
  const cols = opts.cols ?? term.cols;
323
365
  const rows = opts.rows ?? term.rows;
324
- const generateVisual = opts.generateVisual ?? (opts.sessionId === undefined ? undefined : visualGeneratorForConversationSession(opts.sessionId));
325
366
  return new Promise((resolve) => {
326
367
  let panel = null;
327
368
  let prevFrameLocal = [];
@@ -408,7 +449,7 @@ export async function resolveInteractionDir(dir, deck, opts = {}) {
408
449
  progressPath: progressPathFor(dir),
409
450
  cols,
410
451
  rows,
411
- generateVisual,
452
+ visualProvider: opts.visualProvider,
412
453
  onEditorRequest: () => {
413
454
  const buffer = panel?.getInputBuffer();
414
455
  if (buffer !== undefined)
@@ -599,7 +640,7 @@ export async function resolveInteractionDir(dir, deck, opts = {}) {
599
640
  }
600
641
  // ── launchTui — file-path entry over the dir resolver (a kept public export
601
642
  // per the interaction-layer plan; consumed until consumers move to ask()) ──
602
- export async function launchTui(decisionsPath, sessionId) {
643
+ export async function launchTui(decisionsPath, opts = {}) {
603
644
  if (!existsSync(decisionsPath)) {
604
645
  throw new Error(`Decisions file not found: ${decisionsPath}`);
605
646
  }
@@ -608,6 +649,6 @@ export async function launchTui(decisionsPath, sessionId) {
608
649
  // The interaction dir is the deck file's directory; progress/response live
609
650
  // there per the convention.
610
651
  const dir = dirname(resolvePath(decisionsPath));
611
- const { responses, completedAt } = await resolveInteractionDir(dir, deck, { sessionId });
652
+ const { responses, completedAt } = await resolveInteractionDir(dir, deck, opts);
612
653
  return { responses, completedAt };
613
654
  }
@@ -219,7 +219,7 @@ function buildItemReviewLayout(state, cols, rows) {
219
219
  postLines.push(` ${DIM}enter${RESET} submit ${DIM}^J/⌥⏎${RESET} newline${state.editorAvailable ? ` ${DIM}^O${RESET} editor` : ''} ${DIM}esc${RESET} cancel`);
220
220
  }
221
221
  else {
222
- postLines.push(...renderActions(interaction, state.selectedAction, maxW, response));
222
+ postLines.push(...renderActions(interaction, state.selectedAction, maxW, response, state.followUp?.status !== 'running'));
223
223
  }
224
224
  // Transient hint (e.g. an empty multi-select Enter that was rejected). Sits
225
225
  // just above the footer; cleared on the next keypress.
@@ -305,7 +305,7 @@ export function renderItemReview(state, cols, rows) {
305
305
  // whole block when the terminal is wider than that.
306
306
  return centerHorizontal(clamped, cols, maxW + 2);
307
307
  }
308
- function renderActions(interaction, selectedAction, maxW, existing) {
308
+ function renderActions(interaction, selectedAction, maxW, existing, showFocus = true) {
309
309
  const lines = [];
310
310
  const opts = interaction.options;
311
311
  // Prefix on first row: " X [s] " — 2 + 1 (cursor) + 1 + 3 ([s]) + 1 = 8 visible cols.
@@ -318,7 +318,7 @@ function renderActions(interaction, selectedAction, maxW, existing) {
318
318
  const optionComments = existing !== undefined ? existing.optionComments : undefined;
319
319
  for (let i = 0; i < opts.length; i++) {
320
320
  const o = opts[i];
321
- const cursor = i === selectedAction ? `${CYAN}▸${RESET}` : ' ';
321
+ const cursor = showFocus && i === selectedAction ? `${CYAN}▸${RESET}` : ' ';
322
322
  const sc = o.shortcut === undefined ? ' ' : o.shortcut;
323
323
  const keyBadge = `${DIM}[${sc}]${RESET}`;
324
324
  const box = multi
@@ -346,7 +346,7 @@ function renderActions(interaction, selectedAction, maxW, existing) {
346
346
  }
347
347
  }
348
348
  if (interaction.allowFreetext && opts.length > 0) {
349
- const cursor = opts.length === selectedAction ? `${CYAN}▸${RESET}` : ' ';
349
+ const cursor = showFocus && opts.length === selectedAction ? `${CYAN}▸${RESET}` : ' ';
350
350
  let label;
351
351
  if (interaction.freetextLabel !== undefined)
352
352
  label = interaction.freetextLabel;
package/dist/tui/tmux.js CHANGED
@@ -87,7 +87,8 @@ export async function toggleInboxPopup(target) {
87
87
  }
88
88
  if (existsSync(paths.controlSocket))
89
89
  rmSync(paths.controlSocket, { force: true });
90
- const command = `${quote(process.execPath)} ${quote(fileURLToPath(new URL('../cli.js', import.meta.url)))} inbox open --control-socket ${quote(paths.controlSocket)}`;
90
+ const targetPaneArg = resolved.targetPane === undefined ? '' : ` --target-pane ${quote(resolved.targetPane)}`;
91
+ const command = `${quote(process.execPath)} ${quote(fileURLToPath(new URL('../cli.js', import.meta.url)))} inbox open --control-socket ${quote(paths.controlSocket)}${targetPaneArg}`;
91
92
  const result = await launchPopup(socket, resolved, paths.controlSocket, command);
92
93
  logPopupEvent('toggle.completed', { ...resolved, controlSocket: paths.controlSocket, result });
93
94
  return result;
package/dist/types.d.ts CHANGED
@@ -68,9 +68,8 @@ export interface DeckSource {
68
68
  * a crouter canvas node. Lets per-node attention scoping attribute the ask
69
69
  * to the node that raised it rather than every sibling sharing the cwd. */
70
70
  nodeId?: string;
71
- /** Durable identifier of the conversation session that originated this ask.
72
- * It is explicit metadata, never inferred from node identity or labels. */
73
- originatingConversationSessionId?: string;
71
+ /** Host-owned durable Visual capability marker. A registered handler is also required. */
72
+ visual?: 'humanloop.visual/v1';
74
73
  }
75
74
  export interface Deck {
76
75
  title?: string;
@@ -248,6 +247,16 @@ export interface CompletionEvent {
248
247
  outcome: 'resolved' | 'canceled';
249
248
  responsePath: string;
250
249
  }
250
+ /** A popup request to reveal the host surface that created a ticket. The root's
251
+ * trusted focus handler decides what that means; humanloop only supplies the
252
+ * selected ticket and the tmux pane underneath the popup. */
253
+ export interface FocusEvent {
254
+ schema: 'humanloop.focus/v1';
255
+ root: string;
256
+ dir: string;
257
+ ticketId: string;
258
+ targetPane: string;
259
+ }
251
260
  /** Options for `display()` — the live-watch tmux pane surface. The pane always
252
261
  * watches the file and live-updates on edits; there is no non-watched mode. */
253
262
  export interface DisplayOpts {
@@ -256,20 +265,47 @@ export interface DisplayOpts {
256
265
  /** Pane budget per window before `'auto'` opens a new window. Default 3. */
257
266
  maxPanes?: number;
258
267
  }
259
- export type GenerateVisual = (interaction: Interaction,
260
- /** Render width owned by the mounting surface. */
261
- cols: number) => Promise<{
262
- ok: true;
263
- ansi: string;
268
+ /** Width-free Interaction snapshot shared by the panel and durable Visual protocol. */
269
+ export interface CanonicalInteractionOption {
270
+ id: string;
271
+ label: string;
272
+ description?: string;
273
+ }
274
+ export interface CanonicalInteraction {
275
+ id: string;
276
+ title: string;
277
+ subtitle?: string;
278
+ body?: string;
279
+ options: CanonicalInteractionOption[];
280
+ multiSelect?: boolean;
281
+ allowFreetext?: boolean;
282
+ freetextLabel?: string;
283
+ kind?: InteractionKind;
284
+ preAnswered?: InteractionPreAnswer;
285
+ }
286
+ export interface VisualRequest {
287
+ requestId: string;
288
+ generationId: string;
289
+ interaction: CanonicalInteraction;
290
+ }
291
+ export type VisualResult = {
292
+ status: 'ready';
264
293
  markdown: string;
265
294
  } | {
266
- ok: false;
295
+ status: 'error';
267
296
  error: string;
268
- }>;
297
+ };
298
+ export interface VisualHandle {
299
+ result: Promise<VisualResult>;
300
+ /** Synchronous and idempotent. A canceled handle has no renderable result. */
301
+ cancel(): void;
302
+ }
303
+ /** Host-injected, Markdown-only Visual capability. Rendering width belongs to the panel. */
304
+ export type VisualProvider = (request: VisualRequest) => VisualHandle;
269
305
  export interface MountedPanelOpts {
270
306
  deck: Deck;
271
307
  progressPath?: string;
272
- generateVisual?: GenerateVisual;
308
+ visualProvider?: VisualProvider;
273
309
  /** Host callback for Ctrl+O while a comment/freetext buffer is active. */
274
310
  onEditorRequest?: () => void;
275
311
  followUpAvailable?: boolean;
@@ -293,6 +329,7 @@ export interface MountedPanel {
293
329
  unmount(): void;
294
330
  loadDeck(deck: Deck, opts?: {
295
331
  progressPath?: string;
332
+ visualProvider?: VisualProvider;
296
333
  }): void;
297
334
  setFollowUpHandlers(available: boolean, onRequest?: (question: string) => void, onCancel?: () => void): void;
298
335
  setFollowUpState(state: FollowUpState): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crouton-kit/humanloop",
3
- "version": "0.3.39",
3
+ "version": "0.4.1",
4
4
  "description": "Human-in-the-loop decision TUI — agents write questions, humans answer them",
5
5
  "engines": {
6
6
  "node": ">=22.19.0"
@@ -36,8 +36,6 @@
36
36
  "test": "node scripts/run-tests.mjs"
37
37
  },
38
38
  "dependencies": {
39
- "@earendil-works/pi-coding-agent": "^0.80.6",
40
- "@r-cli/sdk": "^1.3.0",
41
39
  "commander": "^13.0.0",
42
40
  "string-width": "^7.0.0",
43
41
  "ws": "^8.21.0",
@@ -46,6 +44,7 @@
46
44
  "devDependencies": {
47
45
  "@types/node": "^22.0.0",
48
46
  "@types/ws": "^8.18.1",
47
+ "esbuild": "^0.27.7",
49
48
  "tsx": "^4.0.0",
50
49
  "typescript": "^5.7.0"
51
50
  }
@@ -1,20 +0,0 @@
1
- export interface ConversationMessage {
2
- role: 'user' | 'assistant';
3
- content: string;
4
- }
5
- export type ConversationReadErrorCode = 'session_not_found' | 'session_ambiguous' | 'session_unreadable' | 'session_id_mismatch' | 'conversation_empty';
6
- export declare class ConversationReadError extends Error {
7
- readonly code: ConversationReadErrorCode;
8
- constructor(code: ConversationReadErrorCode);
9
- }
10
- export interface ConversationReaderOptions {
11
- /** Claude SQLite path override for a real-store fixture; production uses Claude's standard store. */
12
- claudeDbPath?: string;
13
- }
14
- /** Read messages from Claude's local conversation store. */
15
- export declare function readConversation(sessionId: string, options?: ConversationReaderOptions): ConversationMessage[];
16
- /** Resolve a pi session by exact header id and return its active useful context. */
17
- export declare function readPiConversationText(sessionId: string): Promise<string>;
18
- /** Resolve by exact membership across provider stores, never by session-id shape. */
19
- export declare function readConversationText(sessionId: string, options?: ConversationReaderOptions): Promise<string>;
20
- export declare function findRecentSessionId(cwd?: string): string | null;