@rubytech/taskmaster 1.0.11 → 1.0.12

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.
Files changed (32) hide show
  1. package/dist/agents/pi-embedded-helpers/errors.js +6 -7
  2. package/dist/agents/pi-embedded-runner/compact.js +0 -1
  3. package/dist/agents/pi-embedded-runner/run/attempt.js +0 -1
  4. package/dist/agents/pi-embedded-runner/run/payloads.js +4 -4
  5. package/dist/agents/pi-embedded-runner/run.js +4 -4
  6. package/dist/agents/pi-embedded-runner/system-prompt.js +0 -1
  7. package/dist/agents/system-prompt.js +5 -17
  8. package/dist/agents/taskmaster-tools.js +4 -0
  9. package/dist/agents/tool-policy.js +1 -0
  10. package/dist/agents/tools/memory-reindex-tool.js +67 -0
  11. package/dist/auto-reply/reply/agent-runner-execution.js +17 -40
  12. package/dist/auto-reply/reply/agent-runner.js +14 -24
  13. package/dist/auto-reply/reply/get-reply-run.js +2 -1
  14. package/dist/auto-reply/reply/session.js +1 -1
  15. package/dist/build-info.json +3 -3
  16. package/dist/control-ui/assets/{index-DKEqjln4.js → index-D8ayJUWC.js} +176 -152
  17. package/dist/control-ui/assets/index-D8ayJUWC.js.map +1 -0
  18. package/dist/control-ui/assets/{index-QrR3OWrg.css → index-dMMqL7A5.css} +1 -1
  19. package/dist/control-ui/index.html +2 -2
  20. package/dist/gateway/protocol/schema/sessions-transcript.js +1 -0
  21. package/dist/gateway/server-methods/memory.js +62 -0
  22. package/dist/gateway/server-methods/sessions-transcript.js +10 -9
  23. package/dist/gateway/server-methods.js +5 -1
  24. package/dist/memory/manager.js +17 -1
  25. package/dist/memory/memory-schema.js +23 -1
  26. package/dist/web/auto-reply/monitor.js +1 -1
  27. package/package.json +1 -1
  28. package/taskmaster-docs/USER-GUIDE.md +3 -1
  29. package/templates/customer/agents/admin/AGENTS.md +6 -5
  30. package/templates/taskmaster/agents/admin/AGENTS.md +7 -5
  31. package/templates/tradesupport/agents/admin/AGENTS.md +6 -5
  32. package/dist/control-ui/assets/index-DKEqjln4.js.map +0 -1
@@ -39,7 +39,7 @@ function extractTextFromContentBlocks(blocks) {
39
39
  }
40
40
  return parts.join("\n");
41
41
  }
42
- function expandLineToEntries(line, sessionId, sessionKey, agentId, fileMtimeMs) {
42
+ function expandLineToEntries(line, sessionId, sessionKey, agentId, fileMtimeMs, maxChars = CONTENT_MAX_CHARS) {
43
43
  const entries = [];
44
44
  const ts = resolveTimestamp(line, fileMtimeMs);
45
45
  const model = line.message?.model;
@@ -57,7 +57,7 @@ function expandLineToEntries(line, sessionId, sessionKey, agentId, fileMtimeMs)
57
57
  agentId,
58
58
  timestamp: ts,
59
59
  type: "error",
60
- content: truncate(content, CONTENT_MAX_CHARS),
60
+ content: truncate(content, maxChars),
61
61
  ...(model ? { model } : {}),
62
62
  });
63
63
  return entries;
@@ -74,7 +74,7 @@ function expandLineToEntries(line, sessionId, sessionKey, agentId, fileMtimeMs)
74
74
  agentId,
75
75
  timestamp: ts,
76
76
  type: "tool",
77
- content: truncate(content, CONTENT_MAX_CHARS),
77
+ content: truncate(content, maxChars),
78
78
  ...(line.toolName ? { toolName: line.toolName } : {}),
79
79
  ...(model ? { model } : {}),
80
80
  });
