@hmharness/agent 0.14.7 → 0.14.10
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/i18n.d.ts +39 -0
- package/dist/i18n.js +70 -4
- package/dist/prompt.d.ts +1 -0
- package/dist/prompt.js +6 -1
- package/dist/runner.d.ts +29 -0
- package/dist/runner.js +28 -2
- package/package.json +3 -3
package/dist/i18n.d.ts
CHANGED
|
@@ -56,6 +56,37 @@ export interface Strings {
|
|
|
56
56
|
thought: string;
|
|
57
57
|
/** busy spinner text, shown in the input-box status line (TUI) */
|
|
58
58
|
tuiRunning: string;
|
|
59
|
+
/** M2: Ctrl+T full-transcript overlay title (B5) */
|
|
60
|
+
tuiTranscript: string;
|
|
61
|
+
/** M2: Ctrl+R history-search modal (B4) */
|
|
62
|
+
histSearchTitle: string;
|
|
63
|
+
histSearchNone: string;
|
|
64
|
+
histSearchHint: string;
|
|
65
|
+
/** M2: @-file palette hint (B2) */
|
|
66
|
+
atPalHint: string;
|
|
67
|
+
/** M2: transcript-overlay / pager footer hint (B5/B9) */
|
|
68
|
+
overlayHint: string;
|
|
69
|
+
/** M2: Esc-Esc fork-edit armed hint (T13) */
|
|
70
|
+
forkArmed: string;
|
|
71
|
+
cmdCompact: string;
|
|
72
|
+
cmdDiff: string;
|
|
73
|
+
cmdNew: string;
|
|
74
|
+
cmdNewDone: string;
|
|
75
|
+
cmdFork: string;
|
|
76
|
+
cmdCopy: string;
|
|
77
|
+
cmdMouse: string;
|
|
78
|
+
cmdMouseOn: string;
|
|
79
|
+
cmdMouseOff: string;
|
|
80
|
+
cmdCopied: string;
|
|
81
|
+
cmdPlan: string;
|
|
82
|
+
cmdPlanOn: string;
|
|
83
|
+
cmdPlanOff: string;
|
|
84
|
+
cmdGoal: string;
|
|
85
|
+
cmdGoalCleared: string;
|
|
86
|
+
cmdUsage: string;
|
|
87
|
+
cmdStatusline: string;
|
|
88
|
+
cmdKeymap: string;
|
|
89
|
+
cmdReview: string;
|
|
59
90
|
cmdModelHint: string;
|
|
60
91
|
/** palette footer key hint: how to drive the slash/model picker (TUI) */
|
|
61
92
|
panelHint: string;
|
|
@@ -74,6 +105,10 @@ export interface Strings {
|
|
|
74
105
|
cmdResumeNone: string;
|
|
75
106
|
cmdResumeNotFound: (p: string) => string;
|
|
76
107
|
cmdResumeLoaded: (n: number) => string;
|
|
108
|
+
/** banner shown when /resume actually loads a transcript into the TUI */
|
|
109
|
+
cmdResuming: (id: string, total: number, hidden: number) => string;
|
|
110
|
+
/** why tools-required cases are skipped inside /evolve (TUI runs a lite loop) */
|
|
111
|
+
cmdEvolveHint: string;
|
|
77
112
|
/** Codex-style resume picker (v0.8.2 transplant) */
|
|
78
113
|
pickerTitle: string;
|
|
79
114
|
pickerHint1: string;
|
|
@@ -91,6 +126,10 @@ export interface Strings {
|
|
|
91
126
|
pickerValCreated: string;
|
|
92
127
|
pickerPos: (pos: number, total: number, pct: string) => string;
|
|
93
128
|
tuiRadarScanning: string;
|
|
129
|
+
opsRadarLine: (n: number, time: string) => string;
|
|
130
|
+
opsRadarNone: string;
|
|
131
|
+
opsWatchingLabel: string;
|
|
132
|
+
opsHint: string;
|
|
94
133
|
tuiEvolveDone: (proposals: number, insights: number, notes: number) => string;
|
|
95
134
|
tuiPassRate: (pct: string) => string;
|
|
96
135
|
tuiStatus: (locale: string, skills: number, model: string) => string;
|
package/dist/i18n.js
CHANGED
|
@@ -40,11 +40,37 @@ const zh = {
|
|
|
40
40
|
tuiApprove: '批准',
|
|
41
41
|
tuiDeny: '拒绝',
|
|
42
42
|
tuiApprovalHint: 'enter/y 批准 · esc/n 拒绝',
|
|
43
|
-
tuiHints: 'enter 发送 ·
|
|
44
|
-
tuiBusyHints: (queued) => `运行中 ·
|
|
43
|
+
tuiHints: 'enter 发送 · Esc 中断/清空 · Ctrl+Enter 注入 · Ctrl+R 历史搜索 · Ctrl+T 转录 · ^P/^N 历史 · esc 清空 · ^C 退出 · /help',
|
|
44
|
+
tuiBusyHints: (queued) => `运行中 · Esc=停止 · 输入=排队(${queued}) · Ctrl+Enter=注入 · /help`,
|
|
45
45
|
thinking: '思考中',
|
|
46
46
|
thought: '已完成思考',
|
|
47
47
|
tuiRunning: '运行中…',
|
|
48
|
+
tuiTranscript: '完整转录(含工具输出)',
|
|
49
|
+
histSearchTitle: '历史搜索',
|
|
50
|
+
histSearchNone: '(无匹配)',
|
|
51
|
+
histSearchHint: '↑↓ 选择 · Enter 采用 · Esc 取消',
|
|
52
|
+
atPalHint: '↑↓ 选择 · Enter 插入路径 · Esc 取消',
|
|
53
|
+
overlayHint: '↑↓/PgUp/PgDn 滚动 · g/G 首尾 · q/Esc 返回',
|
|
54
|
+
forkArmed: '再按 Esc:编辑上一条并 fork',
|
|
55
|
+
cmdCompact: '压缩上下文并报告释放量',
|
|
56
|
+
cmdDiff: '工作区 git diff(含未跟踪文件)的 pager 查看',
|
|
57
|
+
cmdNew: '新会话(清转录,开新 rollout)',
|
|
58
|
+
cmdNewDone: '✓ 新会话开始(旧转录可用 Ctrl+T 回看)',
|
|
59
|
+
cmdFork: 'fork 当前会话为新线程(继承全部上下文)',
|
|
60
|
+
cmdCopy: '复制最后一条 AI 输出到剪贴板',
|
|
61
|
+
cmdMouse: '切换全时鼠标捕获(仅无滚轮翻译的终端需要;默认 ?1007 已覆盖大多数终端)',
|
|
62
|
+
cmdMouseOn: '🖱 全时鼠标捕获已开启(滚轮直读;原生划选复制请按住 Shift 再拖动)',
|
|
63
|
+
cmdMouseOff: '🖱 全时鼠标捕获已关闭(滚轮走终端方向键翻译;划选复制恢复原生)',
|
|
64
|
+
cmdCopied: '✓ 已复制最后一条 AI 输出',
|
|
65
|
+
cmdPlan: '计划模式开关(先出计划待确认再执行)',
|
|
66
|
+
cmdPlanOn: '🔥 计划模式:先出计划,确认后才执行',
|
|
67
|
+
cmdPlanOff: '计划模式关闭',
|
|
68
|
+
cmdGoal: '设置/查看/清除会话目标(/goal <text>|clear)',
|
|
69
|
+
cmdGoalCleared: '目标已清除',
|
|
70
|
+
cmdUsage: '本次会话 token 用量与上下文占用',
|
|
71
|
+
cmdStatusline: '自定义底栏字段(/statusline "{model} · {cwd}")',
|
|
72
|
+
cmdKeymap: '查看/重映射动作键(/keymap <action>=<key>)',
|
|
73
|
+
cmdReview: '一键发起「审查当前工作树」任务',
|
|
48
74
|
cmdModelHint: '切换 chat 路由: /model <name>',
|
|
49
75
|
panelHint: '↑↓/滚轮/点击 选择 · Enter 确认 · Esc 关闭',
|
|
50
76
|
cmdModelPreset: (name, envVar) => `${name} 尚未配置 — 设置 ${envVar} 后运行 /providers scan 添加`,
|
|
@@ -60,6 +86,9 @@ const zh = {
|
|
|
60
86
|
cmdResumeNone: '还没有历史会话',
|
|
61
87
|
cmdResumeNotFound: (p) => `没有匹配 "${p}" 的会话`,
|
|
62
88
|
cmdResumeLoaded: (n) => `已恢复会话(${n} 条消息),直接输入内容继续对话`,
|
|
89
|
+
cmdResuming: (id, total, hidden) => '--- 已恢复会话 ' + id + ' · ' + total + ' 条消息'
|
|
90
|
+
+ (hidden > 0 ? '(前 ' + hidden + ' 条保留在上下文中,未显示)' : '') + ' ---',
|
|
91
|
+
cmdEvolveHint: '(TUI 内跳过;运行 hmh evolve 执行完整循环)',
|
|
63
92
|
pickerTitle: '恢复会话',
|
|
64
93
|
pickerHint1: 'Enter 恢复 · ↑↓ 浏览 · Tab 切筛选/排序 · ←→ 调整',
|
|
65
94
|
pickerHint2: 'Esc 关闭 · 输入即过滤(标题/id/目录/分支)',
|
|
@@ -76,6 +105,10 @@ const zh = {
|
|
|
76
105
|
pickerValCreated: '创建时间',
|
|
77
106
|
pickerPos: (pos, total, pct) => ` ${pos} / ${total} · ${pct}% `,
|
|
78
107
|
tuiRadarScanning: '雷达扫描中…',
|
|
108
|
+
opsRadarLine: (n, time) => `生态雷达:已扫描 ${n} 次,最近一次 ${time}`,
|
|
109
|
+
opsRadarNone: '生态雷达:还没有扫描记录——输入 /ops scan 立即扫描上游',
|
|
110
|
+
opsWatchingLabel: '监控来源:',
|
|
111
|
+
opsHint: '更多操作:/ops scan 立即扫描并生成今日简报 · /ops brief 查看最近简报 · /ops stats 各包 npm 下载量',
|
|
79
112
|
tuiEvolveDone: (p, i, n) => `evolve 完成: ${p} 提案 · 洞察 ${i} · 记忆 ${n}`,
|
|
80
113
|
tuiPassRate: (pct) => `pass rate: ${pct}`,
|
|
81
114
|
tuiStatus: (locale, skills, model) => `${locale} · ${skills} 技能 · ${model}`,
|
|
@@ -150,11 +183,37 @@ const en = {
|
|
|
150
183
|
tuiApprove: 'Approve',
|
|
151
184
|
tuiDeny: 'Deny',
|
|
152
185
|
tuiApprovalHint: 'enter/y approve · esc/n deny',
|
|
153
|
-
tuiHints: 'enter send ·
|
|
154
|
-
tuiBusyHints: (queued) => `running ·
|
|
186
|
+
tuiHints: 'enter send · Esc interrupt/clear · Ctrl+Enter inject · Ctrl+R history · Ctrl+T transcript · ^P/^N history · esc clear · ^C quit · /help',
|
|
187
|
+
tuiBusyHints: (queued) => `running · Esc=stop · typing queues (${queued}) · Ctrl+Enter=inject · /help`,
|
|
155
188
|
thinking: 'thinking',
|
|
156
189
|
thought: 'thought (done)',
|
|
157
190
|
tuiRunning: 'running…',
|
|
191
|
+
tuiTranscript: 'full transcript (incl. tool outputs)',
|
|
192
|
+
histSearchTitle: 'history search',
|
|
193
|
+
histSearchNone: '(no match)',
|
|
194
|
+
histSearchHint: '↑↓ select · Enter adopt · Esc close',
|
|
195
|
+
atPalHint: '↑↓ select · Enter insert path · Esc close',
|
|
196
|
+
overlayHint: '↑↓/PgUp/PgDn scroll · g/G top/bottom · q/Esc back',
|
|
197
|
+
forkArmed: 'Esc again: edit last message and fork',
|
|
198
|
+
cmdCompact: 'compact the context and report freed space',
|
|
199
|
+
cmdDiff: 'pager-view the worktree git diff (incl. untracked)',
|
|
200
|
+
cmdNew: 'new session (clear transcript, fresh rollout)',
|
|
201
|
+
cmdNewDone: '✓ new session (old transcript: Ctrl+T to replay)',
|
|
202
|
+
cmdFork: 'fork this session to a new thread (inherits full context)',
|
|
203
|
+
cmdCopy: 'copy the last AI output to the clipboard',
|
|
204
|
+
cmdMouse: 'toggle full-time mouse capture (only for terminals without wheel translation; ?1007 covers most)',
|
|
205
|
+
cmdMouseOn: '🖱 full-time mouse capture ON (direct wheel; hold Shift to select natively)',
|
|
206
|
+
cmdMouseOff: '🖱 full-time mouse capture OFF (wheel via terminal translation; native selection restored)',
|
|
207
|
+
cmdCopied: '✓ copied the last AI output',
|
|
208
|
+
cmdPlan: 'plan mode toggle (plan first, confirm, then execute)',
|
|
209
|
+
cmdPlanOn: '🔥 plan mode: present a plan, wait for confirmation before executing',
|
|
210
|
+
cmdPlanOff: 'plan mode off',
|
|
211
|
+
cmdGoal: 'set/view/clear the session goal (/goal <text>|clear)',
|
|
212
|
+
cmdGoalCleared: 'goal cleared',
|
|
213
|
+
cmdUsage: 'session token usage and context occupancy',
|
|
214
|
+
cmdStatusline: 'custom bottom-line fields (/statusline "{model} · {cwd}")',
|
|
215
|
+
cmdKeymap: 'view/remap action keys (/keymap <action>=<key>)',
|
|
216
|
+
cmdReview: 'one-click "review the current worktree" task',
|
|
158
217
|
cmdModelHint: 'switch chat route: /model <name>',
|
|
159
218
|
panelHint: '↑↓ / wheel / click to select · Enter confirms · Esc closes',
|
|
160
219
|
cmdModelPreset: (name, envVar) => `${name} not configured - set ${envVar}, then run /providers scan`,
|
|
@@ -170,6 +229,9 @@ const en = {
|
|
|
170
229
|
cmdResumeNone: 'no past sessions yet',
|
|
171
230
|
cmdResumeNotFound: (p) => `no session matches "${p}"`,
|
|
172
231
|
cmdResumeLoaded: (n) => `session resumed (${n} messages) - just type to continue`,
|
|
232
|
+
cmdResuming: (id, total, hidden) => '--- resumed ' + id + ' · ' + total + ' messages'
|
|
233
|
+
+ (hidden > 0 ? ' (' + hidden + ' earlier kept in context, not shown)' : '') + ' ---',
|
|
234
|
+
cmdEvolveHint: '(skipped in tui; run hmh evolve)',
|
|
173
235
|
pickerTitle: 'Resume session',
|
|
174
236
|
pickerHint1: 'Enter resume · ↑↓ browse · Tab filter/sort · ←→ change',
|
|
175
237
|
pickerHint2: 'Esc close · type to filter (title/id/dir/branch)',
|
|
@@ -186,6 +248,10 @@ const en = {
|
|
|
186
248
|
pickerValCreated: 'Created',
|
|
187
249
|
pickerPos: (pos, total, pct) => ` ${pos} / ${total} · ${pct}% `,
|
|
188
250
|
tuiRadarScanning: 'scanning radar…',
|
|
251
|
+
opsRadarLine: (n, time) => `ecosystem radar: ${n} scans, last ${time}`,
|
|
252
|
+
opsRadarNone: 'ecosystem radar: no scans yet - run /ops scan to pull upstream now',
|
|
253
|
+
opsWatchingLabel: 'watching:',
|
|
254
|
+
opsHint: 'more: /ops scan pull upstream + write today brief · /ops brief latest brief · /ops stats npm downloads per package',
|
|
189
255
|
tuiEvolveDone: (p, i, n) => `evolve done: ${p} proposals · ${i} insights · ${n} notes`,
|
|
190
256
|
tuiPassRate: (pct) => `pass rate: ${pct}`,
|
|
191
257
|
tuiStatus: (locale, skills, model) => `${locale} · ${skills} skills · ${model}`,
|
package/dist/prompt.d.ts
CHANGED
package/dist/prompt.js
CHANGED
|
@@ -14,7 +14,12 @@ export function buildSystemPrompt(opts) {
|
|
|
14
14
|
// ---- [DeepSeek] 分段执行 + 合理质疑 ----
|
|
15
15
|
'', 'For tasks requiring reasoning across multiple steps, work in phases: search and list the key facts, plan the concrete steps, then execute. Do not skip planning to act directly.', 'If the user\'s requested approach carries risk or there is a clearly better alternative, raise it proactively - state the tradeoffs and let the user choose. Only execute confirmed plans.',
|
|
16
16
|
// ---- [Codex] 用户反馈 ----
|
|
17
|
-
'', "When the user gives feedback saying something didn't work, treat it as ground truth. Use available tools to verify the current state, identify the root cause, fix it, and explain what changed."
|
|
17
|
+
'', "When the user gives feedback saying something didn't work, treat it as ground truth. Use available tools to verify the current state, identify the root cause, fix it, and explain what changed.");
|
|
18
|
+
// ---- 会话目标 ----
|
|
19
|
+
if (opts.goal) {
|
|
20
|
+
parts.push('', '## Active goal', 'Work toward this goal across turns until it is complete or explicitly cleared:', opts.goal);
|
|
21
|
+
}
|
|
22
|
+
parts.push(
|
|
18
23
|
// ---- 宿主环境 ----
|
|
19
24
|
'', '## Host environment (facts - rely on these, do not guess)', `- OS: ${process.platform} (${process.arch}). The run_command tool executes through ${isWin ? 'cmd.exe (Windows cmd - NOT bash, NOT PowerShell)' : '/bin/sh'}.`, isWin
|
|
20
25
|
? '- cmd.exe has NO grep/head/tail/ls/cat/$(...) and wmic may be absent. Use: findstr (search), "more" or node -e (head/text ops), dir /b (ls), type (cat), where (which). For anything richer: powershell -NoProfile -Command "..."'
|
package/dist/runner.d.ts
CHANGED
|
@@ -73,6 +73,8 @@ export interface RunnerEvents {
|
|
|
73
73
|
onToolCall?(name: string, args: Record<string, unknown>): void;
|
|
74
74
|
onToolResult?(name: string, output: string, isError: boolean): void;
|
|
75
75
|
onApproval?(name: string, args: Record<string, unknown>, granted: boolean): void;
|
|
76
|
+
/** A runtime-steering user message entered the running loop's transcript. */
|
|
77
|
+
onInjected?(message: string): void;
|
|
76
78
|
onFinal?(r: {
|
|
77
79
|
text: string;
|
|
78
80
|
turns: number;
|
|
@@ -84,6 +86,22 @@ export interface RunnerEvents {
|
|
|
84
86
|
};
|
|
85
87
|
}): void;
|
|
86
88
|
}
|
|
89
|
+
/** Live runtime-steering channel (web Ctrl+Enter / TUI Enter-while-running):
|
|
90
|
+
* the caller pushes user messages into `queue`; the running loop drains them
|
|
91
|
+
* between tool batches. `active` flips to false when the task ends, so the
|
|
92
|
+
* caller can route a late push into the next task's queue instead. */
|
|
93
|
+
export interface InjectChannel {
|
|
94
|
+
queue: string[];
|
|
95
|
+
active: boolean;
|
|
96
|
+
}
|
|
97
|
+
/** Poll-shaped runtime steering (codex Enter-inject semantics): poll() is
|
|
98
|
+
* drained at every turn boundary; each returned entry becomes a user turn
|
|
99
|
+
* in the running loop. */
|
|
100
|
+
export interface InjectPoll {
|
|
101
|
+
poll(): Array<{
|
|
102
|
+
text: string;
|
|
103
|
+
}>;
|
|
104
|
+
}
|
|
87
105
|
export interface AgentTaskOptions {
|
|
88
106
|
task: string;
|
|
89
107
|
registry: Registry;
|
|
@@ -99,6 +117,17 @@ export interface AgentTaskOptions {
|
|
|
99
117
|
events?: RunnerEvents;
|
|
100
118
|
/** AbortSignal: cancels the agent loop at the next turn boundary. */
|
|
101
119
|
signal?: AbortSignal;
|
|
120
|
+
/** Runtime-steering channel (web Ctrl+Enter / TUI Enter-while-running):
|
|
121
|
+
* either the push queue (InjectChannel, drained between tool batches) or
|
|
122
|
+
* the poll shape (drained at every turn boundary). */
|
|
123
|
+
inject?: InjectChannel | InjectPoll;
|
|
124
|
+
/** Fork this run from an existing session: the new rollout records the
|
|
125
|
+
* parent session id as `forkedFrom` in its session_meta. */
|
|
126
|
+
forkFrom?: string;
|
|
127
|
+
/** Session goal: injected into the system prompt and persisted under
|
|
128
|
+
* HMH_HOME/goals.json (when unset or null, the persisted goal for the
|
|
129
|
+
* session is used, so the goal survives resumes/forks). */
|
|
130
|
+
goal?: string | null;
|
|
102
131
|
}
|
|
103
132
|
/** Run one full agent task end-to-end; audit + insight recording included. */
|
|
104
133
|
export declare function runAgentTask(opts: AgentTaskOptions): Promise<LoopResult & {
|
package/dist/runner.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Also owns the native registry factory (spawn_agent recursion) and the
|
|
6
6
|
* approval gate construction.
|
|
7
7
|
*/
|
|
8
|
-
import { homeDir, loadConfig, resolveProvider, mcpServerTools, Registry, runLoop, Session, } from '@hmharness/kernel';
|
|
8
|
+
import { homeDir, loadConfig, resolveProvider, getGoal, mcpServerTools, Registry, runLoop, Session, } from '@hmharness/kernel';
|
|
9
9
|
import { brief, createTrajectoryRecorder } from '@hmharness/observability';
|
|
10
10
|
import { readFile } from 'node:fs/promises';
|
|
11
11
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
@@ -230,10 +230,18 @@ export async function runAgentTask(opts) {
|
|
|
230
230
|
const cfg = opts.cfg ?? (await loadConfig());
|
|
231
231
|
const ctx = opts.ctx ?? { cwd: process.cwd(), home: homeDir() };
|
|
232
232
|
const events = opts.events ?? {};
|
|
233
|
+
// inject accepts either the push channel (legacy) or the poll shape
|
|
234
|
+
const injectChannel = opts.inject && 'queue' in opts.inject ? opts.inject : undefined;
|
|
235
|
+
const injectPoll = opts.inject && 'poll' in opts.inject ? opts.inject : undefined;
|
|
236
|
+
if (injectChannel)
|
|
237
|
+
injectChannel.active = true; // channel is live for this task
|
|
233
238
|
// resume = append to the same rollout; a missing/unreadable id falls back
|
|
234
239
|
// to a fresh session so a renamed-away file never breaks the conversation
|
|
235
240
|
const session = (opts.sessionId ? await Session.resume(ctx.home, opts.sessionId) : null)
|
|
236
|
-
?? Session.create(ctx.home, ctx.cwd, cfg.provider.model);
|
|
241
|
+
?? Session.create(ctx.home, ctx.cwd, cfg.provider.model, opts.forkFrom ? { forkedFrom: opts.forkFrom } : {});
|
|
242
|
+
// session-level goal: explicit opts.goal wins; otherwise the persisted
|
|
243
|
+
// goal (HMH_HOME/goals.json) carries over into resumed/forked sessions
|
|
244
|
+
const goal = opts.goal ?? (await getGoal(ctx.home, session.id));
|
|
237
245
|
// V2 M1 flight recorder: every run leaves a typed, replayable trajectory
|
|
238
246
|
// under <home>/runs/<run-id>/. Best-effort by contract - storage failures
|
|
239
247
|
// are swallowed inside the recorder and can never fail the task itself.
|
|
@@ -280,6 +288,7 @@ export async function runAgentTask(opts) {
|
|
|
280
288
|
model: cfg.provider.model,
|
|
281
289
|
locale: cfg.locale,
|
|
282
290
|
agentsMd: agentsMd ?? undefined,
|
|
291
|
+
...(goal ? { goal } : {}),
|
|
283
292
|
});
|
|
284
293
|
// system prompt token count: the prompt has been quietly growing (Codex 9
|
|
285
294
|
// instructions + AGENTS.md + memory + skills + insights) while no cost gate
|
|
@@ -339,9 +348,22 @@ export async function runAgentTask(opts) {
|
|
|
339
348
|
maxTurns: cfg.maxTurns,
|
|
340
349
|
maxContextChars: cfg.maxContextChars,
|
|
341
350
|
summarizeContext,
|
|
351
|
+
// runtime steering: drain the caller's injection channel after each tool
|
|
352
|
+
// batch; the injected text lands as a user message in the live transcript
|
|
353
|
+
// (web Ctrl+Enter / TUI Enter-while-running)
|
|
354
|
+
injectQueue: injectChannel ? () => injectChannel.queue.length ? injectChannel.queue.shift() : null : undefined,
|
|
355
|
+
// poll-shaped steering: drained at every turn boundary (codex semantics)
|
|
356
|
+
injections: injectPoll,
|
|
342
357
|
approval: spawnBase.current.approval,
|
|
343
358
|
events: {
|
|
344
359
|
onDelta: (kind, chunk) => events.onDelta?.(kind, chunk),
|
|
360
|
+
onInjected: (message) => {
|
|
361
|
+
// keep the rollout complete: an injected user message is part of the
|
|
362
|
+
// session transcript, not just the working copy
|
|
363
|
+
void session.user(message);
|
|
364
|
+
traj.emit('user.injected', 'user', { preview: brief(message, 160) });
|
|
365
|
+
events.onInjected?.(message);
|
|
366
|
+
},
|
|
345
367
|
onToolCall: (name, args) => {
|
|
346
368
|
toolsUsed.push(name);
|
|
347
369
|
traj.emit('tool.requested', 'tool', { name, args: brief(args, 120) });
|
|
@@ -375,6 +397,10 @@ export async function runAgentTask(opts) {
|
|
|
375
397
|
traj.finish({ success: false, reason: 'error', error: brief(String(err), 200) }, { toolUses: toolsUsed.length });
|
|
376
398
|
throw err;
|
|
377
399
|
});
|
|
400
|
+
// the task ended (or died): late pushes must queue for the NEXT task instead
|
|
401
|
+
// of silently vanishing into a channel nothing drains
|
|
402
|
+
if (injectChannel)
|
|
403
|
+
injectChannel.active = false;
|
|
378
404
|
traj.finish({ success: result.reason === 'final', reason: result.reason }, { turns: result.turns, toolUses: result.toolUses, promptTokens: result.usage.promptTokens, completionTokens: result.usage.completionTokens });
|
|
379
405
|
// routing.outcome backfill: did the ACTUAL route succeed? (shadow stats)
|
|
380
406
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hmharness/agent",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.10",
|
|
4
4
|
"description": "hmharness agent execution layer: base tools, system prompt, sub-agent spawn, and the shared task runner that frontends (cli, web) drive.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@hmharness/domain-harmony": "0.13.5",
|
|
19
19
|
"@hmharness/domain-ops": "0.8.0",
|
|
20
|
-
"@hmharness/evolution": "0.14.
|
|
21
|
-
"@hmharness/kernel": "0.9.
|
|
20
|
+
"@hmharness/evolution": "0.14.9",
|
|
21
|
+
"@hmharness/kernel": "0.9.1",
|
|
22
22
|
"@hmharness/observability": "0.7.0",
|
|
23
23
|
"@hmharness/sandbox": "0.8.0"
|
|
24
24
|
},
|