atris 3.15.30 → 3.15.31

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.
package/ax CHANGED
@@ -1,12 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const fs = require('fs');
4
3
  const http = require('http');
5
- const https = require('https');
6
- const os = require('os');
7
- const path = require('path');
8
4
  const readline = require('readline');
9
- const { loadCredentials } = require('./utils/auth');
10
5
 
11
6
  const EXIT_WORDS = new Set(['exit', 'quit', ':q']);
12
7
  const BACKEND = {
@@ -14,39 +9,6 @@ const BACKEND = {
14
9
  port: 8000,
15
10
  path: '/api/atris2/turn'
16
11
  };
17
- const DEFAULT_BACKEND_BASE = `http://${BACKEND.host}:${BACKEND.port}`;
18
- const CONNECTION_STATUS_PATH = '/api/integrations/status';
19
- const CONNECTION_CAPABILITIES_PATH = '/api/atris2/connection-capabilities';
20
- const ATRIS2_CONNECTION_STATUS_PATH = '/api/atris2/connection-status';
21
- const CONNECTOR_NAMES = {
22
- gmail: 'Gmail',
23
- google_calendar: 'Google Calendar',
24
- google_drive: 'Google Drive',
25
- google_docs: 'Google Docs',
26
- github: 'GitHub',
27
- slack: 'Slack',
28
- linear: 'Linear',
29
- notion: 'Notion',
30
- hubspot: 'HubSpot',
31
- twitter: 'Twitter',
32
- microsoft: 'Microsoft',
33
- discord: 'Discord',
34
- jira: 'Jira',
35
- linkedin: 'LinkedIn',
36
- whatsapp: 'WhatsApp',
37
- salesforce: 'Salesforce'
38
- };
39
- const CONNECTOR_SCOPES = {
40
- gmail: ['mail'],
41
- google_calendar: ['calendar'],
42
- google_drive: ['drive'],
43
- google_docs: ['docs'],
44
- github: ['repos', 'issues', 'pull_requests'],
45
- slack: ['messages'],
46
- linear: ['issues'],
47
- notion: ['pages', 'databases'],
48
- hubspot: ['crm']
49
- };
50
12
  const ANSI = {
51
13
  reset: '\x1b[0m',
52
14
  bold: '\x1b[1m',
@@ -88,54 +50,37 @@ function formatUsage() {
88
50
  'ax - Atris 2 local coding agent',
89
51
  '',
90
52
  'Usage:',
91
- ' ax [--pro|--fast] [--local|--cloud] <message>',
92
- ' ax [--pro|--fast] [--local|--cloud] --chat',
53
+ ' ax [--pro|--fast] <message>',
54
+ ' ax [--pro|--fast] --chat',
93
55
  ' ax [--pro|--fast] --doctor',
94
- ' ax [--fast] --benchmark',
95
56
  '',
96
57
  'Modes:',
97
58
  ' --pro local workspace agent, deeper tool loop',
98
59
  ' --fast local workspace agent, faster low-latency turns',
99
- ' --local force local workspace tools',
100
- ' --cloud force authenticated cloud connectors/chat',
101
60
  '',
102
61
  'Examples:',
103
62
  ' ax --pro find the config file and explain it',
104
63
  ' ax --fast what files are here',
105
- ' ax --fast what is on my calendar today',
106
64
  ' ax --pro --chat',
107
65
  ].join('\n');
108
66
  }
109
67
 
110
- function backendBaseUrl() {
111
- return (process.env.AX_BACKEND_URL
112
- || process.env.OBELISK_LOCAL_ATRIS2_BACKEND_URL
113
- || process.env.OBELISK_ATRIS2_BACKEND_URL
114
- || DEFAULT_BACKEND_BASE).replace(/\/$/, '');
115
- }
116
-
117
68
  function backendUrl() {
118
- return new URL(BACKEND.path, backendBaseUrl()).toString();
119
- }
120
-
121
- function backendPathUrl(pathname) {
122
- return new URL(pathname, backendBaseUrl()).toString();
69
+ return `http://${BACKEND.host}:${BACKEND.port}${BACKEND.path}`;
123
70
  }
124
71
 
125
72
  function buildRunProfile(options = {}) {
126
73
  const mode = options.mode === 'fast' ? 'fast' : 'pro';
127
74
  const cwd = options.cwd || process.cwd();
128
- const route = resolveRoute(options.message || 'doctor', options);
129
- const payload = buildPayload(options.message || 'doctor', { mode, cwd, route });
75
+ const payload = buildPayload(options.message || 'doctor', { mode, cwd });
130
76
  return {
131
77
  endpoint: backendUrl(),
132
78
  mode,
133
- route,
134
79
  model: payload.model,
135
- workspace_path: payload.workspace_path || 'cloud',
80
+ workspace_path: payload.workspace_path,
136
81
  max_turns: payload.max_turns,
137
82
  streaming: true,
138
- runtime: route === 'cloud' ? 'authenticated cloud connectors/chat' : 'local workspace',
83
+ runtime: 'local workspace',
139
84
  reasoning: mode === 'pro'
140
85
  ? 'backend reports run row; Pro workspace tool loop uses API default medium'
141
86
  : 'backend reports run row; Fast workspace tool loop uses provider default'
@@ -146,7 +91,6 @@ function formatRunProfile(profile, options = {}) {
146
91
  const rows = [
147
92
  ['mode', `${profile.mode} (${profile.model})`],
148
93
  ['endpoint', profile.endpoint],
149
- ['route', profile.route || 'auto'],
150
94
  ['workspace', formatPathSubject(profile.workspace_path, options)],
151
95
  ['turns', String(profile.max_turns)],
152
96
  ['streaming', profile.streaming ? 'yes' : 'no'],
@@ -156,196 +100,6 @@ function formatRunProfile(profile, options = {}) {
156
100
  return rows.map(([label, value]) => formatAuxRow(label, value, options)).join('\n');
157
101
  }
158
102
 
159
- function canonicalConnectorId(value) {
160
- return String(value || '').trim().toLowerCase().replace(/-/g, '_');
161
- }
162
-
163
- function connectorDisplayName(id) {
164
- const key = canonicalConnectorId(id);
165
- if (CONNECTOR_NAMES[key]) return CONNECTOR_NAMES[key];
166
- return key.split('_').map(part => part ? part[0].toUpperCase() + part.slice(1) : '').join(' ');
167
- }
168
-
169
- function authToken() {
170
- if (process.env.OBELISK_AUTH_TOKEN) return process.env.OBELISK_AUTH_TOKEN;
171
- if (process.env.ATRIS_TOKEN) return process.env.ATRIS_TOKEN;
172
- try {
173
- const creds = loadCredentials();
174
- return creds && creds.token ? creds.token : '';
175
- } catch (_) {
176
- return '';
177
- }
178
- }
179
-
180
- function authUserId() {
181
- if (process.env.OBELISK_USER_ID) return process.env.OBELISK_USER_ID;
182
- if (process.env.ATRIS_USER_ID) return process.env.ATRIS_USER_ID;
183
- try {
184
- const creds = loadCredentials();
185
- return creds && creds.user_id ? creds.user_id : '';
186
- } catch (_) {
187
- return '';
188
- }
189
- }
190
-
191
- function isLoopbackBackend() {
192
- try {
193
- const parsed = new URL(backendBaseUrl());
194
- return ['127.0.0.1', 'localhost', '::1'].includes(parsed.hostname);
195
- } catch (_) {
196
- return false;
197
- }
198
- }
199
-
200
- function requestJson(pathname, { token = authToken(), timeoutMs = 6000 } = {}) {
201
- const url = backendPathUrl(pathname);
202
- const parsed = new URL(url);
203
- const transport = parsed.protocol === 'https:' ? https : http;
204
- return new Promise((resolve) => {
205
- const req = transport.request({
206
- method: 'GET',
207
- hostname: parsed.hostname,
208
- port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
209
- path: `${parsed.pathname}${parsed.search}`,
210
- headers: {
211
- Accept: 'application/json',
212
- ...(token ? { Authorization: `Bearer ${token}` } : {})
213
- }
214
- }, (res) => {
215
- const chunks = [];
216
- res.on('data', chunk => chunks.push(chunk));
217
- res.on('end', () => {
218
- const text = Buffer.concat(chunks).toString('utf8');
219
- try {
220
- resolve({ ok: res.statusCode >= 200 && res.statusCode < 300, status: res.statusCode, data: text ? JSON.parse(text) : null, text });
221
- } catch (_) {
222
- resolve({ ok: false, status: res.statusCode, data: null, text });
223
- }
224
- });
225
- });
226
- req.on('error', error => resolve({ ok: false, status: 0, data: null, text: '', error: error.message }));
227
- req.setTimeout(timeoutMs, () => {
228
- req.destroy();
229
- resolve({ ok: false, status: 0, data: null, text: '', error: `timeout after ${timeoutMs}ms` });
230
- });
231
- req.end();
232
- });
233
- }
234
-
235
- function defaultAuthority(id) {
236
- const key = canonicalConnectorId(id);
237
- if (key === 'gmail') return { list_messages: 'read_only', get_message: 'read_only', send_message: 'approval_required' };
238
- if (key === 'google_calendar') return { list_events: 'read_only', get_event: 'read_only', create_event: 'approval_required' };
239
- if (key === 'google_drive') return { list_files: 'read_only', search_files: 'read_only', download_file: 'read_only', upload_file: 'approval_required' };
240
- if (key === 'google_docs') return { list_documents: 'read_only', get_document: 'read_only', create_document: 'approval_required' };
241
- if (key === 'github') return { list_repos: 'read_only', get_repo: 'read_only', list_issues: 'read_only', create_issue: 'approval_required' };
242
- if (key === 'slack') return { list_channels: 'read_only', search_messages: 'read_only', post_message: 'approval_required' };
243
- if (key === 'linear') return { list_issues: 'read_only', get_issue: 'read_only', create_issue: 'approval_required' };
244
- if (key === 'notion') return { search: 'read_only', get_page: 'read_only', create_page: 'approval_required' };
245
- if (key === 'hubspot') return { list_contacts: 'read_only', search_contacts: 'read_only', create_contact: 'approval_required' };
246
- return { inspect_status: 'read_only' };
247
- }
248
-
249
- function connectionFromStatus(id, connected, contractConnector = null) {
250
- const key = canonicalConnectorId(id);
251
- const authority = contractConnector && contractConnector.authority && typeof contractConnector.authority === 'object'
252
- ? contractConnector.authority
253
- : defaultAuthority(key);
254
- return {
255
- id: key,
256
- name: connectorDisplayName(key),
257
- connected: connected === true,
258
- local: false,
259
- actions: Object.keys(authority),
260
- scopes: CONNECTOR_SCOPES[key] || [],
261
- authority,
262
- source: 'ax_integrations_status'
263
- };
264
- }
265
-
266
- function statusConnected(statuses, id) {
267
- const keys = [id, id.replace(/_/g, '-'), canonicalConnectorId(id)];
268
- for (const key of keys) {
269
- const value = statuses[key];
270
- if (value === true) return true;
271
- if (value && typeof value === 'object') {
272
- if (value.connected === true) return true;
273
- if (String(value.status || '').toLowerCase() === 'connected') return true;
274
- }
275
- }
276
- return false;
277
- }
278
-
279
- async function buildConnectionContext(options = {}) {
280
- const token = options.token || authToken();
281
- const localStatusUserId = isLoopbackBackend() ? authUserId() : '';
282
- const statusPath = localStatusUserId
283
- ? `${ATRIS2_CONNECTION_STATUS_PATH}?connection_user_id=${encodeURIComponent(localStatusUserId)}`
284
- : CONNECTION_STATUS_PATH;
285
- const [statusRes, contractRes] = await Promise.all([
286
- localStatusUserId || token ? requestJson(statusPath, { token: localStatusUserId ? '' : token }) : Promise.resolve({ ok: false, data: null }),
287
- requestJson(CONNECTION_CAPABILITIES_PATH, { token: '' })
288
- ]);
289
- const statusData = statusRes.ok && statusRes.data && typeof statusRes.data === 'object' ? statusRes.data : {};
290
- const statuses = statusData.statuses && typeof statusData.statuses === 'object' ? statusData.statuses : statusData;
291
- const contractConnectors = new Map();
292
- if (contractRes.ok && contractRes.data && Array.isArray(contractRes.data.connectors)) {
293
- for (const connector of contractRes.data.connectors) {
294
- const key = canonicalConnectorId(connector.id);
295
- if (key) contractConnectors.set(key, connector);
296
- }
297
- }
298
- const ids = new Set([
299
- ...Object.keys(CONNECTOR_NAMES),
300
- ...Object.keys(statuses).map(canonicalConnectorId),
301
- ...contractConnectors.keys(),
302
- ]);
303
- const connections = [...ids].sort().map(id => connectionFromStatus(
304
- id,
305
- statusConnected(statuses, id),
306
- contractConnectors.get(id)
307
- ));
308
- connections.push({
309
- id: 'computer.local',
310
- name: 'This Mac workspace',
311
- connected: options.localWorkspace === true,
312
- local: true,
313
- status: options.localWorkspace === true ? 'available' : 'cloud_only',
314
- actions: ['inspect_status', 'use_local_workspace'],
315
- scopes: ['computer', 'workspace'],
316
- authority: { inspect_status: 'read_only', use_local_workspace: 'read_only' },
317
- source: 'ax'
318
- });
319
- return {
320
- schema: 'atris.connection_capabilities.v1',
321
- source: 'ax',
322
- stale: false,
323
- capability_contract_source: contractRes.ok ? 'backend' : 'fallback',
324
- capability_contract_schema: contractRes.data && contractRes.data.schema,
325
- capability_contract_connectors: contractConnectors.size || undefined,
326
- connections
327
- };
328
- }
329
-
330
- function mentionsConnector(message) {
331
- return /\b(gmail|email|mail|inbox|calendar|events?|meetings?|schedule|google drive|drive|docs?|sheets?|github|slack|linear|notion|hubspot|integrations?|connectors?|connections?|connected apps?|connected tools?)\b/i.test(message || '');
332
- }
333
-
334
- function connectorWriteIntent(message) {
335
- return mentionsConnector(message) && /\b(send|post|dm|message|reply|draft|compose|schedule|book|create|update|delete|archive|move|share|comment|invite)\b/i.test(message || '');
336
- }
337
-
338
- function workspaceIntent(message) {
339
- return /\b(files?|folders?|repo|workspace|project|directory|tree|read|open|inspect|search|grep|find|locate|where|edit|write|change|modify|patch|fix|test|tests?|build|src|source|code|diff|git|backend|frontend|atris task|atris xp|xp game|career xp|agentxp|todo|map)\b/i.test(message || '');
340
- }
341
-
342
- function resolveRoute(message, options = {}) {
343
- if (options.route === 'local' || options.forceLocal) return 'local';
344
- if (options.route === 'cloud' || options.forceCloud) return 'cloud';
345
- if (mentionsConnector(message) && !workspaceIntent(message)) return 'cloud';
346
- return 'local';
347
- }
348
-
349
103
  function formatPrompt() {
350
104
  return '› ';
351
105
  }
@@ -588,29 +342,13 @@ function buildMessage(message, history = []) {
588
342
 
589
343
  function buildPayload(message, options = {}) {
590
344
  const mode = options.mode === 'fast' ? 'fast' : 'pro';
591
- const route = resolveRoute(message, options);
592
- const local = route !== 'cloud';
593
- const payload = {
345
+ return {
594
346
  message: buildMessage(message, options.history || []),
347
+ workspace_path: options.cwd || process.cwd(),
595
348
  model: modelForMode(mode),
596
- max_turns: local ? (mode === 'pro' ? 14 : 8) : 1,
349
+ max_turns: mode === 'pro' ? 14 : 6,
597
350
  verify_command: 'true'
598
351
  };
599
- if (local) {
600
- payload.workspace_path = options.cwd || process.cwd();
601
- }
602
- if (options.connectionContext) {
603
- payload.connection_context = options.connectionContext;
604
- }
605
- if (!local && options.connectionUserId) {
606
- payload.connection_user_id = options.connectionUserId;
607
- }
608
- if (!local && connectorWriteIntent(message)) {
609
- payload.allow_external_actions = true;
610
- payload.cleanup_external_actions = true;
611
- payload.max_turns = mode === 'pro' ? 4 : 2;
612
- }
613
- return payload;
614
352
  }
615
353
 
616
354
  function handleEvent(event, state, output) {
@@ -663,24 +401,11 @@ function handleEvent(event, state, output) {
663
401
  }
664
402
  }
665
403
 
666
- async function postTurn(message, options = {}) {
667
- const route = resolveRoute(message, options);
668
- const local = route !== 'cloud';
669
- const token = authToken();
670
- const shouldSendConnectionContext = options.connectionContext
671
- || route === 'cloud'
672
- || mentionsConnector(message)
673
- || /\b(can you use|can you access|connected|connections?|integrations?|tools?|capabilities)\b/i.test(message || '');
674
- const connectionContext = options.connectionContext || (shouldSendConnectionContext
675
- ? await buildConnectionContext({ token, localWorkspace: local })
676
- : null);
677
- const connectionUserId = !local && isLoopbackBackend() ? authUserId() : '';
678
- const payload = buildPayload(message, { ...options, route, connectionContext, connectionUserId });
404
+ function postTurn(message, options = {}) {
405
+ const payload = buildPayload(message, options);
679
406
  const postData = JSON.stringify(payload);
680
407
  const output = options.output || process.stdout;
681
408
  const timeoutMs = payload.model === 'atris:pro' ? 180000 : 60000;
682
- const turnUrl = new URL(backendUrl());
683
- const transport = turnUrl.protocol === 'https:' ? https : http;
684
409
  const state = {
685
410
  events: [],
686
411
  errors: [],
@@ -710,16 +435,15 @@ async function postTurn(message, options = {}) {
710
435
  else resolve(value);
711
436
  };
712
437
 
713
- const req = transport.request({
714
- hostname: turnUrl.hostname,
715
- port: turnUrl.port || (turnUrl.protocol === 'https:' ? 443 : 80),
716
- path: `${turnUrl.pathname}${turnUrl.search}`,
438
+ const req = http.request({
439
+ hostname: BACKEND.host,
440
+ port: BACKEND.port,
441
+ path: BACKEND.path,
717
442
  method: 'POST',
718
443
  headers: {
719
444
  'Content-Type': 'application/json',
720
445
  'Content-Length': Buffer.byteLength(postData),
721
- Accept: 'text/event-stream',
722
- ...(token ? { Authorization: `Bearer ${token}` } : {})
446
+ Accept: 'text/event-stream'
723
447
  }
724
448
  }, (res) => {
725
449
  res.setEncoding('utf8');
@@ -830,174 +554,7 @@ async function chat(options = {}) {
830
554
  function printBackendHint() {
831
555
  console.log('');
832
556
  console.log('Start backend:');
833
- console.log(`cd /Users/keshavrao/arena/atrisos-backend/backend && ATRIS2_ALLOW_LOCAL_WORKSPACE=1 ENVIRONMENT=development ENV=development ../venv/bin/uvicorn main:app --host ${BACKEND.host} --port ${BACKEND.port}`);
834
- }
835
-
836
- function bufferedOutput() {
837
- let text = '';
838
- return {
839
- isTTY: false,
840
- write(chunk) {
841
- text += String(chunk || '');
842
- return true;
843
- },
844
- text() {
845
- return text;
846
- }
847
- };
848
- }
849
-
850
- function receiptFromState(state) {
851
- const event = [...(state.events || [])].reverse().find(item => item && item.type === 'receipt' && item.receipt);
852
- return event ? event.receipt : null;
853
- }
854
-
855
- function toolEventsFromState(state) {
856
- const receipt = receiptFromState(state);
857
- if (receipt && Array.isArray(receipt.tool_events)) return receipt.tool_events;
858
- return [];
859
- }
860
-
861
- function assertBenchmark(condition, message) {
862
- if (!condition) throw new Error(message);
863
- }
864
-
865
- function toolEventText(events) {
866
- return JSON.stringify(events || []);
867
- }
868
-
869
- async function runBenchmarkCase(label, fn, results, output) {
870
- const startedAt = Date.now();
871
- output.write(`- ${label} ... `);
872
- try {
873
- const detail = await fn();
874
- const duration = formatDuration(Date.now() - startedAt);
875
- output.write(`ok (${duration})${detail ? ` - ${detail}` : ''}\n`);
876
- results.push({ label, ok: true, duration_ms: Date.now() - startedAt, detail });
877
- } catch (error) {
878
- const duration = formatDuration(Date.now() - startedAt);
879
- output.write(`fail (${duration}) - ${error.message}\n`);
880
- results.push({ label, ok: false, duration_ms: Date.now() - startedAt, error: error.message });
881
- }
882
- }
883
-
884
- async function runBenchmark(options = {}) {
885
- const mode = options.mode === 'pro' ? 'pro' : 'fast';
886
- const output = options.output || process.stdout;
887
- const results = [];
888
- const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'ax-atris2-benchmark-'));
889
- const localOut = () => bufferedOutput();
890
-
891
- output.write(`Atris 2 ${mode === 'fast' ? 'Fast' : 'Pro'} benchmark\n`);
892
- output.write(`${backendUrl()}\n\n`);
893
-
894
- try {
895
- await runBenchmarkCase('search through files', async () => {
896
- const workspace = path.join(tempRoot, 'search');
897
- fs.mkdirSync(path.join(workspace, 'src', 'deep'), { recursive: true });
898
- fs.writeFileSync(path.join(workspace, 'package.json'), '{"name":"ax-search-bench"}\n');
899
- fs.writeFileSync(path.join(workspace, 'src', 'deep', 'needle.txt'), 'AX_SEARCH_SENTINEL=found-here\n');
900
- const sink = localOut();
901
- const state = await postTurn(
902
- 'Use local tools to search for AX_SEARCH_SENTINEL and answer with the exact file path. Do not edit.',
903
- { mode, cwd: workspace, route: 'local', output: sink, showProgress: false }
904
- );
905
- const combined = `${state.output}\n${toolEventText(toolEventsFromState(state))}`;
906
- assertBenchmark(/src\/deep\/needle\.txt/.test(combined), 'missing proof path src/deep/needle.txt');
907
- assertBenchmark(/AX_SEARCH_SENTINEL/.test(combined), 'missing sentinel proof');
908
- return 'found src/deep/needle.txt';
909
- }, results, output);
910
-
911
- await runBenchmarkCase('modify files', async () => {
912
- const workspace = path.join(tempRoot, 'edit');
913
- fs.mkdirSync(path.join(workspace, 'src'), { recursive: true });
914
- fs.writeFileSync(path.join(workspace, 'package.json'), '{"name":"ax-edit-bench"}\n');
915
- const target = path.join(workspace, 'src', 'bench-target.txt');
916
- fs.writeFileSync(target, 'status=AX_BEFORE\n');
917
- const sink = localOut();
918
- const state = await postTurn(
919
- 'Use local tools to edit src/bench-target.txt. Replace AX_BEFORE with AX_AFTER, then report the changed file. Do not create any other files.',
920
- { mode, cwd: workspace, route: 'local', output: sink, showProgress: false }
921
- );
922
- const text = fs.readFileSync(target, 'utf8');
923
- assertBenchmark(text.includes('AX_AFTER'), 'src/bench-target.txt was not modified');
924
- const combined = `${state.output}\n${toolEventText(toolEventsFromState(state))}`;
925
- assertBenchmark(/bench-target\.txt/.test(combined), 'missing changed-file proof in response or receipt');
926
- return 'edited src/bench-target.txt';
927
- }, results, output);
928
-
929
- await runBenchmarkCase('use Atris workspace state', async () => {
930
- const cwd = options.cwd || process.cwd();
931
- assertBenchmark(fs.existsSync(path.join(cwd, 'atris')), 'current workspace has no atris/ directory');
932
- const sink = localOut();
933
- const state = await postTurn(
934
- 'Use Atris local tools to inspect current task or map state. Report one concrete Atris file or task source you used. Do not edit files.',
935
- { mode, cwd, route: 'local', output: sink, showProgress: false }
936
- );
937
- const combined = `${state.output}\n${toolEventText(toolEventsFromState(state))}`;
938
- assertBenchmark(/atris\/(TODO|MAP|atris)\.md|local_task_op|local_map_op|Task|Map/.test(combined), 'missing Atris task/map proof');
939
- return 'read Atris task/map surface';
940
- }, results, output);
941
-
942
- await runBenchmarkCase('know XP system', async () => {
943
- const cwd = options.cwd || process.cwd();
944
- assertBenchmark(fs.existsSync(path.join(cwd, 'atris')), 'current workspace has no atris/ directory');
945
- const sink = localOut();
946
- const state = await postTurn(
947
- 'Use the local RewardRubric/Atris tools or workspace files to explain the Career XP reward system in this workspace. Mention proof, review/acceptance, and reward/XP. Cite the exact tool or file you used. Do not edit files.',
948
- { mode, cwd, route: 'local', output: sink, showProgress: false }
949
- );
950
- const combined = `${state.output}\n${toolEventText(toolEventsFromState(state))}`;
951
- assertBenchmark(/xp|reward|rubric/i.test(combined), 'missing XP/reward/rubric proof');
952
- assertBenchmark(/proof|review|accept|approval/i.test(combined), 'missing proof/review/acceptance explanation');
953
- assertBenchmark(/RewardRubric|local_reward_rubric_op|atris\/|main\.js|taskBoard/i.test(combined), 'missing cited XP source or tool');
954
- return 'explained XP reward loop';
955
- }, results, output);
956
-
957
- await runBenchmarkCase('list connections', async () => {
958
- const context = await buildConnectionContext({ token: authToken(), localWorkspace: false });
959
- const connected = (context.connections || []).filter(row => row.connected);
960
- assertBenchmark((context.connections || []).length >= 5, 'connection capability context is missing');
961
- const sink = localOut();
962
- const state = await postTurn(
963
- 'Which Atris integrations are connected for me? Include Google Calendar status. Answer from live connection context.',
964
- { mode, route: 'cloud', output: sink, showProgress: false, connectionContext: context }
965
- );
966
- const receipt = receiptFromState(state);
967
- assertBenchmark(receipt && receipt.connection_context, 'receipt missing connection_context');
968
- const answer = `${state.output}\n${receipt && receipt.final ? receipt.final : ''}`;
969
- assertBenchmark(/calendar|gmail|github|slack|drive|integration|connected/i.test(answer), 'answer did not discuss integrations');
970
- return `${connected.length} connected reported`;
971
- }, results, output);
972
-
973
- await runBenchmarkCase('read calendar', async () => {
974
- const sink = localOut();
975
- const state = await postTurn(
976
- 'What is on my calendar today? Use the connected Google Calendar read path and be concise.',
977
- { mode, route: 'cloud', output: sink, showProgress: false }
978
- );
979
- const events = toolEventsFromState(state);
980
- const text = toolEventText(events);
981
- assertBenchmark(/google_calendar/.test(text), 'calendar tool event missing');
982
- assertBenchmark(!/"status":"not_connected"/.test(text), 'Google Calendar is not connected for this account');
983
- assertBenchmark(/Calendar|event|No calendar events|meeting/i.test(state.output), 'calendar answer missing');
984
- return 'calendar read path responded';
985
- }, results, output);
986
- } finally {
987
- try {
988
- fs.rmSync(tempRoot, { recursive: true, force: true });
989
- } catch (_) {}
990
- }
991
-
992
- const failed = results.filter(result => !result.ok);
993
- output.write('\n');
994
- output.write(failed.length ? `Benchmark failed: ${failed.length}/${results.length} failed\n` : `Benchmark passed: ${results.length}/${results.length}\n`);
995
- if (failed.length) {
996
- const error = new Error(failed.map(result => `${result.label}: ${result.error}`).join('; '));
997
- error.results = results;
998
- throw error;
999
- }
1000
- return results;
557
+ console.log(`cd /Users/keshavrao/arena/atrisos-backend/backend && uvicorn main:app --host ${BACKEND.host} --port ${BACKEND.port}`);
1001
558
  }
1002
559
 
1003
560
  async function main() {
@@ -1009,36 +566,27 @@ async function main() {
1009
566
 
1010
567
  const mode = args.includes('--fast') ? 'fast' : 'pro';
1011
568
  const doctor = args.includes('--doctor');
1012
- const benchmark = args.includes('--benchmark');
1013
- const forceCloud = args.includes('--cloud');
1014
- const forceLocal = args.includes('--local');
1015
- const route = forceCloud ? 'cloud' : forceLocal ? 'local' : 'auto';
1016
569
  const prompt = args
1017
- .filter(arg => !['--fast', '--pro', '--chat', '--doctor', '--benchmark', '--local', '--cloud', '--help', '-h'].includes(arg))
570
+ .filter(arg => !['--fast', '--pro', '--chat', '--doctor', '--help', '-h'].includes(arg))
1018
571
  .join(' ')
1019
572
  .trim();
1020
573
 
1021
574
  try {
1022
- if (benchmark) {
1023
- await runBenchmark({ mode, cwd: process.cwd(), output: process.stdout });
1024
- return;
1025
- }
1026
-
1027
575
  if (doctor) {
1028
576
  console.log(formatHeader({ mode, cwd: process.cwd(), chat: false }));
1029
577
  console.log('');
1030
- console.log(formatRunProfile(buildRunProfile({ mode, cwd: process.cwd(), route: route === 'auto' ? undefined : route }), process.stdout));
578
+ console.log(formatRunProfile(buildRunProfile({ mode, cwd: process.cwd() }), process.stdout));
1031
579
  return;
1032
580
  }
1033
581
 
1034
582
  if (!prompt || args.includes('--chat')) {
1035
- await chat({ mode, cwd: process.cwd(), route: route === 'auto' ? undefined : route });
583
+ await chat({ mode, cwd: process.cwd() });
1036
584
  return;
1037
585
  }
1038
586
 
1039
587
  console.log(formatHeader({ mode, cwd: process.cwd(), chat: false }));
1040
588
  console.log('');
1041
- const result = await postTurn(prompt, { mode, cwd: process.cwd(), route: route === 'auto' ? undefined : route });
589
+ const result = await postTurn(prompt, { mode, cwd: process.cwd() });
1042
590
  console.log('');
1043
591
  console.log(formatDoneLine(result.durationMs));
1044
592
  } catch (error) {
@@ -1053,12 +601,8 @@ if (require.main === module) {
1053
601
  }
1054
602
 
1055
603
  module.exports = {
1056
- authToken,
1057
- authUserId,
1058
- backendBaseUrl,
1059
604
  backendUrl,
1060
605
  buildPayload,
1061
- buildConnectionContext,
1062
606
  buildRunProfile,
1063
607
  chat,
1064
608
  createProgressReporter,
@@ -1076,8 +620,6 @@ module.exports = {
1076
620
  modelForMode,
1077
621
  parseSseBlock,
1078
622
  postTurn,
1079
- resolveRoute,
1080
- runBenchmark,
1081
623
  summarizeToolInput,
1082
624
  summarizeToolResult
1083
625
  };