@metaphi-ai/hum 0.1.4 → 0.1.6
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/LICENSE +16 -0
- package/dist/cli.mjs +245 -32
- package/package.json +2 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Copyright (c) 2026 Metaphi, Inc. All rights reserved.
|
|
2
|
+
|
|
3
|
+
Hum is proprietary software of Metaphi, Inc.
|
|
4
|
+
|
|
5
|
+
Metaphi, Inc. grants users it has authorized a personal, non-exclusive,
|
|
6
|
+
non-transferable, revocable right to install and run the distributed
|
|
7
|
+
packages (hum-cli on PyPI, @metaphi-ai/hum on npm) for use with Metaphi
|
|
8
|
+
services.
|
|
9
|
+
|
|
10
|
+
No other right is granted. Do not copy, modify, distribute, sublicense,
|
|
11
|
+
reverse engineer, or create derivative works from this software, in whole
|
|
12
|
+
or in part, without prior written permission from Metaphi, Inc.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED. IN NO EVENT SHALL METAPHI, INC. BE LIABLE FOR ANY CLAIM, DAMAGES
|
|
16
|
+
OR OTHER LIABILITY ARISING FROM THE USE OF THE SOFTWARE.
|
package/dist/cli.mjs
CHANGED
|
@@ -1257,7 +1257,7 @@ var require_react_development = __commonJS({
|
|
|
1257
1257
|
}
|
|
1258
1258
|
return lazyType;
|
|
1259
1259
|
}
|
|
1260
|
-
function
|
|
1260
|
+
function forwardRef3(render2) {
|
|
1261
1261
|
{
|
|
1262
1262
|
if (render2 != null && render2.$$typeof === REACT_MEMO_TYPE) {
|
|
1263
1263
|
error("forwardRef requires a render function but received a `memo` component. Instead of forwardRef(memo(...)), use memo(forwardRef(...)).");
|
|
@@ -1402,7 +1402,7 @@ var require_react_development = __commonJS({
|
|
|
1402
1402
|
var dispatcher = resolveDispatcher();
|
|
1403
1403
|
return dispatcher.useMemo(create2, deps);
|
|
1404
1404
|
}
|
|
1405
|
-
function
|
|
1405
|
+
function useImperativeHandle2(ref, create2, deps) {
|
|
1406
1406
|
var dispatcher = resolveDispatcher();
|
|
1407
1407
|
return dispatcher.useImperativeHandle(ref, create2, deps);
|
|
1408
1408
|
}
|
|
@@ -2155,7 +2155,7 @@ var require_react_development = __commonJS({
|
|
|
2155
2155
|
exports.createElement = createElement$1;
|
|
2156
2156
|
exports.createFactory = createFactory;
|
|
2157
2157
|
exports.createRef = createRef;
|
|
2158
|
-
exports.forwardRef =
|
|
2158
|
+
exports.forwardRef = forwardRef3;
|
|
2159
2159
|
exports.isValidElement = isValidElement;
|
|
2160
2160
|
exports.lazy = lazy;
|
|
2161
2161
|
exports.memo = memo;
|
|
@@ -2167,7 +2167,7 @@ var require_react_development = __commonJS({
|
|
|
2167
2167
|
exports.useDeferredValue = useDeferredValue;
|
|
2168
2168
|
exports.useEffect = useEffect5;
|
|
2169
2169
|
exports.useId = useId;
|
|
2170
|
-
exports.useImperativeHandle =
|
|
2170
|
+
exports.useImperativeHandle = useImperativeHandle2;
|
|
2171
2171
|
exports.useInsertionEffect = useInsertionEffect;
|
|
2172
2172
|
exports.useLayoutEffect = useLayoutEffect2;
|
|
2173
2173
|
exports.useMemo = useMemo3;
|
|
@@ -83569,22 +83569,34 @@ function renderMarkdown(text, width) {
|
|
|
83569
83569
|
// src/input.jsx
|
|
83570
83570
|
var import_react23 = __toESM(require_react(), 1);
|
|
83571
83571
|
var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
|
|
83572
|
-
|
|
83572
|
+
var MultilineInput = (0, import_react23.forwardRef)(function MultilineInput2({
|
|
83573
83573
|
value,
|
|
83574
83574
|
onChange,
|
|
83575
83575
|
onSubmit,
|
|
83576
|
+
onPasteImage,
|
|
83576
83577
|
focus = true,
|
|
83577
83578
|
placeholder = "",
|
|
83578
83579
|
history = [],
|
|
83579
83580
|
navActive = true
|
|
83580
|
-
}) {
|
|
83581
|
+
}, ref) {
|
|
83581
83582
|
const [cursor, setCursor] = (0, import_react23.useState)(value.length);
|
|
83582
83583
|
const hist = (0, import_react23.useRef)({ index: null, stash: "" });
|
|
83584
|
+
const latest = (0, import_react23.useRef)({ value, cursor });
|
|
83585
|
+
latest.current = { value, cursor };
|
|
83583
83586
|
const clamp = (n, len) => Math.max(0, Math.min(n, len));
|
|
83584
83587
|
const set = (v, c2) => {
|
|
83585
83588
|
onChange(v);
|
|
83586
83589
|
setCursor(clamp(c2, v.length));
|
|
83587
83590
|
};
|
|
83591
|
+
(0, import_react23.useImperativeHandle)(ref, () => ({
|
|
83592
|
+
// text placed at the cursor as it is NOW (an image chip arrives after a round trip)
|
|
83593
|
+
insert(text) {
|
|
83594
|
+
const { value: v, cursor: cu } = latest.current;
|
|
83595
|
+
const c2 = clamp(cu, v.length);
|
|
83596
|
+
const sep = c2 > 0 && !/\s$/.test(v.slice(0, c2)) ? " " : "";
|
|
83597
|
+
set(v.slice(0, c2) + sep + text + v.slice(c2), c2 + sep.length + text.length);
|
|
83598
|
+
}
|
|
83599
|
+
}));
|
|
83588
83600
|
const lineStart = (v, c2) => v.lastIndexOf("\n", c2 - 1) + 1;
|
|
83589
83601
|
const lineEnd = (v, c2) => {
|
|
83590
83602
|
const i = v.indexOf("\n", c2);
|
|
@@ -83660,6 +83672,8 @@ function MultilineInput({
|
|
|
83660
83672
|
else if (input === "w") {
|
|
83661
83673
|
const head = value.slice(0, c2).replace(/\S+\s*$/, "");
|
|
83662
83674
|
set(head + value.slice(c2), head.length);
|
|
83675
|
+
} else if (input === "v") {
|
|
83676
|
+
if (onPasteImage) onPasteImage();
|
|
83663
83677
|
}
|
|
83664
83678
|
return;
|
|
83665
83679
|
}
|
|
@@ -83684,7 +83698,8 @@ function MultilineInput({
|
|
|
83684
83698
|
shown = value.slice(0, c) + source_default.inverse(value[c]) + value.slice(c + 1);
|
|
83685
83699
|
}
|
|
83686
83700
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: shown });
|
|
83687
|
-
}
|
|
83701
|
+
});
|
|
83702
|
+
var input_default = MultilineInput;
|
|
83688
83703
|
|
|
83689
83704
|
// src/app.jsx
|
|
83690
83705
|
var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
|
|
@@ -83717,12 +83732,18 @@ var COMMANDS = [
|
|
|
83717
83732
|
{ name: "/compact", desc: "summarise and restart the context now" },
|
|
83718
83733
|
{ name: "/model", desc: "switch the model for this session (/model <name>)" },
|
|
83719
83734
|
{ name: "/cost", desc: "tokens and cost so far" },
|
|
83735
|
+
{ name: "/approve", desc: "approvals on|off \u2014 whether changes and commands wait for you first" },
|
|
83736
|
+
{ name: "/continue", desc: "after a pause: let the agent carry on" },
|
|
83737
|
+
{ name: "/image", desc: "attach an image file to your next message (/image <path>); ctrl+v pastes a screenshot" },
|
|
83720
83738
|
{ name: "/tools", desc: "what is mounted" },
|
|
83721
83739
|
{ name: "/help", desc: "keys and commands" },
|
|
83722
83740
|
{ name: "/quit", desc: "save and leave" }
|
|
83723
83741
|
];
|
|
83724
|
-
var HELP = "!cmd runs a command as you \xB7 esc interrupts the model (what it had is kept) \xB7 type while it works to steer \xB7 edit files in your editor any time (recorded as your turn) \xB7 /resume picks a past session \xB7 /compact restarts the context from a summary \xB7 ctrl+c twice quits";
|
|
83725
|
-
|
|
83742
|
+
var HELP = "!cmd runs a command as you \xB7 esc interrupts the model (what it had is kept) \xB7 type while it works to steer \xB7 edit files in your editor any time (recorded as your turn) \xB7 ctrl+o shows the full output of the last tool result \xB7 ctrl+v pastes a screenshot from the clipboard \xB7 @path.png or /image <path> attaches an image file \xB7 /approve makes changes and commands wait for you (enter approves, a typed reason rejects) \xB7 /resume picks a past session \xB7 /compact restarts the context from a summary \xB7 ctrl+c twice quits";
|
|
83743
|
+
var DIFF_TAIL_LINES = 20;
|
|
83744
|
+
var OUTPUT_MAX_LINES = 200;
|
|
83745
|
+
var ERR_TAIL_LINES = 5;
|
|
83746
|
+
function App2({ engine: engine2, task, resume, images = [] }) {
|
|
83726
83747
|
const { exit } = use_app_default();
|
|
83727
83748
|
const { stdout } = use_stdout_default();
|
|
83728
83749
|
const width = (stdout?.columns || 80) - 2;
|
|
@@ -83739,6 +83760,11 @@ function App2({ engine: engine2, task, resume }) {
|
|
|
83739
83760
|
const [elapsed, setElapsed] = (0, import_react24.useState)(0);
|
|
83740
83761
|
const [ctrlc, setCtrlc] = (0, import_react24.useState)(0);
|
|
83741
83762
|
const [picker, setPicker] = (0, import_react24.useState)(null);
|
|
83763
|
+
const [proposal, setProposal] = (0, import_react24.useState)(null);
|
|
83764
|
+
const [gate, setGate] = (0, import_react24.useState)("off");
|
|
83765
|
+
const [paused, setPaused] = (0, import_react24.useState)(false);
|
|
83766
|
+
const attachRef = (0, import_react24.useRef)([]);
|
|
83767
|
+
const inputRef = (0, import_react24.useRef)(null);
|
|
83742
83768
|
const nextId = (0, import_react24.useRef)(0);
|
|
83743
83769
|
const phaseRef = (0, import_react24.useRef)("connecting");
|
|
83744
83770
|
const turnT0 = (0, import_react24.useRef)(null);
|
|
@@ -83747,12 +83773,15 @@ function App2({ engine: engine2, task, resume }) {
|
|
|
83747
83773
|
const toolsRef = (0, import_react24.useRef)({});
|
|
83748
83774
|
const historyRef = (0, import_react24.useRef)(loadHistory());
|
|
83749
83775
|
const modelRef = (0, import_react24.useRef)("");
|
|
83776
|
+
const lastOutRef = (0, import_react24.useRef)(null);
|
|
83777
|
+
const [meter, setMeter] = (0, import_react24.useState)({ tokens: 0, cap: 0, input: 0, output: 0 });
|
|
83750
83778
|
const setPhaseBoth = (p) => {
|
|
83751
83779
|
phaseRef.current = p;
|
|
83752
83780
|
setPhase(p);
|
|
83753
83781
|
};
|
|
83754
83782
|
const append = (item) => setItems((prev) => [...prev, { id: nextId.current++, ...item }]);
|
|
83755
83783
|
const menu = value.trimStart().startsWith("/") && !value.includes(" ") ? COMMANDS.filter((c) => c.name.startsWith(value.trim())) : [];
|
|
83784
|
+
const ctxPct = meter.cap > 0 && meter.tokens > 0 ? Math.round(meter.tokens / meter.cap * 100) : 0;
|
|
83756
83785
|
(0, import_react24.useEffect)(() => {
|
|
83757
83786
|
if (phase !== "busy" && phase !== "connecting") return void 0;
|
|
83758
83787
|
const started = Date.now();
|
|
@@ -83814,13 +83843,21 @@ function App2({ engine: engine2, task, resume }) {
|
|
|
83814
83843
|
case "hello":
|
|
83815
83844
|
setHello(ev);
|
|
83816
83845
|
modelRef.current = ev.model;
|
|
83846
|
+
setMeter((m) => ({ ...m, tokens: 0, cap: ev.compact_at || 0 }));
|
|
83847
|
+
setGate(ev.gate === "approve" ? "approve" : "off");
|
|
83817
83848
|
append({ kind: "banner", text: `${ev.model}${ev.user ? ` \xB7 ${ev.user}` : ""} \xB7 ${ev.cwd} \xB7 tools: ${ev.tools.join(", ")}` });
|
|
83818
|
-
if (task)
|
|
83819
|
-
|
|
83849
|
+
if (task) {
|
|
83850
|
+
if (images.length && !ev.images) append({ kind: "warn", text: "this engine does not take images; update hum" });
|
|
83851
|
+
engine2.send({ cmd: "start", task, images: ev.images ? images : [] });
|
|
83852
|
+
} else if (resume === "pick") {
|
|
83820
83853
|
setPhaseBoth("idle");
|
|
83821
83854
|
engine2.send({ cmd: "sessions" });
|
|
83822
83855
|
} else if (resume) engine2.send({ cmd: "resume", name: resume });
|
|
83823
|
-
else
|
|
83856
|
+
else {
|
|
83857
|
+
setPhaseBoth("idle");
|
|
83858
|
+
if (ev.images) images.forEach((p) => engine2.send({ cmd: "attach", path: p }));
|
|
83859
|
+
else if (images.length) append({ kind: "warn", text: "this engine does not take images; update hum" });
|
|
83860
|
+
}
|
|
83824
83861
|
break;
|
|
83825
83862
|
case "sessions":
|
|
83826
83863
|
if (!ev.items || ev.items.length === 0) {
|
|
@@ -83832,6 +83869,8 @@ function App2({ engine: engine2, task, resume }) {
|
|
|
83832
83869
|
case "session":
|
|
83833
83870
|
setSession(ev.name);
|
|
83834
83871
|
setSpend(0);
|
|
83872
|
+
setPaused(false);
|
|
83873
|
+
setProposal(null);
|
|
83835
83874
|
if (ev.model) modelRef.current = ev.model;
|
|
83836
83875
|
append({ kind: "info", text: `session ${ev.name}` });
|
|
83837
83876
|
break;
|
|
@@ -83858,15 +83897,26 @@ function App2({ engine: engine2, task, resume }) {
|
|
|
83858
83897
|
case "tool_pending":
|
|
83859
83898
|
break;
|
|
83860
83899
|
// the status line already shows it; the call itself lands as a transcript line
|
|
83861
|
-
case "tool_call":
|
|
83900
|
+
case "tool_call": {
|
|
83862
83901
|
foldThinking();
|
|
83863
83902
|
flushDraft();
|
|
83864
83903
|
toolsRef.current[ev.id] = ev;
|
|
83865
|
-
|
|
83904
|
+
const label = ev.name === "write_file" && ev.lines != null ? `${ev.desc || ev.name} (${ev.lines} lines)` : ev.desc || ev.name;
|
|
83905
|
+
append({ kind: "tool", text: label, name: ev.name, args: ev.args });
|
|
83906
|
+
if (ev.diff) {
|
|
83907
|
+
const ls = ev.diff.split("\n");
|
|
83908
|
+
append({ kind: "diff", lines: ls.slice(0, DIFF_TAIL_LINES), more: Math.max(0, ls.length - DIFF_TAIL_LINES) });
|
|
83909
|
+
}
|
|
83866
83910
|
break;
|
|
83911
|
+
}
|
|
83867
83912
|
case "tool_result": {
|
|
83868
|
-
|
|
83913
|
+
lastOutRef.current = { name: ev.name, content: ev.content || "" };
|
|
83914
|
+
const more = (ev.lines || 0) > 1 ? ` (+${ev.lines - 1} lines \xB7 ctrl+o expands)` : "";
|
|
83869
83915
|
append({ kind: "tool_result", text: `${ev.summary || "(no output)"}${more}`, isError: !ev.ok });
|
|
83916
|
+
if (!ev.ok && ev.content) {
|
|
83917
|
+
const t = ev.content.split("\n").filter((l) => l.trim()).slice(-ERR_TAIL_LINES);
|
|
83918
|
+
if (t.length > 1) append({ kind: "errtail", lines: t });
|
|
83919
|
+
}
|
|
83870
83920
|
break;
|
|
83871
83921
|
}
|
|
83872
83922
|
case "plan": {
|
|
@@ -83876,11 +83926,37 @@ function App2({ engine: engine2, task, resume }) {
|
|
|
83876
83926
|
append({ kind: "plan", steps: (ev.steps || []).map((st) => ({ ...st, glyph: glyph[st.status] || "\u25FB" })), explanation: ev.explanation });
|
|
83877
83927
|
break;
|
|
83878
83928
|
}
|
|
83929
|
+
case "proposal":
|
|
83930
|
+
foldThinking();
|
|
83931
|
+
flushDraft();
|
|
83932
|
+
setProposal({ content: ev.content || "", calls: ev.calls || [] });
|
|
83933
|
+
setStatus("waiting for your decision");
|
|
83934
|
+
break;
|
|
83935
|
+
case "paused":
|
|
83936
|
+
foldThinking();
|
|
83937
|
+
flushDraft();
|
|
83938
|
+
setPaused(true);
|
|
83939
|
+
append({ kind: "paused", text: ev.detail, trip: ev.kind });
|
|
83940
|
+
break;
|
|
83941
|
+
case "resumed":
|
|
83942
|
+
setPaused(false);
|
|
83943
|
+
turnT0.current = Date.now();
|
|
83944
|
+
setPhaseBoth("busy");
|
|
83945
|
+
break;
|
|
83946
|
+
case "proposal_released":
|
|
83947
|
+
setProposal(null);
|
|
83948
|
+
append({ kind: "info", text: "approvals off: the waiting proposal ran" });
|
|
83949
|
+
break;
|
|
83950
|
+
case "gate":
|
|
83951
|
+
setGate(ev.mode);
|
|
83952
|
+
append({ kind: "info", text: ev.mode === "approve" ? "approvals on: changes and commands wait for you first" : "approvals off: the agent acts, you steer" });
|
|
83953
|
+
break;
|
|
83879
83954
|
case "human_edit":
|
|
83880
83955
|
append({ kind: "info", text: `you edited files: ${ev.stat}` });
|
|
83881
83956
|
break;
|
|
83882
83957
|
case "compaction":
|
|
83883
|
-
append({ kind: "info", text:
|
|
83958
|
+
append({ kind: "info", text: `context summarised and restarted${ev.prompt_tokens_before ? ` at ${ev.prompt_tokens_before.toLocaleString()} tokens` : ""} (the session keeps everything)` });
|
|
83959
|
+
setMeter((m) => ({ ...m, tokens: 0 }));
|
|
83884
83960
|
break;
|
|
83885
83961
|
case "retry":
|
|
83886
83962
|
append({ kind: "info", text: `model connection: ${ev.text} \u2014 retrying (${ev.attempt})` });
|
|
@@ -83888,6 +83964,8 @@ function App2({ engine: engine2, task, resume }) {
|
|
|
83888
83964
|
case "interrupted":
|
|
83889
83965
|
foldThinking();
|
|
83890
83966
|
flushDraft();
|
|
83967
|
+
setProposal(null);
|
|
83968
|
+
setPaused(false);
|
|
83891
83969
|
append({ kind: "info", text: ev.kept ? "stopped \xB7 what it had so far is kept in the session" : "stopped" });
|
|
83892
83970
|
setPhaseBoth("idle");
|
|
83893
83971
|
setStatus("");
|
|
@@ -83895,14 +83973,22 @@ function App2({ engine: engine2, task, resume }) {
|
|
|
83895
83973
|
case "turn_end": {
|
|
83896
83974
|
foldThinking();
|
|
83897
83975
|
flushDraft();
|
|
83976
|
+
setMeter((m) => ({ ...m, tokens: ev.context_tokens ?? m.tokens, input: ev.input_tokens || 0, output: ev.output_tokens || 0 }));
|
|
83898
83977
|
setSpend(ev.cost_usd);
|
|
83899
83978
|
const dur = turnT0.current ? fmtDur((Date.now() - turnT0.current) / 1e3) : fmtDur(ev.elapsed_s);
|
|
83900
83979
|
turnT0.current = null;
|
|
83901
|
-
append({ kind: "turn", text: `${dur} \xB7 ${ev.turns} steps \xB7 ${fmtCost(ev.cost_usd)} session` + (ev.stop_reason !== "done" ? ` \xB7 stopped: ${ev.stop_reason}` : "") });
|
|
83980
|
+
append({ kind: "turn", text: `${dur} \xB7 ${ev.turns} steps \xB7 ${fmtCost(ev.cost_usd)} session` + (ev.stop_reason !== "done" && !String(ev.stop_reason).startsWith("paused") ? ` \xB7 stopped: ${ev.stop_reason}` : "") });
|
|
83902
83981
|
setPhaseBoth("idle");
|
|
83903
83982
|
setStatus("");
|
|
83904
83983
|
break;
|
|
83905
83984
|
}
|
|
83985
|
+
case "image": {
|
|
83986
|
+
const n = attachRef.current.length + 1;
|
|
83987
|
+
const { type, label, ...ref } = ev;
|
|
83988
|
+
attachRef.current = [...attachRef.current, { ref, n, label: label || ref.name || "image", chip: `[Image #${n}]` }];
|
|
83989
|
+
if (inputRef.current) inputRef.current.insert(`[Image #${n}] `);
|
|
83990
|
+
break;
|
|
83991
|
+
}
|
|
83906
83992
|
case "info":
|
|
83907
83993
|
append({ kind: "info", text: ev.text });
|
|
83908
83994
|
break;
|
|
@@ -83952,6 +84038,25 @@ function App2({ engine: engine2, task, resume }) {
|
|
|
83952
84038
|
quit();
|
|
83953
84039
|
return;
|
|
83954
84040
|
}
|
|
84041
|
+
if (key.ctrl && input === "o") {
|
|
84042
|
+
const last = lastOutRef.current;
|
|
84043
|
+
if (last && last.content) {
|
|
84044
|
+
const ls = last.content.split("\n");
|
|
84045
|
+
append({ kind: "output", name: last.name, lines: ls.slice(0, OUTPUT_MAX_LINES), more: Math.max(0, ls.length - OUTPUT_MAX_LINES) });
|
|
84046
|
+
}
|
|
84047
|
+
return;
|
|
84048
|
+
}
|
|
84049
|
+
if (proposal && !picker) {
|
|
84050
|
+
if (key.return && !value.trim()) {
|
|
84051
|
+
decide("accept");
|
|
84052
|
+
return;
|
|
84053
|
+
}
|
|
84054
|
+
if (key.escape) {
|
|
84055
|
+
setProposal(null);
|
|
84056
|
+
engine2.send({ cmd: "interrupt" });
|
|
84057
|
+
return;
|
|
84058
|
+
}
|
|
84059
|
+
}
|
|
83955
84060
|
if (picker) {
|
|
83956
84061
|
if (key.upArrow) setPicker((p) => ({ ...p, index: (p.index + p.items.length - 1) % p.items.length }));
|
|
83957
84062
|
else if (key.downArrow || key.tab) setPicker((p) => ({ ...p, index: (p.index + 1) % p.items.length }));
|
|
@@ -83973,6 +84078,13 @@ function App2({ engine: engine2, task, resume }) {
|
|
|
83973
84078
|
(0, import_react24.useEffect)(() => {
|
|
83974
84079
|
setMenuIndex(0);
|
|
83975
84080
|
}, [value]);
|
|
84081
|
+
const decide = (decision, note) => {
|
|
84082
|
+
const p = proposal;
|
|
84083
|
+
setProposal(null);
|
|
84084
|
+
const what = p && p.calls.length ? p.calls.map((c) => c.desc).join(" \xB7 ") : "the proposal";
|
|
84085
|
+
append({ kind: "you", text: decision === "accept" ? `approved: ${what}` : `rejected: ${what}${note ? ` \u2014 ${note}` : ""}` });
|
|
84086
|
+
engine2.send({ cmd: "decide", decision, note: note || "" });
|
|
84087
|
+
};
|
|
83976
84088
|
const runCommand = (line) => {
|
|
83977
84089
|
const [name, ...rest] = line.split(/\s+/);
|
|
83978
84090
|
const arg = rest.join(" ").trim();
|
|
@@ -84000,12 +84112,27 @@ function App2({ engine: engine2, task, resume }) {
|
|
|
84000
84112
|
engine2.send({ cmd: "model", name: arg });
|
|
84001
84113
|
} else append({ kind: "info", text: `model: ${modelRef.current}` });
|
|
84002
84114
|
break;
|
|
84003
|
-
case "/cost":
|
|
84004
|
-
|
|
84115
|
+
case "/cost": {
|
|
84116
|
+
const ctx = ctxPct > 0 ? ` \xB7 context ${ctxPct}% of the compaction threshold` : "";
|
|
84117
|
+
append({ kind: "info", text: `${fmtCost(spend)} this session \xB7 ${meter.input.toLocaleString()}+${meter.output.toLocaleString()} tokens${ctx}` });
|
|
84118
|
+
break;
|
|
84119
|
+
}
|
|
84120
|
+
case "/image":
|
|
84121
|
+
if (!hello?.images) append({ kind: "warn", text: "this engine does not take images; update hum" });
|
|
84122
|
+
else if (!arg) append({ kind: "info", text: "/image <path> attaches the file to your next message \xB7 ctrl+v pastes a screenshot \xB7 @path.png in a message works too" });
|
|
84123
|
+
else engine2.send({ cmd: "attach", path: arg });
|
|
84005
84124
|
break;
|
|
84006
84125
|
case "/tools":
|
|
84007
84126
|
append({ kind: "info", text: (hello?.tools || []).join(", ") });
|
|
84008
84127
|
break;
|
|
84128
|
+
case "/continue":
|
|
84129
|
+
engine2.send({ cmd: "continue" });
|
|
84130
|
+
break;
|
|
84131
|
+
case "/approve": {
|
|
84132
|
+
const mode = arg === "on" ? "approve" : arg === "off" ? "off" : gate === "approve" ? "off" : "approve";
|
|
84133
|
+
engine2.send({ cmd: "gate", mode });
|
|
84134
|
+
break;
|
|
84135
|
+
}
|
|
84009
84136
|
default:
|
|
84010
84137
|
break;
|
|
84011
84138
|
}
|
|
@@ -84013,12 +84140,20 @@ function App2({ engine: engine2, task, resume }) {
|
|
|
84013
84140
|
const onSubmit = (line) => {
|
|
84014
84141
|
const text = line.trim();
|
|
84015
84142
|
setValue("");
|
|
84016
|
-
|
|
84143
|
+
const queued = attachRef.current;
|
|
84144
|
+
const attached = text ? queued.filter((a) => text.includes(a.chip)) : queued;
|
|
84145
|
+
const dropped = queued.filter((a) => !attached.includes(a));
|
|
84146
|
+
if (!text && attached.length === 0) return;
|
|
84147
|
+
if (!text && proposal) return;
|
|
84017
84148
|
const hist = historyRef.current;
|
|
84018
|
-
if (hist[hist.length - 1] !== text) {
|
|
84149
|
+
if (text && hist[hist.length - 1] !== text) {
|
|
84019
84150
|
hist.push(text);
|
|
84020
84151
|
saveHistory(hist);
|
|
84021
84152
|
}
|
|
84153
|
+
if (proposal && !/^\/(approve|quit|exit)\b/.test(text)) {
|
|
84154
|
+
decide("reject", text);
|
|
84155
|
+
return;
|
|
84156
|
+
}
|
|
84022
84157
|
if (text.startsWith("/")) {
|
|
84023
84158
|
runCommand(text);
|
|
84024
84159
|
return;
|
|
@@ -84029,14 +84164,21 @@ function App2({ engine: engine2, task, resume }) {
|
|
|
84029
84164
|
if (phaseRef.current === "idle") setPhaseBoth("busy");
|
|
84030
84165
|
return;
|
|
84031
84166
|
}
|
|
84032
|
-
|
|
84033
|
-
|
|
84167
|
+
if (paused && /^(continue|go on|carry on|keep going)\.?$/i.test(text)) {
|
|
84168
|
+
runCommand("/continue");
|
|
84169
|
+
return;
|
|
84170
|
+
}
|
|
84171
|
+
if (paused) setPaused(false);
|
|
84172
|
+
if (dropped.length) append({ kind: "info", text: `not sent (chip removed): ${dropped.map((a) => a.label).join(", ")}` });
|
|
84173
|
+
append({ kind: "you", text, images: attached.map((a) => `#${a.n} ${a.label}`), steering: phaseRef.current === "busy" });
|
|
84174
|
+
engine2.send({ cmd: "message", text, images: attached.map((a) => a.ref) });
|
|
84175
|
+
attachRef.current = [];
|
|
84034
84176
|
if (phaseRef.current === "idle") {
|
|
84035
84177
|
turnT0.current = Date.now();
|
|
84036
84178
|
setPhaseBoth("busy");
|
|
84037
84179
|
}
|
|
84038
84180
|
};
|
|
84039
|
-
const statusLine = phase === "connecting" ? "starting the engine \u2026" : `${modelRef.current || ""}${session ? ` \xB7 ${session}` : ""} \xB7 ${fmtCost(spend)}` + (ctrlc ? " \xB7 ctrl+c again to quit" : " \xB7 / for commands");
|
|
84181
|
+
const statusLine = phase === "connecting" ? "starting the engine \u2026" : `${modelRef.current || ""}${session ? ` \xB7 ${session}` : ""} \xB7 ${fmtCost(spend)}` + (ctxPct >= 10 ? ` \xB7 ctx ${ctxPct}%` : "") + (gate === "approve" ? " \xB7 approvals on" : "") + (ctrlc ? " \xB7 ctrl+c again to quit" : " \xB7 / for commands");
|
|
84040
84182
|
const spinnerLabel = phase === "connecting" ? " starting \u2026" : ` ${status || "working"}${elapsed >= 2 ? ` \xB7 ${fmtDur(elapsed)}` : ""}` + (thinkChars.current > 4e3 ? ` \xB7 ${(thinkChars.current / 1e3).toFixed(0)}k thinking` : "");
|
|
84041
84183
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
84042
84184
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Static, { items, children: (item) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", marginBottom: ["hum", "you", "banner", "turn"].includes(item.kind) ? 1 : 0, children: [
|
|
@@ -84057,9 +84199,15 @@ function App2({ engine: engine2, task, resume }) {
|
|
|
84057
84199
|
] }),
|
|
84058
84200
|
item.kind === "you" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { children: [
|
|
84059
84201
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Box_default, { width: 2, flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "cyan", bold: true, children: "\u276F" }) }),
|
|
84060
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
84061
|
-
item.text,
|
|
84062
|
-
|
|
84202
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
84203
|
+
item.text ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { color: "white", children: [
|
|
84204
|
+
item.text,
|
|
84205
|
+
item.steering ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: " (steering)" }) : null
|
|
84206
|
+
] }) : null,
|
|
84207
|
+
(item.images || []).map((im, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { dimColor: true, children: [
|
|
84208
|
+
"\u{1F4CE} ",
|
|
84209
|
+
im
|
|
84210
|
+
] }, i))
|
|
84063
84211
|
] })
|
|
84064
84212
|
] }),
|
|
84065
84213
|
item.kind === "agent" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { color: "magenta", children: [
|
|
@@ -84098,10 +84246,36 @@ function App2({ engine: engine2, task, resume }) {
|
|
|
84098
84246
|
item.agent ? " \u23BF " : " \u23BF ",
|
|
84099
84247
|
item.text
|
|
84100
84248
|
] })),
|
|
84249
|
+
item.kind === "diff" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 4, children: [
|
|
84250
|
+
item.lines.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
84251
|
+
Text,
|
|
84252
|
+
{
|
|
84253
|
+
color: l.startsWith("+") ? "green" : l.startsWith("-") ? "red" : void 0,
|
|
84254
|
+
dimColor: !l.startsWith("+") && !l.startsWith("-"),
|
|
84255
|
+
children: l || " "
|
|
84256
|
+
},
|
|
84257
|
+
i
|
|
84258
|
+
)),
|
|
84259
|
+
item.more > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: `\u2026 +${item.more} more diff lines` }) : null
|
|
84260
|
+
] }),
|
|
84261
|
+
item.kind === "errtail" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Box_default, { flexDirection: "column", paddingLeft: 6, children: item.lines.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "red", dimColor: true, children: l }, i)) }),
|
|
84262
|
+
item.kind === "output" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 4, children: [
|
|
84263
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: `full output \xB7 ${item.name}` }),
|
|
84264
|
+
item.lines.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: l || " " }, i)),
|
|
84265
|
+
item.more > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: `\u2026 +${item.more} more lines (the session file keeps everything)` }) : null
|
|
84266
|
+
] }),
|
|
84101
84267
|
item.kind === "turn" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { children: [
|
|
84102
84268
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: ACCENT, children: "\u273B " }),
|
|
84103
84269
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: item.text })
|
|
84104
84270
|
] }),
|
|
84271
|
+
item.kind === "paused" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { borderStyle: "round", borderColor: "yellow", paddingX: 1, flexDirection: "column", children: [
|
|
84272
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { color: "yellow", bold: true, children: [
|
|
84273
|
+
"\u23F8 paused \u2014 ",
|
|
84274
|
+
item.trip === "spend" ? "spend cap" : item.trip === "repeat" ? "repeating itself" : "no progress"
|
|
84275
|
+
] }),
|
|
84276
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: item.text }),
|
|
84277
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: "say what to do next \xB7 /continue lets it carry on" })
|
|
84278
|
+
] }),
|
|
84105
84279
|
item.kind === "info" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: item.text }),
|
|
84106
84280
|
item.kind === "warn" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "yellow", children: item.text }),
|
|
84107
84281
|
item.kind === "error" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "red", children: item.text })
|
|
@@ -84118,6 +84292,41 @@ function App2({ engine: engine2, task, resume }) {
|
|
|
84118
84292
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: ACCENT, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(build_default, { type: "dots" }) }),
|
|
84119
84293
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: spinnerLabel })
|
|
84120
84294
|
] }),
|
|
84295
|
+
proposal && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", borderStyle: "round", borderColor: ACCENT, paddingX: 1, marginTop: 1, children: [
|
|
84296
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { children: [
|
|
84297
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: ACCENT, bold: true, children: "? " }),
|
|
84298
|
+
"hum wants to ",
|
|
84299
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: "\xB7 enter approves \xB7 type a reason to reject \xB7 esc stops" })
|
|
84300
|
+
] }),
|
|
84301
|
+
proposal.content ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { dimColor: true, children: [
|
|
84302
|
+
" ",
|
|
84303
|
+
proposal.content.split("\n")[0].slice(0, width - 4)
|
|
84304
|
+
] }) : null,
|
|
84305
|
+
proposal.calls.map((c) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
84306
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { children: [
|
|
84307
|
+
" \xB7 ",
|
|
84308
|
+
c.desc,
|
|
84309
|
+
c.lines != null ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: ` (${c.lines} lines)` }) : null
|
|
84310
|
+
] }),
|
|
84311
|
+
c.name === "bash" || c.name === "powershell" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { color: "cyan", children: [
|
|
84312
|
+
" $ ",
|
|
84313
|
+
String(c.args?.command || "").split("\n").join(" \u23CE ").slice(0, 300)
|
|
84314
|
+
] }) : null,
|
|
84315
|
+
c.diff ? c.diff.split("\n").slice(0, DIFF_TAIL_LINES).map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
84316
|
+
Text,
|
|
84317
|
+
{
|
|
84318
|
+
color: l.startsWith("+") ? "green" : l.startsWith("-") ? "red" : void 0,
|
|
84319
|
+
dimColor: !l.startsWith("+") && !l.startsWith("-"),
|
|
84320
|
+
children: [
|
|
84321
|
+
" ",
|
|
84322
|
+
l || " "
|
|
84323
|
+
]
|
|
84324
|
+
},
|
|
84325
|
+
i
|
|
84326
|
+
)) : null,
|
|
84327
|
+
c.diff && c.diff.split("\n").length > DIFF_TAIL_LINES ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { dimColor: true, children: ` \u2026 +${c.diff.split("\n").length - DIFF_TAIL_LINES} more diff lines` }) : null
|
|
84328
|
+
] }, c.id))
|
|
84329
|
+
] }),
|
|
84121
84330
|
picker && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
|
|
84122
84331
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { children: [
|
|
84123
84332
|
"pick a session to continue ",
|
|
@@ -84137,15 +84346,17 @@ function App2({ engine: engine2, task, resume }) {
|
|
|
84137
84346
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { borderStyle: "round", borderColor: "gray", paddingX: 1, marginTop: 1, children: [
|
|
84138
84347
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "cyan", children: "\u276F " }),
|
|
84139
84348
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
84140
|
-
|
|
84349
|
+
input_default,
|
|
84141
84350
|
{
|
|
84351
|
+
ref: inputRef,
|
|
84352
|
+
onPasteImage: () => hello?.images ? engine2.send({ cmd: "paste_image" }) : append({ kind: "warn", text: "this engine does not take images; update hum" }),
|
|
84142
84353
|
value,
|
|
84143
84354
|
onChange: setValue,
|
|
84144
84355
|
onSubmit,
|
|
84145
84356
|
focus: phase !== "dead" && !picker,
|
|
84146
84357
|
navActive: menu.length === 0,
|
|
84147
84358
|
history: historyRef.current,
|
|
84148
|
-
placeholder: phase === "busy" ? "type to steer \xB7 esc to stop \xB7 !cmd runs a command" : session ? "what next?" : "what are we doing?"
|
|
84359
|
+
placeholder: proposal ? "enter to approve \xB7 or type why not" : paused ? "paused \xB7 say what to do, or /continue" : phase === "busy" ? "type to steer \xB7 esc to stop \xB7 !cmd runs a command" : session ? "what next?" : "what are we doing?"
|
|
84149
84360
|
}
|
|
84150
84361
|
)
|
|
84151
84362
|
] }),
|
|
@@ -84264,15 +84475,17 @@ var Engine = class {
|
|
|
84264
84475
|
// src/cli.jsx
|
|
84265
84476
|
var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
|
|
84266
84477
|
function parseArgs(argv) {
|
|
84267
|
-
const a = { task: null, resume: null, cwd: process.cwd() };
|
|
84478
|
+
const a = { task: null, resume: null, cwd: process.cwd(), images: [] };
|
|
84268
84479
|
for (let i = 0; i < argv.length; i += 1) {
|
|
84269
84480
|
const x = argv[i];
|
|
84270
84481
|
if (x === "resume") {
|
|
84271
84482
|
a.resume = argv[i + 1] && !argv[i + 1].startsWith("-") ? argv[++i] : "pick";
|
|
84272
84483
|
} else if ((x === "-C" || x === "--cwd") && argv[i + 1]) {
|
|
84273
84484
|
a.cwd = argv[++i];
|
|
84485
|
+
} else if (x === "--image" && argv[i + 1]) {
|
|
84486
|
+
a.images.push(argv[++i]);
|
|
84274
84487
|
} else if (x === "-h" || x === "--help") {
|
|
84275
|
-
process.stdout.write("usage: hum [task] | hum resume [name]\nIn a session: type to talk, !cmd runs a command, esc stops the model, / for commands.\n");
|
|
84488
|
+
process.stdout.write("usage: hum [task] [--image path] | hum resume [name]\nIn a session: type to talk, !cmd runs a command, esc stops the model, ctrl+v pastes a screenshot, / for commands.\n");
|
|
84276
84489
|
process.exit(0);
|
|
84277
84490
|
} else if (!x.startsWith("-")) {
|
|
84278
84491
|
a.task = a.task ? `${a.task} ${x}` : x;
|
|
@@ -84303,7 +84516,7 @@ try {
|
|
|
84303
84516
|
process.stderr.write(exc.message + "\n");
|
|
84304
84517
|
process.exit(1);
|
|
84305
84518
|
}
|
|
84306
|
-
var { waitUntilExit } = render_default(/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(App2, { engine, task: args.task, resume: args.resume }), { exitOnCtrlC: false });
|
|
84519
|
+
var { waitUntilExit } = render_default(/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(App2, { engine, task: args.task, resume: args.resume, images: args.images }), { exitOnCtrlC: false });
|
|
84307
84520
|
waitUntilExit().then(() => {
|
|
84308
84521
|
engine.close();
|
|
84309
84522
|
if (child) setTimeout(() => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metaphi-ai/hum",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Hum
|
|
3
|
+
"version": "0.1.6",
|
|
4
|
+
"description": "Hum (हम): a self-improving coding agent for your terminal.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": { "hum": "bin/hum.js" },
|