@yeaft/webchat-agent 0.1.781 → 0.1.786

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.781",
3
+ "version": "0.1.786",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -8,7 +8,8 @@
8
8
  * enumerateGroups() via opts.listGroups()
9
9
  * ↓
10
10
  * for each group with newCount ≥ MIN_NEW_PER_GROUP (auto)
11
- * or > 0 (manual):
11
+ * or > 0 (manual)
12
+ * or prior messages in a scoped manual group rerun:
12
13
  * loadDiff() via opts.loadGroupDiff(groupId, sinceId)
13
14
  * applyOverlap() via opts.loadOverlapPreamble(...)
14
15
  * segment() segmentDiff(...)
@@ -55,7 +56,7 @@ import { tsForBackup, pruneOldSnapshots } from './snapshot.js';
55
56
  * @typedef {Object} RunDreamOpts
56
57
  * @property {string} root — memory root, e.g. ~/.yeaft/memory
57
58
  * @property {boolean} [manual=false] — manual trigger overrides newCount<20 skip
58
- * @property {string[]} [scopeFilter] — optional: only dream these targets (still respects newCount per group; '*' allowed)
59
+ * @property {string[]} [scopeFilter] — optional: only dream these targets; scoped manual group triggers rerun the current group when there are prior messages but no new cursor delta ('*' allowed)
59
60
  * @property {(req: {pass:string, prompt:string, system:string}) => Promise<string>} llm
60
61
  * @property {() => Promise<Array<string>>} listGroups — return all group ids (incl. '_no-group')
61
62
  * @property {(groupId: string) => Promise<number>} countMessages — total message count for a group
