@dotsetlabs/dotclaw 2.3.0 → 2.5.0

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 (104) hide show
  1. package/config-examples/runtime.json +29 -3
  2. package/container/agent-runner/src/agent-config.ts +19 -3
  3. package/container/agent-runner/src/container-protocol.ts +11 -0
  4. package/container/agent-runner/src/context-overflow-recovery.ts +39 -0
  5. package/container/agent-runner/src/index.ts +744 -123
  6. package/container/agent-runner/src/memory.ts +18 -68
  7. package/container/agent-runner/src/system-prompt.ts +36 -34
  8. package/container/agent-runner/src/tool-loop-policy.ts +724 -0
  9. package/container/agent-runner/src/tools.ts +211 -8
  10. package/dist/agent-context.d.ts +1 -0
  11. package/dist/agent-context.d.ts.map +1 -1
  12. package/dist/agent-context.js +21 -9
  13. package/dist/agent-context.js.map +1 -1
  14. package/dist/agent-execution.d.ts +2 -0
  15. package/dist/agent-execution.d.ts.map +1 -1
  16. package/dist/agent-execution.js +164 -15
  17. package/dist/agent-execution.js.map +1 -1
  18. package/dist/agent-semaphore.d.ts +24 -1
  19. package/dist/agent-semaphore.d.ts.map +1 -1
  20. package/dist/agent-semaphore.js +109 -20
  21. package/dist/agent-semaphore.js.map +1 -1
  22. package/dist/cli.js +3 -11
  23. package/dist/cli.js.map +1 -1
  24. package/dist/config.d.ts +2 -0
  25. package/dist/config.d.ts.map +1 -1
  26. package/dist/config.js +2 -0
  27. package/dist/config.js.map +1 -1
  28. package/dist/container-protocol.d.ts +22 -0
  29. package/dist/container-protocol.d.ts.map +1 -1
  30. package/dist/container-protocol.js.map +1 -1
  31. package/dist/container-runner.d.ts +7 -0
  32. package/dist/container-runner.d.ts.map +1 -1
  33. package/dist/container-runner.js +417 -143
  34. package/dist/container-runner.js.map +1 -1
  35. package/dist/db.d.ts.map +1 -1
  36. package/dist/db.js +46 -12
  37. package/dist/db.js.map +1 -1
  38. package/dist/failover-policy.d.ts +41 -0
  39. package/dist/failover-policy.d.ts.map +1 -0
  40. package/dist/failover-policy.js +261 -0
  41. package/dist/failover-policy.js.map +1 -0
  42. package/dist/index.js +1 -0
  43. package/dist/index.js.map +1 -1
  44. package/dist/ipc-dispatcher.d.ts.map +1 -1
  45. package/dist/ipc-dispatcher.js +27 -43
  46. package/dist/ipc-dispatcher.js.map +1 -1
  47. package/dist/mcp-config.d.ts +22 -0
  48. package/dist/mcp-config.d.ts.map +1 -0
  49. package/dist/mcp-config.js +94 -0
  50. package/dist/mcp-config.js.map +1 -0
  51. package/dist/memory-backend.d.ts +27 -0
  52. package/dist/memory-backend.d.ts.map +1 -0
  53. package/dist/memory-backend.js +112 -0
  54. package/dist/memory-backend.js.map +1 -0
  55. package/dist/memory-recall.d.ts.map +1 -1
  56. package/dist/memory-recall.js +135 -22
  57. package/dist/memory-recall.js.map +1 -1
  58. package/dist/memory-store.d.ts +1 -0
  59. package/dist/memory-store.d.ts.map +1 -1
  60. package/dist/memory-store.js +55 -7
  61. package/dist/memory-store.js.map +1 -1
  62. package/dist/message-pipeline.d.ts +24 -0
  63. package/dist/message-pipeline.d.ts.map +1 -1
  64. package/dist/message-pipeline.js +131 -27
  65. package/dist/message-pipeline.js.map +1 -1
  66. package/dist/metrics.d.ts +1 -0
  67. package/dist/metrics.d.ts.map +1 -1
  68. package/dist/metrics.js +9 -0
  69. package/dist/metrics.js.map +1 -1
  70. package/dist/recall-policy.d.ts +12 -0
  71. package/dist/recall-policy.d.ts.map +1 -0
  72. package/dist/recall-policy.js +89 -0
  73. package/dist/recall-policy.js.map +1 -0
  74. package/dist/runtime-config.d.ts +33 -0
  75. package/dist/runtime-config.d.ts.map +1 -1
  76. package/dist/runtime-config.js +111 -11
  77. package/dist/runtime-config.js.map +1 -1
  78. package/dist/streaming.d.ts.map +1 -1
  79. package/dist/streaming.js +125 -33
  80. package/dist/streaming.js.map +1 -1
  81. package/dist/task-scheduler.d.ts.map +1 -1
  82. package/dist/task-scheduler.js +27 -10
  83. package/dist/task-scheduler.js.map +1 -1
  84. package/dist/tool-policy.d.ts.map +1 -1
  85. package/dist/tool-policy.js +26 -4
  86. package/dist/tool-policy.js.map +1 -1
  87. package/dist/trace-writer.d.ts +12 -0
  88. package/dist/trace-writer.d.ts.map +1 -1
  89. package/dist/trace-writer.js.map +1 -1
  90. package/dist/turn-hygiene.d.ts +14 -0
  91. package/dist/turn-hygiene.d.ts.map +1 -0
  92. package/dist/turn-hygiene.js +214 -0
  93. package/dist/turn-hygiene.js.map +1 -0
  94. package/dist/webhook.d.ts.map +1 -1
  95. package/dist/webhook.js +1 -0
  96. package/dist/webhook.js.map +1 -1
  97. package/package.json +15 -1
  98. package/scripts/benchmark-baseline.js +365 -0
  99. package/scripts/benchmark-harness.js +1413 -0
  100. package/scripts/benchmark-scenarios.js +301 -0
  101. package/scripts/canary-suite.js +123 -0
  102. package/scripts/generate-controlled-traces.js +230 -0
  103. package/scripts/release-slo-check.js +214 -0
  104. package/scripts/run-live-canary.js +339 -0
