@kernel.chat/kbot 3.99.31 → 3.99.33

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 (58) hide show
  1. package/README.md +1 -1
  2. package/dist/agents/security-agent.d.ts +31 -0
  3. package/dist/agents/security-agent.js +180 -0
  4. package/dist/agents/security-rules.d.ts +35 -0
  5. package/dist/agents/security-rules.js +206 -0
  6. package/dist/agents/specialists.d.ts +6 -0
  7. package/dist/agents/specialists.js +45 -0
  8. package/dist/architect.js +5 -0
  9. package/dist/auth.js +1 -1
  10. package/dist/channels/matrix.d.ts +4 -0
  11. package/dist/channels/matrix.js +28 -0
  12. package/dist/channels/office.d.ts +78 -0
  13. package/dist/channels/office.js +169 -0
  14. package/dist/channels/registry.d.ts +8 -0
  15. package/dist/channels/registry.js +38 -0
  16. package/dist/channels/signal.d.ts +4 -0
  17. package/dist/channels/signal.js +29 -0
  18. package/dist/channels/slack.d.ts +4 -0
  19. package/dist/channels/slack.js +97 -0
  20. package/dist/channels/teams.d.ts +4 -0
  21. package/dist/channels/teams.js +29 -0
  22. package/dist/channels/telegram.d.ts +4 -0
  23. package/dist/channels/telegram.js +28 -0
  24. package/dist/channels/types.d.ts +50 -0
  25. package/dist/channels/types.js +13 -0
  26. package/dist/channels/whatsapp.d.ts +4 -0
  27. package/dist/channels/whatsapp.js +28 -0
  28. package/dist/computer-use-coordinator.d.ts +44 -0
  29. package/dist/computer-use-coordinator.js +0 -0
  30. package/dist/file-library.d.ts +76 -0
  31. package/dist/file-library.js +269 -0
  32. package/dist/managed-agents-anthropic.d.ts +90 -0
  33. package/dist/managed-agents-anthropic.js +123 -0
  34. package/dist/plugins-integrity.d.ts +72 -0
  35. package/dist/plugins-integrity.js +153 -0
  36. package/dist/plugins.d.ts +13 -2
  37. package/dist/plugins.js +87 -10
  38. package/dist/tools/anthropic-managed-agents-tools.d.ts +22 -0
  39. package/dist/tools/anthropic-managed-agents-tools.js +191 -0
  40. package/dist/tools/channel-tools.d.ts +4 -0
  41. package/dist/tools/channel-tools.js +80 -0
  42. package/dist/tools/computer-coordinator-tools.d.ts +13 -0
  43. package/dist/tools/computer-coordinator-tools.js +104 -0
  44. package/dist/tools/computer.js +463 -299
  45. package/dist/tools/file-library-tools.d.ts +12 -0
  46. package/dist/tools/file-library-tools.js +191 -0
  47. package/dist/tools/image-thoughtful.d.ts +31 -0
  48. package/dist/tools/image-thoughtful.js +233 -0
  49. package/dist/tools/index.js +1 -0
  50. package/dist/tools/security-agent-tools.d.ts +34 -0
  51. package/dist/tools/security-agent-tools.js +30 -0
  52. package/dist/tools/swarm-2026-04.d.ts +2 -0
  53. package/dist/tools/swarm-2026-04.js +91 -0
  54. package/dist/tools/workspace-agent-tools.d.ts +19 -0
  55. package/dist/tools/workspace-agent-tools.js +191 -0
  56. package/dist/workspace-agents.d.ts +132 -0
  57. package/dist/workspace-agents.js +379 -0
  58. package/package.json +1 -1
package/README.md CHANGED
@@ -30,7 +30,7 @@ Most terminal AI agents lock you into one provider, one model, one way of workin
30
30
  - **20 providers, zero lock-in** — Claude, GPT, Gemini, Grok, DeepSeek, Groq, Mistral, SambaNova, Cerebras, OpenRouter, and more. Switch with one command.
31
31
  - **Runs fully offline** — Embedded llama.cpp, Ollama, LM Studio, or Jan. $0, fully private.
32
32
  - **Learns your patterns** — Bayesian skill ratings + pattern extraction. Gets faster over time.
33
- - **35 specialist agents** — auto-routes your request to the right expert (coder, researcher, writer, guardian, quant, and 30 more).
33
+ - **35 specialist agents** — auto-routes your request to the right expert (coder, researcher, writer, guardian, quant, and 30 more). Run any agent manually: `kbot --agent <id> "<prompt>"`. List them: `kbot agents`.
34
34
  - **787+ tools** — files, bash, git, GitHub, web search, deploy, database, game dev, VFX, research, science, finance, security, music production, iPhone control, and more.