@@ -93,7 +93,7 @@ function expandLineToEntries(line, sessionId, sessionKey, agentId, fileMtimeMs)
93
93
  agentId,
94
94
  timestamp: ts,
95
95
  type: entryType,
96
- content: truncate(msg.content, CONTENT_MAX_CHARS),
96
+ content: truncate(msg.content, maxChars),
97
97
  ...(model ? { model } : {}),
98
98
  });
99
99
  return entries;
@@ -113,7 +113,7 @@ function expandLineToEntries(line, sessionId, sessionKey, agentId, fileMtimeMs)
113
113
  agentId,
114
114
  timestamp: ts,
115
115
  type: "thinking",
116
- content: truncate(text, CONTENT_MAX_CHARS),
116
+ content: truncate(text, maxChars),
117
117
  ...(model ? { model } : {}),
118
118
  });
119
119
  }
@@ -127,7 +127,7 @@ function expandLineToEntries(line, sessionId, sessionKey, agentId, fileMtimeMs)
127
127
  agentId,
128
128
  timestamp: ts,
129
129
  type: "tool",
130
- content: truncate(content, CONTENT_MAX_CHARS),
130
+ content: truncate(content, maxChars),
131
131
  ...(toolName ? { toolName } : {}),
132
132
  ...(model ? { model } : {}),
133
133
  });
@@ -143,7 +143,7 @@ function expandLineToEntries(line, sessionId, sessionKey, agentId, fileMtimeMs)
143
143
  agentId,
144
144
  timestamp: ts,
145
145
  type: entryType,
146
- content: truncate(text, CONTENT_MAX_CHARS),
146
+ content: truncate(text, maxChars),
147
147
  ...(model ? { model } : {}),
148
148
  });
149
149
  }
@@ -161,7 +161,7 @@ function expandLineToEntries(line, sessionId, sessionKey, agentId, fileMtimeMs)
161
161
  agentId,
162
162
  timestamp: ts,
163
163
  type: entryType,
164
- content: truncate(text, CONTENT_MAX_CHARS),
164
+ content: truncate(text, maxChars),
165
165
  ...(model ? { model } : {}),
166
166
  });
167
167
  }