@@ -4,8 +4,17 @@
4
4
  "container": {
5
5
  "mode": "daemon",
6
6
  "privileged": true,
7
+ "daemonPollMs": 100,
7
8
  "instanceId": ""
8
9
  },
10
+ "messageQueue": {
11
+ "promptMaxChars": 24000
12
+ },
13
+ "concurrency": {
14
+ "maxAgents": 4,
15
+ "laneStarvationMs": 15000,
16
+ "maxConsecutiveInteractive": 3
17
+ },
9
18
  "metrics": {
10
19
  "port": 3001,
11
20
  "enabled": true
@@ -20,6 +29,10 @@
20
29
  },
21
30
  "embeddings": {
22
31
  "enabled": true
32
+ },
33
+ "backend": {
34
+ "strategy": "builtin",
35
+ "modulePath": ""
23
36
  }
24
37
  },
25
38
  "routing": {
@@ -30,7 +43,11 @@
30
43
  "maxToolSteps": 200,
31
44
  "temperature": 0.6,
32
45
  "recallMaxResults": 8,
33
- "recallMaxTokens": 1500
46
+ "recallMaxTokens": 1500,
47
+ "hostFailover": {
48
+ "enabled": true,
49
+ "maxRetries": 1
50
+ }
34
51
  },
35
52
  "webhook": {
36
53
  "enabled": false,
@@ -39,7 +56,7 @@
39
56
  },
40
57
  "streaming": {
41
58
  "enabled": true,
42
- "chunkFlushIntervalMs": 200,
59
+ "chunkFlushIntervalMs": 120,
43
60
  "editIntervalMs": 400,
44
61
  "maxEditLength": 3800
45
62
  },
@@ -57,6 +74,7 @@
57
74
  "assistantName": "Rain",
58
75
  "reasoning": { "effort": "medium" },
59
76
  "context": {
77
+ "recentContextTokens": 0,
60
78
  "maxHistoryTurns": 40,
61
79
  "contextPruning": {
62
80
  "softTrimMaxChars": 4000,
@@ -72,6 +90,14 @@
72
90
  "enableBash": true,
73
91
  "enableWebSearch": true,
74
92
  "enableWebFetch": true,
93
+ "completionGuard": {
94
+ "idempotentRetryAttempts": 2,
95
+ "idempotentRetryBackoffMs": 500,
96
+ "repeatedSignatureThreshold": 3,
97
+ "repeatedRoundThreshold": 2,
98
+ "nonRetryableFailureThreshold": 3,
99
+ "forceSynthesisAfterTools": true
100
+ },
75
101
  "bash": {
76
102
  "timeoutMs": 600000
77
103
  },
@@ -87,7 +113,7 @@
87
113
  "openaiVoice": "alloy"
88
114
  },
89
115
  "mcp": {
90
- "enabled": true,
116
+ "enabled": false,
91
117
  "servers": []
92
118
  }
93
119
  }