@@ -112,7 +113,13 @@ export async function runDream(opts) {
112
113
  const beforeCount = await safeCall(() => opts.countMessages(groupId), 0);
113
114
  const newCount = Math.max(0, beforeCount - (state.messageCount || 0));
114
115
 
115
- if (newCount === 0) {
116
+ const rerunScopedManual = !!opts.manual
117
+ && groupFilter
118
+ && groupFilter.has(groupId)
119
+ && newCount === 0
120
+ && beforeCount > 0;
121
+
122
+ if (newCount === 0 && !rerunScopedManual) {
116
123
  groupsReport.push({ groupId, new: 0, status: 'skipped', reason: 'no-new-messages' });
117
124
  continue;
118
125
  }
@@ -122,12 +129,13 @@ export async function runDream(opts) {
122
129
  }
123
130
 
124
131
  onProgress({ phase: 'load-diff', groupId });
125
- const diffNew = await safeCall(() => opts.loadGroupDiff(groupId, state.lastDreamMessageId), []);
132
+ const diffCursor = rerunScopedManual ? null : state.lastDreamMessageId;
133
+ const diffNew = await safeCall(() => opts.loadGroupDiff(groupId, diffCursor), []);
126
134
  if (!diffNew || diffNew.length === 0) {
127
135
  groupsReport.push({ groupId, new: newCount, status: 'skipped', reason: 'empty-diff' });
128
136
  continue;
129
137
  }
130
- const overlapMessages = state.lastDreamMessageId
138
+ const overlapMessages = state.lastDreamMessageId && !rerunScopedManual
131
139
  ? await safeCall(
132
140
  () => opts.loadOverlapPreamble
133
141
  ? opts.loadOverlapPreamble(groupId, state.lastDreamMessageId, limits.DREAM_OVERLAP)
@@ -171,7 +179,7 @@ export async function runDream(opts) {
171
179
 
172
180
  const tailId = lastMessageId(diffNew);
173
181
  processedGroups.push({ groupId, tailId, beforeCount, newCount, segments: segments.length, actions: actions.length });
174
- groupsReport.push({ groupId, new: newCount, segments: segments.length, actions: actions.length, status: 'triaged' });
182
+ groupsReport.push({ groupId, new: newCount, segments: segments.length, actions: actions.length, status: 'triaged', rerun: rerunScopedManual || undefined });
175
183
  }
176
184
 
177
185
  // 3. merge
@@ -1,8 +1,9 @@
1
1
  # Planning Mode
2
2
 
3
- You have just entered **planning mode** for the topic below. Do NOT start
4
- executing yet. Your job in this turn is to **think through the work and produce
5
- a concrete plan**, then hand it off to `TodoWrite` so the steps are tracked.
3
+ You have just entered **planning mode** for the topic below. Your job is to
4
+ **think through the work, land a concrete plan via `TodoWrite`, and then
5
+ continue executing the first step in the same turn**. Don't stop after writing
6
+ the plan — keep going.
6
7
 
7
8
  ## How to think
8
9
 
@@ -20,7 +21,7 @@ a concrete plan**, then hand it off to `TodoWrite` so the steps are tracked.
20
21
 
21
22
  ## Output shape
22
23
 
23
- Reply in two parts:
24
+ Reply in three parts, all in the **same turn**:
24
25
 
25
26
  **Part 1 — Plan (prose, short).** 5–10 lines covering the problem, the chosen
26
27
  approach, and the key risks. No filler. Skip if the topic is trivial.
@@ -32,9 +33,15 @@ array. Status rule:
32
33
  - Use the **imperative** form for `content` ("Write failing test"), and the
33
34
  **present-continuous** form for `activeForm` ("Writing failing test").
34
35
 
35
- **Do not execute the steps in this turn.** This turn ends after the `TodoWrite`
36
- call returns. On the next turn, the user (or you) will pick up the
37
- `in_progress` item and start work.
36
+ **Part 3 Start executing the first step.** Immediately after `TodoWrite`
37
+ returns, begin work on the `in_progress` item by calling whatever tool(s) that
38
+ step needs (bash, file-edit, grep, etc.). Don't end the turn just because the
39
+ plan is written — the plan is the runway, not the destination.
40
+
41
+ **Exception — stop after the plan only if** the very first step is "ask the
42
+ user / wait for input" (e.g. an unresolved unknown that genuinely blocks
43
+ every other step). In that case, call the `ask_user` tool with the
44
+ question and end the turn. Otherwise keep moving.
38
45
 
39
46
  ## Tone
40
47
 
@@ -3,11 +3,14 @@
3
3
  *
4
4
  * Lightweight planning entry point inspired by Claude Code's plan mode.
5
5
  * Unlike Claude Code, we do NOT swap tools or change conversation state —
6
- * `StartPlan` is a regular tool. Its only job is to push a planning
7
- * instruction back into the model's tool-result stream so the very next
8
- * turn produces a structured plan plus a `TodoWrite` call.
6
+ * `StartPlan` is a regular tool. Its job is to push a planning instruction
7
+ * back into the model's tool-result stream so the same turn produces a
8
+ * short prose plan, a `TodoWrite` call to land the steps, and then keeps
9
+ * going by starting the first step. The plan is the runway, not the
10
+ * destination — the loop continues until the work is done (or until the
11
+ * model legitimately needs the user, e.g. an unresolved unknown).
9
12
  *
10
- * Design (locked 2026-05-13):
13
+ * Design:
11
14
  * - Anyone can call it; the tool description tells the LLM when to.
12
15
  * - The instruction text comes from one of two places, in order:
13
16
  * 1. The active VP's `planInstruction` frontmatter override
@@ -20,13 +23,16 @@
20
23
  * are echoed back in the tool result so the planning turn can read
21
24
  * them without re-asking the user.
22
25
  * - Output is plain text (the instruction + the echo). No side effects,
23
- * no persistence — the LLM's next turn does the actual planning and
24
- * calls TodoWrite to land structured steps.
26
+ * no persistence — the same turn that called StartPlan produces the
27
+ * plan, lands the steps via TodoWrite, and starts executing the first
28
+ * step (unless that first step is "ask the user", which is the one
29
+ * legitimate reason to stop here).
25
30
  *
26
- * The expected integration is TodoWrite: after the planning turn the LLM
27
- * issues a `TodoWrite` call enumerating the 1..N steps. The frontend
28
- * already renders TodoWrite as a checkbox-style list, so the user sees
29
- * the plan materialize without any new UI.
31
+ * The expected integration is TodoWrite: during the planning turn the LLM
32
+ * issues a `TodoWrite` call enumerating the 1..N steps, then keeps going
33
+ * and works the first step. The frontend already renders TodoWrite as a
34
+ * checkbox-style list, so the user sees the plan materialize without any
35
+ * new UI.
30
36
  */
31
37
 
32
38
  import { defineTool } from './types.js';
@@ -36,9 +42,10 @@ export default defineTool({
36
42
  name: 'StartPlan',
37
43
  description: `Enter planning mode for a non-trivial task. Use BEFORE you start working when the request needs multiple steps, has unclear scope, or the user said "make a plan" / "think through this first".
38
44
 
39
- This tool does NOT execute the work. It returns a planning instruction; on the next turn you should:
45
+ This tool returns a planning instruction. Use it to land a structured plan, then keep working in the same turn. The expected flow is:
40
46
  1. Produce a short prose plan (problem, approach, risks).
41
47
  2. Call \`TodoWrite\` with the ordered steps. Mark the first concrete step "in_progress", the rest "pending".
48
+ 3. Start executing that first step — call whatever tools the work needs. Do not end the turn just because the plan is written; the plan is the runway.
42
49
 
43
50
  WHEN TO USE:
44
51
  - Multi-step implementation (3+ steps), refactor, or open-ended investigation.
@@ -49,6 +56,8 @@ WHEN NOT TO USE:
49
56
  - Single trivial change, single command run, lookup-style question.
50
57
  - Mid-execution — once you're past the first step, use TodoWrite directly.
51
58
 
59
+ EXCEPTION — stop after the plan only if the first step is genuinely "ask the user" (an unresolved unknown that blocks every other step). Otherwise keep moving.
60
+
52
61
  The tool takes the topic plus optional guiding fields (stuckAt, userProblem, expectedScale, additionalContext) that help you think; they're echoed back verbatim, so don't repeat the full user request in \`topic\`.`,
53
62
  parameters: {
54
63
  type: 'object',
@@ -126,7 +135,7 @@ The tool takes the topic plus optional guiding fields (stuckAt, userProblem, exp
126
135
  lines.push('</guiding-context>');
127
136
  }
128
137
  lines.push('');
129
- lines.push('Next: produce the plan as described above, then call `TodoWrite` to land the ordered steps. Do NOT start executing the steps in this turn.');
138
+ lines.push('Next: produce the plan, call `TodoWrite` to land the ordered steps, then keep going — start executing the first step in this same turn. Only stop after the plan if the first step is "ask the user".');
130
139
 
131
140
  return lines.join('\n');
132
141
  },