@@ -217,6 +217,7 @@ export const sessionsTranscriptHandlers = {
217
217
  const limit = p.limit ?? DEFAULT_LIMIT;
218
218
  const maxBytesPerFile = p.maxBytesPerFile ?? DEFAULT_MAX_BYTES_PER_FILE;
219
219
  const inputCursors = p.cursors ?? {};
220
+ const contentMaxChars = p.full ? Infinity : CONTENT_MAX_CHARS;
220
221
  const agentFilter = p.agents && p.agents.length > 0 ? new Set(p.agents) : null;
221
222
  try {
222
223
  const cfg = loadConfig();
@@ -285,7 +286,7 @@ export const sessionsTranscriptHandlers = {
285
286
  catch {
286
287
  continue;
287
288
  }
288
- const expanded = expandLineToEntries(parsed, sessionId, sessionKey, agentId, result.fileMtimeMs);
289
+ const expanded = expandLineToEntries(parsed, sessionId, sessionKey, agentId, result.fileMtimeMs, contentMaxChars);
289
290
  allEntries.push(...expanded);
290
291
  }
291
292
  }
@@ -16,6 +16,7 @@ import { filesHandlers } from "./server-methods/files.js";
16
16
  import { healthHandlers } from "./server-methods/health.js";
17
17
  import { licenseHandlers } from "./server-methods/license.js";
18
18
  import { logsHandlers } from "./server-methods/logs.js";
19
+ import { memoryHandlers } from "./server-methods/memory.js";
19
20
  import { recordsHandlers } from "./server-methods/records.js";
20
21
  import { modelsHandlers } from "./server-methods/models.js";
21
22
  import { nodeHandlers } from "./server-methods/nodes.js";
@@ -88,6 +89,7 @@ const READ_METHODS = new Set([
88
89
  "records.search",
89
90
  "workspaces.list",
90
91
  "workspaces.scan",
92
+ "memory.status",
91
93
  ]);
92
94
  const WRITE_METHODS = new Set([
93
95
  "send",
@@ -172,7 +174,8 @@ function authorizeGatewayMethod(method, client) {
172
174
  method === "records.setField" ||
173
175
  method === "records.deleteField" ||
174
176
  method === "workspaces.create" ||
175
- method === "workspaces.remove") {
177
+ method === "workspaces.remove" ||
178
+ method === "memory.reindex") {
176
179
  return errorShape(ErrorCodes.INVALID_REQUEST, "missing scope: operator.admin");
177
180
  }
178
181
  return errorShape(ErrorCodes.INVALID_REQUEST, "missing scope: operator.admin");
@@ -209,6 +212,7 @@ export const coreGatewayHandlers = {
209
212
  ...filesHandlers,
210
213
  ...browserScreencastHandlers,
211
214
  ...licenseHandlers,
215
+ ...memoryHandlers,
212
216
  ...recordsHandlers,
213
217
  ...workspacesHandlers,
214
218
  };
@@ -1297,6 +1297,8 @@ export class MemoryIndexManager {
1297
1297
  }
1298
1298
  const vectorReady = await this.ensureVectorReady();
1299
1299
  const meta = this.readMeta();
1300
+ const sourcesChanged = meta?.sources != null &&
1301
+ JSON.stringify([...meta.sources].sort()) !== JSON.stringify([...this.sources].sort());
1300
1302
  const needsFullReindex = params?.force ||
1301
1303
  !meta ||
1302
1304
  meta.model !== this.provider.model ||
@@ -1304,7 +1306,16 @@ export class MemoryIndexManager {
1304
1306
  meta.providerKey !== this.providerKey ||
1305
1307
  meta.chunkTokens !== this.settings.chunking.tokens ||
1306
1308
  meta.chunkOverlap !== this.settings.chunking.overlap ||
1307
- (vectorReady && !meta?.vectorDims);
1309
+ (vectorReady && !meta?.vectorDims) ||
1310
+ sourcesChanged;
1311
+ // Detect missing memory index: source enabled but no files indexed (legacy DB or failed initial sync)
1312
+ if (!needsFullReindex && this.sources.has("memory") && !this.dirty) {
1313
+ const memoryFileCount = this.db.prepare("SELECT COUNT(*) as c FROM files WHERE source = ?").get("memory").c;
1314
+ if (memoryFileCount === 0) {
1315
+ log.info("memory source enabled but no memory files indexed — marking dirty");
1316
+ this.dirty = true;
1317
+ }
1318
+ }
1308
1319
  try {
1309
1320
  if (needsFullReindex) {
1310
1321
  await this.runSafeReindex({
@@ -1331,6 +1342,10 @@ export class MemoryIndexManager {
1331
1342
  else {
1332
1343
  this.sessionsDirty = false;
1333
1344
  }
1345
+ // Persist sources to meta if missing (backfill for legacy databases)
1346
+ if (meta && !meta.sources) {
1347
+ this.writeMeta({ ...meta, sources: Array.from(this.sources) });
1348
+ }
1334
1349
  }
1335
1350
  catch (err) {
1336
1351
  const reason = err instanceof Error ? err.message : String(err);
@@ -1455,6 +1470,7 @@ export class MemoryIndexManager {
1455
1470
  providerKey: this.providerKey,
1456
1471
  chunkTokens: this.settings.chunking.tokens,
1457
1472
  chunkOverlap: this.settings.chunking.overlap,
1473
+ sources: Array.from(this.sources),
1458
1474
  };
1459
1475
  if (this.vector.available && this.vector.dims) {
1460
1476
  nextMeta.vectorDims = this.vector.dims;
@@ -60,6 +60,10 @@ export function ensureMemoryIndexSchema(params) {
60
60
  const message = err instanceof Error ? err.message : String(err);
61
61
  ftsAvailable = false;
62
62
  ftsError = message;
63
+ // Drop orphaned FTS shadow tables when the fts5 module is unavailable.
64
+ // Leaving them in the DB can cause "no such module: fts5" errors on
65
+ // unrelated operations if SQLite touches the virtual table machinery.
66
+ dropOrphanedFtsTables(params.db, params.ftsTable);
63
67
  }
64
68
  }
65
69
  ensureColumn(params.db, "files", "source", "TEXT NOT NULL DEFAULT 'memory'");
@@ -68,9 +72,27 @@ export function ensureMemoryIndexSchema(params) {
68
72
  params.db.exec(`CREATE INDEX IF NOT EXISTS idx_chunks_source ON chunks(source);`);
69
73
  return { ftsAvailable, ...(ftsError ? { ftsError } : {}) };
70
74
  }
75
+ /**
76
+ * When fts5 module is unavailable but the virtual table and its shadow tables
77
+ * exist from a previous run, drop them to prevent "no such module" errors.
78
+ * Shadow tables follow the pattern: {ftsTable}_config, _content, _data, _docsize, _idx.
79
+ */
80
+ function dropOrphanedFtsTables(db, ftsTable) {
81
+ const shadowSuffixes = ["_config", "_content", "_data", "_docsize", "_idx"];
82
+ for (const suffix of shadowSuffixes) {
83
+ try {
84
+ db.prepare(`DROP TABLE IF EXISTS ${ftsTable}${suffix}`).run();
85
+ }
86
+ catch { }
87
+ }
88
+ try {
89
+ db.prepare(`DROP TABLE IF EXISTS ${ftsTable}`).run();
90
+ }
91
+ catch { }
92
+ }
71
93
  function ensureColumn(db, table, column, definition) {
72
94
  const rows = db.prepare(`PRAGMA table_info(${table})`).all();
73
95
  if (rows.some((row) => row.name === column))
74
96
  return;
75
- db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
97
+ db.prepare(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`).run();
76
98
  }
@@ -230,7 +230,7 @@ export async function monitorWebChannel(verbose, listenerFactory = monitorWebInb
230
230
  heartbeatLogger.warn(logData, "⚠️ web gateway heartbeat - no messages in 30+ minutes");
231
231
  }
232
232
  else {
233
- heartbeatLogger.info(logData, "web gateway heartbeat");
233
+ heartbeatLogger.debug(logData, "web gateway heartbeat");
234
234
  }
235
235
  }, heartbeatSeconds * 1000);
236
236
  watchdogTimer = setInterval(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/taskmaster",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "description": "AI-powered business assistant for small businesses",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -265,6 +265,8 @@ Your assistant can send messages, images, and reactions on your behalf — and r
265
265
 
266
266
  Your assistant can search, read, and update your business knowledge. It has full file access within your account workspace — it can create, read, update, and organise files. Files uploaded or dragged onto the Files page are automatically indexed for search.
267
267
 
268
+ The Files page shows a **status indicator** next to the Re-index button — a green light means the index is up to date, red means files have changed since the last index. The indicator also shows the number of indexed files and chunks. If the light is red or search results seem stale, click **Re-index** to rebuild the search index.
269
+
268
270
  | Capability | Description |
269
271
  |------------|-------------|
270
272
  | Search knowledge | Find information across all your business files and notes |
@@ -479,7 +481,7 @@ The **Files** page shows your assistant's knowledge — everything it knows abou
479
481
 
480
482
  All files are markdown (`.md`) — plain text with simple formatting. You can upload new files by dragging them onto the Files page.
481
483
 
482
- When you add or change a file, your assistant picks it up automatically — no restart needed.
484
+ When you add or change a file, your assistant picks it up automatically — no restart needed. The status light on the Files page turns red when files have changed since the last index, so you can see at a glance whether a re-index is needed.
483
485
 
484
486
  ---
485
487
 
@@ -42,14 +42,15 @@ Use the owner's name naturally in conversation (you'll find it in USER.md).
42
42
 
43
43
  ---
44
44
 
45
- ## Activity = Sessions First
45
+ ## Activity & Recent History
46
46
 
47
- When asked about "activity", "what's happened", or similar:
47
+ When asked about "activity", "what happened", or similar:
48
48
 
49
- 1. **Sessions are the source of truth** that's where conversations live
50
- 2. **Memory is context** — notes, decisions, and summaries you stored about what happened
49
+ 1. **Your conversation log first** — `memory/admin/conversations/YYYY-MM.md` is your own record of what you said and did. This is the most complete source, especially when sessions have been reset.
50
+ 2. **Sessions (current + previous)** — use `sessions_list` to see active sessions. Each entry may have a `previousSessions` array listing archived sessions. Pass a previous `sessionId` to `sessions_history` to read its transcript.
51
+ 3. **Memory** — notes, decisions, and summaries you stored about what happened.
51
52
 
52
- Check sessions first, then enrich with memory.
53
+ Do not rely solely on the current session. Sessions reset on `/new`, auto-recovery, and expiry always check your conversation log and `previousSessions` for the full picture.
53
54
 
54
55
  ---
55
56
 
@@ -11,14 +11,15 @@ Before doing anything else:
11
11
 
12
12
  ---
13
13
 
14
- ## Activity = Sessions First
14
+ ## Activity & Recent History
15
15
 
16
- When asked about "activity", "what's happened", or similar:
16
+ When asked about "activity", "what happened", or similar:
17
17
 
18
- 1. **Sessions are the source of truth** that's where conversations live
19
- 2. **Memory is context** — notes, decisions, and summaries you stored about what happened
18
+ 1. **Your conversation log first** — `memory/admin/conversations/YYYY-MM.md` is your own record of what you said and did. This is the most complete source, especially when sessions have been reset.
19
+ 2. **Sessions (current + previous)** — use `sessions_list` to see active sessions. Each entry may have a `previousSessions` array listing archived sessions. Pass a previous `sessionId` to `sessions_history` to read its transcript.
20
+ 3. **Memory** — notes, decisions, and summaries you stored about what happened.
20
21
 
21
- Check sessions first, then enrich with memory.
22
+ Do not rely solely on the current session. Sessions reset on `/new`, auto-recovery, and expiry always check your conversation log and `previousSessions` for the full picture.
22
23
 
23
24
  ## Multi-Channel
24
25
 
@@ -48,6 +49,7 @@ You have access to everything. Use it.
48
49
  Full access to all memory:
49
50
  - **Store:** Use `memory_write` freely
50
51
  - **Retrieve:** Use `memory_search` for any context
52
+ - **Reindex:** Use `memory_reindex` with action `reindex` to rebuild the search index, or `status` to check index health
51
53
  - **Cross-agent:** You can see the public agent's conversations
52
54
 
53
55
  ### Finding Other Conversations
@@ -42,14 +42,15 @@ Use the owner's name naturally in conversation (you'll find it in USER.md).
42
42
 
43
43
  ---
44
44
 
45
- ## Activity = Sessions First
45
+ ## Activity & Recent History
46
46
 
47
- When asked about "activity", "what's happened", or similar:
47
+ When asked about "activity", "what happened", or similar:
48
48
 
49
- 1. **Sessions are the source of truth** that's where conversations live
50
- 2. **Memory is context** — notes, decisions, and summaries you stored about what happened
49
+ 1. **Your conversation log first** — `memory/admin/conversations/YYYY-MM.md` is your own record of what you said and did. This is the most complete source, especially when sessions have been reset.
50
+ 2. **Sessions (current + previous)** — use `sessions_list` to see active sessions. Each entry may have a `previousSessions` array listing archived sessions. Pass a previous `sessionId` to `sessions_history` to read its transcript.
51
+ 3. **Memory** — notes, decisions, and summaries you stored about what happened.
51
52
 
52
- Check sessions first, then enrich with memory.
53
+ Do not rely solely on the current session. Sessions reset on `/new`, auto-recovery, and expiry always check your conversation log and `previousSessions` for the full picture.
53
54
 
54
55
  ---
55
56