discoclaw 1.2.4 → 1.3.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 (42) hide show
  1. package/.context/voice.md +30 -2
  2. package/.env.example +6 -0
  3. package/dist/cli/dashboard.js +7 -1
  4. package/dist/config.js +7 -0
  5. package/dist/cron/executor.js +72 -1
  6. package/dist/dashboard/api/metrics.js +7 -0
  7. package/dist/dashboard/api/metrics.test.js +16 -0
  8. package/dist/dashboard/api/traces.js +14 -0
  9. package/dist/dashboard/api/traces.test.js +40 -0
  10. package/dist/dashboard/page.js +187 -8
  11. package/dist/dashboard/server.js +81 -14
  12. package/dist/dashboard/server.test.js +120 -4
  13. package/dist/discord/deferred-runner.js +306 -219
  14. package/dist/discord/message-coordinator.js +1 -28
  15. package/dist/discord/reaction-handler.js +81 -3
  16. package/dist/index.js +15 -1
  17. package/dist/observability/trace-store.js +56 -0
  18. package/dist/observability/trace-utils.js +31 -0
  19. package/dist/runtime/codex-cli.js +3 -2
  20. package/dist/runtime/codex-cli.test.js +33 -0
  21. package/dist/runtime/model-tiers.js +1 -1
  22. package/dist/runtime/model-tiers.test.js +9 -0
  23. package/dist/runtime/openai-tool-schemas.js +17 -0
  24. package/dist/voice/audio-pipeline.js +246 -6
  25. package/dist/voice/audio-pipeline.test.js +481 -0
  26. package/dist/voice/audio-receiver.js +8 -0
  27. package/dist/voice/audio-receiver.test.js +16 -0
  28. package/dist/voice/conversation-buffer.js +16 -6
  29. package/dist/voice/providers/gemini-live-provider.js +481 -0
  30. package/dist/voice/providers/gemini-live-provider.test.js +834 -0
  31. package/dist/voice/providers/gemini-live-responder.js +267 -0
  32. package/dist/voice/providers/gemini-live-responder.test.js +615 -0
  33. package/dist/voice/providers/gemini-live-token-estimator.js +100 -0
  34. package/dist/voice/providers/gemini-live-token-estimator.test.js +160 -0
  35. package/dist/voice/providers/gemini-live-types.js +32 -0
  36. package/dist/voice/providers/gemini-tool-mapper.js +91 -0
  37. package/dist/voice/providers/gemini-tool-mapper.test.js +253 -0
  38. package/dist/voice/providers/index.js +3 -0
  39. package/dist/voice/types.test.js +6 -0
  40. package/dist/voice/voice-prompt-builder.js +26 -17
  41. package/dist/voice/voice-prompt-builder.test.js +16 -1
  42. package/package.json +1 -1
@@ -125,6 +125,21 @@ function estimateSection(chars) {
125
125
  included: safeChars > 0,
126
126
  };
127
127
  }
