dot-agents 0.6.0 → 0.7.5

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 (80) hide show
  1. package/dist/cli/commands/channel.d.ts.map +1 -1
  2. package/dist/cli/commands/channel.js +70 -2
  3. package/dist/cli/commands/channel.js.map +1 -1
  4. package/dist/daemon/api/channels.d.ts +6 -0
  5. package/dist/daemon/api/channels.d.ts.map +1 -0
  6. package/dist/daemon/api/channels.js +143 -0
  7. package/dist/daemon/api/channels.js.map +1 -0
  8. package/dist/daemon/api/server.d.ts.map +1 -1
  9. package/dist/daemon/api/server.js +57 -0
  10. package/dist/daemon/api/server.js.map +1 -1
  11. package/dist/daemon/daemon.d.ts +28 -0
  12. package/dist/daemon/daemon.d.ts.map +1 -1
  13. package/dist/daemon/daemon.js +174 -6
  14. package/dist/daemon/daemon.js.map +1 -1
  15. package/dist/daemon/lib/index.d.ts +1 -0
  16. package/dist/daemon/lib/index.d.ts.map +1 -1
  17. package/dist/daemon/lib/index.js +1 -0
  18. package/dist/daemon/lib/index.js.map +1 -1
  19. package/dist/daemon/lib/safeguards.d.ts +135 -0
  20. package/dist/daemon/lib/safeguards.d.ts.map +1 -0
  21. package/dist/daemon/lib/safeguards.js +250 -0
  22. package/dist/daemon/lib/safeguards.js.map +1 -0
  23. package/dist/daemon/lib/watcher.d.ts.map +1 -1
  24. package/dist/daemon/lib/watcher.js +48 -8
  25. package/dist/daemon/lib/watcher.js.map +1 -1
  26. package/dist/daemon/web/app.js +433 -0
  27. package/dist/daemon/web/index.html +68 -0
  28. package/dist/daemon/web/styles.css +452 -0
  29. package/dist/lib/environment.d.ts +1 -0
  30. package/dist/lib/environment.d.ts.map +1 -1
  31. package/dist/lib/environment.js +14 -0
  32. package/dist/lib/environment.js.map +1 -1
  33. package/dist/lib/invoke.d.ts +8 -0
  34. package/dist/lib/invoke.d.ts.map +1 -1
  35. package/dist/lib/invoke.js +28 -16
  36. package/dist/lib/invoke.js.map +1 -1
  37. package/dist/lib/processor.d.ts +8 -0
  38. package/dist/lib/processor.d.ts.map +1 -1
  39. package/dist/lib/processor.js +16 -2
  40. package/dist/lib/processor.js.map +1 -1
  41. package/dist/lib/validation/persona.d.ts.map +1 -1
  42. package/dist/lib/validation/persona.js +41 -4
  43. package/dist/lib/validation/persona.js.map +1 -1
  44. package/package.json +15 -9
  45. package/dist/lib/channel.test.d.ts +0 -2
  46. package/dist/lib/channel.test.d.ts.map +0 -1
  47. package/dist/lib/channel.test.js +0 -33
  48. package/dist/lib/channel.test.js.map +0 -1
  49. package/dist/lib/frontmatter.test.d.ts +0 -2
  50. package/dist/lib/frontmatter.test.d.ts.map +0 -1
  51. package/dist/lib/frontmatter.test.js +0 -60
  52. package/dist/lib/frontmatter.test.js.map +0 -1
  53. package/dist/lib/integration.test.d.ts +0 -2
  54. package/dist/lib/integration.test.d.ts.map +0 -1
  55. package/dist/lib/integration.test.js +0 -445
  56. package/dist/lib/integration.test.js.map +0 -1
  57. package/dist/lib/invoke.test.d.ts +0 -2
  58. package/dist/lib/invoke.test.d.ts.map +0 -1
  59. package/dist/lib/invoke.test.js +0 -82
  60. package/dist/lib/invoke.test.js.map +0 -1
  61. package/dist/lib/persona.test.d.ts +0 -2
  62. package/dist/lib/persona.test.d.ts.map +0 -1
  63. package/dist/lib/persona.test.js +0 -324
  64. package/dist/lib/persona.test.js.map +0 -1
  65. package/dist/lib/processor.test.d.ts +0 -2
  66. package/dist/lib/processor.test.d.ts.map +0 -1
  67. package/dist/lib/processor.test.js +0 -134
  68. package/dist/lib/processor.test.js.map +0 -1
  69. package/dist/lib/registry.test.d.ts +0 -2
  70. package/dist/lib/registry.test.d.ts.map +0 -1
  71. package/dist/lib/registry.test.js +0 -236
  72. package/dist/lib/registry.test.js.map +0 -1
  73. package/dist/lib/session-thread.test.d.ts +0 -2
  74. package/dist/lib/session-thread.test.d.ts.map +0 -1
  75. package/dist/lib/session-thread.test.js +0 -235
  76. package/dist/lib/session-thread.test.js.map +0 -1
  77. package/dist/lib/session.test.d.ts +0 -2
  78. package/dist/lib/session.test.d.ts.map +0 -1
  79. package/dist/lib/session.test.js +0 -336
  80. package/dist/lib/session.test.js.map +0 -1
