@causyne/agent 1.10.13 → 1.10.18
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/docs/ARCHITECTURE.md +118 -17
- package/docs/SECURITY_REVIEW.md +13 -1
- package/docs/THIRD_PARTY_NOTICES.md +46 -10
- package/package.json +10 -4
- package/src/agentLoop.js +123 -21
- package/src/agentProfiles.js +135 -0
- package/src/agentProfiles.ts +143 -0
- package/src/approvalGrants.js +17 -35
- package/src/approvalGrants.ts +54 -0
- package/src/auditLog.d.ts +3 -0
- package/src/browser/browserRuntime.js +88 -0
- package/src/browser/browserRuntime.ts +107 -0
- package/src/browser/causyneBrowser.js +495 -0
- package/src/browser/causyneBrowser.ts +594 -0
- package/src/browser/searchProviders.js +156 -0
- package/src/browser/searchProviders.ts +156 -0
- package/src/browser/snapshotWindow.js +28 -0
- package/src/browser/snapshotWindow.ts +40 -0
- package/src/browser/urlPolicy.js +89 -0
- package/src/browser/urlPolicy.ts +89 -0
- package/src/checkpoints.js +19 -6
- package/src/cliMode.js +13 -1
- package/src/commands.js +37 -8
- package/src/config.d.ts +2 -0
- package/src/decisionPolicy.js +68 -0
- package/src/decisionPolicy.ts +106 -0
- package/src/hooks.js +121 -107
- package/src/hooks.ts +193 -0
- package/src/implementationPlan.js +50 -0
- package/src/implementationPlan.ts +59 -0
- package/src/mcp/mcpClient.js +520 -132
- package/src/mcp/mcpClient.ts +584 -0
- package/src/mcp/mcpRegistry.js +399 -185
- package/src/mcp/mcpRegistry.ts +451 -0
- package/src/planCache.js +473 -450
- package/src/planCache.ts +840 -0
- package/src/plugins/pluginManager.js +40 -5
- package/src/plugins/pluginSkills.js +84 -0
- package/src/plugins/pluginSkills.ts +106 -0
- package/src/provisioning/capabilityProvisioner.d.ts +10 -0
- package/src/provisioning/capabilityProvisioner.js +5 -0
- package/src/provisioning/capabilityTools.js +2 -1
- package/src/sandbox.js +32 -2
- package/src/sessionJournal.js +471 -0
- package/src/sessionJournal.ts +496 -0
- package/src/sessionStore.js +3 -279
- package/src/skillsManager.js +18 -0
- package/src/subagentJournal.js +215 -0
- package/src/subagentJournal.ts +254 -0
- package/src/taskShape.js +179 -198
- package/src/taskShape.ts +259 -0
- package/src/toolPacks.js +7 -5
- package/src/tools/browserAutomator.js +45 -241
- package/src/tools/browserAutomator.ts +57 -0
- package/src/tools/manageLocalAiDependencies.js +1 -1
- package/src/tools/registry.js +4 -3
- package/src/tools/requestDecision.js +87 -0
- package/src/tools/requestDecision.ts +107 -0
- package/src/tools/subagentOrchestrator.js +73 -12
- package/src/tools/updatePlan.js +37 -84
- package/src/tools/updatePlan.ts +48 -0
- package/src/tools/webFetch.js +183 -68
- package/src/tools/webFetch.ts +185 -0
- package/src/tools/webSearch.js +50 -75
- package/src/tools/webSearch.ts +56 -0
- package/src/tui/ask.js +41 -1
- package/src/tui/chatRenderer.js +28 -0
- package/src/tui/dialogs.js +91 -0
- package/src/tui/prompt.js +32 -3
- package/src/tui/toolDisplay.js +0 -1
- package/src/types/contracts.js +8 -0
- package/src/types/contracts.ts +263 -0
- package/src/utils/paths.d.ts +3 -0
- package/src/version.d.ts +1 -0
- package/src/mcp/jsonRpc.js +0 -165
- package/src/tools/todoWrite.js +0 -75
package/src/mcp/jsonRpc.js
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { spawn } from 'child_process';
|
|
4
|
-
|
|
5
|
-
const DEFAULT_TIMEOUT_MS = 30000;
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* A JSON-RPC channel over a child process's stdin/stdout.
|
|
9
|
-
*
|
|
10
|
-
* MCP frames stdio traffic as newline-delimited JSON. stderr is deliberately
|
|
11
|
-
* NOT parsed — servers use it for human-readable logging, and treating it as
|
|
12
|
-
* protocol is how a chatty server becomes a hang.
|
|
13
|
-
*/
|
|
14
|
-
export class StdioChannel {
|
|
15
|
-
constructor({ command, args = [], env = {}, cwd }) {
|
|
16
|
-
this.command = command;
|
|
17
|
-
this.args = args;
|
|
18
|
-
this.env = env;
|
|
19
|
-
this.cwd = cwd;
|
|
20
|
-
this.child = null;
|
|
21
|
-
this.buffer = '';
|
|
22
|
-
this.pending = new Map();
|
|
23
|
-
this.nextId = 1;
|
|
24
|
-
this.notificationHandler = null;
|
|
25
|
-
this.stderrTail = [];
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async open() {
|
|
29
|
-
this.child = spawn(this.command, this.args, {
|
|
30
|
-
cwd: this.cwd || process.cwd(),
|
|
31
|
-
env: { ...process.env, ...this.env },
|
|
32
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
this.child.stdout.setEncoding('utf-8');
|
|
36
|
-
this.child.stdout.on('data', chunk => this.#ingest(chunk));
|
|
37
|
-
|
|
38
|
-
// Kept only so a failure can be explained. Bounded, because a server that
|
|
39
|
-
|
|
40
|
-
this.child.stderr.setEncoding('utf-8');
|
|
41
|
-
this.child.stderr.on('data', chunk => {
|
|
42
|
-
this.stderrTail.push(String(chunk));
|
|
43
|
-
if (this.stderrTail.length > 20) this.stderrTail.shift();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
this.child.on('exit', code => this.#failAll(new Error(`server exited (code ${code})${this.lastError()}`)));
|
|
47
|
-
this.child.on('error', err => this.#failAll(err));
|
|
48
|
-
|
|
49
|
-
// Surface a command that does not exist as an error rather than a timeout.
|
|
50
|
-
await new Promise((resolve, reject) => {
|
|
51
|
-
const onError = (err) => reject(err);
|
|
52
|
-
this.child.once('error', onError);
|
|
53
|
-
setTimeout(() => { this.child.removeListener('error', onError); resolve(); }, 150);
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
lastError() {
|
|
58
|
-
const tail = this.stderrTail.join('').trim();
|
|
59
|
-
return tail ? `: ${tail.slice(-400)}` : '';
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
#ingest(chunk) {
|
|
63
|
-
this.buffer += chunk;
|
|
64
|
-
let newline;
|
|
65
|
-
while ((newline = this.buffer.indexOf('\n')) !== -1) {
|
|
66
|
-
const line = this.buffer.slice(0, newline).trim();
|
|
67
|
-
this.buffer = this.buffer.slice(newline + 1);
|
|
68
|
-
if (!line) continue;
|
|
69
|
-
let frame;
|
|
70
|
-
try { frame = JSON.parse(line); } catch (e) { continue; }
|
|
71
|
-
this.#dispatch(frame);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
#dispatch(frame) {
|
|
76
|
-
if (frame.id !== undefined && this.pending.has(frame.id)) {
|
|
77
|
-
const { resolve, reject, timer } = this.pending.get(frame.id);
|
|
78
|
-
this.pending.delete(frame.id);
|
|
79
|
-
clearTimeout(timer);
|
|
80
|
-
if (frame.error) reject(new Error(frame.error.message || 'JSON-RPC error'));
|
|
81
|
-
else resolve(frame.result);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
if (frame.method && this.notificationHandler) {
|
|
85
|
-
try { this.notificationHandler(frame); } catch (e) {}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
#failAll(error) {
|
|
90
|
-
for (const [, { reject, timer }] of this.pending) {
|
|
91
|
-
clearTimeout(timer);
|
|
92
|
-
reject(error);
|
|
93
|
-
}
|
|
94
|
-
this.pending.clear();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
send(method, params, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
98
|
-
if (!this.child || this.child.killed) return Promise.reject(new Error('channel is closed'));
|
|
99
|
-
const id = this.nextId++;
|
|
100
|
-
const frame = { jsonrpc: '2.0', id, method, params: params ?? {} };
|
|
101
|
-
|
|
102
|
-
return new Promise((resolve, reject) => {
|
|
103
|
-
const timer = setTimeout(() => {
|
|
104
|
-
this.pending.delete(id);
|
|
105
|
-
reject(new Error(`${method} timed out after ${timeoutMs} ms`));
|
|
106
|
-
}, timeoutMs);
|
|
107
|
-
this.pending.set(id, { resolve, reject, timer });
|
|
108
|
-
try {
|
|
109
|
-
this.child.stdin.write(JSON.stringify(frame) + '\n');
|
|
110
|
-
} catch (e) {
|
|
111
|
-
clearTimeout(timer);
|
|
112
|
-
this.pending.delete(id);
|
|
113
|
-
reject(e);
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
notify(method, params) {
|
|
119
|
-
if (!this.child || this.child.killed) return;
|
|
120
|
-
try {
|
|
121
|
-
this.child.stdin.write(JSON.stringify({ jsonrpc: '2.0', method, params: params ?? {} }) + '\n');
|
|
122
|
-
} catch (e) {}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
close() {
|
|
126
|
-
this.#failAll(new Error('channel closed'));
|
|
127
|
-
try { if (this.child) this.child.kill('SIGTERM'); } catch (e) {}
|
|
128
|
-
this.child = null;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** A JSON-RPC channel over HTTP POST. One request, one response, no session. */
|
|
133
|
-
export class HttpChannel {
|
|
134
|
-
constructor({ url, headers = {} }) {
|
|
135
|
-
this.url = url;
|
|
136
|
-
this.headers = headers;
|
|
137
|
-
this.nextId = 1;
|
|
138
|
-
this.notificationHandler = null;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
async open() {
|
|
142
|
-
// Nothing to open. Validate the URL now so a typo fails at connect time
|
|
143
|
-
// rather than on the first tool call.
|
|
144
|
-
const parsed = new URL(this.url);
|
|
145
|
-
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
146
|
-
throw new Error(`unsupported transport protocol: ${parsed.protocol}`);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
async send(method, params, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
151
|
-
const response = await fetch(this.url, {
|
|
152
|
-
method: 'POST',
|
|
153
|
-
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', ...this.headers },
|
|
154
|
-
body: JSON.stringify({ jsonrpc: '2.0', id: this.nextId++, method, params: params ?? {} }),
|
|
155
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
156
|
-
});
|
|
157
|
-
if (!response.ok) throw new Error(`HTTP ${response.status} from ${this.url}`);
|
|
158
|
-
const frame = await response.json();
|
|
159
|
-
if (frame.error) throw new Error(frame.error.message || 'JSON-RPC error');
|
|
160
|
-
return frame.result;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
notify() { /* HTTP transport has no notification channel */ }
|
|
164
|
-
close() { /* nothing to close */ }
|
|
165
|
-
}
|
package/src/tools/todoWrite.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
|
|
4
|
-
export const todoWrite = {
|
|
5
|
-
schema: {
|
|
6
|
-
name: 'TodoWrite',
|
|
7
|
-
description: 'Dynamic Context Injector: Create and manage structured tasks. Forces atomic, granular steps instead of vague goals.',
|
|
8
|
-
parameters: {
|
|
9
|
-
type: 'object',
|
|
10
|
-
properties: {
|
|
11
|
-
todos: {
|
|
12
|
-
type: 'array',
|
|
13
|
-
description: 'The updated todo list (JSON state)',
|
|
14
|
-
items: {
|
|
15
|
-
type: 'object',
|
|
16
|
-
properties: {
|
|
17
|
-
id: { type: 'string' },
|
|
18
|
-
content: { type: 'string', minLength: 5, description: 'Must be an atomic, actionable step.' },
|
|
19
|
-
status: {
|
|
20
|
-
type: 'string',
|
|
21
|
-
enum: ['pending', 'in_progress', 'completed']
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
required: ['id', 'content', 'status'],
|
|
25
|
-
additionalProperties: false
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
required: ['todos'],
|
|
30
|
-
additionalProperties: false
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
async execute(args) {
|
|
34
|
-
try {
|
|
35
|
-
const todoPath = path.join(process.cwd(), '.causyne_todos.json');
|
|
36
|
-
const mdPath = path.join(process.cwd(), 'AGI_PLAN.md');
|
|
37
|
-
|
|
38
|
-
// Phase 2 AGI Upgrade: Atomic Validation
|
|
39
|
-
for (const task of args.todos) {
|
|
40
|
-
const words = task.content.split(' ').length;
|
|
41
|
-
if (words < 2) {
|
|
42
|
-
return { success: false, error: `Task [${task.id}] is too vague ("${task.content}"). Rewrite it into an atomic, actionable step (e.g., "Scaffold React Navbar").` };
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Save JSON state
|
|
47
|
-
await fs.writeFile(todoPath, JSON.stringify(args.todos, null, 2), 'utf8');
|
|
48
|
-
|
|
49
|
-
// Render to MD for user visibility
|
|
50
|
-
let mdContent = '# AGI Execution Plan\n\n';
|
|
51
|
-
for (const t of args.todos) {
|
|
52
|
-
let box = '[ ]';
|
|
53
|
-
if (t.status === 'in_progress') box = '[/]';
|
|
54
|
-
if (t.status === 'completed') box = '[x]';
|
|
55
|
-
mdContent += `- ${box} ${t.id}: ${t.content}\n`;
|
|
56
|
-
}
|
|
57
|
-
await fs.writeFile(mdPath, mdContent, 'utf8');
|
|
58
|
-
|
|
59
|
-
// Phase 2 AGI Upgrade: Memory Hook (Signaling back to AgentLoop context window)
|
|
60
|
-
const inProgress = args.todos.filter(t => t.status === 'in_progress');
|
|
61
|
-
const completed = args.todos.filter(t => t.status === 'completed');
|
|
62
|
-
|
|
63
|
-
if (inProgress.length > 1) {
|
|
64
|
-
return { success: true, output: `[Memory Hook] Saved. WARNING: You have ${inProgress.length} tasks in_progress. Only ONE task may be in progress at a time.` };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
success: true,
|
|
69
|
-
output: `[Memory Hook] Tasks synchronized. ${completed.length} completed, ${inProgress.length} active. Focus locked on: ${inProgress[0]?.content || 'None'}`
|
|
70
|
-
};
|
|
71
|
-
} catch (e) {
|
|
72
|
-
return { success: false, error: e.message };
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
};
|