@dobby.ai/dobby 0.1.0 → 0.1.2

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 (156) hide show
  1. package/README.md +84 -39
  2. package/dist/src/agent/event-forwarder.js +185 -16
  3. package/dist/src/cli/commands/cron.js +39 -35
  4. package/dist/src/cli/commands/doctor.js +81 -2
  5. package/dist/src/cli/commands/extension.js +3 -1
  6. package/dist/src/cli/commands/init.js +43 -173
  7. package/dist/src/cli/commands/topology.js +38 -14
  8. package/dist/src/cli/program.js +15 -137
  9. package/dist/src/cli/shared/config-io.js +3 -31
  10. package/dist/src/cli/shared/config-mutators.js +33 -9
  11. package/dist/src/cli/shared/configure-sections.js +52 -12
  12. package/dist/src/cli/shared/init-catalog.js +89 -46
  13. package/dist/src/cli/shared/local-extension-specs.js +85 -0
  14. package/dist/src/cli/shared/schema-prompts.js +26 -2
  15. package/dist/src/core/gateway.js +3 -1
  16. package/dist/src/core/routing.js +53 -38
  17. package/dist/src/core/types.js +2 -0
  18. package/dist/src/cron/config.js +2 -2
  19. package/dist/src/cron/service.js +87 -23
  20. package/dist/src/cron/store.js +1 -1
  21. package/dist/src/main.js +0 -0
  22. package/dist/src/shared/dobby-repo.js +40 -0
  23. package/package.json +11 -4
  24. package/.env.example +0 -9
  25. package/AGENTS.md +0 -267
  26. package/ROADMAP.md +0 -34
  27. package/config/cron.example.json +0 -9
  28. package/config/gateway.example.json +0 -128
  29. package/config/models.custom.example.json +0 -27
  30. package/dist/src/agent/tests/event-forwarder.test.js +0 -113
  31. package/dist/src/cli/shared/config-path.js +0 -207
  32. package/dist/src/cli/shared/init-models-file.js +0 -65
  33. package/dist/src/cli/shared/presets.js +0 -86
  34. package/dist/src/cli/tests/config-command.test.js +0 -42
  35. package/dist/src/cli/tests/config-io.test.js +0 -64
  36. package/dist/src/cli/tests/config-mutators.test.js +0 -47
  37. package/dist/src/cli/tests/config-path.test.js +0 -21
  38. package/dist/src/cli/tests/discord-config.test.js +0 -23
  39. package/dist/src/cli/tests/doctor.test.js +0 -107
  40. package/dist/src/cli/tests/init-catalog.test.js +0 -87
  41. package/dist/src/cli/tests/presets.test.js +0 -41
  42. package/dist/src/cli/tests/program-options.test.js +0 -92
  43. package/dist/src/cli/tests/routing-config.test.js +0 -199
  44. package/dist/src/cli/tests/routing-legacy.test.js +0 -191
  45. package/dist/src/core/tests/control-command.test.js +0 -17
  46. package/dist/src/core/tests/gateway-update-strategy.test.js +0 -167
  47. package/dist/src/core/tests/runtime-registry.test.js +0 -116
  48. package/dist/src/core/tests/typing-controller.test.js +0 -103
  49. package/docs/BOXLITE_SANDBOX_FEASIBILITY.md +0 -175
  50. package/docs/CRON_SCHEDULER_DESIGN.md +0 -374
  51. package/docs/DOCKER_SANDBOX_vs_BOXLITE.md +0 -77
  52. package/docs/EXTENSION_SYSTEM_ARCHITECTURE.md +0 -119
  53. package/docs/MVP.md +0 -135
  54. package/docs/RUNBOOK.md +0 -242
  55. package/docs/TEAMWORK_HANDOFF_DESIGN.md +0 -440
  56. package/plugins/connector-discord/dobby.manifest.json +0 -18
  57. package/plugins/connector-discord/index.js +0 -1
  58. package/plugins/connector-discord/package-lock.json +0 -360
  59. package/plugins/connector-discord/package.json +0 -38
  60. package/plugins/connector-discord/src/connector.ts +0 -350
  61. package/plugins/connector-discord/src/contribution.ts +0 -21
  62. package/plugins/connector-discord/src/mapper.ts +0 -102
  63. package/plugins/connector-discord/tsconfig.json +0 -19
  64. package/plugins/connector-feishu/dobby.manifest.json +0 -18
  65. package/plugins/connector-feishu/index.js +0 -1
  66. package/plugins/connector-feishu/package-lock.json +0 -618
  67. package/plugins/connector-feishu/package.json +0 -38
  68. package/plugins/connector-feishu/src/connector.ts +0 -343
  69. package/plugins/connector-feishu/src/contribution.ts +0 -26
  70. package/plugins/connector-feishu/src/mapper.ts +0 -401
  71. package/plugins/connector-feishu/tsconfig.json +0 -19
  72. package/plugins/plugin-sdk/index.d.ts +0 -261
  73. package/plugins/plugin-sdk/index.js +0 -1
  74. package/plugins/plugin-sdk/package-lock.json +0 -12
  75. package/plugins/plugin-sdk/package.json +0 -22
  76. package/plugins/provider-claude/dobby.manifest.json +0 -17
  77. package/plugins/provider-claude/index.js +0 -1
  78. package/plugins/provider-claude/package-lock.json +0 -3398
  79. package/plugins/provider-claude/package.json +0 -39
  80. package/plugins/provider-claude/src/contribution.ts +0 -1018
  81. package/plugins/provider-claude/tsconfig.json +0 -19
  82. package/plugins/provider-claude-cli/dobby.manifest.json +0 -17
  83. package/plugins/provider-claude-cli/index.js +0 -1
  84. package/plugins/provider-claude-cli/package-lock.json +0 -2898
  85. package/plugins/provider-claude-cli/package.json +0 -38
  86. package/plugins/provider-claude-cli/src/contribution.ts +0 -1673
  87. package/plugins/provider-claude-cli/tsconfig.json +0 -19
  88. package/plugins/provider-pi/dobby.manifest.json +0 -17
  89. package/plugins/provider-pi/index.js +0 -1
  90. package/plugins/provider-pi/package-lock.json +0 -3877
  91. package/plugins/provider-pi/package.json +0 -40
  92. package/plugins/provider-pi/src/contribution.ts +0 -476
  93. package/plugins/provider-pi/tsconfig.json +0 -19
  94. package/plugins/sandbox-core/boxlite.js +0 -1
  95. package/plugins/sandbox-core/dobby.manifest.json +0 -17
  96. package/plugins/sandbox-core/docker.js +0 -1
  97. package/plugins/sandbox-core/package-lock.json +0 -136
  98. package/plugins/sandbox-core/package.json +0 -39
  99. package/plugins/sandbox-core/src/boxlite-context.ts +0 -2
  100. package/plugins/sandbox-core/src/boxlite-contribution.ts +0 -53
  101. package/plugins/sandbox-core/src/boxlite-executor.ts +0 -911
  102. package/plugins/sandbox-core/src/docker-contribution.ts +0 -43
  103. package/plugins/sandbox-core/src/docker-executor.ts +0 -217
  104. package/plugins/sandbox-core/tsconfig.json +0 -19
  105. package/scripts/local-extensions.mjs +0 -168
  106. package/src/agent/event-forwarder.ts +0 -414
  107. package/src/cli/commands/config.ts +0 -328
  108. package/src/cli/commands/configure.ts +0 -92
  109. package/src/cli/commands/cron.ts +0 -410
  110. package/src/cli/commands/doctor.ts +0 -230
  111. package/src/cli/commands/extension.ts +0 -205
  112. package/src/cli/commands/init.ts +0 -396
  113. package/src/cli/commands/start.ts +0 -223
  114. package/src/cli/commands/topology.ts +0 -383
  115. package/src/cli/index.ts +0 -9
  116. package/src/cli/program.ts +0 -465
  117. package/src/cli/shared/config-io.ts +0 -277
  118. package/src/cli/shared/config-mutators.ts +0 -440
  119. package/src/cli/shared/config-schema.ts +0 -228
  120. package/src/cli/shared/config-types.ts +0 -121
  121. package/src/cli/shared/configure-sections.ts +0 -551
  122. package/src/cli/shared/discord-config.ts +0 -14
  123. package/src/cli/shared/init-catalog.ts +0 -189
  124. package/src/cli/shared/init-models-file.ts +0 -77
  125. package/src/cli/shared/runtime.ts +0 -33
  126. package/src/cli/shared/schema-prompts.ts +0 -414
  127. package/src/cli/tests/config-command.test.ts +0 -56
  128. package/src/cli/tests/config-io.test.ts +0 -92
  129. package/src/cli/tests/config-mutators.test.ts +0 -59
  130. package/src/cli/tests/doctor.test.ts +0 -120
  131. package/src/cli/tests/init-catalog.test.ts +0 -96
  132. package/src/cli/tests/program-options.test.ts +0 -113
  133. package/src/cli/tests/routing-config.test.ts +0 -209
  134. package/src/core/control-command.ts +0 -12
  135. package/src/core/dedup-store.ts +0 -103
  136. package/src/core/gateway.ts +0 -607
  137. package/src/core/routing.ts +0 -379
  138. package/src/core/runtime-registry.ts +0 -141
  139. package/src/core/tests/control-command.test.ts +0 -20
  140. package/src/core/tests/runtime-registry.test.ts +0 -140
  141. package/src/core/tests/typing-controller.test.ts +0 -129
  142. package/src/core/types.ts +0 -318
  143. package/src/core/typing-controller.ts +0 -119
  144. package/src/cron/config.ts +0 -154
  145. package/src/cron/schedule.ts +0 -61
  146. package/src/cron/service.ts +0 -249
  147. package/src/cron/store.ts +0 -155
  148. package/src/cron/types.ts +0 -60
  149. package/src/extension/loader.ts +0 -145
  150. package/src/extension/manager.ts +0 -355
  151. package/src/extension/manifest.ts +0 -26
  152. package/src/extension/registry.ts +0 -229
  153. package/src/main.ts +0 -8
  154. package/src/sandbox/executor.ts +0 -44
  155. package/src/sandbox/host-executor.ts +0 -118
  156. package/tsconfig.json +0 -18