@@ -0,0 +1,250 @@
1
+ import { hasFrontmatter, parseFrontmatter } from "../../lib/frontmatter.js";
2
+ /**
3
+ * Check if a message is a self-reply (from the same persona)
4
+ *
5
+ * Parses the from: field from message frontmatter and compares it
6
+ * against the target persona name. Returns true if the message
7
+ * should be skipped (i.e., it's from the same persona).
8
+ *
9
+ * Fails open: if the from field can't be parsed or is missing,
10
+ * the message is NOT skipped to avoid dropping legitimate messages.
11
+ *
12
+ * @param messageContent - The raw message content (may include frontmatter)
13
+ * @param personaName - The name of the target persona (without @ prefix)
14
+ * @returns true if the message should be skipped (self-reply)
15
+ */
16
+ export function isSelfReply(messageContent, personaName) {
17
+ // Can't parse frontmatter -> fail open (don't skip)
18
+ if (!hasFrontmatter(messageContent)) {
19
+ return false;
20
+ }
21
+ try {
22
+ const { frontmatter } = parseFrontmatter(messageContent);
23
+ // Missing from field -> fail open (don't skip)
24
+ if (!frontmatter.from) {
25
+ return false;
26
+ }
27
+ const from = frontmatter.from;
28
+ // The from field can be in various formats:
29
+ // - "agent:persona-name" (agent sender)
30
+ // - "@persona-name" (DM address format)
31
+ // - "human:username" (human sender)
32
+ // - Just "persona-name" (simple format)
33
+ // Normalize the from field - extract the persona name
34
+ let senderName = from;
35
+ // Strip agent: prefix
36
+ if (senderName.startsWith("agent:")) {
37
+ senderName = senderName.slice(6);
38
+ }
39
+ // Strip @ prefix
40
+ if (senderName.startsWith("@")) {
41
+ senderName = senderName.slice(1);
42
+ }
43
+ // Compare normalized sender to target persona
44
+ return senderName === personaName;
45
+ }
46
+ catch {
47
+ // Parse error -> fail open (don't skip)
48
+ return false;
49
+ }
50
+ }
51
+ /**
52
+ * Rate limiter for persona invocations
53
+ *
54
+ * Tracks invocations per persona with a sliding window.
55
+ * In-memory state - resets on daemon restart.
56
+ */
57
+ export class RateLimiter {
58
+ maxInvocations;
59
+ windowMs;
60
+ /** Map of persona name -> list of invocation timestamps */
61
+ invocations = new Map();
62
+ /**
63
+ * Create a new rate limiter
64
+ *
65
+ * @param maxInvocations - Maximum invocations per window (default: 5)
66
+ * @param windowMs - Time window in milliseconds (default: 60000 = 1 minute)
67
+ */
68
+ constructor(maxInvocations = 5, windowMs = 60_000) {
69
+ this.maxInvocations = maxInvocations;
70
+ this.windowMs = windowMs;
71
+ }
72
+ /**
73
+ * Check if an invocation should be allowed for a persona
74
+ *
75
+ * @param personaName - The persona to check
76
+ * @returns true if the invocation should be allowed, false if rate limited
77
+ */
78
+ isAllowed(personaName) {
79
+ const now = Date.now();
80
+ const timestamps = this.invocations.get(personaName) ?? [];
81
+ // Filter to only timestamps within the window
82
+ const windowStart = now - this.windowMs;
83
+ const recentTimestamps = timestamps.filter((ts) => ts > windowStart);
84
+ // Check if under limit
85
+ return recentTimestamps.length < this.maxInvocations;
86
+ }
87
+ /**
88
+ * Record an invocation for a persona
89
+ *
90
+ * Should be called when an invocation is actually made (after isAllowed check passes).
91
+ *
92
+ * @param personaName - The persona being invoked
93
+ */
94
+ recordInvocation(personaName) {
95
+ const now = Date.now();
96
+ const timestamps = this.invocations.get(personaName) ?? [];
97
+ // Clean up old timestamps and add new one
98
+ const windowStart = now - this.windowMs;
99
+ const recentTimestamps = timestamps.filter((ts) => ts > windowStart);
100
+ recentTimestamps.push(now);
101
+ this.invocations.set(personaName, recentTimestamps);
102
+ }
103
+ /**
104
+ * Check if allowed and record in one operation
105
+ *
106
+ * @param personaName - The persona to check
107
+ * @returns true if the invocation was allowed and recorded, false if rate limited
108
+ */
109
+ tryInvoke(personaName) {
110
+ if (!this.isAllowed(personaName)) {
111
+ return false;
112
+ }
113
+ this.recordInvocation(personaName);
114
+ return true;
115
+ }
116
+ /**
117
+ * Get current invocation count for a persona within the window
118
+ *
119
+ * @param personaName - The persona to check
120
+ * @returns Number of recent invocations
121
+ */
122
+ getInvocationCount(personaName) {
123
+ const now = Date.now();
124
+ const timestamps = this.invocations.get(personaName) ?? [];
125
+ const windowStart = now - this.windowMs;
126
+ return timestamps.filter((ts) => ts > windowStart).length;
127
+ }
128
+ /**
129
+ * Reset all rate limiting state (for testing)
130
+ */
131
+ reset() {
132
+ this.invocations.clear();
133
+ }
134
+ }
135
+ /**
136
+ * Circuit breaker for daemon-wide failure protection
137
+ *
138
+ * Tracks consecutive failures across all executions. When failures exceed
139
+ * a threshold within a time window, the breaker "trips" and blocks new
140
+ * executions until a cooldown period passes.
141
+ *
142
+ * This protects against cascading failures, external service outages,
143
+ * and unforeseen doom loops that bypass other safeguards.
144
+ */
145
+ export class CircuitBreaker {
146
+ failureThreshold;
147
+ windowMs;
148
+ cooldownMs;
149
+ /** Timestamps of recent failures */
150
+ failures = [];
151
+ /** Whether the breaker is currently tripped */
152
+ tripped = false;
153
+ /** When the breaker was tripped (for cooldown calculation) */
154
+ trippedAt = null;
155
+ /**
156
+ * Create a new circuit breaker
157
+ *
158
+ * @param failureThreshold - Number of failures to trigger trip (default: 10)
159
+ * @param windowMs - Time window for counting failures (default: 60000 = 1 minute)
160
+ * @param cooldownMs - Time before auto-reset after trip (default: 300000 = 5 minutes)
161
+ */
162
+ constructor(failureThreshold = 10, windowMs = 60_000, cooldownMs = 300_000) {
163
+ this.failureThreshold = failureThreshold;
164
+ this.windowMs = windowMs;
165
+ this.cooldownMs = cooldownMs;
166
+ }
167
+ /**
168
+ * Record a failure
169
+ *
170
+ * Adds the failure to the tracking window and checks if the breaker should trip.
171
+ */
172
+ recordFailure() {
173
+ const now = Date.now();
174
+ this.failures.push(now);
175
+ // Clean old failures outside the window
176
+ const windowStart = now - this.windowMs;
177
+ this.failures = this.failures.filter((ts) => ts > windowStart);
178
+ // Check if we should trip
179
+ if (!this.tripped && this.failures.length >= this.failureThreshold) {
180
+ this.tripped = true;
181
+ this.trippedAt = now;
182
+ console.error(`[circuit-breaker] TRIPPED: ${this.failures.length} failures in ${this.windowMs / 1000}s. ` +
183
+ `Blocking executions for ${this.cooldownMs / 1000}s.`);
184
+ }
185
+ }
186
+ /**
187
+ * Record a success
188
+ *
189
+ * Clears the failure count on success (indicates system is healthy).
190
+ */
191
+ recordSuccess() {
192
+ this.failures = [];
193
+ }
194
+ /**
195
+ * Check if the breaker is currently tripped
196
+ *
197
+ * Also handles auto-reset after cooldown period.
198
+ *
199
+ * @returns true if executions should be blocked
200
+ */
201
+ isTripped() {
202
+ if (!this.tripped) {
203
+ return false;
204
+ }
205
+ // Check if cooldown has passed
206
+ const now = Date.now();
207
+ if (this.trippedAt && now - this.trippedAt >= this.cooldownMs) {
208
+ console.log(`[circuit-breaker] Auto-reset after ${this.cooldownMs / 1000}s cooldown. Allowing executions.`);
209
+ this.reset();
210
+ return false;
211
+ }
212
+ return true;
213
+ }
214
+ /**
215
+ * Get time remaining until auto-reset (in seconds)
216
+ *
217
+ * @returns Seconds until reset, or 0 if not tripped
218
+ */
219
+ getTimeUntilReset() {
220
+ if (!this.tripped || !this.trippedAt) {
221
+ return 0;
222
+ }
223
+ const elapsed = Date.now() - this.trippedAt;
224
+ const remaining = this.cooldownMs - elapsed;
225
+ return Math.max(0, Math.ceil(remaining / 1000));
226
+ }
227
+ /**
228
+ * Manually reset the breaker
229
+ */
230
+ reset() {
231
+ this.tripped = false;
232
+ this.trippedAt = null;
233
+ this.failures = [];
234
+ }
235
+ /**
236
+ * Get current state for status reporting
237
+ */
238
+ getState() {
239
+ // Clean old failures for accurate count
240
+ const now = Date.now();
241
+ const windowStart = now - this.windowMs;
242
+ this.failures = this.failures.filter((ts) => ts > windowStart);
243
+ return {
244
+ tripped: this.tripped,
245
+ failureCount: this.failures.length,
246
+ timeUntilReset: this.getTimeUntilReset(),
247
+ };
248
+ }
249
+ }
250
+ //# sourceMappingURL=safeguards.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safeguards.js","sourceRoot":"","sources":["../../../src/daemon/lib/safeguards.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../../src/daemon/lib/watcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,gBAAgB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,kBAAkB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,kBAAkB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,eAAe,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAClC,iBAAiB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,iBAAiB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,aAAa,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3E,iBAAiB,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CAChF;AAED;;GAEG;AACH,qBAAa,OAAQ,SAAQ,YAAY;IAOrC,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,WAAW,CAAC;IARtB,OAAO,CAAC,eAAe,CAA0B;IACjD,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,OAAO,CAAS;gBAGd,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,YAAA;IAK9B;;OAEG;IACH,KAAK,IAAI,IAAI;IA8Eb;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B;;OAEG;IACH,SAAS,IAAI,OAAO;CAGrB"}
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../../src/daemon/lib/watcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAa3C;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,gBAAgB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,kBAAkB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,kBAAkB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,eAAe,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAClC,iBAAiB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,iBAAiB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,aAAa,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3E,iBAAiB,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CAChF;AAED;;GAEG;AACH,qBAAa,OAAQ,SAAQ,YAAY;IAOrC,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,WAAW,CAAC;IARtB,OAAO,CAAC,eAAe,CAA0B;IACjD,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,OAAO,CAAS;gBAGd,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,YAAA;IAK9B;;OAEG;IACH,KAAK,IAAI,IAAI;IA4Gb;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B;;OAEG;IACH,SAAS,IAAI,OAAO;CAGrB"}
@@ -1,6 +1,15 @@
1
1
  import { watch } from "chokidar";
