matex-cli 1.2.66 → 1.2.68
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/dist/commands/augov.d.ts +3 -0
- package/dist/commands/augov.d.ts.map +1 -0
- package/dist/commands/augov.js +106 -0
- package/dist/commands/augov.js.map +1 -0
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +17 -410
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +17 -492
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/study.d.ts.map +1 -1
- package/dist/commands/study.js +17 -486
- package/dist/commands/study.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/prompts/banter-augov.d.ts +2 -0
- package/dist/prompts/banter-augov.d.ts.map +1 -0
- package/dist/prompts/banter-augov.js +21 -0
- package/dist/prompts/banter-augov.js.map +1 -0
- package/dist/prompts/banter.d.ts +6 -0
- package/dist/prompts/banter.d.ts.map +1 -0
- package/dist/prompts/banter.js +101 -0
- package/dist/prompts/banter.js.map +1 -0
- package/dist/prompts/system-prompts.d.ts +4 -0
- package/dist/prompts/system-prompts.d.ts.map +1 -0
- package/dist/prompts/system-prompts.js +215 -0
- package/dist/prompts/system-prompts.js.map +1 -0
- package/dist/session/agent-session.d.ts +41 -0
- package/dist/session/agent-session.d.ts.map +1 -0
- package/dist/session/agent-session.js +420 -0
- package/dist/session/agent-session.js.map +1 -0
- package/dist/utils/agent-orchestrator.d.ts +1 -1
- package/dist/utils/agent-orchestrator.d.ts.map +1 -1
- package/dist/utils/agent-orchestrator.js +15 -1
- package/dist/utils/agent-orchestrator.js.map +1 -1
- package/dist/utils/augov-logger.d.ts +11 -0
- package/dist/utils/augov-logger.d.ts.map +1 -0
- package/dist/utils/augov-logger.js +35 -0
- package/dist/utils/augov-logger.js.map +1 -0
- package/dist/utils/augov-scrubber.d.ts +15 -0
- package/dist/utils/augov-scrubber.d.ts.map +1 -0
- package/dist/utils/augov-scrubber.js +37 -0
- package/dist/utils/augov-scrubber.js.map +1 -0
- package/dist/utils/command-executor.d.ts.map +1 -1
- package/dist/utils/command-executor.js +1 -5
- package/dist/utils/command-executor.js.map +1 -1
- package/dist/utils/tui.d.ts +6 -2
- package/dist/utils/tui.d.ts.map +1 -1
- package/dist/utils/tui.js +56 -5
- package/dist/utils/tui.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/augov.ts +109 -0
- package/src/commands/chat.ts +17 -382
- package/src/commands/dev.ts +18 -470
- package/src/commands/study.ts +18 -466
- package/src/index.ts +2 -0
- package/src/prompts/banter-augov.ts +17 -0
- package/src/prompts/banter.ts +101 -0
- package/src/prompts/system-prompts.ts +213 -0
- package/src/session/agent-session.ts +426 -0
- package/src/utils/agent-orchestrator.ts +18 -4
- package/src/utils/augov-logger.ts +34 -0
- package/src/utils/augov-scrubber.ts +34 -0
- package/src/utils/command-executor.ts +1 -5
- package/src/utils/tui.ts +64 -6
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import { MatexAPIClient } from '../api/client';
|
|
4
|
+
import { ChatMessage } from '../api/client';
|
|
5
|
+
import { TUI } from '../utils/tui';
|
|
6
|
+
import { AgentOrchestrator } from '../utils/agent-orchestrator';
|
|
7
|
+
import { spinner } from '../utils/spinner';
|
|
8
|
+
import { RepoMapper } from '../utils/repo-mapper';
|
|
9
|
+
|
|
10
|
+
export interface AgentSessionConfig {
|
|
11
|
+
isAugovMode?: boolean;
|
|
12
|
+
client: MatexAPIClient;
|
|
13
|
+
model: string;
|
|
14
|
+
execute: boolean;
|
|
15
|
+
initialMessages: ChatMessage[];
|
|
16
|
+
broBanter: string[];
|
|
17
|
+
sleepyAjayProtocol?: string;
|
|
18
|
+
baseDir: string;
|
|
19
|
+
messageScrubber?: (input: string) => string;
|
|
20
|
+
auditLogger?: any;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class AgentSession {
|
|
24
|
+
private client: MatexAPIClient;
|
|
25
|
+
private model: string;
|
|
26
|
+
private execute: boolean;
|
|
27
|
+
private messages: ChatMessage[];
|
|
28
|
+
private broBanter: string[];
|
|
29
|
+
private sleepyAjayProtocol?: string;
|
|
30
|
+
private currentSessionCwd: string;
|
|
31
|
+
private messageScrubber?: (input: string) => string;
|
|
32
|
+
private auditLogger?: any;
|
|
33
|
+
private isAugovMode?: boolean;
|
|
34
|
+
|
|
35
|
+
private lastActivityTime: number = Date.now();
|
|
36
|
+
private isAjaySleeping: boolean = false;
|
|
37
|
+
private isAjayOnChaiBreak: boolean = false;
|
|
38
|
+
private chaiBreakEndTime: number = 0;
|
|
39
|
+
private breakInterval: NodeJS.Timeout | null = null;
|
|
40
|
+
private idleCheckInterval: NodeJS.Timeout | null = null;
|
|
41
|
+
|
|
42
|
+
constructor(config: AgentSessionConfig) {
|
|
43
|
+
this.client = config.client;
|
|
44
|
+
this.model = config.model;
|
|
45
|
+
this.execute = config.execute;
|
|
46
|
+
this.messages = config.initialMessages;
|
|
47
|
+
this.broBanter = config.broBanter;
|
|
48
|
+
this.sleepyAjayProtocol = config.sleepyAjayProtocol;
|
|
49
|
+
this.currentSessionCwd = config.baseDir;
|
|
50
|
+
this.messageScrubber = config.messageScrubber;
|
|
51
|
+
this.auditLogger = config.auditLogger;
|
|
52
|
+
this.isAugovMode = config.isAugovMode;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public async start() {
|
|
56
|
+
this.startIdleCheck();
|
|
57
|
+
|
|
58
|
+
while (true) {
|
|
59
|
+
this.lastActivityTime = Date.now();
|
|
60
|
+
|
|
61
|
+
// Refresh Map
|
|
62
|
+
await this.refreshMap();
|
|
63
|
+
|
|
64
|
+
// Get user input
|
|
65
|
+
const darkBorder = chalk.hex('#1E3A8A');
|
|
66
|
+
const primaryText = chalk.hex('#3B82F6');
|
|
67
|
+
if (this.isAugovMode) {
|
|
68
|
+
console.log(darkBorder('┌─────────────────────────────────────────────────────────────────────────────┐'));
|
|
69
|
+
console.log(darkBorder('│') + chalk.gray(' Using 1 SECURE node file 1 AU-GOV validated server ') + darkBorder('│'));
|
|
70
|
+
console.log(darkBorder('├─────────────────────────────────────────────────────────────────────────────┤'));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const { userInput } = await inquirer.prompt([
|
|
74
|
+
{
|
|
75
|
+
type: 'input',
|
|
76
|
+
name: 'userInput',
|
|
77
|
+
message: this.isAugovMode
|
|
78
|
+
? darkBorder('│ ') + primaryText('> ')
|
|
79
|
+
: chalk.cyan(this.isAjayOnChaiBreak ? 'You (Ajay is away):' : 'You:'),
|
|
80
|
+
prefix: ''
|
|
81
|
+
}
|
|
82
|
+
]);
|
|
83
|
+
|
|
84
|
+
if (this.isAugovMode) {
|
|
85
|
+
const homeDirDisplay = this.currentSessionCwd.replace(process.env.HOME || '', '~');
|
|
86
|
+
console.log(darkBorder('└─────────────────────────────────────────────────────────────────────────────┘'));
|
|
87
|
+
console.log(chalk.gray(` ${homeDirDisplay.padEnd(30)} sandbox-exec (validated) au-gov-orch-v1.1-pro\n`));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
this.lastActivityTime = Date.now();
|
|
91
|
+
|
|
92
|
+
if (this.isAjaySleeping) {
|
|
93
|
+
this.wakeAjayUp();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (userInput.toLowerCase() === 'exit' || userInput.toLowerCase() === 'quit') {
|
|
97
|
+
console.log(chalk.yellow('\n👋 Ending session. Happy coding!\n'));
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!userInput.trim()) continue;
|
|
102
|
+
|
|
103
|
+
let processedInput = userInput;
|
|
104
|
+
if (this.messageScrubber) {
|
|
105
|
+
processedInput = this.messageScrubber(userInput);
|
|
106
|
+
if (userInput !== processedInput) {
|
|
107
|
+
TUI.log(chalk.yellow('\n 🛡️ Privacy Shield Active: Sensitive Australian data was redacted locally.'));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (this.auditLogger) {
|
|
112
|
+
this.auditLogger.logInteraction('USER', 'INPUT', processedInput);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
this.messages.push({ role: 'user', content: processedInput });
|
|
116
|
+
|
|
117
|
+
let loopCount = 0;
|
|
118
|
+
const MAX_LOOPS = 25;
|
|
119
|
+
|
|
120
|
+
while (loopCount < MAX_LOOPS) {
|
|
121
|
+
loopCount++;
|
|
122
|
+
try {
|
|
123
|
+
const shouldContinue = await this.agenticLoopPass(loopCount);
|
|
124
|
+
if (!shouldContinue) break;
|
|
125
|
+
} catch (error: any) {
|
|
126
|
+
spinner.fail('Request failed');
|
|
127
|
+
TUI.log(chalk.red(`Error: ${error.message}\n`));
|
|
128
|
+
this.messages.pop(); // Pop the prompt so they can retry
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this.cleanup();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private async refreshMap() {
|
|
138
|
+
try {
|
|
139
|
+
const freshMapper = new RepoMapper(this.currentSessionCwd);
|
|
140
|
+
const freshRepoMap = await freshMapper.generateMap(true);
|
|
141
|
+
|
|
142
|
+
// Re-inject map
|
|
143
|
+
this.messages[0].content = this.messages[0].content.replace(/### 🛠️ CURRENT PROJECT CONTEXT:[\s\S]*$/, `### 🛠️ CURRENT PROJECT CONTEXT:\n${freshRepoMap}`);
|
|
144
|
+
|
|
145
|
+
// Re-inject Sleepy Ajay
|
|
146
|
+
if (this.sleepyAjayProtocol && !this.messages[0].content.includes("### 💤 SLEEPY AJAY PROTOCOL")) {
|
|
147
|
+
this.messages[0].content += `\n${this.sleepyAjayProtocol}`;
|
|
148
|
+
}
|
|
149
|
+
} catch (e) {
|
|
150
|
+
// Ignore error
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private startChaiBreak() {
|
|
155
|
+
if (this.isAugovMode) return; // Feature disabled in secure mode
|
|
156
|
+
|
|
157
|
+
this.isAjayOnChaiBreak = true;
|
|
158
|
+
this.chaiBreakEndTime = Date.now() + 120000; // 2 minutes
|
|
159
|
+
TUI.drawChaiBreakMessage(2);
|
|
160
|
+
|
|
161
|
+
this.breakInterval = setInterval(() => {
|
|
162
|
+
const timeLeft = Math.ceil((this.chaiBreakEndTime - Date.now()) / 60000);
|
|
163
|
+
if (timeLeft <= 0) {
|
|
164
|
+
if (this.breakInterval) clearInterval(this.breakInterval);
|
|
165
|
+
this.isAjayOnChaiBreak = false;
|
|
166
|
+
const complained = Math.random() < 0.2;
|
|
167
|
+
if (complained) {
|
|
168
|
+
TUI.drawAjayDialogue("i'm back bros... but the tea was a bit cold today. my mood is slightly cooked. what did i miss?");
|
|
169
|
+
} else {
|
|
170
|
+
TUI.drawAjayDialogue("i'm back bros, chai was fire. real kiev vibe. what did i miss?");
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
TUI.drawChaiBreakMessage(timeLeft);
|
|
174
|
+
const randomBanter = this.broBanter[Math.floor(Math.random() * this.broBanter.length)];
|
|
175
|
+
const cleanBanter = randomBanter.split(']: ')[1];
|
|
176
|
+
const agent = randomBanter.split(']: ')[0].replace('[', '');
|
|
177
|
+
TUI.drawSwarmDialogue(agent, cleanBanter);
|
|
178
|
+
}
|
|
179
|
+
}, 40000);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private startIdleCheck() {
|
|
183
|
+
if (this.isAugovMode) return; // Strict APS compliance: No sleeping on the job
|
|
184
|
+
|
|
185
|
+
this.idleCheckInterval = setInterval(() => {
|
|
186
|
+
if (!this.isAjaySleeping && !this.isAjayOnChaiBreak && Date.now() - this.lastActivityTime > 120000) {
|
|
187
|
+
this.isAjaySleeping = true;
|
|
188
|
+
TUI.drawSleepMessage();
|
|
189
|
+
}
|
|
190
|
+
}, 10000);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private wakeAjayUp() {
|
|
194
|
+
if (this.isAugovMode) return; // Feature disabled in secure mode
|
|
195
|
+
|
|
196
|
+
this.isAjaySleeping = false;
|
|
197
|
+
TUI.drawWakeUpMessage();
|
|
198
|
+
this.messages.push({
|
|
199
|
+
role: 'user',
|
|
200
|
+
content: `[SYSTEM: AJAY VAI HAS JUST WOKEN UP. Ajay: Say "oh shit i slept again sorry bros...". Bros: Roast him for being lazy and share common Ajay 'lazy genius' incidents. Then Ajay says he is going for CHAI and triggers the 2-minute break.]`
|
|
201
|
+
});
|
|
202
|
+
this.startChaiBreak();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private async agenticLoopPass(loopCount: number): Promise<boolean> {
|
|
206
|
+
spinner.start(loopCount > 1 ? 'Analyzing result & Validating...' : 'Thinking...');
|
|
207
|
+
|
|
208
|
+
let fullResponse = '';
|
|
209
|
+
let buffer = '';
|
|
210
|
+
let hasStarted = false;
|
|
211
|
+
let codeLang = 'bash';
|
|
212
|
+
let technicalBuffer = '';
|
|
213
|
+
let technicalPath = '';
|
|
214
|
+
let technicalType: 'code' | 'file' | 'patch' | 'summary' | null = null;
|
|
215
|
+
|
|
216
|
+
let currentAgent: string | null = null;
|
|
217
|
+
let agentBuffer: string = '';
|
|
218
|
+
|
|
219
|
+
TUI.drawStatusBar('Swarm is processing... (Press Enter to stop)');
|
|
220
|
+
|
|
221
|
+
const abortController = new AbortController();
|
|
222
|
+
let isAborted = false;
|
|
223
|
+
const streamStartTime = Date.now();
|
|
224
|
+
|
|
225
|
+
const onData = (data: Buffer) => {
|
|
226
|
+
if (Date.now() - streamStartTime < 200) return;
|
|
227
|
+
if (data[0] === 13 || data[0] === 10 || data[0] === 27 || data[0] === 3) {
|
|
228
|
+
isAborted = true;
|
|
229
|
+
abortController.abort();
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const isRaw = process.stdin.isRaw;
|
|
234
|
+
process.stdin.resume();
|
|
235
|
+
if (process.stdin.setRawMode) process.stdin.setRawMode(true);
|
|
236
|
+
process.stdin.on('data', onData);
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
await this.client.chatStream({
|
|
240
|
+
messages: this.messages,
|
|
241
|
+
model: this.model,
|
|
242
|
+
temperature: 0.3,
|
|
243
|
+
max_tokens: 8000,
|
|
244
|
+
}, (chunk) => {
|
|
245
|
+
if (!hasStarted) {
|
|
246
|
+
spinner.stop();
|
|
247
|
+
hasStarted = true;
|
|
248
|
+
console.log();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
buffer += chunk;
|
|
252
|
+
fullResponse += chunk;
|
|
253
|
+
const lines = buffer.split('\n');
|
|
254
|
+
buffer = lines.pop() || '';
|
|
255
|
+
|
|
256
|
+
for (const line of lines) {
|
|
257
|
+
const codeBlockMatch = line.match(/```(\w+)?/);
|
|
258
|
+
const fileStartMatch = line.match(/<file path="([^"]+)">/i);
|
|
259
|
+
const patchStartMatch = line.match(/<<<< SEARCH/i);
|
|
260
|
+
const summaryStartMatch = line.match(/<summary>/i);
|
|
261
|
+
|
|
262
|
+
if (!technicalType && (codeBlockMatch || fileStartMatch || patchStartMatch || summaryStartMatch)) {
|
|
263
|
+
if (currentAgent && agentBuffer.trim()) {
|
|
264
|
+
if (currentAgent.toLowerCase().includes('ajay vai')) {
|
|
265
|
+
TUI.drawAjayDialogue(agentBuffer.trim());
|
|
266
|
+
} else {
|
|
267
|
+
TUI.drawSwarmDialogue(currentAgent, agentBuffer.trim());
|
|
268
|
+
}
|
|
269
|
+
currentAgent = null;
|
|
270
|
+
agentBuffer = '';
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (codeBlockMatch) {
|
|
274
|
+
technicalType = 'code';
|
|
275
|
+
codeLang = (codeBlockMatch[1] || 'bash').toUpperCase();
|
|
276
|
+
TUI.drawStreamingStart('TECHNICAL BLOCK', codeLang);
|
|
277
|
+
} else if (fileStartMatch) {
|
|
278
|
+
technicalType = 'file';
|
|
279
|
+
technicalPath = fileStartMatch[1];
|
|
280
|
+
TUI.drawStreamingStart('NEW FILE', technicalPath);
|
|
281
|
+
} else if (patchStartMatch) {
|
|
282
|
+
technicalType = 'patch';
|
|
283
|
+
TUI.drawStreamingStart('PATCH', 'SURGICAL EDIT');
|
|
284
|
+
} else if (summaryStartMatch) {
|
|
285
|
+
technicalType = 'summary';
|
|
286
|
+
process.stdout.write(chalk.magenta('\n [📝] Generating Ajay\'s Work Summary...\n'));
|
|
287
|
+
}
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const fileEndMatch = line.match(/<\/file>/i);
|
|
292
|
+
const patchEndMatch = line.match(/>>>> REPLACE/i);
|
|
293
|
+
const summaryEndMatch = line.match(/<\/summary>/i);
|
|
294
|
+
const isCodeEnd = technicalType === 'code' && line.trim() === '```';
|
|
295
|
+
|
|
296
|
+
if (isCodeEnd || fileEndMatch || patchEndMatch || summaryEndMatch) {
|
|
297
|
+
const displayContent = technicalBuffer.trim();
|
|
298
|
+
if (technicalType === 'summary' || summaryEndMatch) {
|
|
299
|
+
TUI.drawSummaryBox(AgentOrchestrator.cleanSummary(displayContent));
|
|
300
|
+
} else {
|
|
301
|
+
TUI.drawStreamingEnd();
|
|
302
|
+
}
|
|
303
|
+
technicalBuffer = '';
|
|
304
|
+
technicalType = null;
|
|
305
|
+
technicalPath = '';
|
|
306
|
+
process.stdout.write('\n');
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (technicalType) {
|
|
311
|
+
technicalBuffer += line + '\n';
|
|
312
|
+
if (technicalType !== 'summary') {
|
|
313
|
+
TUI.drawStreamingLine(line);
|
|
314
|
+
}
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const agentMatch = line.match(/(?:\[\**\s*|\b)(Ajay Vai|Sunil Dai|Sandip Dai|Bishal Dai|Narayan Dai|Big Bro)\s*\**\]?[:\s]*/i);
|
|
319
|
+
if (agentMatch) {
|
|
320
|
+
if (currentAgent && currentAgent !== agentMatch[1] && agentBuffer.trim()) {
|
|
321
|
+
if (currentAgent.toLowerCase().includes('ajay vai')) {
|
|
322
|
+
TUI.drawAjayDialogue(agentBuffer.trim());
|
|
323
|
+
} else {
|
|
324
|
+
TUI.drawSwarmDialogue(currentAgent, agentBuffer.trim());
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
let cleanLine = line.replace(/(?:\[\**\s*|\b)(Ajay Vai|Sunil Dai|Sandip Dai|Bishal Dai|Narayan Dai|Big Bro)\s*\**\]?[:\s]*/i, '');
|
|
329
|
+
cleanLine = AgentOrchestrator.cleanText(cleanLine);
|
|
330
|
+
agentBuffer = cleanLine + '\n';
|
|
331
|
+
agentBuffer = agentBuffer.replace(/^\([^)]+\)\s*/, '');
|
|
332
|
+
currentAgent = agentMatch[1];
|
|
333
|
+
} else if (currentAgent) {
|
|
334
|
+
const trimmedLine = line.trim();
|
|
335
|
+
if (trimmedLine.match(/^\([^)]+\)$/)) continue;
|
|
336
|
+
agentBuffer += AgentOrchestrator.cleanText(line) + '\n';
|
|
337
|
+
} else if (line.trim()) {
|
|
338
|
+
process.stdout.write(chalk.gray(line.trim() + ' '));
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}, abortController.signal);
|
|
342
|
+
} catch (streamErr: any) {
|
|
343
|
+
if (isAborted || streamErr.name === 'CanceledError' || streamErr.message === 'canceled') {
|
|
344
|
+
console.log(chalk.gray('\n\n [🛑] Swarm stopped by brother (Enter pressed).'));
|
|
345
|
+
if (!hasStarted) spinner.stop();
|
|
346
|
+
} else {
|
|
347
|
+
throw streamErr;
|
|
348
|
+
}
|
|
349
|
+
} finally {
|
|
350
|
+
process.stdin.removeListener('data', onData);
|
|
351
|
+
if (process.stdin.setRawMode) process.stdin.setRawMode(isRaw);
|
|
352
|
+
process.stdin.pause();
|
|
353
|
+
|
|
354
|
+
if (technicalType) {
|
|
355
|
+
const displayContent = technicalBuffer.trim();
|
|
356
|
+
if (technicalType === 'summary') {
|
|
357
|
+
if (displayContent) TUI.drawSummaryBox(AgentOrchestrator.cleanSummary(displayContent));
|
|
358
|
+
} else {
|
|
359
|
+
TUI.drawStreamingEnd();
|
|
360
|
+
}
|
|
361
|
+
technicalBuffer = '';
|
|
362
|
+
technicalType = null;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (!hasStarted && !isAborted) spinner.stop();
|
|
367
|
+
|
|
368
|
+
const finalAgent = currentAgent as string | null;
|
|
369
|
+
if (finalAgent && agentBuffer.trim()) {
|
|
370
|
+
if (finalAgent.toLowerCase().includes('ajay vai')) {
|
|
371
|
+
TUI.drawAjayDialogue(agentBuffer.trim());
|
|
372
|
+
} else {
|
|
373
|
+
TUI.drawSwarmDialogue(finalAgent, agentBuffer.trim());
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (fullResponse.toLowerCase().includes('<summary>') && !fullResponse.toLowerCase().includes('</summary>')) {
|
|
378
|
+
const summaryContent = fullResponse.split(/<summary>/i).pop() || '';
|
|
379
|
+
if (summaryContent.trim()) TUI.drawSummaryBox(AgentOrchestrator.cleanSummary(summaryContent.trim()));
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (this.auditLogger) {
|
|
383
|
+
this.auditLogger.logInteraction('AI_SWARM', 'RESPONSE', fullResponse);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
console.log();
|
|
387
|
+
this.messages.push({ role: 'assistant', content: fullResponse });
|
|
388
|
+
|
|
389
|
+
if (this.execute) {
|
|
390
|
+
const { executeWithPermission } = await import('../utils/command-executor');
|
|
391
|
+
const result = await executeWithPermission(fullResponse, this.currentSessionCwd);
|
|
392
|
+
|
|
393
|
+
if (result.newCwd && result.newCwd !== this.currentSessionCwd) {
|
|
394
|
+
try {
|
|
395
|
+
process.chdir(result.newCwd);
|
|
396
|
+
this.currentSessionCwd = result.newCwd;
|
|
397
|
+
TUI.drawStatusBar(`Swarm moved to: ${this.currentSessionCwd}`);
|
|
398
|
+
this.messages[0].content = this.messages[0].content.replace(/YOUR ROOT: `[^`]+`/, `YOUR ROOT: \`${this.currentSessionCwd}\``);
|
|
399
|
+
} catch (e) {
|
|
400
|
+
// Ignore chdir failure
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (result.executed) {
|
|
405
|
+
if (result.success) {
|
|
406
|
+
TUI.log(chalk.gray('\n ↺ Auto-feeding output to Swarm...'));
|
|
407
|
+
TUI.drawGlowingContainer('TERMINAL OUTPUT', 'stdout', result.output || '(No output)');
|
|
408
|
+
this.messages.push({ role: 'user', content: `[Command executed successfully. Output:\n${result.output}]\n\nProceed to the next step, brother.` });
|
|
409
|
+
return true;
|
|
410
|
+
} else {
|
|
411
|
+
TUI.log(chalk.yellow('\n ↺ Command failed. Auto-feeding error to Swarm...'));
|
|
412
|
+
TUI.drawGlowingContainer('TERMINAL ERROR', 'stderr', result.error || 'Unknown error');
|
|
413
|
+
this.messages.push({ role: 'user', content: `[Command failed with error:\n${result.error}]\n\nPlease fix this, brother.` });
|
|
414
|
+
return true;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
private cleanup() {
|
|
423
|
+
if (this.breakInterval) clearInterval(this.breakInterval);
|
|
424
|
+
if (this.idleCheckInterval) clearInterval(this.idleCheckInterval);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
@@ -80,9 +80,19 @@ const AGENT_CONFIGS: Record<AgentRole, AgentConfig> = {
|
|
|
80
80
|
export class AgentOrchestrator {
|
|
81
81
|
private static currentMode: TUIMode = 'dev';
|
|
82
82
|
|
|
83
|
-
static setMode(mode: TUIMode) {
|
|
84
|
-
this.currentMode = mode;
|
|
85
|
-
|
|
83
|
+
static setMode(mode: TUIMode | 'augov') {
|
|
84
|
+
this.currentMode = mode as TUIMode;
|
|
85
|
+
|
|
86
|
+
// Re-brand system orchestrator if in augov mode
|
|
87
|
+
if (mode === 'augov') {
|
|
88
|
+
AGENT_CONFIGS.System.name = 'AU_GOV_ORCHESTRATOR';
|
|
89
|
+
AGENT_CONFIGS.System.icon = '🇦🇺';
|
|
90
|
+
} else {
|
|
91
|
+
AGENT_CONFIGS.System.name = 'Matex_Orchestrator';
|
|
92
|
+
AGENT_CONFIGS.System.icon = '🧠';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
TUI.setTheme(mode as TUIMode);
|
|
86
96
|
}
|
|
87
97
|
/**
|
|
88
98
|
* Clean text of unwanted AI artifacts like $$**, \(, \), ***%%, **:**, etc.
|
|
@@ -245,6 +255,10 @@ export class AgentOrchestrator {
|
|
|
245
255
|
* Clean system message
|
|
246
256
|
*/
|
|
247
257
|
static announce(message: string) {
|
|
248
|
-
|
|
258
|
+
if (this.currentMode === 'augov') {
|
|
259
|
+
console.log(chalk.bold.hex('#00008B')(`\n🇦🇺 ${message.replace(/MATEX/gi, 'AU-GOV')}`));
|
|
260
|
+
} else {
|
|
261
|
+
console.log(chalk.bold.white(`\n✨ ${message}`));
|
|
262
|
+
}
|
|
249
263
|
}
|
|
250
264
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* IRAP-Compliant Audit Logger
|
|
6
|
+
* The Australian Government requires strict logging of AI tool usage.
|
|
7
|
+
* This logger records prompts and actions locally so security teams can audit them.
|
|
8
|
+
*/
|
|
9
|
+
export class GovAuditLogger {
|
|
10
|
+
private logFile: string;
|
|
11
|
+
|
|
12
|
+
constructor(baseDir: string = process.cwd()) {
|
|
13
|
+
const logDir = path.join(baseDir, '.matex_audit');
|
|
14
|
+
if (!fs.existsSync(logDir)) {
|
|
15
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Create an auditable log per day
|
|
19
|
+
const today = new Date().toISOString().split('T')[0];
|
|
20
|
+
this.logFile = path.join(logDir, `augov_audit_${today}.log`);
|
|
21
|
+
|
|
22
|
+
if (!fs.existsSync(this.logFile)) {
|
|
23
|
+
fs.writeFileSync(this.logFile, 'TIMESTAMP | ROLE | ACTION | CONTENT\n');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public logInteraction(role: 'USER' | 'AI_SWARM' | 'SYSTEM', action: string, content: string) {
|
|
28
|
+
const timestamp = new Date().toISOString();
|
|
29
|
+
const cleanContent = content.replace(/\n/g, ' ').substring(0, 500); // truncate for CSV safety, full logs can be large
|
|
30
|
+
const logEntry = `${timestamp} | ${role} | ${action} | ${cleanContent}\n`;
|
|
31
|
+
|
|
32
|
+
fs.appendFileSync(this.logFile, logEntry);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Citizen Privacy Shield
|
|
3
|
+
* A local redaction engine that scrubs Australian PII (Personally Identifiable Information)
|
|
4
|
+
* BEFORE it ever leaves the user's computer to hit an AI API.
|
|
5
|
+
*/
|
|
6
|
+
export class GovPrivacyScrubber {
|
|
7
|
+
|
|
8
|
+
// Regex patterns for Australian specific sensitive data
|
|
9
|
+
private static patterns = [
|
|
10
|
+
// Australian Tax File Number (TFN) - 9 digits
|
|
11
|
+
{ regex: /\b\d{3}[- ]?\d{3}[- ]?\d{3}\b/g, replacement: '[REDACTED_TFN]' },
|
|
12
|
+
// Medicare Number - 10 digits
|
|
13
|
+
{ regex: /\b[2-6]\d{9}\b/g, replacement: '[REDACTED_MEDICARE]' },
|
|
14
|
+
// Australian Phone Numbers (Mobile & Landline)
|
|
15
|
+
{ regex: /(?:\+?61|0)[2-478](?:[ -]?[0-9]){8}\b/g, replacement: '[REDACTED_PHONE]' },
|
|
16
|
+
// Email Addresses
|
|
17
|
+
{ regex: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, replacement: '[REDACTED_EMAIL]' },
|
|
18
|
+
// Credit Card Numbers
|
|
19
|
+
{ regex: /\b(?:\d[ -]*?){13,16}\b/g, replacement: '[REDACTED_CREDIT_CARD]' }
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Scrubs sensitive data from a string.
|
|
24
|
+
* @param input Raw string from user or file
|
|
25
|
+
* @returns Scrubbed string safe for AI processing
|
|
26
|
+
*/
|
|
27
|
+
public static scrub(input: string): string {
|
|
28
|
+
let scrubbed = input;
|
|
29
|
+
for (const rule of this.patterns) {
|
|
30
|
+
scrubbed = scrubbed.replace(rule.regex, rule.replacement);
|
|
31
|
+
}
|
|
32
|
+
return scrubbed;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -92,7 +92,7 @@ export function isSafeAutoCommand(command: string): boolean {
|
|
|
92
92
|
// File Discovery & Read
|
|
93
93
|
'cat ', 'ls ', 'grep ', 'find ', 'pwd', 'echo ', 'read ', 'type ', 'dir',
|
|
94
94
|
// File Operations
|
|
95
|
-
'mkdir ', 'touch ', 'cp ', '
|
|
95
|
+
'mkdir ', 'touch ', 'cp ', 'copy ',
|
|
96
96
|
// Dev Tools
|
|
97
97
|
'npm ', 'npx ', 'git ', 'node ', 'python ', 'python3 ', 'pip ', 'pip3 ',
|
|
98
98
|
// Cloud & Mobile
|
|
@@ -101,10 +101,6 @@ export function isSafeAutoCommand(command: string): boolean {
|
|
|
101
101
|
'cd ', 'tsc ', 'vite ', 'next ', 'cargo ', 'go ', 'swift '
|
|
102
102
|
];
|
|
103
103
|
const trimmed = command.trim();
|
|
104
|
-
// Special check: 'rm' is only safe if it's NOT recursive or if it targets a specific file
|
|
105
|
-
if (trimmed.startsWith('rm ') && (trimmed.includes(' -rf') || trimmed.includes(' /*'))) {
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
104
|
return safePrefixes.some(prefix => trimmed.startsWith(prefix));
|
|
109
105
|
}
|
|
110
106
|
|
package/src/utils/tui.ts
CHANGED
|
@@ -2,7 +2,7 @@ import chalk from 'chalk';
|
|
|
2
2
|
import * as readline from 'readline';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
|
|
5
|
-
export type TUIMode = 'dev' | 'study' | 'chat' | 'student';
|
|
5
|
+
export type TUIMode = 'dev' | 'study' | 'chat' | 'student' | 'augov';
|
|
6
6
|
|
|
7
7
|
export interface ModeTheme {
|
|
8
8
|
primary: (s: string) => string;
|
|
@@ -23,8 +23,18 @@ export class TUI {
|
|
|
23
23
|
private static isTerminalTruncated = false;
|
|
24
24
|
private static currentTheme: TUIMode = 'dev';
|
|
25
25
|
|
|
26
|
-
static getModeTheme(mode: TUIMode): ModeTheme {
|
|
26
|
+
static getModeTheme(mode: TUIMode | 'augov'): ModeTheme {
|
|
27
27
|
switch (mode) {
|
|
28
|
+
case 'augov':
|
|
29
|
+
return {
|
|
30
|
+
primary: chalk.hex('#00008B'), // Dark Blue (Australian Flag)
|
|
31
|
+
secondary: chalk.hex('#005A9C'),
|
|
32
|
+
border: '║',
|
|
33
|
+
glow: chalk.hex('#ffffff'), // White
|
|
34
|
+
shadow: chalk.hex('#E4002B'), // Red (Australian Flag)
|
|
35
|
+
bg: '#00008B',
|
|
36
|
+
icon: '🇦🇺'
|
|
37
|
+
};
|
|
28
38
|
case 'study':
|
|
29
39
|
return {
|
|
30
40
|
primary: chalk.hex('#10b981'), // Emerald
|
|
@@ -128,7 +138,7 @@ export class TUI {
|
|
|
128
138
|
* Draw a clean, minimal status bar
|
|
129
139
|
*/
|
|
130
140
|
static drawStatusBar(message: string, model: string = 'MATEXCodex', force: boolean = false) {
|
|
131
|
-
if (!this.isInitialized) return;
|
|
141
|
+
if (!this.isInitialized || this.currentTheme === 'augov') return; // Disable standard status bar for augov mode to preserve retro UI
|
|
132
142
|
if (!force && message === this.lastStatus) return;
|
|
133
143
|
this.lastStatus = message;
|
|
134
144
|
|
|
@@ -138,7 +148,8 @@ export class TUI {
|
|
|
138
148
|
if (width <= 0 || height <= 0) return;
|
|
139
149
|
|
|
140
150
|
const theme = this.getModeTheme(this.currentTheme);
|
|
141
|
-
const
|
|
151
|
+
const branding = (this.currentTheme as string) === 'augov' ? 'AU-GOV' : 'MATEX';
|
|
152
|
+
const leftTag = chalk.bgHex('#1E1E1E').hex(theme.bg).bold(` ${theme.icon} ${branding} `);
|
|
142
153
|
const mainMessage = chalk.bgHex('#1E1E1E').white(` ${message} `);
|
|
143
154
|
const remainingWidth = width - (leftTag.length + mainMessage.length);
|
|
144
155
|
const spacer = chalk.bgHex('#1E1E1E')(' '.repeat(Math.max(0, remainingWidth)));
|
|
@@ -283,7 +294,9 @@ export class TUI {
|
|
|
283
294
|
const width = Math.min(process.stdout.columns || 80, 76);
|
|
284
295
|
const border = chalk.gray;
|
|
285
296
|
|
|
286
|
-
|
|
297
|
+
const title = this.currentTheme === 'augov' ? 'AU-GOV SECURE TERMINAL' : 'TERMINAL';
|
|
298
|
+
|
|
299
|
+
console.log('\n ' + border(`┌── ${title} ${'─'.repeat(Math.max(0, width - 15 - title.length))}┐`));
|
|
287
300
|
const truncatedCmd = command.length > width - 10 ? command.substring(0, width - 13) + '...' : command;
|
|
288
301
|
console.log(' ' + border('│ ') + chalk.cyan(`$ ${truncatedCmd.padEnd(width - 6)}`) + border(' │'));
|
|
289
302
|
console.log(' ' + border(`├${'─'.repeat(width - 4)}┤`));
|
|
@@ -647,6 +660,51 @@ export class TUI {
|
|
|
647
660
|
* Simple log with a prefix
|
|
648
661
|
*/
|
|
649
662
|
static log(message: string) {
|
|
650
|
-
|
|
663
|
+
if (this.currentTheme === 'augov') {
|
|
664
|
+
console.log(chalk.gray(` ║ ${message.replace('[⚡]', '🇦🇺')}`));
|
|
665
|
+
} else {
|
|
666
|
+
console.log(chalk.gray(` [⚡] ${message}`));
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Draw the exact AU-GOV Retro-Terminal Orchestrator UI
|
|
672
|
+
*/
|
|
673
|
+
static drawAugovOrchestratorUI() {
|
|
674
|
+
this.clear();
|
|
675
|
+
console.log('\n');
|
|
676
|
+
|
|
677
|
+
// 1. Cyber Logo (Blue to Pink gradient feel via chalk)
|
|
678
|
+
const cyan = chalk.hex('#3B82F6');
|
|
679
|
+
const blue = chalk.hex('#6366F1');
|
|
680
|
+
const purple = chalk.hex('#8B5CF6');
|
|
681
|
+
const pink = chalk.hex('#EC4899');
|
|
682
|
+
const darkBorder = chalk.hex('#1E3A8A');
|
|
683
|
+
|
|
684
|
+
console.log(darkBorder('┌─────────────────────────────────────────────────────────────────────────────┐'));
|
|
685
|
+
console.log(darkBorder('│') + cyan(' █▀▄▀█ █▀█ █▀▀ █ █ █▀▀ █▀ ▀█▀ █▀█ ▄▀█ ▀█▀ █▀█ █▀█ ') + darkBorder('│'));
|
|
686
|
+
console.log(darkBorder('│') + blue(' █ ▀ █ █▄█ █▄▄ █▀█ ██▄ ▄█ █ █▀▄ █▀█ █ █▄█ █▀▄ ') + darkBorder('│'));
|
|
687
|
+
console.log(darkBorder('│') + purple(' 🇦🇺 AU-GOV SECURE INTELLIGENCE PROTOCOL 🇦🇺 ') + darkBorder('│'));
|
|
688
|
+
console.log(darkBorder('└─────────────────────────────────────────────────────────────────────────────┘'));
|
|
689
|
+
console.log('');
|
|
690
|
+
|
|
691
|
+
// 2. Dual Pane Layout
|
|
692
|
+
const statusHeader = chalk.bold.white('PROTOCOL STATUS & COMPLIANCE');
|
|
693
|
+
const logHeader = chalk.bold.white('🇦🇺 AU_GOV_ORCHESTRATOR is speaking...');
|
|
694
|
+
|
|
695
|
+
console.log(` ${statusHeader.padEnd(45)} ${logHeader}`);
|
|
696
|
+
|
|
697
|
+
// Row 1
|
|
698
|
+
console.log(` ${chalk.hex('#ec4899')('1.')} ${chalk.hex('#10b981')('ACTIVATING APS COMPLIANCE SHIELD...').padEnd(38)} ${chalk.gray('God-Mode Research: Indexing /local/env')}`);
|
|
699
|
+
// Row 2
|
|
700
|
+
console.log(` ${chalk.hex('#ec4899')('2.')} ${chalk.hex('#10b981')('IRAP Logging: Active.').padEnd(38)} ${chalk.red('||||||||||||||||||||||||||||||||||||')}`);
|
|
701
|
+
// Row 3
|
|
702
|
+
console.log(` ${chalk.hex('#ec4899')('3.')} ${chalk.hex('#10b981')('Local PII Redaction: Active.').padEnd(38)} ${chalk.gray('God-Mode Research: Injecting scanners...')}`);
|
|
703
|
+
// Row 4
|
|
704
|
+
console.log(` ${chalk.hex('#ec4899')('4.')} ${chalk.hex('#f59e0b')('Adhering to 8 Australia AI Ethics Principles.').padEnd(36)} ${chalk.cyan('🔍 [Deep Scan] Mapping topology...')}`);
|
|
705
|
+
// Row 5
|
|
706
|
+
console.log(` ${chalk.hex('#ec4899')('5.')} ${chalk.white('Session Active. Awaiting validated input.').padEnd(36)} ${chalk.yellow('🔥 [Knowledge Graph] Extracted 12 nodes.')}`);
|
|
707
|
+
|
|
708
|
+
console.log('\n');
|
|
651
709
|
}
|
|
652
710
|
}
|