@parallel-cli/parallel 0.3.3 → 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.js CHANGED
@@ -101,8 +101,8 @@ if (headless) {
101
101
  if (config.language)
102
102
  setLang(config.language);
103
103
  const ctl = new Controller(config, process.cwd());
104
- // No human in the loop: commands are auto-approved (like /approvals auto).
105
- ctl.setSessionApprovalMode('auto');
104
+ // No human in the loop: commands are auto-approved.
105
+ ctl.setSessionApprovalMode('yolo');
106
106
  const provider = ctl.sessionProvider();
107
107
  if (!provider || !provider.apiKey) {
108
108
  console.error('Headless mode needs a configured provider + API key. Run `parallel` interactively once, or set PARALLEL_API_KEY.');
@@ -177,9 +177,11 @@ const useAltScreen = process.stdout.isTTY && process.env.PARALLEL_NO_ALT_SCREEN
177
177
  const restoreTerminal = () => {
178
178
  if (!useAltScreen)
179
179
  return;
180
+ // Show cursor + leave alternate screen.
180
181
  process.stdout.write('\x1b[?25h\x1b[?1049l');
181
182
  };
182
183
  if (useAltScreen) {
184
+ // Alternate screen + clear.
183
185
  process.stdout.write('\x1b[?1049h\x1b[2J\x1b[3J\x1b[H');
184
186
  process.once('exit', restoreTerminal);
185
187
  process.once('SIGINT', () => {
package/dist/pricing.js CHANGED
@@ -30,6 +30,33 @@ const BUILTIN = {
30
30
  // Alibaba
31
31
  'qwen2.5-coder': { input: 0.09, output: 0.09 },
32
32
  'qwen-max': { input: 1.6, output: 6.4 },
33
+ // xAI (Grok)
34
+ 'grok-4': { input: 4.0, output: 16.0 },
35
+ 'grok-3-beta': { input: 3.0, output: 12.0 },
36
+ 'grok-3-mini': { input: 0.55, output: 2.2 },
37
+ // Perplexity
38
+ 'sonar-pro': { input: 3.0, output: 15.0 },
39
+ 'sonar': { input: 1.0, output: 1.0 },
40
+ 'sonar-reasoning': { input: 2.0, output: 16.0 },
41
+ // Cohere
42
+ 'command-a': { input: 2.5, output: 10.0 },
43
+ 'command-r-plus': { input: 2.5, output: 10.0 },
44
+ 'command-r': { input: 0.5, output: 1.5 },
45
+ // DeepInfra
46
+ 'llama-4-maverick': { input: 0.2, output: 0.6 }, // approximate
47
+ 'wizardlm-2-8x22b': { input: 0.5, output: 0.5 }, // approximate
48
+ // Fireworks
49
+ 'llama-4-scout': { input: 0.1, output: 0.3 }, // approximate
50
+ 'mixtral-8x22b': { input: 0.9, output: 0.9 }, // approximate
51
+ // Cerebras
52
+ 'llama-3.3-70b @cerebras': { input: 0.5, output: 1.5 }, // approximate
53
+ 'llama-3.1-8b': { input: 0.05, output: 0.1 }, // approximate
54
+ // Novita
55
+ 'deepseek-r1': { input: 2.0, output: 8.0 },
56
+ 'deepseek-v3': { input: 1.25, output: 5.0 },
57
+ 'llama-3.1-70b': { input: 0.35, output: 0.4 }, // approximate
58
+ // Hyperbolic
59
+ 'qwen3-235b': { input: 0.5, output: 1.5 }, // approximate
33
60
  // Local endpoints are free
34
61
  'ollama': { input: 0, output: 0 },
35
62
  };
package/dist/server.js CHANGED
@@ -18,6 +18,7 @@ export function startSessionServer(ctl) {
18
18
  }
19
19
  const clients = new Set();
20
20
  const infoFor = (ref) => ctl.board.getAgentByName(ref);
21
+ const spawnMode = (mode) => (mode === 'ask' || mode === 'plan' || mode === 'task' ? mode : 'task');
21
22
  const send = (socket, msg) => {
22
23
  try {
23
24
  socket.write(JSON.stringify(msg) + '\n');
@@ -112,7 +113,7 @@ export function startSessionServer(ctl) {
112
113
  // its own dedicated terminal then opens automatically.
113
114
  const task = msg.text.trim();
114
115
  if (task)
115
- ctl.spawnAgent(task);
116
+ ctl.spawnAgent(task, undefined, undefined, undefined, undefined, undefined, spawnMode(msg.mode));
116
117
  }
117
118
  }
118
119
  });
@@ -1,25 +1,94 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useRef, useState, useEffect } from 'react';
2
3
  import { Box, Text } from 'ink';
3
- import { Spinner } from './Spinner.js';
4
4
  import { fmtCost } from '../pricing.js';
5
- import { STATE_LABEL, stateLabel, elapsed, truncate } from './theme.js';
5
+ import { elapsed, truncate } from './theme.js';
6
6
  import { Md } from './Md.js';
7
- import { t } from '../i18n.js';
7
+ import { Spinner } from './Spinner.js';
8
+ import { Timeline } from './Timeline.js';
9
+ import { MARK, MODE, STATE_META, UI, ANIM } from './tokens.js';
8
10
  export const KIND_COLOR = {
9
- tool: 'cyanBright',
10
- llm: 'gray',
11
- error: 'red',
12
- note: 'magentaBright',
13
- system: 'yellow',
14
- info: 'white',
11
+ tool: UI.accent,
12
+ llm: UI.muted,
13
+ error: UI.danger,
14
+ note: UI.note,
15
+ system: UI.warn,
16
+ info: UI.text,
15
17
  };
16
- /** Thinking/commentary lines (kind 'llm') are dimmed + italic, à la Codex/Claude Code. */
17
18
  export const KIND_DIM = { llm: true };
19
+ export function cleanHubSummary(text) {
20
+ return text
21
+ .replace(/^#{1,6}\s+/gm, '')
22
+ .replace(/\*\*(.*?)\*\*/g, '$1')
23
+ .replace(/`([^`]+)`/g, '$1')
24
+ .replace(/^\s*[-*]\s+/gm, '')
25
+ .replace(/\s+/g, ' ')
26
+ .trim();
27
+ }
28
+ export function formatAgentTelemetry(agent) {
29
+ return `${elapsed(agent.startedAt)} · ${agent.cost === null ? '$-' : fmtCost(agent.cost)}`;
30
+ }
31
+ function ResultBlock({ agent, compact = false }) {
32
+ if (!agent.lastResult)
33
+ return null;
34
+ if (compact) {
35
+ return (_jsxs(Text, { wrap: "truncate-end", children: [_jsxs(Text, { color: UI.ok, children: [MARK.done, " "] }), _jsx(Text, { children: truncate(agent.lastResult, 110) })] }));
36
+ }
37
+ return (_jsxs(Box, { borderStyle: "single", borderColor: "gray", flexDirection: "column", paddingX: 1, marginTop: 1, children: [_jsx(Text, { color: UI.ok, bold: true, children: "Result" }), _jsx(Md, { text: agent.lastResult })] }));
38
+ }
39
+ const SPINNER_STATES = new Set(['thinking', 'working', 'listening', 'waiting']);
40
+ function spinnerColor(state) {
41
+ if (state === 'working')
42
+ return 'cyan';
43
+ return 'yellow'; // thinking, listening, waiting
44
+ }
45
+ function modeChar(mode) {
46
+ if (mode === 'ask')
47
+ return { char: '?', color: MODE.ask };
48
+ if (mode === 'plan')
49
+ return { char: '△', color: MODE.plan };
50
+ return null; // task = no mark
51
+ }
52
+ function agentDisplayName(agent) {
53
+ return agent.alias && agent.alias !== agent.name ? `${agent.alias} ${agent.name}` : agent.alias || agent.name;
54
+ }
55
+ export function AgentRow({ agent, logs, cols, }) {
56
+ const meta = STATE_META[agent.state];
57
+ // ── State transition pulse (Phase 5) ──
58
+ const prevState = useRef(agent.state);
59
+ const [pulse, setPulse] = useState(false);
60
+ useEffect(() => {
61
+ if (agent.state !== prevState.current) {
62
+ setPulse(true);
63
+ const timer = setTimeout(() => setPulse(false), ANIM.pulseMs);
64
+ prevState.current = agent.state;
65
+ return () => clearTimeout(timer);
66
+ }
67
+ }, [agent.state]);
68
+ // Pulse bumps the mark/spinner color to whiteBright for 400ms
69
+ const pulseColor = pulse ? 'whiteBright' : null;
70
+ const name = agentDisplayName(agent);
71
+ const mode = modeChar(agent.mode);
72
+ const taskMax = Math.max(10, cols - 18);
73
+ const line2Max = Math.max(10, cols - 2);
74
+ const telemetry = formatAgentTelemetry(agent);
75
+ // Line 2 content
76
+ let line2 = null;
77
+ if (agent.lastResult) {
78
+ line2 = { text: `✓ ${truncate(cleanHubSummary(agent.lastResult), line2Max)}`, color: UI.ok };
79
+ }
80
+ else if (agent.currentAction) {
81
+ line2 = { text: `▸ ${truncate(agent.currentAction, line2Max)}`, color: UI.accent };
82
+ }
83
+ else {
84
+ line2 = { text: meta.label, color: meta.color };
85
+ }
86
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: 0, paddingLeft: 1, children: [_jsxs(Text, { wrap: "truncate-end", children: [SPINNER_STATES.has(agent.state) ? (_jsx(Spinner, { color: pulseColor ?? spinnerColor(agent.state) })) : (_jsx(Text, { color: pulseColor ?? meta.color, bold: true, children: meta.mark })), _jsx(Text, { children: " " }), _jsx(Text, { color: agent.color, bold: true, children: name }), mode ? (_jsxs(Text, { color: mode.color, children: [" ", mode.char] })) : null, _jsxs(Text, { color: UI.text, children: [" ", truncate(agent.task, taskMax)] })] }), _jsxs(Box, { flexDirection: "row", justifyContent: "space-between", children: [_jsx(Text, { color: line2.color, wrap: "truncate-end", children: line2.text }), _jsx(Text, { color: UI.muted, children: telemetry })] })] }));
87
+ }
88
+ export function AgentTranscript({ agent, logs, raw = false, scrolled = 0, }) {
89
+ const meta = STATE_META[agent.state];
90
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { children: [_jsx(Text, { color: agent.color, bold: true, children: agent.name }), agent.alias && agent.alias !== agent.name ? _jsxs(Text, { color: UI.muted, children: [" @", agent.alias] }) : null, _jsx(Text, { color: UI.muted, children: " " }), _jsxs(Text, { color: meta.color, bold: true, children: [meta.mark, " ", meta.label] })] }), _jsxs(Text, { color: UI.muted, wrap: "truncate-end", children: [agent.model, " \u00B7 ", formatAgentTelemetry(agent)] })] }), _jsxs(Text, { color: UI.muted, wrap: "wrap", children: ["Task ", _jsx(Text, { color: UI.text, children: agent.task })] }), agent.claims && agent.claims.length > 0 ? (_jsxs(Text, { color: UI.warn, wrap: "truncate-end", children: ["Claims ", agent.claims.join(' ')] })) : null, agent.currentAction ? (_jsxs(Text, { color: UI.accent, wrap: "truncate-end", children: ["Current ", truncate(agent.currentAction, 140)] })) : null, agent.state === 'done' || agent.lastResult ? _jsx(ResultBlock, { agent: agent }) : null, _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { color: UI.muted, bold: true, children: ["Activity", raw ? ' raw' : ''] }), _jsx(Timeline, { logs: logs, raw: raw })] }), _jsxs(Text, { color: UI.muted, wrap: "truncate-end", children: ["PgUp/PgDn scroll \u00B7 /raw toggles detail \u00B7 Esc returns", scrolled > 0 ? ` · ${scrolled} older` : ''] })] }));
91
+ }
18
92
  export function AgentPanel({ agent, logs, width, expanded = false, }) {
19
- const st = STATE_LABEL[agent.state];
20
- const busy = agent.state === 'thinking' || agent.state === 'working' || agent.state === 'listening';
21
- return (_jsx(Box, { width: width, paddingX: 0, children: _jsxs(Box, { borderStyle: agent.state === 'listening' ? 'double' : 'round', borderColor: agent.state === 'error' ? 'red' : agent.state === 'listening' ? 'cyanBright' : agent.color, flexDirection: "column", paddingX: 1, flexGrow: 1, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Box, { children: [_jsxs(Text, { color: agent.color, bold: true, children: ["\u25C6 ", agent.name, agent.alias && agent.alias !== agent.name ? _jsxs(Text, { color: "gray", children: [" @", agent.alias] }) : null, ' '] }), _jsxs(Text, { backgroundColor: st.color, color: "black", bold: true, children: [' ', st.icon, " ", stateLabel(agent.state), ' '] }), busy && (_jsxs(Text, { children: [' ', _jsx(Spinner, { color: agent.color })] }))] }), _jsxs(Text, { color: "gray", wrap: "truncate-end", children: [agent.specialist ? `🎓${agent.specialist} · ` : '', truncate(agent.model, 18), " \u00B7 ", elapsed(agent.startedAt), " \u00B7 ", agent.steps, " st \u00B7", ' ', Math.round((agent.tokensIn + agent.tokensOut) / 1000), "k \u00B7", ' ', agent.ctxPct !== undefined ? (_jsxs(Text, { color: agent.ctxPct >= 90 ? 'redBright' : agent.ctxPct >= 70 ? 'yellowBright' : 'gray', children: ["\u25D4", agent.ctxPct, "% \u00B7", ' '] })) : null, _jsx(Text, { color: "greenBright", children: agent.cost === null ? '$—' : fmtCost(agent.cost) })] })] }), _jsxs(Text, { color: "gray", wrap: expanded ? 'wrap' : 'truncate-end', children: ["\u25E6 ", expanded ? agent.task : truncate(agent.task, 120)] }), agent.claims && agent.claims.length > 0 ? (_jsxs(Text, { color: "yellowBright", wrap: "truncate-end", children: ["\uD83D\uDEA9 ", agent.claims.join(' ')] })) : null, agent.currentAction ? (_jsxs(Text, { color: agent.color, wrap: "truncate-end", children: ["\u25B8 ", truncate(agent.currentAction, 120)] })) : null, agent.lastResult ? (_jsxs(Box, { borderStyle: "single", borderColor: "gray", flexDirection: "column", paddingX: 1, marginTop: 1, children: [_jsx(Text, { color: "greenBright", bold: true, children: t('agent.summary') }), expanded || agent.state === 'done' ? (
22
- // A finished agent's summary is the deliverable: show it ENTIRELY,
23
- // wrapped and lightly formatted — never truncated.
24
- _jsx(Md, { text: agent.lastResult })) : (_jsx(Text, { color: "white", wrap: "truncate-end", children: truncate(agent.lastResult, 260) }))] })) : null, _jsx(Box, { flexDirection: "column", marginTop: 0, children: logs.map((l, i) => (_jsx(Text, { color: KIND_COLOR[l.kind] ?? 'white', italic: KIND_DIM[l.kind] ?? false, wrap: "truncate-end", children: truncate(l.text, expanded ? 220 : 140) }, i))) })] }) }));
93
+ return (_jsx(Box, { width: width, flexDirection: "column", children: expanded ? _jsx(AgentTranscript, { agent: agent, logs: logs }) : _jsx(AgentRow, { agent: agent, logs: logs, cols: 100 }) }));
25
94
  }