2
2
  import { EventEmitter } from "node:events";
3
+ import { readFile } from "node:fs/promises";
3
4
  import { dirname, basename } from "node:path";
5
+ /**
6
+ * Check if a string looks like an ISO timestamp (message ID format)
7
+ * Message IDs are ISO timestamps like "2026-01-24T23:29:20.778Z"
8
+ * UUIDs are like "621f4c3e-69f8-4c16-994b-3cbda5e27f97"
9
+ */
10
+ function isISOTimestamp(str) {
11
+ return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(str);
12
+ }
4
13
  /**
5
14
  * File watcher for workflows, personas, and channels
6
15
  */
@@ -60,17 +69,25 @@ export class Watcher extends EventEmitter {
60
69
  this.channelWatcher = watch(this.channelsDir, {
61
70
  ignoreInitial: true,
62
71
  persistent: true,
63
- depth: 3, // channels/{@name|#name}/message-id/message.md
72
+ depth: 3, // channels/{@name|#name}/{thread-id}/{message-id}.md
73
+ // Wait for files to stabilize before emitting events
74
+ // This helps with cloud-synced files (iCloud, Syncthing) that appear
75
+ // in the filesystem before they're fully written/readable
76
+ awaitWriteFinish: {
77
+ stabilityThreshold: 500, // Wait 500ms after last change
78
+ pollInterval: 100,
79
+ },
64
80
  });
65
- this.channelWatcher.on("add", (path) => {
66
- // Only process message.md files
67
- if (!path.endsWith("/message.md")) {
81
+ this.channelWatcher.on("add", async (path) => {
82
+ // Only process .md message files
83
+ if (!path.endsWith(".md")) {
68
84
  return;
69
85
  }
70
- // Path: {channelsDir}/{@persona|#channel}/{message-id}/message.md
71
- const messageDir = dirname(path);
72
- const messageId = basename(messageDir);
73
- const channelDir = dirname(messageDir);
86
+ // Path: {channelsDir}/{@persona|#channel}/{thread-id}/{message-id}.md
87
+ const messageId = basename(path, ".md");
88
+ const threadDir = dirname(path);
89
+ const threadId = basename(threadDir);
90
+ const channelDir = dirname(threadDir);
74
91
  const channel = basename(channelDir);
75
92
  // Emit appropriate event based on channel type
76
93
  if (channel.startsWith("@")) {
@@ -79,6 +96,29 @@ export class Watcher extends EventEmitter {
79
96
  }
80
97
  else if (channel.startsWith("#")) {
81
98
  // Public channel -> trigger workflow
99
+ // Skip thread replies by checking frontmatter thread_id
100
+ // - New messages have UUID thread_id (random identifier)
101
+ // - Replies have timestamp thread_id (pointing to another message)
102
+ try {
103
+ const content = await readFile(path, "utf-8");
104
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
105
+ if (frontmatterMatch) {
106
+ const threadIdMatch = frontmatterMatch[1].match(/thread_id:\s*["']?([^\s"'\n]+)/);
107
+ if (threadIdMatch) {
108
+ const frontmatterThreadId = threadIdMatch[1];
109
+ // If thread_id is a timestamp (not UUID), it's a reply - skip it
110
+ if (isISOTimestamp(frontmatterThreadId)) {
111
+ console.log(`[watcher] Skipping thread reply: ${messageId} (reply to: ${frontmatterThreadId})`);
112
+ return;
113
+ }
114
+ }
115
+ }
116
+ }
117
+ catch {
118
+ // If we can't read the file, skip it (fail closed for safety)
119
+ console.warn(`[watcher] Could not read message file: ${path}`);
120
+ return;
121
+ }
82
122
  this.emit("channel:message", { channel, messageId, messagePath: path });
83
123
  }
84
124
  });
@@ -1 +1 @@
1
- {"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../../src/daemon/lib/watcher.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../../src/daemon/lib/watcher.ts"],"names":[],"mappings":"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"}