@@ -1,414 +0,0 @@
1
- import type {
2
- ConnectorPlugin,
3
- ConnectorUpdateStrategy,
4
- GatewayAgentEvent,
5
- GatewayLogger,
6
- InboundEnvelope,
7
- Platform,
8
- } from "../core/types.js";
9
-
10
- interface ForwarderOptions {
11
- updateIntervalMs?: number;
12
- toolMessageMode?: "none" | "errors" | "all";
13
- onOutboundActivity?: () => void;
14
- }
15
-
16
- function truncate(text: string, max?: number): string {
17
- if (max === undefined) return text;
18
- if (max <= 0) return "";
19
- if (text.length <= max) return text;
20
- const suffix = "\n...(truncated)";
21
- if (max <= suffix.length) {
22
- return text.slice(0, max);
23
- }
24
- return `${text.slice(0, max - suffix.length)}${suffix}`;
25
- }
26
-
27
- function splitForMaxLength(text: string, max?: number, options: { preserveWhitespace?: boolean } = {}): string[] {
28
- if (max === undefined) {
29
- return [text];
30
- }
31
- if (max <= 0) {
32
- return [""];
33
- }
34
- if (text.length <= max) {
35
- return [text];
36
- }
37
-
38
- const preserveWhitespace = options.preserveWhitespace ?? false;
39
- const chunks: string[] = [];
40
- let offset = 0;
41
-
42
- while (offset < text.length) {
43
- const remainingLength = text.length - offset;
44
- if (remainingLength <= max) {
45
- chunks.push(text.slice(offset));
46
- break;
47
- }
48
-
49
- if (preserveWhitespace) {
50
- chunks.push(text.slice(offset, offset + max));
51
- offset += max;
52
- continue;
53
- }
54
-
55
- const hardLimit = offset + max;
56
- let splitAt = text.lastIndexOf("\n", hardLimit);
57
- if (splitAt < offset + Math.floor(max * 0.6)) {
58
- splitAt = hardLimit;
59
- } else {
60
- splitAt += 1;
61
- }
62
- chunks.push(text.slice(offset, splitAt));
63
- offset = splitAt;
64
- }
65
-
66
- return chunks;
67
- }
68
-
69
- export class EventForwarder {
70
- private rootMessageId: string | null;
71
- private responseText = "";
72
- private appendEmittedText = "";
73
- private pendingFlush: NodeJS.Timeout | null = null;
74
- private flushSerial: Promise<void> = Promise.resolve();
75
- private readonly pendingOps: Array<Promise<unknown>> = [];
76
- private readonly updateIntervalMs: number;
77
- private readonly toolMessageMode: "none" | "errors" | "all";
78
- private readonly maxTextLength: number | undefined;
79
- private readonly onOutboundActivity: (() => void) | undefined;
80
- private readonly updateStrategy: ConnectorUpdateStrategy;
81
- private lastEditPrimaryText: string | null = null;
82
-
83
- constructor(
84
- private readonly connector: ConnectorPlugin,
85
- private readonly inbound: InboundEnvelope,
86
- rootMessageId: string | null,
87
- private readonly logger: GatewayLogger,
88
- options: ForwarderOptions = {},
89
- ) {
90
- this.rootMessageId = rootMessageId;
91
- this.updateIntervalMs = options.updateIntervalMs ?? 400;
92
- this.toolMessageMode = options.toolMessageMode ?? "none";
93
- this.onOutboundActivity = options.onOutboundActivity;
94
- this.updateStrategy = this.connector.capabilities.updateStrategy;
95
- const capabilityMaxTextLength = this.connector.capabilities.maxTextLength;
96
- this.maxTextLength = typeof capabilityMaxTextLength === "number" && capabilityMaxTextLength > 0
97
- ? capabilityMaxTextLength
98
- : undefined;
99
- }
100
-
101
- primaryMessageId(): string | null {
102
- return this.rootMessageId;
103
- }
104
-
105
- handleEvent = (event: GatewayAgentEvent): void => {
106
- if (event.type === "message_delta") {
107
- this.responseText += event.delta;
108
- if (this.updateStrategy !== "final_only") {
109
- this.scheduleFlush();
110
- }
111
- return;
112
- }
113
-
114
- if (event.type === "message_complete") {
115
- if (event.text.trim().length > 0) {
116
- this.responseText = event.text;
117
- if (this.updateStrategy !== "final_only") {
118
- void this.flushNow();
119
- }
120
- }
121
- return;
122
- }
123
-
124
- if (event.type === "tool_start") {
125
- this.logger.info(
126
- {
127
- toolName: event.toolName,
128
- conversation: `${this.inbound.platform}:${this.inbound.accountId}:${this.inbound.chatId}:${this.inbound.threadId ?? "root"}`,
129
- },
130
- "Tool execution started",
131
- );
132
- if (this.updateStrategy !== "final_only" && this.toolMessageMode === "all") {
133
- this.enqueueSend(`_-> Running tool: ${event.toolName}_`);
134
- }
135
- return;
136
- }
137
-
138
- if (event.type === "tool_end") {
139
- const summary = event.output;
140
- this.logger.info(
141
- {
142
- toolName: event.toolName,
143
- isError: event.isError,
144
- conversation: `${this.inbound.platform}:${this.inbound.accountId}:${this.inbound.chatId}:${this.inbound.threadId ?? "root"}`,
145
- },
146
- event.isError ? "Tool execution finished with error" : "Tool execution finished",
147
- );
148
- if (
149
- this.updateStrategy !== "final_only" &&
150
- (this.toolMessageMode === "all" || (this.toolMessageMode === "errors" && event.isError))
151
- ) {
152
- const prefix = event.isError ? "ERR" : "OK";
153
- const header = `*${prefix} ${event.toolName}*\n\`\`\`\n`;
154
- const footer = "\n```";
155
- const availableSummaryLength = this.maxTextLength === undefined
156
- ? undefined
157
- : Math.max(0, this.maxTextLength - header.length - footer.length);
158
- this.enqueueSend(`${header}${truncate(summary, availableSummaryLength)}${footer}`);
159
- }
160
- return;
161
- }
162
-
163
- if (event.type === "status") {
164
- if (this.updateStrategy === "final_only") {
165
- return;
166
- }
167
- this.enqueueSend(`_${event.message}_`);
168
- }
169
- };
170
-
171
- async finalize(): Promise<void> {
172
- if (this.updateStrategy === "final_only") {
173
- await this.finalizeFinalOnly();
174
- await Promise.allSettled(this.pendingOps);
175
- return;
176
- }
177
-
178
- if (this.updateStrategy === "append") {
179
- await this.finalizeAppend();
180
- await Promise.allSettled(this.pendingOps);
181
- return;
182
- }
183
-
184
- await this.finalizeEdit();
185
- await Promise.allSettled(this.pendingOps);
186
- }
187
-
188
- private baseEnvelope(): { platform: Platform; accountId: string; chatId: string; threadId?: string } {
189
- return {
190
- platform: this.inbound.platform,
191
- accountId: this.inbound.accountId,
192
- chatId: this.inbound.chatId,
193
- ...(this.inbound.threadId ? { threadId: this.inbound.threadId } : {}),
194
- };
195
- }
196
-
197
- private scheduleFlush(): void {
198
- if (this.pendingFlush) return;
199
- this.pendingFlush = setTimeout(() => {
200
- void this.flushNow();
201
- }, this.updateIntervalMs);
202
- }
203
-
204
- private async flushNow(): Promise<void> {
205
- const run = this.flushSerial.then(async () => {
206
- if (this.pendingFlush) {
207
- clearTimeout(this.pendingFlush);
208
- this.pendingFlush = null;
209
- }
210
-
211
- if (this.responseText.trim().length === 0) {
212
- return;
213
- }
214
-
215
- try {
216
- if (this.updateStrategy === "append") {
217
- await this.flushAppendProgress();
218
- return;
219
- }
220
-
221
- if (this.updateStrategy === "edit") {
222
- const content = truncate(this.responseText, this.maxTextLength);
223
- await this.sendEditPrimary(content);
224
- }
225
- } catch (error) {
226
- this.logger.warn(
227
- {
228
- err: error,
229
- connectorId: this.inbound.connectorId,
230
- chatId: this.inbound.chatId,
231
- targetMessageId: this.rootMessageId,
232
- contentLength: this.responseText.length,
233
- updateStrategy: this.updateStrategy,
234
- },
235
- "Failed to flush streaming update",
236
- );
237
- }
238
- });
239
-
240
- this.flushSerial = run.catch(() => {
241
- // keep chain alive for future flush calls
242
- });
243
-
244
- await run;
245
- }
246
-
247
- private enqueueSend(text: string): void {
248
- const promise = this.connector
249
- .send({
250
- ...this.baseEnvelope(),
251
- mode: "create",
252
- ...(this.rootMessageId ? { replyToMessageId: this.rootMessageId } : {}),
253
- text,
254
- })
255
- .then(() => {
256
- this.noteOutboundActivity();
257
- })
258
- .catch((error) => {
259
- this.logger.warn(
260
- {
261
- err: error,
262
- connectorId: this.inbound.connectorId,
263
- chatId: this.inbound.chatId,
264
- replyToMessageId: this.rootMessageId,
265
- },
266
- "Failed to send connector update message",
267
- );
268
- });
269
-
270
- this.pendingOps.push(promise);
271
- }
272
-
273
- private async sendEditPrimary(text: string): Promise<void> {
274
- if (this.rootMessageId && this.lastEditPrimaryText === text) {
275
- return;
276
- }
277
-
278
- if (this.rootMessageId) {
279
- await this.connector.send({
280
- ...this.baseEnvelope(),
281
- mode: "update",
282
- targetMessageId: this.rootMessageId,
283
- text,
284
- });
285
- this.lastEditPrimaryText = text;
286
- this.noteOutboundActivity();
287
- return;
288
- }
289
-
290
- const created = await this.connector.send({
291
- ...this.baseEnvelope(),
292
- mode: "create",
293
- text,
294
- });
295
- this.rootMessageId = created.messageId ?? this.rootMessageId;
296
- this.lastEditPrimaryText = text;
297
- this.noteOutboundActivity();
298
- }
299
-
300
- private async sendCreate(text: string): Promise<void> {
301
- const created = await this.connector.send({
302
- ...this.baseEnvelope(),
303
- mode: "create",
304
- ...(this.rootMessageId ? { replyToMessageId: this.rootMessageId } : {}),
305
- text,
306
- });
307
- this.rootMessageId = this.rootMessageId ?? created.messageId ?? null;
308
- this.noteOutboundActivity();
309
- }
310
-
311
- private async flushAppendProgress(): Promise<void> {
312
- if (this.responseText.startsWith(this.appendEmittedText)) {
313
- const unsent = this.responseText.slice(this.appendEmittedText.length);
314
- if (unsent.length === 0) {
315
- return;
316
- }
317
- const chunks = splitForMaxLength(unsent, this.maxTextLength, { preserveWhitespace: true });
318
- for (const chunk of chunks) {
319
- await this.sendCreate(chunk);
320
- }
321
- this.appendEmittedText = this.responseText;
322
- return;
323
- }
324
-
325
- const snapshotChunks = splitForMaxLength(this.responseText, this.maxTextLength);
326
- for (const chunk of snapshotChunks) {
327
- await this.sendCreate(chunk);
328
- }
329
- this.appendEmittedText = this.responseText;
330
- }
331
-
332
- private async finalizeEdit(): Promise<void> {
333
- await this.flushNow();
334
-
335
- if (this.responseText.trim().length === 0) {
336
- await this.sendEditPrimary("(completed with no text response)");
337
- return;
338
- }
339
-
340
- const chunks = splitForMaxLength(this.responseText, this.maxTextLength);
341
- try {
342
- await this.sendEditPrimary(chunks[0] ?? "");
343
-
344
- for (const chunk of chunks.slice(1)) {
345
- await this.sendCreate(chunk);
346
- }
347
- } catch (error) {
348
- this.logger.warn(
349
- {
350
- err: error,
351
- connectorId: this.inbound.connectorId,
352
- chatId: this.inbound.chatId,
353
- targetMessageId: this.rootMessageId,
354
- },
355
- "Failed to send split final response",
356
- );
357
- }
358
- }
359
-
360
- private async finalizeFinalOnly(): Promise<void> {
361
- if (this.pendingFlush) {
362
- clearTimeout(this.pendingFlush);
363
- this.pendingFlush = null;
364
- }
365
-
366
- try {
367
- if (this.responseText.trim().length === 0) {
368
- await this.sendCreate("(completed with no text response)");
369
- return;
370
- }
371
-
372
- const chunks = splitForMaxLength(this.responseText, this.maxTextLength);
373
- for (const chunk of chunks) {
374
- await this.sendCreate(chunk);
375
- }
376
- } catch (error) {
377
- this.logger.warn(
378
- {
379
- err: error,
380
- connectorId: this.inbound.connectorId,
381
- chatId: this.inbound.chatId,
382
- updateStrategy: this.updateStrategy,
383
- },
384
- "Failed to send final-only response",
385
- );
386
- }
387
- }
388
-
389
- private async finalizeAppend(): Promise<void> {
390
- await this.flushNow();
391
-
392
- if (this.responseText.trim().length > 0 || this.appendEmittedText.trim().length > 0) {
393
- return;
394
- }
395
-
396
- try {
397
- await this.sendCreate("(completed with no text response)");
398
- } catch (error) {
399
- this.logger.warn(
400
- {
401
- err: error,
402
- connectorId: this.inbound.connectorId,
403
- chatId: this.inbound.chatId,
404
- updateStrategy: this.updateStrategy,
405
- },
406
- "Failed to send append fallback response",
407
- );
408
- }
409
- }
410
-
411
- private noteOutboundActivity(): void {
412
- this.onOutboundActivity?.();
413
- }
414
- }