@@ -56,6 +56,14 @@ export type AgentRuntimeConfig = {
56
56
  enableBash: boolean;
57
57
  enableWebSearch: boolean;
58
58
  enableWebFetch: boolean;
59
+ completionGuard: {
60
+ idempotentRetryAttempts: number;
61
+ idempotentRetryBackoffMs: number;
62
+ repeatedSignatureThreshold: number;
63
+ repeatedRoundThreshold: number;
64
+ nonRetryableFailureThreshold: number;
65
+ forceSynthesisAfterTools: boolean;
66
+ };
59
67
  webfetch: {
60
68
  blockPrivate: boolean;
61
69
  allowlist: string[];
@@ -143,7 +151,7 @@ export type AgentRuntimeConfig = {
143
151
 
144
152
  const CONFIG_PATH = '/workspace/config/runtime.json';
145
153
  const DEFAULT_DEFAULT_MODEL = 'moonshotai/kimi-k2.5';
146
- const DEFAULT_DAEMON_POLL_MS = 200;
154
+ const DEFAULT_DAEMON_POLL_MS = 100;
147
155
  const DEFAULT_DAEMON_HEARTBEAT_INTERVAL_MS = 1_000;
148
156
 
149
157
  const DEFAULT_AGENT_CONFIG: AgentRuntimeConfig['agent'] = {
@@ -163,7 +171,7 @@ const DEFAULT_AGENT_CONFIG: AgentRuntimeConfig['agent'] = {
163
171
  context: {
164
172
  maxContextTokens: 128_000,
165
173
  compactionTriggerTokens: 120_000,
166
- recentContextTokens: 0, // 0 = auto: 60% of model context window
174
+ recentContextTokens: 0, // 0 = auto: 35% of model context window (capped at 24K)
167
175
  summaryUpdateEveryMessages: 20,
168
176
  maxOutputTokens: 8192,
169
177
  summaryMaxOutputTokens: 2048,
@@ -198,6 +206,14 @@ const DEFAULT_AGENT_CONFIG: AgentRuntimeConfig['agent'] = {
198
206
  enableBash: true,
199
207
  enableWebSearch: true,
200
208
  enableWebFetch: true,
209
+ completionGuard: {
210
+ idempotentRetryAttempts: 2,
211
+ idempotentRetryBackoffMs: 500,
212
+ repeatedSignatureThreshold: 3,
213
+ repeatedRoundThreshold: 2,
214
+ nonRetryableFailureThreshold: 3,
215
+ forceSynthesisAfterTools: true
216
+ },
201
217
  webfetch: {
202
218
  blockPrivate: true,
203
219
  allowlist: [],
@@ -256,7 +272,7 @@ const DEFAULT_AGENT_CONFIG: AgentRuntimeConfig['agent'] = {
256
272
  screenshotQuality: 80
257
273
  },
258
274
  mcp: {
259
- enabled: true,
275
+ enabled: false,
260
276
  servers: [],
261
277
  connectionTimeoutMs: 10_000
262
278
  },
@@ -13,6 +13,7 @@ export interface ContainerInput {
13
13
  userName?: string;
14
14
  maxToolSteps?: number;
15
15
  memoryRecall?: string[];
16
+ memoryRecallAttempted?: boolean;
16
17
  userProfile?: string | null;
17
18
  memoryStats?: {
18
19
  total: number;
@@ -92,4 +93,14 @@ export interface ContainerOutput {
92
93
  replyToId?: string;
93
94
  /** Set by the host container-runner when stdout was truncated before parsing */
94
95
  stdoutTruncated?: boolean;
96
+ /** Number of in-loop idempotent tool retries attempted */
97
+ tool_retry_attempts?: number;
98
+ /** True when a forced final synthesis pass was used after tool execution */
99
+ tool_outcome_verification_forced?: boolean;
100
+ /** True when repeated tool signatures triggered loop-break protection */
101
+ tool_loop_breaker_triggered?: boolean;
102
+ /** Reason emitted when the tool loop breaker triggered */
103
+ tool_loop_breaker_reason?: string;
104
+ /** Error from fire-and-forget memory extraction in daemon mode */
105
+ memory_extraction_error?: string;
95
106
  }
@@ -0,0 +1,39 @@
1
+ export type ContextMessageLike = {
2
+ role: 'user' | 'assistant';
3
+ content: string;
4
+ };
5
+
6
+ export type ContextOverflowRecoveryPlan = {
7
+ toCompact: ContextMessageLike[];
8
+ toKeep: ContextMessageLike[];
9
+ retryInput: Array<{ role: 'user' | 'assistant'; content: string }>;
10
+ };
11
+
12
+ export function buildContextOverflowRecoveryPlan(params: {
13
+ contextMessages: ContextMessageLike[];
14
+ emergencySummary?: string | null;
15
+ keepRecentCount?: number;
16
+ }): ContextOverflowRecoveryPlan {
17
+ const keepRecentCount = Number.isFinite(params.keepRecentCount)
18
+ ? Math.max(1, Math.floor(Number(params.keepRecentCount)))
19
+ : 4;
20
+ const contextMessages = Array.isArray(params.contextMessages)
21
+ ? params.contextMessages
22
+ : [];
23
+
24
+ const keepStart = Math.max(0, contextMessages.length - keepRecentCount);
25
+ const toCompact = contextMessages.slice(0, keepStart);
26
+ const toKeep = contextMessages.slice(keepStart);
27
+ const emergencySummary = (params.emergencySummary || '').trim();
28
+
29
+ const retryInput = emergencySummary
30
+ ? [{ role: 'user' as const, content: `[Previous conversation summary: ${emergencySummary}]` },
31
+ ...toKeep.map(msg => ({ role: msg.role, content: msg.content }))]
32
+ : toKeep.map(msg => ({ role: msg.role, content: msg.content }));
33
+
34
+ return {
35
+ toCompact,
36
+ toKeep,
37
+ retryInput
38
+ };
39
+ }