@yeaft/webchat-agent 0.1.652 → 0.1.654

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.652",
3
+ "version": "0.1.654",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -61,15 +61,9 @@ import {
61
61
  featureSummaryPost,
62
62
  } from './feature-tools.js';
63
63
 
64
- // --- P1 Thread tools (task-299 Phase 1) ---
65
- import {
66
- spawnThread,
67
- switchThread,
68
- listThreads,
69
- attachThreadToFeature,
70
- readThreadSummary,
71
- readThreadRecent,
72
- } from './thread-tools.js';
64
+ // H2.f.4: thread tools (spawnThread/switchThread/listThreads/...) deleted.
65
+ // The agent now runs in a single conversation; multi-thread orchestration
66
+ // has been retired across the H2.f series.
73
67
 
74
68
  // --- P2 Auxiliary tools ---
75
69
  // task-333b L1 delete: ToolSearch and WriteStdin removed — the function-call
@@ -137,14 +131,6 @@ export const allTools = [
137
131
  updatePlan,
138
132
  featureSummaryPost,
139
133
 
140
- // P1 Thread (task-299 Phase 1)
141
- spawnThread,
142
- switchThread,
143
- listThreads,
144
- attachThreadToFeature,
145
- readThreadSummary,
146
- readThreadRecent,
147
-
148
134
  // P2 Auxiliary
149
135
  jsRepl,
150
136
  jsReplReset,
@@ -1,289 +0,0 @@
1
- /**
2
- * thread-tools.js — Thread-spawning tools for Unify Engine (Phase 1).
3
- *
4
- * Phase 1 scope (task-299 rework):
5
- * - SpawnThread — create a new thread
6
- * - SwitchThread — set the engine's currentThreadId marker
7
- * - ListThreads — list threads + current marker + cached stats
8
- * - AttachThreadToFeature — bind a thread to an existing feature
9
- * - ReadThreadSummary — cross-reference: summary of a thread (id/name/
10
- * status/messageCount/lastMessageAt/feature)
11
- * - ReadThreadRecent — cross-reference: last N messages of a thread
12
- *
13
- * Phase 1 uses the in-memory ThreadStore (agent/unify/threads/store.js)
14
- * and the existing ConversationStore for messages. When task-298 merges,
15
- * ThreadStore becomes file-backed with the SAME API, so these tools
16
- * continue to work unchanged.
17
- */
18
-
19
- import { defineTool } from './types.js';
20
- import { getThreadStore, MAIN_THREAD_ID } from '../threads/store.js';
21
- import { getFeatureStore } from './feature-tools.js';
22
-
23
- // ─── SpawnThread ─────────────────────────────────────────
24
-
25
- export const spawnThread = defineTool({
26
- name: 'SpawnThread',
27
- description: `Create a new thread (conversation track) for parallel work.
28
-
29
- A thread is a named conversation track that groups related messages and
30
- tool calls under a single goal. Use when the work needs a fresh focus
31
- track separate from the current conversation.
32
-
33
- Returns the new threadId (format: "thr-xxxxxxxx"). Does NOT switch the
34
- engine to the new thread — call SwitchThread to activate it.`,
35
- parameters: {
36
- type: 'object',
37
- properties: {
38
- name: { type: 'string', description: 'Short human-readable name' },
39
- goal: { type: 'string', description: 'Optional one-sentence goal' },
40
- parent_thread_id: {
41
- type: 'string',
42
- description: 'Optional parent threadId for hierarchy',
43
- },
44
- },
45
- required: ['name'],
46
- },
47
- isConcurrencySafe: () => false,
48
- isReadOnly: () => false,
49
- async execute(input) {
50
- const { name, goal, parent_thread_id } = input || {};
51
- if (!name) return JSON.stringify({ error: 'name is required' });
52
- try {
53
- const store = getThreadStore();
54
- const t = store.create({ name, goal, parentThreadId: parent_thread_id || null });
55
- return JSON.stringify({
56
- success: true,
57
- thread: {
58
- id: t.id,
59
- name: t.name,
60
- goal: t.goal,
61
- parentThreadId: t.parentThreadId,
62
- status: t.status,
63
- },
64
- message: `Thread created: ${t.name} (${t.id})`,
65
- });
66
- } catch (err) {
67
- return JSON.stringify({ error: err.message || String(err) });
68
- }
69
- },
70
- });
71
-
72
- // ─── SwitchThread ────────────────────────────────────────
73
-
74
- export const switchThread = defineTool({
75
- name: 'SwitchThread',
76
- description: `Switch the engine's active thread marker.
77
-
78
- All messages and tool calls persisted after this call will carry the new
79
- threadId (Phase 1: marker only — the Engine reads it when persisting and
80
- the web-bridge forwards it to the UI). Use 'main' to return to the root
81
- thread.`,
82
- parameters: {
83
- type: 'object',
84
- properties: {
85
- thread_id: { type: 'string', description: 'Thread id to switch to' },
86
- },
87
- required: ['thread_id'],
88
- },
89
- isConcurrencySafe: () => false,
90
- isReadOnly: () => false,
91
- async execute(input) {
92
- const { thread_id } = input || {};
93
- if (!thread_id) return JSON.stringify({ error: 'thread_id is required' });
94
- try {
95
- const store = getThreadStore();
96
- store.switch(thread_id);
97
- return JSON.stringify({
98
- success: true,
99
- currentThreadId: store.currentId,
100
- message: `Switched to thread ${thread_id}`,
101
- });
102
- } catch (err) {
103
- return JSON.stringify({ error: err.message || String(err) });
104
- }
105
- },
106
- });
107
-
108
- // ─── ListThreads ─────────────────────────────────────────
109
-
110
- export const listThreads = defineTool({
111
- name: 'ListThreads',
112
- description: `List all threads with cached status / messageCount / lastMessageAt.
113
-
114
- Returns the data needed by the Phase 2 sidebar (task-300): each entry
115
- exposes id, name, goal, parentThreadId, status ('active'|'idle'|'archived'),
116
- messageCount, lastMessageAt, archived, attachedTaskId. Reads only cached
117
- fields — does not scan messages.`,
118
- parameters: { type: 'object', properties: {} },
119
- isConcurrencySafe: () => true,
120
- isReadOnly: () => true,
121
- async execute() {
122
- const store = getThreadStore();
123
- const threads = store.list().map(t => ({
124
- id: t.id,
125
- name: t.name,
126
- goal: t.goal,
127
- parentThreadId: t.parentThreadId,
128
- status: t.status,
129
- messageCount: t.messageCount,
130
- lastMessageAt: t.lastMessageAt,
131
- lastActivityAt: t.lastActivityAt ?? t.lastMessageAt,
132
- archived: t.archived,
133
- unread: t.unread || 0,
134
- preview: t.preview || '',
135
- attachedFeatureId: store.attachedFeature(t.id),
136
- }));
137
- return JSON.stringify(
138
- {
139
- currentThreadId: store.currentId,
140
- threads,
141
- totalCount: threads.length,
142
- },
143
- null,
144
- 2,
145
- );
146
- },
147
- });
148
-
149
- // ─── AttachThreadToFeature ──────────────────────────────────
150
-
151
- export const attachThreadToFeature = defineTool({
152
- name: 'AttachThreadToFeature',
153
- description: `Link an existing thread to an existing feature.
154
-
155
- Use to record which thread is responsible for which feature. The thread and
156
- the feature must both already exist. Overwrites any previous attachment for
157
- the same thread.`,
158
- parameters: {
159
- type: 'object',
160
- properties: {
161
- thread_id: { type: 'string' },
162
- feature_id: { type: 'string' },
163
- },
164
- required: ['thread_id', 'feature_id'],
165
- },
166
- isConcurrencySafe: () => false,
167
- isReadOnly: () => false,
168
- async execute(input) {
169
- const { thread_id, feature_id } = input || {};
170
- if (!thread_id) return JSON.stringify({ error: 'thread_id is required' });
171
- if (!feature_id) return JSON.stringify({ error: 'feature_id is required' });
172
-
173
- const featureStore = getFeatureStore();
174
- if (featureStore) {
175
- const feature = featureStore.get(feature_id);
176
- if (!feature) return JSON.stringify({ error: `Feature not found: ${feature_id}` });
177
- }
178
-
179
- try {
180
- const store = getThreadStore();
181
- store.attachFeature(thread_id, feature_id);
182
- return JSON.stringify({
183
- success: true,
184
- threadId: thread_id,
185
- featureId: feature_id,
186
- message: `Thread ${thread_id} attached to feature ${feature_id}`,
187
- });
188
- } catch (err) {
189
- return JSON.stringify({ error: err.message || String(err) });
190
- }
191
- },
192
- });
193
-
194
- // ─── SpawnTask removed (task-333b) — use FeatureCreate with parent_feature_id
195
-
196
- // ─── ReadThreadSummary (cross-reference, design §6 Q5) ──
197
-
198
- export const readThreadSummary = defineTool({
199
- name: 'ReadThreadSummary',
200
- description: `Return a one-shot summary of a thread: id, name, goal, status,
201
- messageCount, lastMessageAt, parentThreadId, attachedFeatureId.
202
-
203
- Use this to cross-reference work on another thread without switching.`,
204
- parameters: {
205
- type: 'object',
206
- properties: {
207
- thread_id: { type: 'string' },
208
- },
209
- required: ['thread_id'],
210
- },
211
- isConcurrencySafe: () => true,
212
- isReadOnly: () => true,
213
- async execute(input) {
214
- const { thread_id } = input || {};
215
- if (!thread_id) return JSON.stringify({ error: 'thread_id is required' });
216
- const store = getThreadStore();
217
- const t = store.get(thread_id);
218
- if (!t) return JSON.stringify({ error: `Thread not found: ${thread_id}` });
219
- return JSON.stringify(
220
- {
221
- id: t.id,
222
- name: t.name,
223
- goal: t.goal,
224
- status: t.status,
225
- archived: t.archived,
226
- messageCount: t.messageCount,
227
- lastMessageAt: t.lastMessageAt,
228
- parentThreadId: t.parentThreadId,
229
- attachedFeatureId: store.attachedFeature(t.id),
230
- createdAt: t.createdAt,
231
- updatedAt: t.updatedAt,
232
- },
233
- null,
234
- 2,
235
- );
236
- },
237
- });
238
-
239
- // ─── ReadThreadRecent (cross-reference, design §6 Q5) ───
240
-
241
- export const readThreadRecent = defineTool({
242
- name: 'ReadThreadRecent',
243
- description: `Return the last N messages on a specific thread.
244
-
245
- Requires an engine ConversationStore in context (ctx.conversationStore).
246
- Reads conversation history and filters by threadId. Default N=20, max 200.
247
- Use this to review another thread's recent activity without switching.`,
248
- parameters: {
249
- type: 'object',
250
- properties: {
251
- thread_id: { type: 'string' },
252
- limit: { type: 'number', description: 'Max messages to return (default 20, max 200)' },
253
- },
254
- required: ['thread_id'],
255
- },
256
- isConcurrencySafe: () => true,
257
- isReadOnly: () => true,
258
- async execute(input, ctx) {
259
- const { thread_id, limit } = input || {};
260
- if (!thread_id) return JSON.stringify({ error: 'thread_id is required' });
261
- const store = getThreadStore();
262
- if (!store.has(thread_id)) {
263
- return JSON.stringify({ error: `Thread not found: ${thread_id}` });
264
- }
265
- const conv = ctx?.conversationStore;
266
- if (!conv || typeof conv.loadRecent !== 'function') {
267
- return JSON.stringify({
268
- error: 'conversation store unavailable in tool context',
269
- });
270
- }
271
- const cap = Math.max(1, Math.min(Number(limit) || 20, 200));
272
- // Over-fetch then filter by thread, so N still applies post-filter.
273
- const raw = conv.loadRecent(cap * 4);
274
- const filtered = raw
275
- .filter(m => (m.threadId || MAIN_THREAD_ID) === thread_id)
276
- .slice(-cap)
277
- .map(m => ({
278
- role: m.role,
279
- content: typeof m.content === 'string' ? m.content : JSON.stringify(m.content),
280
- createdAt: m.createdAt || null,
281
- threadId: m.threadId || MAIN_THREAD_ID,
282
- }));
283
- return JSON.stringify(
284
- { threadId: thread_id, count: filtered.length, messages: filtered },
285
- null,
286
- 2,
287
- );
288
- },
289
- });