discoclaw 1.2.4 → 2.0.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 (87) hide show
  1. package/.context/voice.md +30 -2
  2. package/.env.example +7 -3
  3. package/.env.example.full +13 -32
  4. package/README.md +1 -1
  5. package/dist/cli/dashboard.js +7 -1
  6. package/dist/cli/dashboard.test.js +0 -4
  7. package/dist/cli/init-wizard.js +4 -8
  8. package/dist/cli/init-wizard.test.js +4 -10
  9. package/dist/config.js +5 -38
  10. package/dist/config.test.js +8 -72
  11. package/dist/cron/executor.js +72 -1
  12. package/dist/dashboard/api/metrics.js +7 -0
  13. package/dist/dashboard/api/metrics.test.js +16 -0
  14. package/dist/dashboard/api/traces.js +14 -0
  15. package/dist/dashboard/api/traces.test.js +40 -0
  16. package/dist/dashboard/page.js +187 -8
  17. package/dist/dashboard/server.js +82 -19
  18. package/dist/dashboard/server.test.js +123 -10
  19. package/dist/discord/actions.js +112 -6
  20. package/dist/discord/actions.test.js +117 -1
  21. package/dist/discord/deferred-runner.js +306 -219
  22. package/dist/discord/help-command.js +1 -1
  23. package/dist/discord/message-coordinator.js +4 -36
  24. package/dist/discord/models-command.js +1 -1
  25. package/dist/discord/reaction-handler.js +83 -5
  26. package/dist/discord/reaction-handler.test.js +55 -0
  27. package/dist/discord/verify-push.js +31 -36
  28. package/dist/discord/verify-push.test.js +34 -6
  29. package/dist/discord/voice-command.js +1 -31
  30. package/dist/discord/voice-command.test.js +21 -259
  31. package/dist/discord/voice-status-command.js +3 -22
  32. package/dist/discord/voice-status-command.test.js +16 -124
  33. package/dist/discord-followup.test.js +133 -0
  34. package/dist/health/config-doctor.js +5 -27
  35. package/dist/health/config-doctor.test.js +1 -4
  36. package/dist/index.js +15 -28
  37. package/dist/observability/trace-store.js +56 -0
  38. package/dist/observability/trace-utils.js +31 -0
  39. package/dist/runtime/codex-cli.js +3 -2
  40. package/dist/runtime/codex-cli.test.js +33 -0
  41. package/dist/runtime/model-tiers.js +1 -1
  42. package/dist/runtime/model-tiers.test.js +9 -0
  43. package/dist/runtime/openai-tool-schemas.js +17 -0
  44. package/dist/runtime-overrides.js +2 -3
  45. package/dist/runtime-overrides.test.js +27 -193
  46. package/dist/tasks/store.js +10 -6
  47. package/dist/tasks/store.test.js +44 -0
  48. package/dist/tasks/task-action-executor.test.js +162 -50
  49. package/dist/tasks/task-action-mutations.js +22 -2
  50. package/dist/tasks/task-action-read-ops.js +7 -1
  51. package/dist/tasks/task-action-runner-types.js +19 -1
  52. package/dist/voice/audio-pipeline.js +183 -96
  53. package/dist/voice/audio-receiver.js +8 -0
  54. package/dist/voice/audio-receiver.test.js +16 -0
  55. package/dist/voice/conversation-buffer.js +16 -6
  56. package/dist/voice/providers/gemini-live-provider.js +481 -0
  57. package/dist/voice/providers/gemini-live-provider.test.js +834 -0
  58. package/dist/voice/providers/gemini-live-responder.js +267 -0
  59. package/dist/voice/providers/gemini-live-responder.test.js +615 -0
  60. package/dist/voice/providers/gemini-live-token-estimator.js +100 -0
  61. package/dist/voice/providers/gemini-live-token-estimator.test.js +160 -0
  62. package/dist/voice/providers/gemini-live-types.js +32 -0
  63. package/dist/voice/providers/gemini-tool-mapper.js +91 -0
  64. package/dist/voice/providers/gemini-tool-mapper.test.js +253 -0
  65. package/dist/voice/providers/index.js +3 -0
  66. package/dist/voice/voice-prompt-builder.js +26 -17
  67. package/dist/voice/voice-prompt-builder.test.js +16 -1
  68. package/docs/configuration.md +4 -9
  69. package/docs/official-docs.md +6 -9
  70. package/docs/runtime-switching.md +1 -1
  71. package/package.json +1 -1
  72. package/dist/voice/audio-pipeline.test.js +0 -619
  73. package/dist/voice/stt-deepgram.js +0 -154
  74. package/dist/voice/stt-deepgram.test.js +0 -275
  75. package/dist/voice/stt-factory.js +0 -42
  76. package/dist/voice/stt-factory.test.js +0 -45
  77. package/dist/voice/stt-openai.js +0 -156
  78. package/dist/voice/stt-openai.test.js +0 -281
  79. package/dist/voice/tts-cartesia.js +0 -169
  80. package/dist/voice/tts-cartesia.test.js +0 -228
  81. package/dist/voice/tts-deepgram.js +0 -84
  82. package/dist/voice/tts-deepgram.test.js +0 -220
  83. package/dist/voice/tts-factory.js +0 -52
  84. package/dist/voice/tts-factory.test.js +0 -53
  85. package/dist/voice/tts-openai.js +0 -70
  86. package/dist/voice/tts-openai.test.js +0 -138
  87. package/dist/voice/types.test.js +0 -84
