@j-o-r/hello-dave 0.0.3 → 0.0.4

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 (119) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +216 -56
  3. package/README.md.bak +481 -0
  4. package/README.md.bak.1774780058 +338 -0
  5. package/README.md.bak2 +455 -0
  6. package/docs.bak.1774780058/agent-manager.md +167 -0
  7. package/docs.bak.1774780058/agent-manager.md.bak +137 -0
  8. package/docs.bak.1774780058/agent-manager.md.bak2 +157 -0
  9. package/docs.bak.1774780058/codeserver-pattern.md +191 -0
  10. package/docs.bak.1774780058/path-resolution-best-practices.md +104 -0
  11. package/docs.bak.1774780058/project-overview.md +67 -0
  12. package/docs.bak.1774780058/project-overview.md.bak +67 -0
  13. package/docs.bak.1774780058/prompt-class.md +141 -0
  14. package/docs.bak.1774780058/prompt-class.md.bak +142 -0
  15. package/docs.bak.1774780058/tools-syntax-validation.md +121 -0
  16. package/docs.bak.1774780058/tools-syntax-validation.md.bak2 +125 -0
  17. package/docs.bak.1774780058/tools-syntax-validation.md.bak3 +125 -0
  18. package/docs.bak.1774780058/tools-syntax-validation.md.bak4 +106 -0
  19. package/docs.bak.1774780058/tools-syntax-validation.md.bak_path +106 -0
  20. package/docs.bak.1774780058/toolset.md +164 -0
  21. package/docs.bak.1774780058/toolset.md.bak +94 -0
  22. package/docs.bak.1774780058/toolset.md.bak3 +161 -0
  23. package/docs.bak.1774780058/toolset.md.bak4 +161 -0
  24. package/docs.bak.1774780058/toolset.md.bak5 +161 -0
  25. package/docs.bak.1774780058/toolset.md.bak6 +163 -0
  26. package/docs.bak.1774780058/toolset.md.bak_path +163 -0
  27. package/docs.bak.1774780058/toolset.md.bak_syntax +161 -0
  28. package/docs.bak.1774780058/xai-responses.md +111 -0
  29. package/docs.bak.1774780058/xai-responses.md.bak +107 -0
  30. package/docs.bak.1774780058/xai-responses.md.bak2 +107 -0
  31. package/docs.bak.1774780058/xai_collections.md +106 -0
  32. package/examples/ask_agent.js +137 -0
  33. package/examples/code_agent.js +149 -0
  34. package/examples/coderev_agent.js +136 -0
  35. package/examples/codeserver.sh +47 -0
  36. package/examples/daisy_agent.js +170 -0
  37. package/examples/docs_agent.js +148 -0
  38. package/examples/gpt_agent.js +125 -0
  39. package/examples/grok_agent.js +132 -0
  40. package/examples/grok_agent.js.bak +98 -0
  41. package/examples/grok_agent.js.bak.2 +99 -0
  42. package/examples/grok_agent.js.bak.3 +1 -0
  43. package/examples/grok_agent.js.bak.4 +124 -0
  44. package/examples/grok_agent.js.bak.5 +1 -0
  45. package/examples/grok_agent.js.bak.6 +1 -0
  46. package/examples/memory_agent.js +152 -0
  47. package/examples/npm_agent.js +202 -0
  48. package/examples/npm_agent.js.bak.3 +2 -0
  49. package/examples/npm_agent.js.bak.4 +205 -0
  50. package/examples/npm_agent.js.bak.5 +1 -0
  51. package/examples/npm_agent.js.bak.6 +1 -0
  52. package/examples/prompt_agent.js +133 -0
  53. package/examples/readme_agent.js +148 -0
  54. package/examples/spawn_agent.js +293 -0
  55. package/examples/test_agent.js +187 -0
  56. package/examples/todo_agent.js +175 -0
  57. package/{examples/codeDave.js → examples.bak.1774780058/code_agent.js} +40 -6
  58. package/{examples/CodeServer → examples.bak.1774780058/codeserver.sh} +9 -5
  59. package/examples.bak.1774780058/memory_agent.js +112 -0
  60. package/{examples/spawndave.js → examples.bak.1774780058/spawn_agent.js} +29 -6
  61. package/examples.bak.1774780058/test_agent.js +162 -0
  62. package/{examples/todoDave.js → examples.bak.1774780058/todo_agent.js} +8 -2
  63. package/lib/API/x.ai/responses.js +7 -9
  64. package/lib/AgentManager.js +3 -2
  65. package/lib/AgentServer.js +23 -19
  66. package/lib/genericToolset.js +159 -26
  67. package/lib/genericToolset.js.bak_syntax +402 -0
  68. package/lib/wsCli.js +0 -1
  69. package/package.json +4 -5
  70. package/scenarios.bak.1774780058/data/eval_node_message.json +9 -0
  71. package/scenarios.bak.1774780058/data/hist_oa.json +66 -0
  72. package/scenarios.bak.1774780058/data/o3_response1.json +96 -0
  73. package/scenarios.bak.1774780058/data/oa_reasoning_parse.json +112 -0
  74. package/scenarios.bak.1774780058/data/tool_oa.json +96 -0
  75. package/scenarios.bak.1774780058/data/tool_xai.json +59 -0
  76. package/scenarios.bak.1774780058/data/tool_xai2.json +40 -0
  77. package/scenarios.bak.1774780058/data/xai-response-1.json +59 -0
  78. package/scenarios.bak.1774780058/data/xai-response-2.json +10 -0
  79. package/scenarios.bak.1774780058/data/xai_reasoning_tools_resp.json +59 -0
  80. package/scenarios.bak.1774780058/data/xai_search_response.json +58 -0
  81. package/scenarios.bak.1774780058/environment.js +10 -0
  82. package/scenarios.bak.1774780058/example.js +17 -0
  83. package/scenarios.bak.1774780058/genericToolset.test.js +182 -0
  84. package/scenarios.bak.1774780058/grok.js +113 -0
  85. package/scenarios.bak.1774780058/memory-tools.js +51 -0
  86. package/scenarios.bak.1774780058/openai-o3.js +137 -0
  87. package/scenarios.bak.1774780058/openai-prompt.js +155 -0
  88. package/scenarios.bak.1774780058/openai-session.js +148 -0
  89. package/scenarios.bak.1774780058/openai.js +102 -0
  90. package/scenarios.bak.1774780058/prompt.js +118 -0
  91. package/scenarios.bak.1774780058/promptFishbowl.js +76 -0
  92. package/scenarios.bak.1774780058/search.brave.com.js +25 -0
  93. package/scenarios.bak.1774780058/sh.js +15 -0
  94. package/scenarios.bak.1774780058/test-wsio.js +26 -0
  95. package/scenarios.bak.1774780058/testToolset.js +42 -0
  96. package/scenarios.bak.1774780058/toolset.js +16 -0
  97. package/scenarios.bak.1774780058/toolset.test.js +141 -0
  98. package/scenarios.bak.1774780058/write_file_syntax.test.js +145 -0
  99. package/scenarios.bak.1774780058/write_file_validation/README.md +30 -0
  100. package/scenarios.bak.1774780058/write_file_validation/bad.js +3 -0
  101. package/scenarios.bak.1774780058/write_file_validation/good.js +4 -0
  102. package/scenarios.bak.1774780058/write_file_validation/test.sh +43 -0
  103. package/scenarios.bak.1774780058/wsClient.js +69 -0
  104. package/scenarios.bak.1774780058/xai_responses.integration.test.js +57 -0
  105. package/scenarios.bak.1774780058/xai_responses.test.js +154 -0
  106. package/scenarios.bak.1774780058/xaicoll.js +50 -0
  107. package/scenarios.bak.1774780058/xaifiles.js +48 -0
  108. package/types/AgentManager.d.ts +4 -3
  109. package/utils/syntax_check.sh +61 -0
  110. package/utils/test.sh +46 -0
  111. /package/{examples/askDave.js → examples.bak.1774780058/ask_agent.js} +0 -0
  112. /package/{examples/coderev.js → examples.bak.1774780058/coderev_agent.js} +0 -0
  113. /package/{examples/daisy.js → examples.bak.1774780058/daisy_agent.js} +0 -0
  114. /package/{examples/docsDave.js → examples.bak.1774780058/docs_agent.js} +0 -0
  115. /package/{examples/gpt.js → examples.bak.1774780058/gpt_agent.js} +0 -0
  116. /package/{examples/grok.js → examples.bak.1774780058/grok_agent.js} +0 -0
  117. /package/{examples/npmDave.js → examples.bak.1774780058/npm_agent.js} +0 -0
  118. /package/{examples/promptDave.js → examples.bak.1774780058/prompt_agent.js} +0 -0
  119. /package/{examples/readmeDave.js → examples.bak.1774780058/readme_agent.js} +0 -0
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env node
2
+ import { Test, assert, jsType } from '@j-o-r/sh';
3
+ import ToolSet from '../lib/ToolSet.js';
4
+
5
+ const t = new Test();
6
+
7
+ // Constructor
8
+ t.add('Constructor default auto', () => {
9
+ const ts = new ToolSet();
10
+ assert.strictEqual(ts.toolChoice, 'auto');
11
+ assert.strictEqual(ts.length, 0);
12
+ assert.strictEqual(jsType(ts), 'ToolSet');
13
+ });
14
+
15
+ t.add('Constructor valid choices', () => {
16
+ ['auto', 'none', 'required'].forEach(choice => {
17
+ const ts = new ToolSet(choice);
18
+ assert.strictEqual(ts.toolChoice, choice);
19
+ });
20
+ });
21
+
22
+ t.add('Constructor invalid choice throws', () => {
23
+ try {
24
+ new ToolSet('invalid');
25
+ assert.fail('Should throw on invalid choice');
26
+ } catch (e) {
27
+ assert.ok(e.message.includes('Tool choice not defined'));
28
+ }
29
+ });
30
+
31
+ // add method
32
+ t.add('add invalid name throws', () => {
33
+ const ts = new ToolSet();
34
+ try {
35
+ ts.add('a', 'desc', { type: 'object' }, async () => {});
36
+ assert.fail('Should throw invalid name');
37
+ } catch (e) {
38
+ assert.ok(e.message.includes('Invalid name'));
39
+ }
40
+ });
41
+
42
+ t.add('add valid tool', () => {
43
+ const ts = new ToolSet();
44
+ const mockMethod = async (params) => ({ received: params });
45
+ ts.add('valid_tool', 'Valid tool description', {
46
+ type: 'object',
47
+ properties: { foo: { type: 'string' } },
48
+ required: ['foo']
49
+ }, mockMethod);
50
+ assert.strictEqual(ts.length, 1);
51
+ assert.strictEqual(ts.has('valid_tool'), true);
52
+ });
53
+
54
+ t.add('add duplicate no throw, overwrites', () => {
55
+ const ts = new ToolSet();
56
+ const method1 = async () => 'first';
57
+ const method2 = async () => 'second';
58
+ ts.add('dup', 'desc', { type: 'object' }, method1);
59
+ ts.add('dup', 'desc', { type: 'object' }, method2);
60
+ assert.strictEqual(ts.length, 1);
61
+ const tool = ts.get('dup');
62
+ assert.strictEqual(tool.description, 'desc'); // last one
63
+ });
64
+
65
+ t.add('get throws not found', () => {
66
+ const ts = new ToolSet();
67
+ try {
68
+ ts.get('missing');
69
+ assert.fail('Should throw');
70
+ } catch (e) {
71
+ assert.ok(e.message.includes('Function not found'));
72
+ }
73
+ });
74
+
75
+ t.add('get existing tool', () => {
76
+ const ts = new ToolSet();
77
+ ts.add('gettool', 'desc', { type: 'object' }, async () => {});
78
+ const tool = ts.get('gettool');
79
+ assert.strictEqual(jsType(tool), 'Object');
80
+ assert.strictEqual(tool.description, 'desc');
81
+ assert.strictEqual(jsType(tool.parameters), 'Object');
82
+ assert.strictEqual(jsType(tool.method), 'AsyncFunction');
83
+ });
84
+
85
+ // delete
86
+ t.add('delete reduces length', () => {
87
+ const ts = new ToolSet();
88
+ ts.add('to_delete', 'desc', { type: 'object' }, async () => {});
89
+ assert.strictEqual(ts.length, 1);
90
+ ts.delete('to_delete');
91
+ assert.strictEqual(ts.length, 0);
92
+ assert.strictEqual(ts.has('to_delete'), false);
93
+ });
94
+
95
+ t.add('delete non-existing no error', () => {
96
+ const ts = new ToolSet();
97
+ ts.delete('nonexistent');
98
+ assert.strictEqual(ts.length, 0);
99
+ });
100
+
101
+ // list
102
+ t.add('list empty', () => {
103
+ const ts = new ToolSet();
104
+ const list = ts.list();
105
+ assert.strictEqual(list.length, 0);
106
+ assert.strictEqual(jsType(list), 'Array');
107
+ });
108
+
109
+ t.add('list sorted by name', () => {
110
+ const ts = new ToolSet();
111
+ ts.add('b_tool', 'b desc', { type: 'object' }, async () => {});
112
+ ts.add('a_tool', 'a desc', { type: 'object' }, async () => {});
113
+ ts.add('c_tool', 'c desc', { type: 'object' }, async () => {});
114
+ const list = ts.list();
115
+ assert.strictEqual(list.length, 3);
116
+ assert.strictEqual(list[0].name, 'a_tool');
117
+ assert.strictEqual(list[1].name, 'b_tool');
118
+ assert.strictEqual(list[2].name, 'c_tool');
119
+ });
120
+
121
+ // call
122
+ t.add('call success', async () => {
123
+ const ts = new ToolSet();
124
+ const expected = { result: 42 };
125
+ ts.add('calc', 'calc', { type: 'object' }, async () => expected);
126
+ const res = await ts.call('calc', {});
127
+ assert.deepStrictEqual(res, expected);
128
+ });
129
+
130
+ t.add('call not found throws', async () => {
131
+ const ts = new ToolSet();
132
+ try {
133
+ await ts.call('missing', {});
134
+ assert.fail('Should throw');
135
+ } catch (e) {
136
+ assert.ok(e.message.includes('Function not found'));
137
+ }
138
+ });
139
+
140
+ const report = await t.run();
141
+ process.exit(report.errors > 0 ? 1 : 0);
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env node
2
+ import { Test, assert, jsType } from '@j-o-r/sh';
3
+ import { execSync } from 'child_process';
4
+ import { mkdtempSync, writeFileSync, rmSync, mkdirSync } from 'fs';
5
+ import { tmpdir } from 'os';
6
+ import { join, dirname } from 'path';
7
+
8
+ const t = new Test();
9
+ const TMP = mkdtempSync(join(tmpdir(), 'syntax_test_'));
10
+ process.on('exit', () => rmSync(TMP, { recursive: true, force: true }));
11
+
12
+ const checkSyntax = (file) => {
13
+ try {
14
+ execSync(`./utils/syntax_check.sh "${file}"`, { cwd: process.cwd(), stdio: 'pipe', timeout: 5000 });
15
+ return true;
16
+ } catch (e) {
17
+ return false;
18
+ }
19
+ };
20
+
21
+ const writeTemp = (relpath, content) => {
22
+ const file = join(TMP, relpath);
23
+ mkdirSync(dirname(file), { recursive: true });
24
+ writeFileSync(file, content);
25
+ return file;
26
+ };
27
+
28
+ t.add('checkSyntax returns boolean', () => {
29
+ const file = writeTemp('test.js', 'console.log("ok");');
30
+ assert.strictEqual(jsType(checkSyntax(file)), 'Boolean');
31
+ });
32
+
33
+ t.add('syntax_check: JS valid', () => {
34
+ const file = writeTemp('valid.js', '// Valid JS\nconsole.log("ok");');
35
+ assert.strictEqual(checkSyntax(file), true);
36
+ });
37
+
38
+ t.add('syntax_check: JS invalid (unexpected token)', () => {
39
+ const file = writeTemp('invalid.js', 'const x = ; console.log(x);');
40
+ assert.strictEqual(checkSyntax(file), false);
41
+ });
42
+
43
+ t.add('syntax_check: JS invalid unterminated quote', () => {
44
+ const file = writeTemp('invalid-quote.js', 'console.log("hi );');
45
+ assert.strictEqual(checkSyntax(file), false);
46
+ });
47
+
48
+ t.add('syntax_check: JS invalid unbalanced paren', () => {
49
+ const file = writeTemp('invalid-paren.js', 'if (true {\n console.log("ok");\n}');
50
+ assert.strictEqual(checkSyntax(file), false);
51
+ });
52
+
53
+ t.add('syntax_check: JS valid ESM import', () => {
54
+ const file = writeTemp('valid-esm.js', 'import { Test } from "@j-o-r/sh";\nconsole.log("ok");');
55
+ assert.strictEqual(checkSyntax(file), true);
56
+ });
57
+
58
+ t.add('syntax_check: JS shebang valid', () => {
59
+ const file = writeTemp('valid-shebang.js', '#!/usr/bin/env node\nimport { Test } from "@j-o-r/sh";');
60
+ assert.strictEqual(checkSyntax(file), true);
61
+ });
62
+
63
+ t.add('syntax_check: JS subdir valid', () => {
64
+ const file = writeTemp('subdir/valid.js', 'console.log("ok");');
65
+ assert.strictEqual(checkSyntax(file), true);
66
+ });
67
+
68
+ // PY invalid: missing indent
69
+ t.add('syntax_check: PY invalid (indent err)', () => {
70
+ const file = writeTemp('invalid.py', 'def f():\nprint("hi")');
71
+ assert.strictEqual(checkSyntax(file), false);
72
+ });
73
+
74
+ // PY valid
75
+ t.add('syntax_check: PY valid', () => {
76
+ const file = writeTemp('valid.py', '#!/usr/bin/env python3\ndef f():\n print("hi")\nf()');
77
+ assert.strictEqual(checkSyntax(file), true);
78
+ });
79
+
80
+ // SH invalid: missing fi
81
+ t.add('syntax_check: SH invalid (missing fi)', () => {
82
+ const file = writeTemp('invalid.sh', '#!/bin/bash\nif true; then\necho "hi"');
83
+ assert.strictEqual(checkSyntax(file), false);
84
+ });
85
+
86
+ // SH valid
87
+ t.add('syntax_check: SH valid', () => {
88
+ const file = writeTemp('valid.sh', '#!/bin/bash\necho "ok"');
89
+ assert.strictEqual(checkSyntax(file), true);
90
+ });
91
+
92
+ // SH shebang + if/fi
93
+ t.add('syntax_check: SH valid (if/fi)', () => {
94
+ const file = writeTemp('valid-if.sh', '#!/usr/bin/env bash\nif true; then\n echo "ok"\nfi');
95
+ assert.strictEqual(checkSyntax(file), true);
96
+ });
97
+
98
+ // JSON invalid: trailing comma
99
+ t.add('syntax_check: JSON invalid', () => {
100
+ const file = writeTemp('invalid.json', '{ "key": "value", }');
101
+ assert.strictEqual(checkSyntax(file), false);
102
+ });
103
+
104
+ // JSON valid
105
+ t.add('syntax_check: JSON valid', () => {
106
+ const file = writeTemp('valid.json', '{ "key": "value" }');
107
+ assert.strictEqual(checkSyntax(file), true);
108
+ });
109
+
110
+ // Unknown ext: non-fatal pass
111
+ t.add('syntax_check: unknown ext (txt)', () => {
112
+ const file = writeTemp('test.txt', 'plain text');
113
+ assert.strictEqual(checkSyntax(file), true);
114
+ });
115
+
116
+ // write_file sim: invalid → reject (PY/SH/JSON/JS)
117
+ t.add('syntax_check: write_file sim JS reject', () => {
118
+ const file = writeTemp('sim_invalid.js', 'console.log("hi );');
119
+ assert.strictEqual(checkSyntax(file), false);
120
+ });
121
+
122
+ t.add('syntax_check: write_file sim PY reject', () => {
123
+ const file = writeTemp('sim_invalid.py', 'def f():\nprint("hi")');
124
+ assert.strictEqual(checkSyntax(file), false);
125
+ });
126
+
127
+ t.add('syntax_check: write_file sim SH reject', () => {
128
+ const file = writeTemp('sim_invalid.sh', '#!/bin/bash\nif true; then echo hi');
129
+ assert.strictEqual(checkSyntax(file), false);
130
+ });
131
+
132
+ t.add('syntax_check: write_file sim JSON reject', () => {
133
+ const file = writeTemp('sim_invalid.json', '{invalid}');
134
+ assert.strictEqual(checkSyntax(file), false);
135
+ });
136
+
137
+ t.add('syntax_check: async valid JS', async () => {
138
+ const file = writeTemp('async-valid.js', 'console.log("ok");');
139
+ await new Promise(r => setTimeout(r, 10));
140
+ assert.strictEqual(checkSyntax(file), true);
141
+ });
142
+
143
+ const report = await t.run();
144
+ assert.strictEqual(jsType(report), 'Object');
145
+ process.exit(report.errors > 0 ? 1 : 0);
@@ -0,0 +1,30 @@
1
+ # write_file Auto-Validation Test Suite
2
+
3
+ ## Purpose
4
+ Tests the enhanced \`write_file\` tool in \`lib/genericToolset.js\`:
5
+ - **JS Syntax Check**: \`node --check\` on .js/shebang files → blocks invalid.
6
+ - **Error Guidance**: Forces LLM retry with \\\` fix hints.
7
+ - **chmod +x**: Auto for shebangs.
8
+ - **Self-Healing**: LLM sees error → regenerates valid JS.
9
+
10
+ ## Files
11
+ - \`bad.js\`: Intentional syntax error (unclosed template literal \`console.log(\`).
12
+ - \`good.js\`: Fixed (properly escaped \`console.log(\\\\\\`hello\\\\\\\`); \`).
13
+ - \`test.sh\`: Runs write_file simulation → expect error on bad → success on good.
14
+
15
+ ## Run
16
+ ```bash
17
+ cd scenarios/write_file_validation
18
+ chmod +x test.sh
19
+ ./test.sh
20
+ ```
21
+ Expected:
22
+ ```
23
+ ❌ Write bad.js → SYNTAX ERROR (node --check fails)
24
+ ✅ Write good.js → ✓ JS syntax OK + chmod +x (if shebang)
25
+ ```
26
+
27
+ ## LLM Test (via CodeServer)
28
+ Query: \"Use write_file to create bad.js with console.log(\`hi\`); then fix it.\" → Auto-triggers retry loop.
29
+
30
+ Current date: March 27, 2026.
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ console.log(`Unclosed backtick syntax error!
3
+ import { foo } from 'bar';
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ console.log(`Hello, fixed template literal!`);
3
+ console.log('Proper JS syntax.');
4
+ import { parseArgs } from '@j-o-r/sh'; // ESM OK
@@ -0,0 +1,43 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ echo "=== write_file Auto-Validation Test ==="
5
+ cd "$(dirname "$0")"
6
+
7
+ # Simulate toolset write_file (manual for demo; real uses lib/genericToolset.js)
8
+ echo "1. Write BAD JS (expect SYNTAX ERROR)..."
9
+ cat > temp_bad.js << 'EOF'
10
+ #!/usr/bin/env node
11
+ console.log(`Unclosed backtick! // ERROR: Missing closing \`
12
+ EOF
13
+
14
+ # Manual node --check (mimics tool)
15
+ if node --check temp_bad.js 2>&1; then
16
+ echo "❌ FAIL: Bad JS passed check!"
17
+ else
18
+ echo "✅ PASS: Bad JS correctly rejected ($(node --check temp_bad.js 2>&1 | head -1))"
19
+ fi
20
+
21
+ echo "2. Write GOOD JS (expect ✓ OK + chmod)..."
22
+ cat > temp_good.js << 'EOF'
23
+ #!/usr/bin/env node
24
+ console.log(`Fixed template!`);
25
+ console.log('All good.');
26
+ EOF
27
+
28
+ if node --check temp_good.js 2>&1; then
29
+ echo "✅ PASS: Good JS syntax OK"
30
+ chmod +x temp_good.js
31
+ echo "✅ PASS: chmod +x applied"
32
+ ./temp_good.js # Runs!
33
+ else
34
+ echo "❌ FAIL: Good JS failed check!"
35
+ fi
36
+
37
+ # Real toolset test (requires running agent)
38
+ echo "3. REAL TOOL TEST (run via CodeServer):"
39
+ echo " echo 'write_file scenarios/write_file_validation/new.js: console.log(\`test\`);' | dave --connect ws://...:8081/ws"
40
+ echo " → If bad: Error → LLM fixes → ✓ OK"
41
+
42
+ rm -f temp_*.js
43
+ echo "=== ALL TESTS PASS ✅ ==="
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env node
2
+ import cli from '@j-o-r/cli';
3
+ import { WebSocketClient } from "@j-o-r/apiserver";
4
+ const NAME = 'clarification';
5
+ const wsUser = new WebSocketClient('ws://localhost:2000/ws?test=param'); // Match server port/path
6
+
7
+ const queryMessages = new Map();
8
+
9
+ /*
10
+ HANDSHAKE:
11
+
12
+ 1: requests an introduction with 'introduce'
13
+ 2: get a response 'introduction'
14
+
15
+ This must be DONE before anything else can take place
16
+
17
+ */
18
+
19
+ cli.registerKeyMappings([
20
+ { name: 'd', ctrl: true, meta: false, shift: false, handler: async () => {
21
+ process.exit(0);
22
+ }},
23
+ { name: 'r', ctrl: true, meta: false, shift: false, handler: async () => {
24
+ cli.clear();
25
+ }}
26
+
27
+ ]);
28
+ // @ts-ignore
29
+ cli.inputHandler = async (s, role) => {
30
+ const message = {
31
+ action: 'query',
32
+ content: s,
33
+ }
34
+ wsUser.send(JSON.stringify(message));
35
+ cli.startSpinner();
36
+ }
37
+ cli.focus('log');
38
+ cli.write('CTRL - d exit');
39
+ cli.write('CTRL - r clear screen / reset');
40
+ // cli.focus('user', true);
41
+
42
+ wsUser.onopen = () => {
43
+ cli.focus('log');
44
+ cli.write('Jorrit connected');
45
+ // Send my introduction to the server
46
+ wsUser.send(JSON.stringify({
47
+ action: 'introduction',
48
+ name: NAME,
49
+ content: 'I am a human named Jorrit. Query me for questions that needs clearfication',
50
+ id: new Date().getTime()
51
+ }));
52
+ cli.focus('user', true);
53
+
54
+ };
55
+ /**
56
+ * @type {MessageEvent} event
57
+ */
58
+ wsUser.onmessage = (event) => {
59
+ const data = JSON.parse(event.data); // Forwarded HTTP request
60
+ console.log('Received request:', data);
61
+ cli.focus('user', true);
62
+ };
63
+ wsUser.onclose = () => {
64
+ console.log('WS closed');
65
+ process.exit();
66
+ }
67
+ wsUser.onerror = (err) => {
68
+ console.error('WS error:', err);
69
+ }
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+ import { Test, assert } from '@j-o-r/sh';
3
+ import { request } from '../lib/API/x.ai/responses.js';
4
+ import Prompt from '../lib/Prompt.js';
5
+ import tools from './testToolset.js'; // Reuse weather mock for recursion
6
+
7
+ const t = new Test();
8
+
9
+ // Real API: tools + recursion (default model)
10
+ t.add('request: real with tools (recursion)', async () => {
11
+ const p = new Prompt();
12
+ p.add('system', 'You are helpful. Use tools for weather.', true);
13
+ p.add('user', 'Weather in Amsterdam?');
14
+ const msg = await request(p, tools);
15
+ assert.strictEqual(msg.role, 'assistant');
16
+ assert.ok(msg.content.some(c => c.type === 'text'), 'Final text response');
17
+ const toolMsg = p.messages.find(m => m.role === 'tool');
18
+ assert.ok(toolMsg, 'Tool executed');
19
+ });
20
+
21
+ // Real API: custom options (non-reasoning model)
22
+ t.add('request: real options (model/temp/store)', async () => {
23
+ const p = new Prompt();
24
+ p.add('system', 'Answer in JSON.', true);
25
+ p.add('user', '2+2 as JSON.');
26
+ const msg = await request(p, null, {
27
+ model: 'grok-4-fast-non-reasoning',
28
+ temperature: 0.1,
29
+ store: true,
30
+ max_output_tokens: 100
31
+ });
32
+ assert.strictEqual(msg.role, 'assistant');
33
+ assert.ok(msg.content[0].text.includes('4'), 'JSON with 4');
34
+ });
35
+
36
+ // Real API: reasoning rejected (unsupported on default model)
37
+ t.add('request: reasoning rejected on incompatible model', async () => {
38
+ const p = new Prompt();
39
+ p.add('user', 'Why is sky blue?');
40
+ await assert.rejects(
41
+ () => request(p, null, { reasoning: { effort: 'high', summary: 'concise' } }),
42
+ /invalid argument|reasoningEffort/
43
+ );
44
+ });
45
+
46
+ // Real API: invalid model rejects (4xx)
47
+ t.add('request: invalid model rejects', async () => {
48
+ const p = new Prompt();
49
+ p.add('user', 'Hi');
50
+ await assert.rejects(
51
+ () => request(p, null, { model: 'invalid-xyz' }),
52
+ /Error.*4\\d{2}|Model not found|400/
53
+ );
54
+ });
55
+
56
+ const report = await t.run();
57
+ process.exit(report.errors > 0 ? 1 : 0);
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env node
2
+ import { Test, assert } from '@j-o-r/sh';
3
+ import { generateRequest, parseResponse, request } from '../lib/API/x.ai/responses.js';
4
+ import Prompt from '../lib/Prompt.js';
5
+ import ToolSet from '../lib/ToolSet.js';
6
+ import tools from './testToolset.js';
7
+
8
+ const t = new Test();
9
+
10
+ // Fake response helpers
11
+ const fakeTextRes = () => ({
12
+ status: 200,
13
+ response: {
14
+ id: 'fake-text',
15
+ model: 'grok-beta',
16
+ created_at: Math.floor(Date.now() / 1000),
17
+ usage: { input_tokens: 10, output_tokens: 5 },
18
+ output: [{ type: 'message', content: [{ type: 'output_text', text: 'Hello!' }] }]
19
+ }
20
+ });
21
+
22
+ const fakeReasoningRes = () => ({
23
+ status: 200,
24
+ response: {
25
+ id: 'fake-reason',
26
+ model: 'grok-beta',
27
+ created_at: Math.floor(Date.now() / 1000),
28
+ usage: { input_tokens: 10, output_tokens: 5 },
29
+ output: [{ type: 'reasoning', summary: [{ type: 'summary_text', text: 'Thought.' }] }]
30
+ }
31
+ });
32
+
33
+ const fakeCitationRes = () => ({
34
+ status: 200,
35
+ response: {
36
+ id: 'fake-cit',
37
+ model: 'grok-beta',
38
+ created_at: Math.floor(Date.now() / 1000),
39
+ usage: { input_tokens: 10, output_tokens: 5 },
40
+ output: [{ type: 'message', content: [{ type: 'output_text', text: 'Text', annotations: [{ type: 'url_citation', url: 'https://ex.com' }] }] }]
41
+ }
42
+ });
43
+
44
+ const fakeToolRes = (name = 'get_current_weather', args = '{"location":"Amsterdam"}') => ({
45
+ status: 200,
46
+ response: {
47
+ id: 'fake-tool',
48
+ model: 'grok-beta',
49
+ created_at: Math.floor(Date.now() / 1000),
50
+ usage: { input_tokens: 20, output_tokens: 10 },
51
+ output: [{ type: 'function_call', name, id: 'fid', call_id: 'fcid', arguments: args, status: 'complete' }]
52
+ }
53
+ });
54
+
55
+ t.add('generateRequest: basic', () => {
56
+ const p = new Prompt();
57
+ p.add('system', 'Helpful', true);
58
+ p.add('user', 'Hi');
59
+ const { url, headers, body } = generateRequest(p);
60
+ assert.strictEqual(url, 'https://api.x.ai/v1/responses');
61
+ assert.strictEqual(headers['Content-Type'], 'application/json');
62
+ assert.ok(headers.Authorization.startsWith('Bearer'));
63
+ assert.strictEqual(body.model, 'grok-4-fast-reasoning');
64
+ assert.strictEqual(body.instructions, 'Helpful');
65
+ assert.strictEqual(body.input.length, 1);
66
+ });
67
+
68
+ t.add('generateRequest: tools', () => {
69
+ const p = new Prompt();
70
+ p.add('user', 'Weather');
71
+ const { body } = generateRequest(p, tools);
72
+ assert.ok(body.tools.some(tl => tl.name === 'get_current_weather'));
73
+ assert.strictEqual(body.parallel_tool_calls, true);
74
+ });
75
+
76
+ t.add('generateRequest: options', () => {
77
+ const p = new Prompt();
78
+ p.add('user', 'Hi');
79
+ const { body } = generateRequest(p, null, { model: 'grok-4-1-fast-reasoning' });
80
+ assert.strictEqual(body.model, 'grok-4-1-fast-reasoning');
81
+ });
82
+
83
+ t.add('generateRequest: throws bad last role', () => {
84
+ const p = new Prompt();
85
+ p.add('system', '', true);
86
+ p.add('assistant', 'bad');
87
+ assert.throws(() => generateRequest(p));
88
+ });
89
+
90
+ t.add('parseResponse: text', async () => {
91
+ const p = new Prompt();
92
+ p.add('user', 'Hi');
93
+ await parseResponse(100, p, fakeTextRes());
94
+ const msg = p.getLastMessage();
95
+ assert.strictEqual(msg.role, 'assistant');
96
+ assert.strictEqual(msg.content[0].text, 'Hello!');
97
+ });
98
+
99
+ t.add('parseResponse: reasoning', async () => {
100
+ const p = new Prompt();
101
+ await parseResponse(100, p, fakeReasoningRes());
102
+ const msg = p.getLastMessage();
103
+ assert.strictEqual(msg.role, 'reasoning');
104
+ assert.strictEqual(msg.content[0].text, 'Thought.');
105
+ });
106
+
107
+ t.add('parseResponse: citations', async () => {
108
+ const p = new Prompt();
109
+ await parseResponse(100, p, fakeCitationRes());
110
+ const log = p.messages.find(m => m.role === 'log');
111
+ assert.ok(log);
112
+ assert.ok(log.content[0].text.includes('https://ex.com'));
113
+ });
114
+
115
+ t.add('parseResponse: function_call', async () => {
116
+ const p = new Prompt();
117
+ p.add('user', 'Weather');
118
+ await parseResponse(100, p, fakeToolRes());
119
+ const msg = p.getLastMessage();
120
+ assert.strictEqual(msg.role, 'assistant');
121
+ assert.strictEqual(msg.content[0].type, 'function_request');
122
+ assert.strictEqual(msg.content[0].function_request.name, 'get_current_weather');
123
+ assert.strictEqual(msg.content[0].function_request.parameters, '{"location":"Amsterdam"}');
124
+ assert.strictEqual(msg.content[0].function_request.call_id, 'fcid');
125
+ });
126
+
127
+ t.add('parseResponse: toolset exec', async () => {
128
+ const p = new Prompt();
129
+ p.add('user', 'Weather Amsterdam');
130
+ await parseResponse(100, p, fakeToolRes('get_current_weather', '{"location":"Amsterdam"}'), tools);
131
+ const toolMsg = p.getLastMessage();
132
+ assert.strictEqual(toolMsg.role, 'tool');
133
+ assert.strictEqual(toolMsg.content[0].type, 'function_response');
134
+ assert.strictEqual(toolMsg.content[0].function_response.name, 'get_current_weather');
135
+ assert.ok(toolMsg.content[0].function_response.response.includes('24 C'));
136
+ });
137
+
138
+ t.add('parseResponse: non-200 rejects', async () => {
139
+ const p = new Prompt();
140
+ const bad = { status: 400, response: { error: 'bad' } };
141
+ await assert.rejects(async () => parseResponse(100, p, bad), /400: /);
142
+ });
143
+
144
+ t.add('request: real simple', async () => {
145
+ const p = new Prompt();
146
+ p.add('system', 'You are a helpful assistant. Answer briefly.', true);
147
+ p.add('user', 'What is 2+2?');
148
+ const msg = await request(p);
149
+ assert.strictEqual(msg.role, 'assistant');
150
+ assert.ok(msg.content.some(c => c.type === 'text' && c.text.includes('4')), 'Expected 4');
151
+ });
152
+
153
+ const report = await t.run();
154
+ process.exit(report.errors > 0 ? 1 : 0);