@hunterzhu/pulse-cli 0.3.0 → 0.4.0
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/bin.js +0 -5
- package/dist/config.d.ts +0 -2
- package/dist/config.js +0 -1
- package/dist/hooks/useRun.js +35 -34
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -46,7 +46,6 @@ Options:
|
|
|
46
46
|
--max-output-tokens <n> maximum generated tokens (default 4096)
|
|
47
47
|
--reasoning-effort <x> low, medium, or high
|
|
48
48
|
--max-turns <n> maximum ReAct model/tool turns (default 32)
|
|
49
|
-
--execution-mode <mode> serial (default) or parallel-read (up to three read-only lanes)
|
|
50
49
|
--auto-compact-percent <n> compact automatically at this percent of the context window (1-90, default 90)
|
|
51
50
|
--format <text|jsonl> output format
|
|
52
51
|
--resume enter the latest saved session
|
|
@@ -234,9 +233,6 @@ export async function hostOptions(parsed) {
|
|
|
234
233
|
? 'auto'
|
|
235
234
|
: requestedApprovalMode === 'ask' ? 'ask' : config.approvalMode;
|
|
236
235
|
const allowNetwork = parsed.options['no-network'] === true ? false : parsed.options['allow-network'] === true || process.env.PULSE_ALLOW_NETWORK === '1' ? true : config.allowNetwork;
|
|
237
|
-
const executionMode = option(parsed.options, 'execution-mode') ?? process.env.PULSE_EXECUTION_MODE ?? config.executionMode ?? 'serial';
|
|
238
|
-
if (executionMode !== 'serial' && executionMode !== 'parallel-read')
|
|
239
|
-
throw new Error('INVALID_EXECUTION_MODE: expected serial or parallel-read');
|
|
240
236
|
const capabilitySettings = config.capabilities ?? {};
|
|
241
237
|
const skillRoots = capabilitySettings.trustedSkillRoots ?? [];
|
|
242
238
|
if (!Array.isArray(skillRoots) || skillRoots.some((root) => typeof root !== 'string' || !isAbsolute(expandHome(root) ?? root)))
|
|
@@ -315,7 +311,6 @@ export async function hostOptions(parsed) {
|
|
|
315
311
|
...(configuredAutoCompactPercent === undefined ? {} : { autoCompactPercent: Math.min(90, configuredAutoCompactPercent) }),
|
|
316
312
|
...(allowNetwork === undefined ? {} : { allowNetwork }),
|
|
317
313
|
...(config.networkHosts === undefined ? {} : { networkHosts: config.networkHosts }),
|
|
318
|
-
executionMode,
|
|
319
314
|
};
|
|
320
315
|
}
|
|
321
316
|
export async function main() {
|
package/dist/config.d.ts
CHANGED
|
@@ -69,8 +69,6 @@ export interface PulseCliConfig {
|
|
|
69
69
|
taskRouting?: Partial<Record<PulseModelTask, string[]>>;
|
|
70
70
|
capabilities?: PulseCliCapabilities;
|
|
71
71
|
approvalMode?: 'read-only' | 'ask' | 'auto';
|
|
72
|
-
/** Parallelism is opt-in and only exposes tools explicitly classified read-only. */
|
|
73
|
-
executionMode?: 'serial' | 'parallel-read';
|
|
74
72
|
maxTurns?: number;
|
|
75
73
|
/** Percent of maxContextTokens that triggers automatic history compaction. Clamped to 1–90. */
|
|
76
74
|
autoCompactPercent?: number;
|
package/dist/config.js
CHANGED
package/dist/hooks/useRun.js
CHANGED
|
@@ -39,8 +39,14 @@ export function useRun({ host, conversationId, addAssistantMessage, }) {
|
|
|
39
39
|
const [isRunning, setIsRunning] = useState(false);
|
|
40
40
|
const [currentStep, setCurrentStep] = useState(null);
|
|
41
41
|
const [error, setError] = useState(null);
|
|
42
|
-
const [
|
|
43
|
-
const
|
|
42
|
+
const [pendingInteractions, setPendingInteractions] = useState([]);
|
|
43
|
+
const enqueueInteraction = useCallback((item) => {
|
|
44
|
+
setPendingInteractions((current) => current.some((entry) => entry.request.effectId === item.request.effectId) ? current : [...current, item]);
|
|
45
|
+
}, []);
|
|
46
|
+
const removeInteraction = useCallback((effectId) => setPendingInteractions((current) => current.filter((entry) => entry.request.effectId !== effectId)), []);
|
|
47
|
+
const activeInteraction = pendingInteractions[0];
|
|
48
|
+
const approvalRequest = activeInteraction?.kind === 'approval' ? activeInteraction.request : null;
|
|
49
|
+
const askRequest = activeInteraction?.kind === 'ask' ? activeInteraction.request : null;
|
|
44
50
|
const [approvalSubmitting, setApprovalSubmitting] = useState(false);
|
|
45
51
|
const [lanes, setLanes] = useState([]);
|
|
46
52
|
const runRef = useRef(null);
|
|
@@ -180,23 +186,21 @@ export function useRun({ host, conversationId, addAssistantMessage, }) {
|
|
|
180
186
|
return typeof item.label === 'string' && typeof item.value === 'string' ? [{ label: item.label, value: item.value }] : [];
|
|
181
187
|
})
|
|
182
188
|
: undefined;
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
});
|
|
189
|
+
enqueueInteraction({ kind: 'ask', request: {
|
|
190
|
+
effectId,
|
|
191
|
+
toolName: typeof input.toolName === 'string' ? input.toolName : `ask.${input.type}`,
|
|
192
|
+
type: input.type,
|
|
193
|
+
prompt: typeof input.prompt === 'string' ? input.prompt : '请输入你的回答。',
|
|
194
|
+
...(options && options.length ? { options } : {}),
|
|
195
|
+
...(typeof input.min === 'number' ? { min: input.min } : {}),
|
|
196
|
+
...(typeof input.max === 'number' ? { max: input.max } : {}),
|
|
197
|
+
...(typeof input.placeholder === 'string' ? { placeholder: input.placeholder } : {}),
|
|
198
|
+
...(typeof input.defaultValue === 'string' ? { defaultValue: input.defaultValue } : {}),
|
|
199
|
+
} });
|
|
195
200
|
setCurrentStep('等待你的回答...');
|
|
196
201
|
addProgress(`waiting:${effectId}`, '等待你的回答…');
|
|
197
202
|
break;
|
|
198
203
|
}
|
|
199
|
-
setAskRequest(null);
|
|
200
204
|
const tools = Array.isArray(input.tools)
|
|
201
205
|
? input.tools.flatMap((tool) => {
|
|
202
206
|
if (!tool || typeof tool !== 'object' || Array.isArray(tool))
|
|
@@ -212,14 +216,14 @@ export function useRun({ host, conversationId, addAssistantMessage, }) {
|
|
|
212
216
|
})
|
|
213
217
|
: [];
|
|
214
218
|
const firstTool = tools[0];
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
219
|
+
enqueueInteraction({ kind: 'approval', request: {
|
|
220
|
+
effectId,
|
|
221
|
+
toolName: tools.length === 1 ? firstTool.name : `${tools.length || 1} 个系统操作`,
|
|
222
|
+
toolArgs: firstTool?.input ?? {},
|
|
223
|
+
prompt: typeof input.prompt === 'string' ? input.prompt : '是否批准执行?',
|
|
224
|
+
...(typeof input.digest === 'string' ? { digest: input.digest } : {}),
|
|
225
|
+
...(tools.length ? { tools } : {}),
|
|
226
|
+
} });
|
|
223
227
|
setCurrentStep('等待用户审批...');
|
|
224
228
|
addProgress(`approval:${effectId}`, `等待审批 · ${tools.map((tool) => tool.name).join('、') || '系统操作'}`);
|
|
225
229
|
break;
|
|
@@ -280,16 +284,14 @@ export function useRun({ host, conversationId, addAssistantMessage, }) {
|
|
|
280
284
|
setError(String(event.data ?? '发生未知错误'));
|
|
281
285
|
setIsRunning(false);
|
|
282
286
|
setCurrentStep(null);
|
|
283
|
-
|
|
284
|
-
setAskRequest(null);
|
|
287
|
+
setPendingInteractions([]);
|
|
285
288
|
setLanes([]);
|
|
286
289
|
break;
|
|
287
290
|
case 'complete':
|
|
288
291
|
addProgress('finished', event.data && typeof event.data === 'object' && !Array.isArray(event.data) && event.data.status === 'succeeded' ? '任务处理完成。' : '任务已结束。');
|
|
289
292
|
setIsRunning(false);
|
|
290
293
|
setCurrentStep(null);
|
|
291
|
-
|
|
292
|
-
setAskRequest(null);
|
|
294
|
+
setPendingInteractions([]);
|
|
293
295
|
if (event.data && typeof event.data === 'object' && !Array.isArray(event.data)) {
|
|
294
296
|
const completion = event.data;
|
|
295
297
|
const accepted = completion.status === 'succeeded' && (!completion.taskOutcome || (typeof completion.taskOutcome === 'object' && completion.taskOutcome !== null && completion.taskOutcome.status === 'accepted'));
|
|
@@ -343,12 +345,11 @@ export function useRun({ host, conversationId, addAssistantMessage, }) {
|
|
|
343
345
|
break;
|
|
344
346
|
}
|
|
345
347
|
}
|
|
346
|
-
}, [addAssistantMessage]);
|
|
348
|
+
}, [addAssistantMessage, enqueueInteraction]);
|
|
347
349
|
const finishRun = useCallback(() => {
|
|
348
350
|
setIsRunning(false);
|
|
349
351
|
setCurrentStep(null);
|
|
350
|
-
|
|
351
|
-
setAskRequest(null);
|
|
352
|
+
setPendingInteractions([]);
|
|
352
353
|
runRef.current = null;
|
|
353
354
|
}, []);
|
|
354
355
|
const sendMessage = useCallback(async (text, targetConversationId) => {
|
|
@@ -409,7 +410,7 @@ export function useRun({ host, conversationId, addAssistantMessage, }) {
|
|
|
409
410
|
setApprovalSubmitting(true);
|
|
410
411
|
try {
|
|
411
412
|
await runRef.current.reply(effectId, { approved, ...(approved ? {} : { reason: reason || '拒绝执行' }) });
|
|
412
|
-
|
|
413
|
+
removeInteraction(effectId);
|
|
413
414
|
setCurrentStep('审批已提交,正在继续...');
|
|
414
415
|
setError(null);
|
|
415
416
|
}
|
|
@@ -420,14 +421,14 @@ export function useRun({ host, conversationId, addAssistantMessage, }) {
|
|
|
420
421
|
finally {
|
|
421
422
|
setApprovalSubmitting(false);
|
|
422
423
|
}
|
|
423
|
-
}, [approvalSubmitting]);
|
|
424
|
+
}, [approvalSubmitting, removeInteraction]);
|
|
424
425
|
const replyAsk = useCallback(async (effectId, value) => {
|
|
425
426
|
if (!effectId || !runRef.current || approvalSubmitting)
|
|
426
427
|
return;
|
|
427
428
|
setApprovalSubmitting(true);
|
|
428
429
|
try {
|
|
429
430
|
await runRef.current.reply(effectId, value);
|
|
430
|
-
|
|
431
|
+
removeInteraction(effectId);
|
|
431
432
|
setCurrentStep('回答已提交,正在继续...');
|
|
432
433
|
setError(null);
|
|
433
434
|
}
|
|
@@ -438,7 +439,7 @@ export function useRun({ host, conversationId, addAssistantMessage, }) {
|
|
|
438
439
|
finally {
|
|
439
440
|
setApprovalSubmitting(false);
|
|
440
441
|
}
|
|
441
|
-
}, [approvalSubmitting]);
|
|
442
|
+
}, [approvalSubmitting, removeInteraction]);
|
|
442
443
|
const cancelRun = useCallback(async () => {
|
|
443
444
|
if (runRef.current) {
|
|
444
445
|
await runRef.current.cancel('USER_CANCELLED');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hunterzhu/pulse-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "A local-first AI task assistant for coding, research, and file workflows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"registry": "https://registry.npmjs.org"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@hunterzhu/pulse-server": "0.
|
|
38
|
+
"@hunterzhu/pulse-server": "0.4.0",
|
|
39
39
|
"chalk": "^5.4.1",
|
|
40
40
|
"highlight.js": "^11.10.0",
|
|
41
41
|
"ink": "^7.1.1",
|