@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/api.d.ts +3 -2
- package/dist/api.js +2 -2
- package/dist/cli.js +6 -2
- package/dist/inbox/controller.d.ts +14 -8
- package/dist/inbox/controller.js +133 -40
- package/dist/inbox/convention.d.ts +2 -2
- package/dist/inbox/convention.js +19 -3
- package/dist/inbox/deck-adapter.d.ts +3 -3
- package/dist/inbox/deck-adapter.js +3 -3
- package/dist/inbox/deck-schema.d.ts +2 -2
- package/dist/inbox/deck-schema.js +4 -4
- package/dist/inbox/maintenance.d.ts +4 -0
- package/dist/inbox/maintenance.js +145 -0
- package/dist/inbox/registry.d.ts +8 -0
- package/dist/inbox/registry.js +17 -5
- package/dist/inbox/tickets.d.ts +5 -0
- package/dist/inbox/tickets.js +35 -36
- package/dist/inbox/visual.d.ts +130 -0
- package/dist/inbox/visual.js +747 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/surfaces/inbox-popup.d.ts +1 -1
- package/dist/surfaces/inbox-popup.js +2 -2
- package/dist/tui/app.d.ts +3 -6
- package/dist/tui/app.js +59 -18
- package/dist/tui/render.js +4 -4
- package/dist/tui/tmux.js +2 -1
- package/dist/types.d.ts +48 -11
- package/package.json +2 -3
- package/dist/conversation/reader.d.ts +0 -20
- package/dist/conversation/reader.js +0 -348
- package/dist/visuals/conversation.d.ts +0 -7
- package/dist/visuals/conversation.js +0 -16
- package/dist/visuals/generate.d.ts +0 -11
- package/dist/visuals/generate.js +0 -81
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,
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
140
|
+
const provider = internals.visualProvider;
|
|
141
|
+
if (provider === undefined)
|
|
123
142
|
return;
|
|
124
|
-
const
|
|
125
|
-
const
|
|
143
|
+
const generation = internals.visualGeneration;
|
|
144
|
+
const generationId = randomUUID();
|
|
126
145
|
for (const interaction of interactions) {
|
|
127
|
-
const
|
|
146
|
+
const requestId = randomUUID();
|
|
128
147
|
internals.state.visuals.set(interaction.id, { questionId: interaction.id, content: '', status: 'loading' });
|
|
129
|
-
|
|
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((
|
|
163
|
+
if (!internals.state.interactions.some((candidate) => candidate.id === interaction.id))
|
|
133
164
|
return;
|
|
134
|
-
internals.state.visuals.set(interaction.id,
|
|
135
|
-
? { questionId: interaction.id, content:
|
|
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((
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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,
|
|
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,
|
|
652
|
+
const { responses, completedAt } = await resolveInteractionDir(dir, deck, opts);
|
|
612
653
|
return { responses, completedAt };
|
|
613
654
|
}
|
package/dist/tui/render.js
CHANGED
|
@@ -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
|
|
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
|
-
/**
|
|
72
|
-
|
|
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
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
"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;
|