@@ -1,7 +1,7 @@
1
1
  import { beforeEach, describe, expect, it, vi } from 'vitest';
2
2
  import { ChannelType, PermissionFlagsBits } from 'discord.js';
3
3
  import { buildUnavailableActionTypesNotice } from './output-common.js';
4
- import { parseDiscordActions, executeDiscordActions, discordActionsPromptSection, buildTieredDiscordActionsPromptSection, buildDisplayResultLines, buildAllResultLines, withoutRequesterGatedActionFlags, } from './actions.js';
4
+ import { parseDiscordActions, executeDiscordActions, discordActionsPromptSection, buildTieredDiscordActionsPromptSection, buildDisplayResultLines, buildAllResultLines, buildCappedResultLines, withoutRequesterGatedActionFlags, } from './actions.js';
5
5
  import { TaskStore } from '../tasks/store.js';
6
6
  import { _resetDestructiveConfirmationForTest } from './destructive-confirmation.js';
7
7
  import { shouldTriggerFollowUp } from './action-categories.js';
@@ -892,6 +892,122 @@ describe('buildAllResultLines', () => {
892
892
  ]);
893
893
  });
894
894
  });
895
+ describe('buildCappedResultLines', () => {
896
+ it('microcompacts multiline readMessages-style payloads to representative id lines', () => {
897
+ const results = [{
898
+ ok: true,
899
+ summary: [
900
+ 'Messages in #ops:',
901
+ '[alice] alpha update (id:1001)',
902
+ '[bob] beta update (id:1002)',
903
+ '[carol] gamma update (id:1003)',
904
+ '[dave] delta update (id:1004)',
905
+ '[erin] epsilon update (id:1005)',
906
+ '[frank] zeta update (id:1006)',
907
+ '[grace] eta update (id:1007)',
908
+ '[heidi] theta update (id:1008)',
909
+ '[ivan] iota update (id:1009)',
910
+ ].join('\n'),
911
+ }];
912
+ const [line] = buildCappedResultLines(results);
913
+ expect(line).toContain('Done: Messages in #ops:');
914
+ expect(line).toContain('(id:1001)');
915
+ expect(line).toContain('(id:1002)');
916
+ expect(line).toContain('(id:1008)');
917
+ expect(line).toContain('(id:1009)');
918
+ expect(line).toContain('...[omitted 5 lines]');
919
+ expect(line).not.toContain('(id:1005)');
920
+ });
921
+ it('keeps task thread jump URLs in multiline closeout blocks for follow-up reuse', () => {
922
+ const results = [{
923
+ ok: true,
924
+ summary: [
925
+ 'Task ws-204 closed',
926
+ 'Status: done',
927
+ 'Thread: https://discord.com/channels/111111111111111111/222222222222222222/333333333333333333',
928
+ 'Project: discoclaw',
929
+ 'Assignee: ClawBot',
930
+ 'Updated by: @weston',
931
+ 'Note: Closeout posted in the linked Discord thread.',
932
+ 'Artifacts: /tmp/task-closeout.md',
933
+ 'Next action: use taskShow ws-204 if you need the stored linkage later.',
934
+ 'Watcher: ops',
935
+ ].join('\n'),
936
+ }];
937
+ const [line] = buildCappedResultLines(results);
938
+ expect(line).toContain('Done: Task ws-204 closed');
939
+ expect(line).toContain('Status: done');
940
+ expect(line).toContain('Thread: https://discord.com/channels/111111111111111111/222222222222222222/333333333333333333');
941
+ expect(line).toContain('Artifacts: /tmp/task-closeout.md');
942
+ expect(line).toContain('Next action: use taskShow ws-204 if you need the stored linkage later.');
943
+ expect(line).toContain('...[omitted 4 lines]');
944
+ expect(line).not.toContain('Assignee: ClawBot');
945
+ });
946
+ it('keeps section headings and first values in memoryShow-style blocks', () => {
947
+ const results = [{
948
+ ok: true,
949
+ summary: [
950
+ '**Durable memory:**',
951
+ '- prefers black coffee',
952
+ '- uses fish shell',
953
+ '**By entity:**',
954
+ '- [project] discoclaw migration',
955
+ '**Rolling summary:**',
956
+ 'Working on prompt compaction.',
957
+ '**Short-term memory:**',
958
+ 'Need task-42 and /tmp/report.md for the next reply.',
959
+ 'Extra noisy note A that should be dropped.',
960
+ 'Extra noisy note B that should be dropped.',
961
+ 'Extra noisy note that should be dropped.',
962
+ ].join('\n'),
963
+ }];
964
+ const [line] = buildCappedResultLines(results);
965
+ expect(line).toContain('Done: **Durable memory:**');
966
+ expect(line).toContain('- prefers black coffee');
967
+ expect(line).toContain('**By entity:**');
968
+ expect(line).toContain('- [project] discoclaw migration');
969
+ expect(line).toContain('**Rolling summary:**');
970
+ expect(line).toContain('Working on prompt compaction.');
971
+ expect(line).toContain('**Short-term memory:**');
972
+ expect(line).toContain('Need task-42 and /tmp/report.md for the next reply.');
973
+ expect(line).toContain('...[omitted ');
974
+ expect(line).not.toContain('Extra noisy note B that should be dropped.');
975
+ });
976
+ it('keeps failed prefixes, paths, and actionable error lines in multiline failures', () => {
977
+ const results = [{
978
+ ok: false,
979
+ error: [
980
+ 'Forge sync failed while opening workspace state',
981
+ 'Workspace: /home/davidmarsh/code/discoclaw/workspace/state.json',
982
+ 'Last error: ENOENT: no such file or directory',
983
+ 'Stack: at openWorkspaceState (src/discord/actions.ts:400:12)',
984
+ 'Stack: at runForgeSync (src/discord/actions.ts:512:8)',
985
+ 'Stack: at async executeForgeAction (src/discord/actions.ts:618:3)',
986
+ 'Retry hint: recreate the state file, then rerun forge sync',
987
+ 'Node: v24.0.0',
988
+ 'cwd: /home/davidmarsh/code/discoclaw',
989
+ ].join('\n'),
990
+ }];
991
+ const [line] = buildCappedResultLines(results);
992
+ expect(line).toContain('Failed: Forge sync failed while opening workspace state');
993
+ expect(line).toContain('Workspace: /home/davidmarsh/code/discoclaw/workspace/state.json');
994
+ expect(line).toContain('Last error: ENOENT: no such file or directory');
995
+ expect(line).toContain('Retry hint: recreate the state file, then rerun forge sync');
996
+ expect(line).toContain('cwd: /home/davidmarsh/code/discoclaw');
997
+ expect(line).toContain('...[omitted ');
998
+ expect(line).not.toContain('Stack: at runForgeSync');
999
+ });
1000
+ it('falls back to the final hard cap when retained text is still too long', () => {
1001
+ const results = [{
1002
+ ok: true,
1003
+ summary: `Downloaded attachment to /tmp/${'x'.repeat(120)}`,
1004
+ }];
1005
+ const [line] = buildCappedResultLines(results, 60);
1006
+ expect(line.length).toBeLessThanOrEqual(60);
1007
+ expect(line).toContain('Done: Downloaded attachment');
1008
+ expect(line.endsWith('...[truncated]')).toBe(true);
1009
+ });
1010
+ });
895
1011
  describe('discordActionsPromptSection', () => {
896
1012
  it('always includes the standard guidance when actions are enabled', () => {
897
1013
  const flags = {