@crouton-kit/humanloop 0.1.4 → 0.2.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 +35 -0
- package/dist/api.js +119 -0
- package/dist/cli.js +348 -97
- package/dist/editor/review.d.ts +24 -0
- package/dist/editor/review.js +425 -0
- package/dist/inbox/convention.d.ts +17 -0
- package/dist/inbox/convention.js +87 -0
- package/dist/inbox/deck-schema.d.ts +41 -0
- package/dist/inbox/deck-schema.js +109 -0
- package/dist/inbox/scan.d.ts +2 -0
- package/dist/inbox/scan.js +62 -0
- package/dist/inbox/tui.d.ts +9 -0
- package/dist/inbox/tui.js +158 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.js +13 -0
- package/dist/render/termrender.d.ts +36 -0
- package/dist/render/termrender.js +236 -0
- package/dist/render/version.d.ts +1 -0
- package/dist/render/version.js +1 -0
- package/dist/scripts/install-renderer.d.ts +2 -0
- package/dist/scripts/install-renderer.js +16 -0
- package/dist/surfaces/display.d.ts +5 -0
- package/dist/surfaces/display.js +19 -0
- package/dist/tui/app.d.ts +24 -1
- package/dist/tui/app.js +48 -113
- package/dist/tui/render.js +2 -42
- package/dist/tui/tmux.d.ts +4 -6
- package/dist/tui/tmux.js +6 -4
- package/dist/types.d.ts +65 -0
- package/dist/visuals/generate.js +2 -27
- package/package.json +4 -2
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Deck, ResolutionEnvelope, GenerateVisual } from './types.js';
|
|
2
|
+
export interface AskOpts {
|
|
3
|
+
/** Interaction directory. Defaults to a managed temp dir under os.tmpdir(). */
|
|
4
|
+
dir?: string;
|
|
5
|
+
sessionId?: string;
|
|
6
|
+
cols?: number;
|
|
7
|
+
rows?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Resolve a deck against an interaction directory and return the resolution
|
|
11
|
+
* envelope. Writes `<dir>/deck.json` (the request, per the convention) and,
|
|
12
|
+
* on completion, `<dir>/response.json`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function ask(deck: Deck, opts?: AskOpts): Promise<ResolutionEnvelope>;
|
|
15
|
+
export interface ApproveOpts {
|
|
16
|
+
subtitle?: string;
|
|
17
|
+
body?: string;
|
|
18
|
+
dir?: string;
|
|
19
|
+
sessionId?: string;
|
|
20
|
+
}
|
|
21
|
+
/** Sugar: a single `kind:'validation'` Yes/No interaction. */
|
|
22
|
+
export declare function approve(title: string, opts?: ApproveOpts): Promise<boolean>;
|
|
23
|
+
/** Sugar: a single `kind:'notify'` acknowledgement. */
|
|
24
|
+
export declare function notify(title: string, body?: string): Promise<void>;
|
|
25
|
+
export interface InboxOpts {
|
|
26
|
+
cols?: number;
|
|
27
|
+
rows?: number;
|
|
28
|
+
generateVisual?: GenerateVisual;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* List → resolve loop across `roots`. Shows pending interactions, lets the
|
|
32
|
+
* human pick one, resolves it (writing its `response.json`), then rescans —
|
|
33
|
+
* resolved items drop out — until the human quits or nothing is pending.
|
|
34
|
+
*/
|
|
35
|
+
export declare function inbox(roots: string[], opts?: InboxOpts): Promise<void>;
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { mkdtempSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { resolveInteractionDir } from './tui/app.js';
|
|
5
|
+
import { scanInbox } from './inbox/scan.js';
|
|
6
|
+
import { pickFromInbox } from './inbox/tui.js';
|
|
7
|
+
import { deckPath, atomicWriteJson, readJson } from './inbox/convention.js';
|
|
8
|
+
import { getTerminalSize } from './tui/terminal.js';
|
|
9
|
+
const RESPONSE_SCHEMA_ID = 'humanloop.response/v2';
|
|
10
|
+
function managedDir() {
|
|
11
|
+
return mkdtempSync(join(tmpdir(), 'hl-ix-'));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Deterministic, no-LLM resolution summary — one line per answered
|
|
15
|
+
* interaction: `"<title>: <option label>[ — <freetext>]"`.
|
|
16
|
+
*/
|
|
17
|
+
function buildSummary(deck, responses) {
|
|
18
|
+
const byId = new Map(responses.map((r) => [r.id, r]));
|
|
19
|
+
const lines = [];
|
|
20
|
+
for (const it of deck.interactions) {
|
|
21
|
+
const r = byId.get(it.id);
|
|
22
|
+
if (r === undefined)
|
|
23
|
+
continue;
|
|
24
|
+
const opt = r.selectedOptionId !== undefined
|
|
25
|
+
? it.options.find((o) => o.id === r.selectedOptionId)
|
|
26
|
+
: undefined;
|
|
27
|
+
const ft = r.freetext !== undefined && r.freetext !== '' ? r.freetext : undefined;
|
|
28
|
+
let val;
|
|
29
|
+
if (opt !== undefined && ft !== undefined)
|
|
30
|
+
val = `${opt.label} — ${ft}`;
|
|
31
|
+
else if (opt !== undefined)
|
|
32
|
+
val = opt.label;
|
|
33
|
+
else if (ft !== undefined)
|
|
34
|
+
val = ft;
|
|
35
|
+
else
|
|
36
|
+
val = '(skipped)';
|
|
37
|
+
lines.push(`${it.title}: ${val}`);
|
|
38
|
+
}
|
|
39
|
+
return lines.join('\n');
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Resolve a deck against an interaction directory and return the resolution
|
|
43
|
+
* envelope. Writes `<dir>/deck.json` (the request, per the convention) and,
|
|
44
|
+
* on completion, `<dir>/response.json`.
|
|
45
|
+
*/
|
|
46
|
+
export async function ask(deck, opts = {}) {
|
|
47
|
+
const dir = opts.dir ?? managedDir();
|
|
48
|
+
mkdirSync(dir, { recursive: true });
|
|
49
|
+
atomicWriteJson(deckPath(dir), deck);
|
|
50
|
+
const { responses, completedAt, responsePath } = await resolveInteractionDir(dir, deck, {
|
|
51
|
+
sessionId: opts.sessionId,
|
|
52
|
+
cols: opts.cols,
|
|
53
|
+
rows: opts.rows,
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
summary: buildSummary(deck, responses),
|
|
57
|
+
responsePath,
|
|
58
|
+
schema: RESPONSE_SCHEMA_ID,
|
|
59
|
+
responses,
|
|
60
|
+
completedAt,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** Sugar: a single `kind:'validation'` Yes/No interaction. */
|
|
64
|
+
export async function approve(title, opts = {}) {
|
|
65
|
+
const deck = {
|
|
66
|
+
interactions: [{
|
|
67
|
+
id: 'approve',
|
|
68
|
+
title,
|
|
69
|
+
...(opts.subtitle !== undefined ? { subtitle: opts.subtitle } : {}),
|
|
70
|
+
...(opts.body !== undefined ? { body: opts.body } : {}),
|
|
71
|
+
kind: 'validation',
|
|
72
|
+
options: [
|
|
73
|
+
{ id: 'yes', label: 'Yes' },
|
|
74
|
+
{ id: 'no', label: 'No' },
|
|
75
|
+
],
|
|
76
|
+
}],
|
|
77
|
+
};
|
|
78
|
+
const env = await ask(deck, { dir: opts.dir, sessionId: opts.sessionId });
|
|
79
|
+
return env.responses[0]?.selectedOptionId === 'yes';
|
|
80
|
+
}
|
|
81
|
+
/** Sugar: a single `kind:'notify'` acknowledgement. */
|
|
82
|
+
export async function notify(title, body) {
|
|
83
|
+
const deck = {
|
|
84
|
+
interactions: [{
|
|
85
|
+
id: 'notify',
|
|
86
|
+
title,
|
|
87
|
+
...(body !== undefined ? { body } : {}),
|
|
88
|
+
kind: 'notify',
|
|
89
|
+
options: [{ id: 'ok', label: 'OK' }],
|
|
90
|
+
}],
|
|
91
|
+
};
|
|
92
|
+
await ask(deck, {});
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* List → resolve loop across `roots`. Shows pending interactions, lets the
|
|
96
|
+
* human pick one, resolves it (writing its `response.json`), then rescans —
|
|
97
|
+
* resolved items drop out — until the human quits or nothing is pending.
|
|
98
|
+
*/
|
|
99
|
+
export async function inbox(roots, opts = {}) {
|
|
100
|
+
for (;;) {
|
|
101
|
+
const items = scanInbox(roots);
|
|
102
|
+
if (items.length === 0)
|
|
103
|
+
return;
|
|
104
|
+
const term = getTerminalSize();
|
|
105
|
+
const cols = opts.cols ?? term.cols;
|
|
106
|
+
const rows = opts.rows ?? term.rows;
|
|
107
|
+
const picked = await pickFromInbox(items, { cols, rows });
|
|
108
|
+
if (picked === null)
|
|
109
|
+
return;
|
|
110
|
+
const deck = readJson(deckPath(picked.dir));
|
|
111
|
+
if (deck === null)
|
|
112
|
+
continue; // raced/removed — rescan
|
|
113
|
+
await resolveInteractionDir(picked.dir, deck, {
|
|
114
|
+
generateVisual: opts.generateVisual,
|
|
115
|
+
cols,
|
|
116
|
+
rows,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|