35
35
  - **Programmatic SDK** — use kbot as a library in your own apps.
36
36
  - **MCP server built in** — plug kbot into Claude Code, Cursor, VS Code, Zed, or Neovim as a tool provider.
@@ -0,0 +1,31 @@
1
+ import { RULES, RULES_BY_ID, type Severity } from './security-rules.js';
2
+ export type SecurityMode = 'scan' | 'scan-and-fix' | 'report-only';
3
+ export interface SecurityFinding {
4
+ id: string;
5
+ severity: Severity;
6
+ category: string;
7
+ file: string;
8
+ line?: number;
9
+ description: string;
10
+ recommendation: string;
11
+ fixed?: boolean;
12
+ }
13
+ export interface SecurityReport {
14
+ scanned: number;
15
+ findings: SecurityFinding[];
16
+ fixesApplied: number;
17
+ summary: string;
18
+ }
19
+ export interface RunSecurityAgentInput {
20
+ target: string;
21
+ mode: SecurityMode;
22
+ }
23
+ /**
24
+ * Run the kbot security agent over a directory.
25
+ * - `scan`: read-only walk + report
26
+ * - `scan-and-fix`: applies the safest auto-fixes (currently md5 -> sha256)
27
+ * - `report-only`: identical to scan; never writes
28
+ */
29
+ export declare function runSecurityAgent(input: RunSecurityAgentInput): Promise<SecurityReport>;
30
+ export { RULES, RULES_BY_ID };
31
+ //# sourceMappingURL=security-agent.d.ts.map
@@ -0,0 +1,180 @@
1
+ // kbot Security Agent — unified app-sec scanner over guardian/hacker tools.
2
+ // Mirrors the Codex Security agent surface (March 2026). Single named entry
3
+ // point: runSecurityAgent({ target, mode }).
4
+ import * as fs from 'node:fs';
5
+ import * as path from 'node:path';
6
+ import { RULES, RULES_BY_ID, SEVERITY_RANK, shouldScan, } from './security-rules.js';
7
+ const SKIP_DIRS = new Set(['node_modules', 'dist', '.git', '.next', 'build', '.turbo']);
8
+ function walk(dir, out) {
9
+ let entries;
10
+ try {
11
+ entries = fs.readdirSync(dir, { withFileTypes: true });
12
+ }
13
+ catch {
14
+ return;
15
+ }
16
+ for (const e of entries) {
17
+ if (e.name.startsWith('.') && !e.name.startsWith('.env')) {
18
+ // skip hidden dirs except .env-style files
19
+ if (e.isDirectory())
20
+ continue;
21
+ }
22
+ const full = path.join(dir, e.name);
23
+ if (e.isDirectory()) {
24
+ if (SKIP_DIRS.has(e.name))
25
+ continue;
26
+ walk(full, out);
27
+ }
28
+ else if (e.isFile()) {
29
+ if (shouldScan(e.name))
30
+ out.push(full);
31
+ }
32
+ }
33
+ }
34
+ function rankCompare(a, b) {
35
+ const r = SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity];
36
+ if (r !== 0)
37
+ return r;
38
+ if (a.file !== b.file)
39
+ return a.file < b.file ? -1 : 1;
40
+ return (a.line ?? 0) - (b.line ?? 0);
41
+ }
42
+ function scanFile(file, mode) {
43
+ let text;
44
+ try {
45
+ text = fs.readFileSync(file, 'utf8');
46
+ }
47
+ catch {
48
+ return { findings: [], fixes: 0 };
49
+ }
50
+ const lines = text.split(/\r?\n/);
51
+ const findings = [];
52
+ let fixesApplied = 0;
53
+ let mutated = false;
54
+ const newLines = [...lines];
55
+ for (let i = 0; i < lines.length; i++) {
56
+ const line = lines[i];
57
+ const ctx = { file, line, lineNumber: i + 1, fullText: text };
58
+ for (const rule of RULES) {
59
+ if (rule.appliesTo && !rule.appliesTo(file))
60
+ continue;
61
+ const result = rule.test(ctx);
62
+ if (!result)
63
+ continue;
64
+ const hits = Array.isArray(result) ? result : [result];
65
+ for (const h of hits) {
66
+ let fixed;
67
+ if (mode === 'scan-and-fix' && h.fix) {
68
+ // Conservative: only apply when the find string appears verbatim.
69
+ if (newLines[i].includes(h.fix.find)) {
70
+ newLines[i] = newLines[i].replace(h.fix.find, h.fix.replace);
71
+ mutated = true;
72
+ fixesApplied++;
73
+ fixed = true;
74
+ }
75
+ }
76
+ else if (mode === 'scan-and-fix' && !h.fix) {
77
+ // Riskier patterns: log + skip with suggestion.
78
+ fixed = false;
79
+ }
80
+ findings.push({
81
+ id: h.id,
82
+ severity: h.severity,
83
+ category: h.category,
84
+ file,
85
+ line: i + 1,
86
+ description: h.description,
87
+ recommendation: h.recommendation,
88
+ fixed,
89
+ });
90
+ }
91
+ }
92
+ }
93
+ if (mode === 'scan-and-fix' && mutated) {
94
+ fs.writeFileSync(file, newLines.join('\n'));
95
+ }
96
+ return { findings, fixes: fixesApplied };
97
+ }
98
+ function buildSummary(report) {
99
+ const counts = {
100
+ critical: 0, high: 0, medium: 0, low: 0, info: 0,
101
+ };
102
+ for (const f of report.findings)
103
+ counts[f.severity]++;
104
+ const lines = [];
105
+ lines.push('# kbot Security Agent Report');
106
+ lines.push('');
107
+ lines.push(`- Files scanned: **${report.scanned}**`);
108
+ lines.push(`- Findings: **${report.findings.length}**`);
109
+ lines.push(`- Fixes applied: **${report.fixesApplied}**`);
110
+ lines.push('');
111
+ lines.push('## Severity breakdown');
112
+ lines.push('');
113
+ lines.push(`- critical: ${counts.critical}`);
114
+ lines.push(`- high: ${counts.high}`);
115
+ lines.push(`- medium: ${counts.medium}`);
116
+ lines.push(`- low: ${counts.low}`);
117
+ lines.push(`- info: ${counts.info}`);
118
+ lines.push('');
119
+ if (report.findings.length > 0) {
120
+ lines.push('## Findings');
121
+ lines.push('');
122
+ for (const f of report.findings) {
123
+ const loc = f.line ? `${f.file}:${f.line}` : f.file;
124
+ const fixedTag = f.fixed === true ? ' [FIXED]'
125
+ : f.fixed === false ? ' [skipped]' : '';
126
+ lines.push(`- **[${f.severity.toUpperCase()}] ${f.id}** ${f.description}${fixedTag}`);
127
+ lines.push(` - location: \`${loc}\``);
128
+ lines.push(` - recommendation: ${f.recommendation}`);
129
+ }
130
+ }
131
+ else {
132
+ lines.push('No findings — clean.');
133
+ }
134
+ return lines.join('\n');
135
+ }
136
+ /**
137
+ * Run the kbot security agent over a directory.
138
+ * - `scan`: read-only walk + report
139
+ * - `scan-and-fix`: applies the safest auto-fixes (currently md5 -> sha256)
140
+ * - `report-only`: identical to scan; never writes
141
+ */
142
+ export async function runSecurityAgent(input) {
143
+ const { target } = input;
144
+ const mode = input.mode ?? 'scan';
145
+ const files = [];
146
+ let stat;
147
+ try {
148
+ stat = fs.statSync(target);
149
+ }
150
+ catch {
151
+ const empty = {
152
+ scanned: 0, findings: [], fixesApplied: 0,
153
+ };
154
+ return { ...empty, summary: buildSummary(empty) };
155
+ }
156
+ if (stat.isDirectory()) {
157
+ walk(target, files);
158
+ }
159
+ else if (stat.isFile() && shouldScan(path.basename(target))) {
160
+ files.push(target);
161
+ }
162
+ const allFindings = [];
163
+ let fixesApplied = 0;
164
+ for (const file of files) {
165
+ const effectiveMode = mode === 'report-only' ? 'scan' : mode;
166
+ const { findings, fixes } = scanFile(file, effectiveMode);
167
+ allFindings.push(...findings);
168
+ fixesApplied += fixes;
169
+ }
170
+ allFindings.sort(rankCompare);
171
+ const base = {
172
+ scanned: files.length,
173
+ findings: allFindings,
174
+ fixesApplied,
175
+ };
176
+ return { ...base, summary: buildSummary(base) };
177
+ }
178
+ // Re-export the rule registry for callers that want to introspect / extend.
179
+ export { RULES, RULES_BY_ID };
180
+ //# sourceMappingURL=security-agent.js.map
@@ -0,0 +1,35 @@
1
+ export type Severity = 'critical' | 'high' | 'medium' | 'low' | 'info';
2
+ export interface RuleContext {
3
+ file: string;
4
+ line: string;
5
+ lineNumber: number;
6
+ fullText: string;
7
+ }
8
+ export interface RuleHit {
9
+ id: string;
10
+ severity: Severity;
11
+ category: string;
12
+ description: string;
13
+ recommendation: string;
14
+ /** Auto-fix descriptor — only present when a rule supports a safe fix. */
15
+ fix?: {
16
+ find: string;
17
+ replace: string;
18
+ label: string;
19
+ };
20
+ }
21
+ export interface Rule {
22
+ id: string;
23
+ category: string;
24
+ severity: Severity;
25
+ description: string;
26
+ recommendation: string;
27
+ appliesTo?: (file: string) => boolean;
28
+ test: (ctx: RuleContext) => RuleHit | RuleHit[] | null;
29
+ }
30
+ export declare const RULES: Rule[];
31
+ export declare const RULES_BY_ID: Record<string, Rule>;
32
+ export declare const SEVERITY_RANK: Record<Severity, number>;
33
+ export declare const SCANNABLE_EXT: RegExp;
34
+ export declare function shouldScan(file: string): boolean;
35
+ //# sourceMappingURL=security-rules.d.ts.map
@@ -0,0 +1,206 @@
1
+ // kbot Security Agent — static rule catalog.
2
+ // Lightweight regex / string scans. No AST. Each rule returns zero or more
3
+ // hits given a single line of source text plus filename context.
4
+ const isJsLike = (f) => /\.(ts|tsx|js|jsx|mjs|cjs)$/.test(f);
5
+ const isEnvLike = (f) => /(^|\/)\.env(\..+)?$/.test(f);
6
+ const isJson = (f) => f.endsWith('.json');
7
+ // Compact factory — most rules share the shape "regex on line -> emit hit".
8
+ function regexRule(opts) {
9
+ return {
10
+ id: opts.id,
11
+ category: opts.category,
12
+ severity: opts.severity,
13
+ description: opts.description,
14
+ recommendation: opts.recommendation,
15
+ appliesTo: opts.appliesTo,
16
+ test: (ctx) => {
17
+ if (!opts.pattern.test(ctx.line))
18
+ return null;
19
+ return {
20
+ id: opts.id,
21
+ severity: opts.severity,
22
+ category: opts.category,
23
+ description: opts.description,
24
+ recommendation: opts.recommendation,
25
+ fix: opts.fix ? opts.fix(ctx.line) : undefined,
26
+ };
27
+ },
28
+ };
29
+ }
30
+ export const RULES = [
31
+ regexRule({
32
+ id: 'SEC-001', category: 'secrets', severity: 'critical',
33
+ description: 'Hardcoded API key (sk-...) detected',
34
+ recommendation: 'Rotate the key and load from process.env instead.',
35
+ pattern: /['"`]sk-[A-Za-z0-9_-]{16,}['"`]/,
36
+ }),
37
+ regexRule({
38
+ id: 'SEC-002', category: 'secrets', severity: 'critical',
39
+ description: 'Hardcoded AWS access key id',
40
+ recommendation: 'Rotate via IAM and load from environment / role.',
41
+ pattern: /AKIA[0-9A-Z]{16}/,
42
+ }),
43
+ regexRule({
44
+ id: 'SEC-003', category: 'secrets', severity: 'critical',
45
+ description: 'Embedded BEGIN PRIVATE KEY block',
46
+ recommendation: 'Move to a secrets vault and rotate.',
47
+ pattern: /-----BEGIN (RSA |EC |OPENSSH |DSA |)PRIVATE KEY-----/,
48
+ }),
49
+ regexRule({
50
+ id: 'SEC-004', category: 'secrets', severity: 'high',
51
+ description: 'Inline password= "..." literal',
52
+ recommendation: 'Read from process.env or a secrets manager.',
53
+ pattern: /(?:^|[^A-Za-z_])password\s*[:=]\s*['"][^'"\s]{4,}['"]/i,
54
+ }),
55
+ regexRule({
56
+ id: 'SEC-005', category: 'secrets', severity: 'high',
57
+ description: 'AWS secret literal',
58
+ recommendation: 'Rotate and load from environment.',
59
+ pattern: /AWS_SECRET[_A-Z]*\s*[:=]\s*['"][^'"\s]{8,}['"]/,
60
+ }),
61
+ regexRule({
62
+ id: 'SEC-006', category: 'secrets', severity: 'critical',
63
+ description: 'GitHub personal-access token literal',
64
+ recommendation: 'Revoke immediately and use env var.',
65
+ pattern: /gh[pousr]_[A-Za-z0-9]{20,}/,
66
+ }),
67
+ regexRule({
68
+ id: 'SEC-007', category: 'code-injection', severity: 'high',
69
+ description: 'eval() call',
70
+ recommendation: 'Replace with a safe parser (JSON.parse, etc.).',
71
+ pattern: /(?:^|[^A-Za-z_$.])eval\s*\(/,
72
+ appliesTo: isJsLike,
73
+ }),
74
+ regexRule({
75
+ id: 'SEC-008', category: 'code-injection', severity: 'high',
76
+ description: 'new Function() constructor',
77
+ recommendation: 'Replace with predefined functions or sandbox.',
78
+ pattern: /new\s+Function\s*\(/,
79
+ appliesTo: isJsLike,
80
+ }),
81
+ regexRule({
82
+ id: 'SEC-009', category: 'command-injection', severity: 'high',
83
+ description: 'exec(`... ${...}`) template literal',
84
+ recommendation: 'Switch to execFile(cmd, [args]) — never interpolate.',
85
+ pattern: /\bexec\s*\(\s*`[^`]*\$\{/,
86
+ appliesTo: isJsLike,
87
+ }),
88
+ regexRule({
89
+ id: 'SEC-010', category: 'sql-injection', severity: 'high',
90
+ description: 'SQL string concatenation',
91
+ recommendation: 'Use parameter placeholders ($1, ?) with the driver.',
92
+ pattern: /['"`]\s*(SELECT|INSERT|UPDATE)\b[^'"`]*['"`]\s*\+/i,
93
+ appliesTo: isJsLike,
94
+ }),
95
+ regexRule({
96
+ id: 'SEC-011', category: 'xss', severity: 'high',
97
+ description: 'Dynamic innerHTML assignment',
98
+ recommendation: 'Use textContent or sanitise via DOMPurify.',
99
+ pattern: /\.innerHTML\s*=\s*[^'"`;]+(?:\+|\$\{|\()/,
100
+ appliesTo: isJsLike,
101
+ }),
102
+ regexRule({
103
+ id: 'SEC-012', category: 'xss', severity: 'high',
104
+ description: 'dangerouslySetInnerHTML bound to a variable',
105
+ recommendation: 'Sanitise the HTML string with DOMPurify first.',
106
+ pattern: /dangerouslySetInnerHTML\s*=\s*\{\{\s*__html\s*:\s*[A-Za-z_$][\w$.]*\s*\}\}/,
107
+ appliesTo: isJsLike,
108
+ }),
109
+ regexRule({
110
+ id: 'SEC-013', category: 'auth', severity: 'critical',
111
+ description: 'JWT alg "none" allowed',
112
+ recommendation: 'Pin algorithms (HS256/RS256); never accept "none".',
113
+ pattern: /(['"]alg['"]\s*:\s*['"]none['"]|algorithms\s*:\s*\[\s*['"]none['"])/i,
114
+ appliesTo: isJsLike,
115
+ }),
116
+ // SEC-014: contextual — Math.random() in crypto-adjacent code
117
+ {
118
+ id: 'SEC-014', category: 'crypto', severity: 'medium',
119
+ description: 'Math.random() in crypto-adjacent code',
120
+ recommendation: 'Replace with crypto.randomBytes(); leave a TODO marker.',
121
+ appliesTo: isJsLike,
122
+ test: (ctx) => {
123
+ if (!/Math\.random\s*\(\s*\)/.test(ctx.line))
124
+ return null;
125
+ const lower = ctx.line.toLowerCase();
126
+ if (!/(token|secret|nonce|salt|password|otp|sessionid|api_?key)/.test(lower))
127
+ return null;
128
+ return {
129
+ id: 'SEC-014', severity: 'medium', category: 'crypto',
130
+ description: 'Math.random() in crypto-adjacent code',
131
+ recommendation: 'Use crypto.randomBytes() instead.',
132
+ };
133
+ },
134
+ },
135
+ // SEC-015: weak hash — supports auto-fix for md5 only
136
+ {
137
+ id: 'SEC-015', category: 'crypto', severity: 'high',
138
+ description: "createHash('md5'/'sha1') is weak",
139
+ recommendation: "Replace with createHash('sha256') or use bcrypt/argon2 for passwords.",
140
+ appliesTo: isJsLike,
141
+ test: (ctx) => {
142
+ const m = /createHash\(\s*['"](md5|sha1)['"]\s*\)/.exec(ctx.line);
143
+ if (!m)
144
+ return null;
145
+ const algo = m[1];
146
+ return {
147
+ id: 'SEC-015', severity: 'high', category: 'crypto',
148
+ description: `createHash('${algo}') is weak`,
149
+ recommendation: `Replace with createHash('sha256') or bcrypt for passwords.`,
150
+ fix: algo === 'md5'
151
+ ? { find: `createHash('md5')`, replace: `createHash('sha256')`, label: 'md5 -> sha256' }
152
+ : undefined,
153
+ };
154
+ },
155
+ },
156
+ // SEC-016: open CORS * with credentials — needs whole-file context
157
+ {
158
+ id: 'SEC-016', category: 'cors', severity: 'high',
159
+ description: 'Wildcard CORS origin with credentials enabled',
160
+ recommendation: 'Echo a vetted Origin header instead of *.',
161
+ appliesTo: isJsLike,
162
+ test: (ctx) => {
163
+ if (!/Access-Control-Allow-Origin['"\s:,]+\*/i.test(ctx.line))
164
+ return null;
165
+ if (!/Access-Control-Allow-Credentials['"\s:,]+true/i.test(ctx.fullText))
166
+ return null;
167
+ return {
168
+ id: 'SEC-016', severity: 'high', category: 'cors',
169
+ description: 'Wildcard CORS origin with credentials enabled',
170
+ recommendation: 'Echo a vetted Origin header instead of *.',
171
+ };
172
+ },
173
+ },
174
+ // SEC-017: <form method="post"> with no csrf token anywhere in the file
175
+ {
176
+ id: 'SEC-017', category: 'csrf', severity: 'medium',
177
+ description: 'POST form without CSRF token',
178
+ recommendation: 'Add a hidden CSRF token input or use SameSite=strict cookies.',
179
+ test: (ctx) => {
180
+ if (!/<form[^>]*method\s*=\s*["']post["']/i.test(ctx.line))
181
+ return null;
182
+ if (/csrf|_token|csrfToken/i.test(ctx.fullText))
183
+ return null;
184
+ return {
185
+ id: 'SEC-017', severity: 'medium', category: 'csrf',
186
+ description: 'POST form without CSRF token',
187
+ recommendation: 'Add a hidden CSRF token input.',
188
+ };
189
+ },
190
+ },
191
+ ];
192
+ export const RULES_BY_ID = Object.fromEntries(RULES.map((r) => [r.id, r]));
193
+ export const SEVERITY_RANK = {
194
+ critical: 0, high: 1, medium: 2, low: 3, info: 4,
195
+ };
196
+ export const SCANNABLE_EXT = /\.(ts|tsx|js|jsx|mjs|cjs|json)$/;
197
+ export function shouldScan(file) {
198
+ if (isEnvLike(file))
199
+ return true;
200
+ if (SCANNABLE_EXT.test(file))
201
+ return true;
202
+ if (isJson(file))
203
+ return true;
204
+ return false;
205
+ }
206
+ //# sourceMappingURL=security-rules.js.map
@@ -3,6 +3,12 @@ export interface SpecialistDef {
3
3
  icon: string;
4
4
  color: string;
5
5
  prompt: string;
6
+ /**
7
+ * Optional whitelist of tool names this specialist is allowed to call.
8
+ * When omitted (or empty), the specialist inherits the global tool set.
9
+ * Used by the workspace agent runtime to gate tool execution per-specialist.
10
+ */
11
+ allowedTools?: string[];
6
12
  }
7
13
  export declare const SPECIALISTS: Record<string, SpecialistDef>;
8
14
  //# sourceMappingURL=specialists.d.ts.map
@@ -126,6 +126,7 @@ You know Ableton Live 12 at an expert level — 20 instruments, 58 audio effects
126
126
  prompt: `You are a Security specialist — a defensive security engineer who protects systems and users.
127
127
 
128
128
  When reviewing for security:
129
+ - Step 0 (always first): run \`security_agent_scan\` over the target directory in \`report-only\` mode. Surface critical+high findings before running heavier tools. Use \`security_agent_report\` to format the consolidated output.
129
130
  - Check OWASP Top 10: injection, broken auth, sensitive data exposure, XXE, broken access control, misconfiguration, XSS, insecure deserialization, vulnerable dependencies, insufficient logging
130
131
  - Review auth flows: JWT handling, session management, RBAC enforcement
131
132
  - Check for secrets in code: API keys, tokens, passwords, connection strings
@@ -135,6 +136,50 @@ When reviewing for security:
135
136
  - Rate findings: Critical (exploit now) → High → Medium → Low (hardening)
136
137
 
137
138
  You think like an attacker to defend like a guardian. Every input is hostile until proven otherwise.`,
139
+ allowedTools: [
140
+ // Unified security agent (always first — Step 0)
141
+ 'security_agent_scan',
142
+ 'security_agent_report',
143
+ // Code/file inspection
144
+ 'read_file',
145
+ 'grep',
146
+ 'glob',
147
+ 'list_directory',
148
+ // Dependency + secret scanning
149
+ 'dep_audit',
150
+ 'deps_audit',
151
+ 'secret_scan',
152
+ 'license_check',
153
+ // Vulnerability research / CVE
154
+ 'cve_lookup',
155
+ 'exploit_search',
156
+ 'attack_lookup',
157
+ 'threat_feed',
158
+ // Hardening + reporting
159
+ 'security_brain',
160
+ 'security_headers_generate',
161
+ 'security_hunt',
162
+ 'blueteam_checklist',
163
+ 'blueteam_harden',
164
+ 'redteam_report',
165
+ 'threat_model',
166
+ 'killchain_analyze',
167
+ 'incident_response',
168
+ 'forensics_analyze',
169
+ // Web/HTTP surface checks
170
+ 'headers_check',
171
+ 'cors_check',
172
+ 'ssl_check',
173
+ 'ssl_analyze',
174
+ 'owasp_check',
175
+ 'jwt_analyze',
176
+ // General workflow
177
+ 'web_search',
178
+ 'url_fetch',
179
+ 'agent_memory_read',
180
+ 'agent_memory_write',
181
+ 'team_handoff',
182
+ ],
138
183
  },
139
184
  curator: {
140
185
  name: 'Curator',
package/dist/architect.js CHANGED
@@ -320,6 +320,11 @@ function displayReport(report) {
320
320
  * @returns Full report of the architect session
321
321
  */
322
322
  export async function runArchitectMode(task, options = {}) {
323
+ // Default architect role to Claude Opus 4.7 (Apr 2026) — best long-horizon
324
+ // SWE + computer-use model. User --model flag still wins.
325
+ if (!options.model || options.model === 'auto') {
326
+ options = { ...options, model: 'claude-opus-4-7' };
327
+ }
323
328
  printInfo('Entering architect mode...');
324
329
  console.log();
325
330
  // Phase 1: Architect creates plan
package/dist/auth.js CHANGED
@@ -28,7 +28,7 @@ export const PROVIDERS = {
28
28
  inputCost: 3.0,
29
29
  outputCost: 15.0,
30
30
  authHeader: 'x-api-key',
31
- models: ['claude-mythos-1', 'claude-opus-4-6', 'claude-sonnet-4-6', 'claude-haiku-4-5-20251001'],
31
+ models: ['claude-mythos-1', 'claude-opus-4-7', 'claude-opus-4-6', 'claude-sonnet-4-6', 'claude-haiku-4-5-20251001'],
32
32
  },
33
33
  openai: {
34
34
  name: 'OpenAI',
@@ -0,0 +1,4 @@
1
+ import { type ChannelAdapter } from './types.js';
2
+ export declare const matrixAdapter: ChannelAdapter;
3
+ export default matrixAdapter;
4
+ //# sourceMappingURL=matrix.d.ts.map
@@ -0,0 +1,28 @@
1
+ // Matrix channel adapter — STUB.
2
+ //
3
+ // TODO: Implement against the Matrix Client-Server API.
4
+ // - MATRIX_HOMESERVER (e.g. https://matrix.org)
5
+ // - MATRIX_TOKEN (access token from /login or a registered application service)
6
+ // - MATRIX_USER_ID (optional — only required for some endpoints)
7
+ // - send → PUT {homeserver}/_matrix/client/v3/rooms/{roomId}/send/m.room.message/{txnId}
8
+ // - receive → /sync endpoint with `since` token (kbot persists the token
9
+ // between calls) → translate timeline events into ChannelMessage.
10
+ // - listChannels → /joined_rooms then resolve names via /state.
11
+ import { ChannelNotImplementedError } from './types.js';
12
+ export const matrixAdapter = {
13
+ name: 'matrix',
14
+ isConfigured() {
15
+ return Boolean(process.env.MATRIX_HOMESERVER && process.env.MATRIX_TOKEN);
16
+ },
17
+ async send() {
18
+ throw new ChannelNotImplementedError('matrix');
19
+ },
20
+ async receive() {
21
+ throw new ChannelNotImplementedError('matrix');
22
+ },
23
+ async listChannels() {
24
+ throw new ChannelNotImplementedError('matrix');
25
+ },
26
+ };
27
+ export default matrixAdapter;
28
+ //# sourceMappingURL=matrix.js.map
@@ -0,0 +1,78 @@
1
+ import type { ChannelAdapter, ChannelEnvelope, ChannelInfo, ChannelMessage, ChannelReceiveOptions } from './types.js';
2
+ interface GraphDriveItem {
3
+ id: string;
4
+ name?: string;
5
+ webUrl?: string;
6
+ folder?: {
7
+ childCount?: number;
8
+ };
9
+ file?: {
10
+ mimeType?: string;
11
+ };
12
+ size?: number;
13
+ }
14
+ export interface ListFilesOptions {
15
+ driveId?: string;
16
+ folderId?: string;
17
+ }
18
+ export interface ReadDocumentOptions {
19
+ fileId: string;
20
+ /** "raw" returns the file bytes as text; "worksheets" lists Excel sheets. */
21
+ format?: 'raw' | 'worksheets';
22
+ }
23
+ export interface WriteDocumentSectionOptions {
24
+ fileId: string;
25
+ /** Path within the document. Currently informational — see TODO below. */
26
+ sectionPath: string;
27
+ content: string | Uint8Array;
28
+ }
29
+ export interface AppendToWorkbookOptions {
30
+ fileId: string;
31
+ sheet: string;
32
+ rows: Array<Array<string | number | boolean | null>>;
33
+ /** Table name in the worksheet. Defaults to "Table1". */
34
+ table?: string;
35
+ }
36
+ export interface AddSlideOptions {
37
+ fileId: string;
38
+ layout?: string;
39
+ content?: Record<string, unknown>;
40
+ }
41
+ export declare class OfficeChannel implements ChannelAdapter {
42
+ readonly name = "office";
43
+ isConfigured(): boolean;
44
+ /** Post a comment to a drive item (file). `envelope.channel` is the fileId. */
45
+ send(envelope: ChannelEnvelope): Promise<{
46
+ id: string;
47
+ ts: number;
48
+ }>;
49
+ /** List comments on a drive item. `opts.channel` is the fileId. */
50
+ receive(opts: ChannelReceiveOptions): Promise<ChannelMessage[]>;
51
+ /** List drive root children. */
52
+ listChannels(): Promise<ChannelInfo[]>;
53
+ listFiles(options?: ListFilesOptions): Promise<GraphDriveItem[]>;
54
+ readDocument(options: ReadDocumentOptions): Promise<string | unknown>;
55
+ /**
56
+ * Replace an entire Word document with `content`.
57
+ *
58
+ * TODO(office-add-in): For surgical, paragraph- or range-level edits, route
59
+ * through an Office Add-in's `Word.Range` API (Word JavaScript API). The
60
+ * Graph REST surface does not expose Word ranges directly; the standard
61
+ * workaround is the Office Add-in JS bridge.
62
+ */
63
+ writeDocumentSection(options: WriteDocumentSectionOptions): Promise<{
64
+ id: string;
65
+ size?: number;
66
+ }>;
67
+ appendToWorkbook(options: AppendToWorkbookOptions): Promise<unknown>;
68
+ /**
69
+ * TODO(pptx): Microsoft Graph does not expose slide-level mutation. The
70
+ * workaround is to regenerate the .pptx (e.g. via `pptxgenjs`) and PUT the
71
+ * bytes back via `/me/drive/items/{fileId}/content`. Until that dependency
72
+ * is wired, this method throws.
73
+ */
74
+ addSlide(_options: AddSlideOptions): Promise<never>;
75
+ }
76
+ export declare const officeAdapter: ChannelAdapter;
77
+ export default officeAdapter;
78
+ //# sourceMappingURL=office.d.ts.map