128
+ function buildVoiceContextSections(parts) {
129
+ const sections = [];
130
+ sections.push(buildPromptPreamble(parts.identity, { skipTrackedTools: true }));
131
+ if (parts.actionsSection) {
132
+ sections.push(parts.actionsSection);
133
+ }
134
+ if (parts.voiceSystemPrompt) {
135
+ sections.push(parts.voiceSystemPrompt);
136
+ }
137
+ sections.push(VOICE_STYLE_INSTRUCTION);
138
+ if (parts.durableMemory) {
139
+ sections.push(`---\nDurable memory (user-specific notes):\n${parts.durableMemory}`);
140
+ }
141
+ return sections;
142
+ }
128
143
  export function buildVoicePromptSectionEstimates(parts) {
129
144
  const charsBySection = {
130
145
  rootPolicy: ROOT_POLICY_CHARS,
@@ -162,28 +177,22 @@ export function buildVoicePromptSectionEstimates(parts) {
162
177
  * 8. User text
163
178
  */
164
179
  export function buildVoicePrompt(parts) {
165
- const sections = [];
166
- // 1. Root policy + identity.
167
- sections.push(buildPromptPreamble(parts.identity, { skipTrackedTools: true }));
168
- // 2. Actions section.
169
- if (parts.actionsSection) {
170
- sections.push(parts.actionsSection);
171
- }
172
- // 3. Voice system prompt (user-configurable).
173
- if (parts.voiceSystemPrompt) {
174
- sections.push(parts.voiceSystemPrompt);
175
- }
176
- // 4. Voice style instruction.
177
- sections.push(VOICE_STYLE_INSTRUCTION);
178
- // 5. Durable memory.
179
- if (parts.durableMemory) {
180
- sections.push(`---\nDurable memory (user-specific notes):\n${parts.durableMemory}`);
181
- }
180
+ const sections = buildVoiceContextSections(parts);
182
181
  // 6. Separator + user text.
183
182
  sections.push(VOICE_INTERNAL_CONTEXT_SEPARATOR);
184
183
  sections.push(parts.userText);
185
184
  return sections.join('\n\n');
186
185
  }
186
+ /**
187
+ * Build the static session instruction used by live voice providers.
188
+ *
189
+ * This contains the same persistent voice context as the classic per-turn
190
+ * prompt, excluding the current user utterance and the internal-context
191
+ * separator that only makes sense for single-shot prompt assembly.
192
+ */
193
+ export function buildVoiceSystemInstruction(parts) {
194
+ return buildVoiceContextSections(parts).join('\n\n');
195
+ }
187
196
  /**
188
197
  * Build a follow-up prompt for voice action result processing.
189
198
  *
@@ -1,7 +1,7 @@
1
1
  import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
2
  import fs from 'node:fs/promises';
3
3
  import path from 'node:path';
4
- import { extractSections, extractSoulEssentials, extractUserEssentials, loadVoiceIdentity, buildVoicePrompt, buildVoiceFollowUpPrompt, buildVoicePromptSectionEstimates, VOICE_INTERNAL_CONTEXT_SEPARATOR, VOICE_IDENTITY_MAX_CHARS, } from './voice-prompt-builder.js';
4
+ import { extractSections, extractSoulEssentials, extractUserEssentials, loadVoiceIdentity, buildVoicePrompt, buildVoiceSystemInstruction, buildVoiceFollowUpPrompt, buildVoicePromptSectionEstimates, VOICE_INTERNAL_CONTEXT_SEPARATOR, VOICE_IDENTITY_MAX_CHARS, } from './voice-prompt-builder.js';
5
5
  import { VOICE_STYLE_INSTRUCTION } from './voice-style-prompt.js';
6
6
  import { ROOT_POLICY, TRACKED_DEFAULTS_PREAMBLE, TRACKED_TOOLS_PREAMBLE, buildPromptPreamble, } from '../discord/prompt-common.js';
7
7
  // ---------------------------------------------------------------------------
@@ -180,6 +180,21 @@ describe('loadVoiceIdentity', () => {
180
180
  expect(identityIdx).toBeLessThan(userIdx);
181
181
  });
182
182
  });
183
+ describe('buildVoiceSystemInstruction', () => {
184
+ it('builds the static voice context without the user-turn separator', () => {
185
+ const result = buildVoiceSystemInstruction({
186
+ identity: 'identity block',
187
+ durableMemory: '',
188
+ actionsSection: '',
189
+ voiceSystemPrompt: 'custom voice system prompt',
190
+ });
191
+ expect(result).toContain(buildPromptPreamble('identity block', { skipTrackedTools: true }));
192
+ expect(result).toContain('custom voice system prompt');
193
+ expect(result).toContain(VOICE_STYLE_INSTRUCTION);
194
+ expect(result).not.toContain(VOICE_INTERNAL_CONTEXT_SEPARATOR);
195
+ expect(result).not.toContain('Current user message:');
196
+ });
197
+ });
183
198
  // ---------------------------------------------------------------------------
184
199
  // buildVoicePrompt
185
200
  // ---------------------------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "discoclaw",
3
- "version": "1.2.4",
3
+ "version": "1.3.0",
4
4
  "description": "Personal AI orchestrator that turns Discord into a persistent workspace",
5
5
  "license": "MIT",
6
6
  "keywords": [