aiden-runtime 3.16.0
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/LICENSE +661 -0
- package/README.md +465 -0
- package/config/devos.config.json +186 -0
- package/config/hardware.json +9 -0
- package/config/model-selection.json +7 -0
- package/config/setup-complete.json +20 -0
- package/dist/api/routes/computerUse.js +112 -0
- package/dist/api/server.js +6870 -0
- package/dist/bin/npx-init.js +71 -0
- package/dist/coordination/commandGate.js +115 -0
- package/dist/coordination/livePulse.js +127 -0
- package/dist/core/agentLoop.js +2718 -0
- package/dist/core/agentShield.js +231 -0
- package/dist/core/aidenIdentity.js +215 -0
- package/dist/core/aidenPersonality.js +166 -0
- package/dist/core/aidenSdk.js +374 -0
- package/dist/core/asyncTasks.js +82 -0
- package/dist/core/auditTrail.js +61 -0
- package/dist/core/auxiliaryClient.js +114 -0
- package/dist/core/bgLLM.js +108 -0
- package/dist/core/bm25.js +68 -0
- package/dist/core/callbackSystem.js +64 -0
- package/dist/core/channels/adapter.js +6 -0
- package/dist/core/channels/discord.js +173 -0
- package/dist/core/channels/email.js +253 -0
- package/dist/core/channels/imessage.js +164 -0
- package/dist/core/channels/manager.js +96 -0
- package/dist/core/channels/signal.js +140 -0
- package/dist/core/channels/slack.js +139 -0
- package/dist/core/channels/twilio.js +144 -0
- package/dist/core/channels/webhook.js +186 -0
- package/dist/core/channels/whatsapp.js +185 -0
- package/dist/core/clarifyBus.js +75 -0
- package/dist/core/codeInterpreter.js +82 -0
- package/dist/core/computerControl.js +439 -0
- package/dist/core/conversationMemory.js +334 -0
- package/dist/core/costTracker.js +221 -0
- package/dist/core/cronManager.js +217 -0
- package/dist/core/deepKB.js +77 -0
- package/dist/core/doctor.js +279 -0
- package/dist/core/dreamEngine.js +334 -0
- package/dist/core/entityGraph.js +169 -0
- package/dist/core/eventBus.js +16 -0
- package/dist/core/evolutionAnalyzer.js +153 -0
- package/dist/core/executionLoop.js +309 -0
- package/dist/core/executor.js +224 -0
- package/dist/core/failureAnalyzer.js +166 -0
- package/dist/core/fastPathExpansion.js +82 -0
- package/dist/core/faultEngine.js +106 -0
- package/dist/core/featureGates.js +70 -0
- package/dist/core/fileIngestion.js +113 -0
- package/dist/core/gateway.js +97 -0
- package/dist/core/goalTracker.js +75 -0
- package/dist/core/growthEngine.js +168 -0
- package/dist/core/hardwareDetector.js +98 -0
- package/dist/core/hooks.js +45 -0
- package/dist/core/httpKeepalive.js +46 -0
- package/dist/core/hybridSearch.js +101 -0
- package/dist/core/importers.js +164 -0
- package/dist/core/instinctSystem.js +223 -0
- package/dist/core/knowledgeBase.js +351 -0
- package/dist/core/learningMemory.js +121 -0
- package/dist/core/lessonsBrowser.js +125 -0
- package/dist/core/licenseManager.js +399 -0
- package/dist/core/logBuffer.js +85 -0
- package/dist/core/machineId.js +87 -0
- package/dist/core/mcpClient.js +442 -0
- package/dist/core/memoryDistiller.js +165 -0
- package/dist/core/memoryExtractor.js +212 -0
- package/dist/core/memoryIds.js +213 -0
- package/dist/core/memoryPreamble.js +113 -0
- package/dist/core/memoryQuery.js +136 -0
- package/dist/core/memoryRecall.js +140 -0
- package/dist/core/memoryStrategy.js +201 -0
- package/dist/core/messageValidator.js +85 -0
- package/dist/core/modelDiscovery.js +108 -0
- package/dist/core/modelRouter.js +118 -0
- package/dist/core/morningBriefing.js +203 -0
- package/dist/core/multiGoalValidator.js +51 -0
- package/dist/core/parallelExecutor.js +43 -0
- package/dist/core/passiveSkillObserver.js +204 -0
- package/dist/core/paths.js +57 -0
- package/dist/core/patternDetector.js +83 -0
- package/dist/core/planResponseRepair.js +64 -0
- package/dist/core/planTool.js +111 -0
- package/dist/core/playwrightBridge.js +356 -0
- package/dist/core/pluginSystem.js +121 -0
- package/dist/core/privateMode.js +85 -0
- package/dist/core/reactLoop.js +156 -0
- package/dist/core/recipeEngine.js +166 -0
- package/dist/core/responseCache.js +128 -0
- package/dist/core/runSandbox.js +132 -0
- package/dist/core/sandboxRunner.js +200 -0
- package/dist/core/scheduler.js +543 -0
- package/dist/core/secretScanner.js +49 -0
- package/dist/core/semanticMemory.js +223 -0
- package/dist/core/sessionMemory.js +259 -0
- package/dist/core/sessionRouter.js +91 -0
- package/dist/core/sessionSearch.js +163 -0
- package/dist/core/setupWizard.js +225 -0
- package/dist/core/skillImporter.js +303 -0
- package/dist/core/skillLibrary.js +144 -0
- package/dist/core/skillLoader.js +471 -0
- package/dist/core/skillTeacher.js +352 -0
- package/dist/core/skillValidator.js +210 -0
- package/dist/core/skillWriter.js +384 -0
- package/dist/core/slashAsTool.js +226 -0
- package/dist/core/spawnManager.js +197 -0
- package/dist/core/statusVerbs.js +43 -0
- package/dist/core/swarmManager.js +109 -0
- package/dist/core/taskQueue.js +119 -0
- package/dist/core/taskRecovery.js +128 -0
- package/dist/core/taskState.js +168 -0
- package/dist/core/telegramBot.js +152 -0
- package/dist/core/todoManager.js +70 -0
- package/dist/core/toolNameRepair.js +71 -0
- package/dist/core/toolRegistry.js +2730 -0
- package/dist/core/tools/calendarTool.js +98 -0
- package/dist/core/tools/companyFilingsTool.js +98 -0
- package/dist/core/tools/gmailTool.js +87 -0
- package/dist/core/tools/marketDataTool.js +135 -0
- package/dist/core/tools/socialResearchTool.js +121 -0
- package/dist/core/truthCheck.js +57 -0
- package/dist/core/updateChecker.js +74 -0
- package/dist/core/userCognitionProfile.js +238 -0
- package/dist/core/userProfile.js +341 -0
- package/dist/core/version.js +5 -0
- package/dist/core/visionAnalyze.js +161 -0
- package/dist/core/voice/audio.js +187 -0
- package/dist/core/voice/stt.js +226 -0
- package/dist/core/voice/tts.js +310 -0
- package/dist/core/voiceInput.js +118 -0
- package/dist/core/voiceOutput.js +130 -0
- package/dist/core/webSearch.js +326 -0
- package/dist/core/workflowTracker.js +72 -0
- package/dist/core/workspaceMemory.js +54 -0
- package/dist/core/youtubeTranscript.js +224 -0
- package/dist/integrations/computerUse/apiRegistry.js +113 -0
- package/dist/integrations/computerUse/screenAgent.js +203 -0
- package/dist/integrations/computerUse/visionLoop.js +296 -0
- package/dist/memory/memoryLayers.js +143 -0
- package/dist/providers/boa.js +93 -0
- package/dist/providers/cerebras.js +70 -0
- package/dist/providers/custom.js +89 -0
- package/dist/providers/gemini.js +82 -0
- package/dist/providers/groq.js +92 -0
- package/dist/providers/index.js +149 -0
- package/dist/providers/nvidia.js +70 -0
- package/dist/providers/ollama.js +99 -0
- package/dist/providers/openrouter.js +74 -0
- package/dist/providers/router.js +497 -0
- package/dist/providers/types.js +6 -0
- package/dist/security/browserVault.js +129 -0
- package/dist/security/dataGuard.js +89 -0
- package/dist/tools/eonetTool.js +72 -0
- package/dist/types/computerUse.js +2 -0
- package/dist/types/executor.js +2 -0
- package/dist-bundle/cli.js +357859 -0
- package/package.json +256 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// DevOS — Autonomous AI Execution System
|
|
4
|
+
// Copyright (c) 2026 Shiva Deore. All rights reserved.
|
|
5
|
+
// ============================================================
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.setSessionPrivate = setSessionPrivate;
|
|
8
|
+
exports.clearSessionPrivate = clearSessionPrivate;
|
|
9
|
+
exports.toggleSessionPrivate = toggleSessionPrivate;
|
|
10
|
+
exports.isSessionPrivate = isSessionPrivate;
|
|
11
|
+
exports.markNextTurnPrivate = markNextTurnPrivate;
|
|
12
|
+
exports.isCurrentTurnPrivate = isCurrentTurnPrivate;
|
|
13
|
+
exports.clearTurnPrivate = clearTurnPrivate;
|
|
14
|
+
// core/privateMode.ts — Phase 5: Per-turn and per-session memory opacity toggle.
|
|
15
|
+
//
|
|
16
|
+
// When private mode is active for a session, all memory writes are suppressed:
|
|
17
|
+
// - memoryLayers.write()
|
|
18
|
+
// - sessionMemory.addExchange()
|
|
19
|
+
// - memoryExtractor.extractFromSession()
|
|
20
|
+
// - conversationMemory.addAssistantMessage() (skipped at call site)
|
|
21
|
+
//
|
|
22
|
+
// Two granularities:
|
|
23
|
+
// Session-level — ALL turns in the session are private until explicitly toggled off.
|
|
24
|
+
// Turn-level — Only the NEXT turn is private; flag auto-clears after that turn.
|
|
25
|
+
//
|
|
26
|
+
// Usage (server.ts):
|
|
27
|
+
// if (!isCurrentTurnPrivate(sessionId)) {
|
|
28
|
+
// sessionMemory.addExchange(...)
|
|
29
|
+
// memoryLayers.write(...)
|
|
30
|
+
// }
|
|
31
|
+
// clearTurnPrivate(sessionId) // always call after turn completes
|
|
32
|
+
//
|
|
33
|
+
// Usage (CLI, /private command):
|
|
34
|
+
// const nowPrivate = toggleSessionPrivate(sessionId)
|
|
35
|
+
// console.log(nowPrivate ? '🔒 Private mode ON' : '🔓 Private mode OFF')
|
|
36
|
+
// ── State ─────────────────────────────────────────────────────────────────────
|
|
37
|
+
/** Sessions where every turn is private until explicitly cleared. */
|
|
38
|
+
const sessionPrivate = new Set();
|
|
39
|
+
/** Sessions where only the NEXT single turn is private (auto-clears). */
|
|
40
|
+
const nextTurnPrivate = new Set();
|
|
41
|
+
// ── Session-level API ─────────────────────────────────────────────────────────
|
|
42
|
+
/** Mark all future turns in `sessionId` as private. */
|
|
43
|
+
function setSessionPrivate(sessionId) {
|
|
44
|
+
sessionPrivate.add(sessionId);
|
|
45
|
+
}
|
|
46
|
+
/** Remove session-level private flag (turns private mode off for the session). */
|
|
47
|
+
function clearSessionPrivate(sessionId) {
|
|
48
|
+
sessionPrivate.delete(sessionId);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Toggle session-level private mode.
|
|
52
|
+
* @returns true when private mode is now ON, false when now OFF.
|
|
53
|
+
*/
|
|
54
|
+
function toggleSessionPrivate(sessionId) {
|
|
55
|
+
if (sessionPrivate.has(sessionId)) {
|
|
56
|
+
sessionPrivate.delete(sessionId);
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
sessionPrivate.add(sessionId);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
/** True when the entire session is set to private. */
|
|
63
|
+
function isSessionPrivate(sessionId) {
|
|
64
|
+
return sessionPrivate.has(sessionId);
|
|
65
|
+
}
|
|
66
|
+
// ── Turn-level API ────────────────────────────────────────────────────────────
|
|
67
|
+
/** Mark only the NEXT turn for `sessionId` as private. Auto-clears after that turn. */
|
|
68
|
+
function markNextTurnPrivate(sessionId) {
|
|
69
|
+
nextTurnPrivate.add(sessionId);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Returns true when the current turn should suppress all memory writes.
|
|
73
|
+
* Checks both session-level and turn-level flags.
|
|
74
|
+
*/
|
|
75
|
+
function isCurrentTurnPrivate(sessionId) {
|
|
76
|
+
return sessionPrivate.has(sessionId) || nextTurnPrivate.has(sessionId);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Clear the one-turn private flag for `sessionId`.
|
|
80
|
+
* Call this after every turn completes, regardless of private state —
|
|
81
|
+
* it is a no-op when the flag is not set.
|
|
82
|
+
*/
|
|
83
|
+
function clearTurnPrivate(sessionId) {
|
|
84
|
+
nextTurnPrivate.delete(sessionId);
|
|
85
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// DevOS — Autonomous AI Execution System
|
|
4
|
+
// Copyright (c) 2026 Shiva Deore. All rights reserved.
|
|
5
|
+
// ============================================================
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.runReActLoop = runReActLoop;
|
|
8
|
+
// core/reactLoop.ts — ReAct (Reasoning + Acting) agent loop.
|
|
9
|
+
// Iterates: Thought → Action → Observation until FINISH or maxIterations.
|
|
10
|
+
const agentLoop_1 = require("./agentLoop");
|
|
11
|
+
const toolRegistry_1 = require("./toolRegistry");
|
|
12
|
+
const livePulse_1 = require("../coordination/livePulse");
|
|
13
|
+
// ── ReAct system prompt ────────────────────────────────────────
|
|
14
|
+
const REACT_SYSTEM = `You are DevOS ReAct agent. You solve goals iteratively using a Thought→Action→Observation loop.
|
|
15
|
+
|
|
16
|
+
At each step, output ONLY valid JSON with this shape:
|
|
17
|
+
{
|
|
18
|
+
"reasoning": "what you're thinking",
|
|
19
|
+
"action": "tool_name or FINISH",
|
|
20
|
+
"actionInput": { ...tool args },
|
|
21
|
+
"finalAnswer": "only when action is FINISH — the complete answer"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
Available tools:
|
|
25
|
+
- web_search { "query": "..." }
|
|
26
|
+
- fetch_url { "url": "https://..." }
|
|
27
|
+
- file_read { "path": "..." }
|
|
28
|
+
- file_write { "path": "...", "content": "..." }
|
|
29
|
+
- run_python { "script": "..." }
|
|
30
|
+
- run_node { "code": "..." }
|
|
31
|
+
- shell_exec { "command": "..." }
|
|
32
|
+
- system_info {}
|
|
33
|
+
- deep_research { "topic": "..." }
|
|
34
|
+
- get_stocks { "market": "NSE", "type": "gainers" }
|
|
35
|
+
|
|
36
|
+
Rules:
|
|
37
|
+
1. Think step-by-step. Use observations from previous steps.
|
|
38
|
+
2. When you have enough information to answer, set action to FINISH and provide finalAnswer.
|
|
39
|
+
3. Output ONLY valid JSON — no markdown, no prose outside the JSON.
|
|
40
|
+
4. If a tool fails, adapt your approach in the next thought.`;
|
|
41
|
+
// ── Parse LLM output to Thought ────────────────────────────────
|
|
42
|
+
function parseThought(raw) {
|
|
43
|
+
try {
|
|
44
|
+
const cleaned = raw
|
|
45
|
+
.replace(/```json\s*/g, '')
|
|
46
|
+
.replace(/```\s*/g, '')
|
|
47
|
+
.trim();
|
|
48
|
+
const match = cleaned.match(/\{[\s\S]*\}/);
|
|
49
|
+
if (!match)
|
|
50
|
+
return null;
|
|
51
|
+
const parsed = JSON.parse(match[0]);
|
|
52
|
+
return {
|
|
53
|
+
reasoning: String(parsed.reasoning || ''),
|
|
54
|
+
action: String(parsed.action || 'FINISH'),
|
|
55
|
+
actionInput: parsed.actionInput && typeof parsed.actionInput === 'object'
|
|
56
|
+
? parsed.actionInput
|
|
57
|
+
: {},
|
|
58
|
+
finalAnswer: parsed.finalAnswer ? String(parsed.finalAnswer) : undefined,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// ── Build context string from prior steps ──────────────────────
|
|
66
|
+
function buildContext(goal, steps) {
|
|
67
|
+
let ctx = `Goal: ${goal}\n\n`;
|
|
68
|
+
for (let i = 0; i < steps.length; i++) {
|
|
69
|
+
const s = steps[i];
|
|
70
|
+
ctx += `Step ${i + 1}:\n`;
|
|
71
|
+
ctx += ` Thought: ${s.thought.reasoning}\n`;
|
|
72
|
+
ctx += ` Action: ${s.thought.action}(${JSON.stringify(s.thought.actionInput)})\n`;
|
|
73
|
+
ctx += ` Observation: ${s.observation.success ? 'OK' : 'ERROR'} — ${s.observation.result.slice(0, 400)}\n\n`;
|
|
74
|
+
}
|
|
75
|
+
ctx += 'What is your next thought and action? Output JSON only.';
|
|
76
|
+
return ctx;
|
|
77
|
+
}
|
|
78
|
+
// ── Main ReAct loop ────────────────────────────────────────────
|
|
79
|
+
async function runReActLoop(goal, apiKey, model, provider, onStep, maxIterations = 5) {
|
|
80
|
+
const steps = [];
|
|
81
|
+
let answer = '';
|
|
82
|
+
livePulse_1.livePulse.act('ReAct', `Starting: ${goal.slice(0, 60)}`);
|
|
83
|
+
for (let iter = 0; iter < maxIterations; iter++) {
|
|
84
|
+
// Build prompt from system + accumulated context
|
|
85
|
+
const contextPrompt = `${REACT_SYSTEM}\n\n${buildContext(goal, steps)}`;
|
|
86
|
+
// Call LLM
|
|
87
|
+
let raw = '';
|
|
88
|
+
try {
|
|
89
|
+
raw = await (0, agentLoop_1.callLLM)(contextPrompt, apiKey, model, provider);
|
|
90
|
+
}
|
|
91
|
+
catch (e) {
|
|
92
|
+
console.warn(`[ReAct] LLM call failed at iter ${iter}: ${e.message}`);
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
if (!raw || raw.trim().length === 0) {
|
|
96
|
+
console.warn(`[ReAct] Empty LLM response at iter ${iter}`);
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
// Parse thought
|
|
100
|
+
const thought = parseThought(raw);
|
|
101
|
+
if (!thought) {
|
|
102
|
+
console.warn(`[ReAct] Could not parse thought at iter ${iter}: ${raw.slice(0, 100)}`);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
livePulse_1.livePulse.act('ReAct', `Iter ${iter + 1}: ${thought.action}`);
|
|
106
|
+
// FINISH — return final answer
|
|
107
|
+
if (thought.action === 'FINISH') {
|
|
108
|
+
answer = thought.finalAnswer || thought.reasoning || 'Done.';
|
|
109
|
+
// Record a terminal step with a synthetic observation
|
|
110
|
+
const terminalStep = {
|
|
111
|
+
thought,
|
|
112
|
+
observation: { tool: 'FINISH', result: answer, success: true },
|
|
113
|
+
};
|
|
114
|
+
steps.push(terminalStep);
|
|
115
|
+
onStep(terminalStep);
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
// Execute the tool
|
|
119
|
+
let toolResult = '';
|
|
120
|
+
let toolSuccess = false;
|
|
121
|
+
try {
|
|
122
|
+
const result = await (0, toolRegistry_1.executeTool)(thought.action, thought.actionInput);
|
|
123
|
+
toolResult = typeof result.output === 'string'
|
|
124
|
+
? result.output
|
|
125
|
+
: JSON.stringify(result.output || result);
|
|
126
|
+
toolSuccess = result.success !== false;
|
|
127
|
+
}
|
|
128
|
+
catch (e) {
|
|
129
|
+
toolResult = `Tool error: ${e.message}`;
|
|
130
|
+
toolSuccess = false;
|
|
131
|
+
}
|
|
132
|
+
const step = {
|
|
133
|
+
thought,
|
|
134
|
+
observation: {
|
|
135
|
+
tool: thought.action,
|
|
136
|
+
result: toolResult,
|
|
137
|
+
success: toolSuccess,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
steps.push(step);
|
|
141
|
+
onStep(step);
|
|
142
|
+
console.log(`[ReAct] Iter ${iter + 1}: ${thought.action} → ${toolSuccess ? 'OK' : 'ERR'} (${toolResult.slice(0, 80)})`);
|
|
143
|
+
}
|
|
144
|
+
// If loop exhausted without FINISH, summarise what was gathered
|
|
145
|
+
if (!answer && steps.length > 0) {
|
|
146
|
+
const lastObs = steps[steps.length - 1].observation;
|
|
147
|
+
answer = lastObs.success
|
|
148
|
+
? lastObs.result.slice(0, 800)
|
|
149
|
+
: `Could not complete the goal after ${steps.length} steps. Last error: ${lastObs.result.slice(0, 200)}`;
|
|
150
|
+
}
|
|
151
|
+
if (!answer) {
|
|
152
|
+
answer = 'I was unable to complete the task.';
|
|
153
|
+
}
|
|
154
|
+
livePulse_1.livePulse.done('ReAct', `Finished in ${steps.length} step(s)`);
|
|
155
|
+
return { answer, steps };
|
|
156
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// DevOS — Autonomous AI Execution System
|
|
4
|
+
// Copyright (c) 2026 Shiva Deore. All rights reserved.
|
|
5
|
+
// ============================================================
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.loadAllRecipes = loadAllRecipes;
|
|
11
|
+
exports.executeRecipe = executeRecipe;
|
|
12
|
+
exports.matchRecipe = matchRecipe;
|
|
13
|
+
// core/recipeEngine.ts — YAML workflow definitions with typed
|
|
14
|
+
// params, explicit tool chains, conditions, and retry logic.
|
|
15
|
+
const fs_1 = __importDefault(require("fs"));
|
|
16
|
+
const path_1 = __importDefault(require("path"));
|
|
17
|
+
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
18
|
+
const toolRegistry_1 = require("./toolRegistry");
|
|
19
|
+
// ── Loader ────────────────────────────────────────────────────
|
|
20
|
+
function loadRecipe(filePath) {
|
|
21
|
+
try {
|
|
22
|
+
const content = fs_1.default.readFileSync(filePath, 'utf8');
|
|
23
|
+
const recipe = js_yaml_1.default.load(content);
|
|
24
|
+
if (!recipe.name || !recipe.steps || recipe.steps.length === 0) {
|
|
25
|
+
console.log(`[Recipe] Invalid recipe: ${filePath} — missing name or steps`);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
return recipe;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
console.log(`[Recipe] Failed to parse: ${filePath}`);
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function loadAllRecipes() {
|
|
36
|
+
const recipeDirs = [
|
|
37
|
+
path_1.default.join(process.cwd(), 'workspace', 'recipes'),
|
|
38
|
+
path_1.default.join(process.cwd(), 'recipes'),
|
|
39
|
+
];
|
|
40
|
+
const recipes = [];
|
|
41
|
+
for (const dir of recipeDirs) {
|
|
42
|
+
if (!fs_1.default.existsSync(dir))
|
|
43
|
+
continue;
|
|
44
|
+
const files = fs_1.default.readdirSync(dir).filter(f => f.endsWith('.yaml') || f.endsWith('.yml'));
|
|
45
|
+
for (const file of files) {
|
|
46
|
+
const recipe = loadRecipe(path_1.default.join(dir, file));
|
|
47
|
+
if (recipe)
|
|
48
|
+
recipes.push(recipe);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (recipes.length > 0) {
|
|
52
|
+
console.log(`[Recipe] Loaded ${recipes.length} recipes`);
|
|
53
|
+
}
|
|
54
|
+
return recipes;
|
|
55
|
+
}
|
|
56
|
+
// ── Template resolution ───────────────────────────────────────
|
|
57
|
+
function resolveTemplate(template, params, stepResults) {
|
|
58
|
+
return template.replace(/\{\{(\w+\.[\w.]+)\}\}/g, (match, dotPath) => {
|
|
59
|
+
const parts = dotPath.split('.');
|
|
60
|
+
if (parts[0] === 'params') {
|
|
61
|
+
return String(params[parts[1]] ?? match);
|
|
62
|
+
}
|
|
63
|
+
if (parts[0] === 'steps') {
|
|
64
|
+
const stepResult = stepResults[parts[1]];
|
|
65
|
+
if (stepResult && parts[2]) {
|
|
66
|
+
return String(stepResult[parts[2]] ?? stepResult.output ?? match);
|
|
67
|
+
}
|
|
68
|
+
return String(stepResult?.output ?? match);
|
|
69
|
+
}
|
|
70
|
+
return match;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function resolveArgs(args, params, stepResults) {
|
|
74
|
+
const resolved = {};
|
|
75
|
+
for (const [key, value] of Object.entries(args)) {
|
|
76
|
+
resolved[key] = typeof value === 'string'
|
|
77
|
+
? resolveTemplate(value, params, stepResults)
|
|
78
|
+
: value;
|
|
79
|
+
}
|
|
80
|
+
return resolved;
|
|
81
|
+
}
|
|
82
|
+
// ── Condition evaluator ───────────────────────────────────────
|
|
83
|
+
function evaluateCondition(condition, stepResults) {
|
|
84
|
+
try {
|
|
85
|
+
const resolved = condition.replace(/steps\.(\w+)\.(\w+)/g, (_full, stepId, prop) => {
|
|
86
|
+
const result = stepResults[stepId];
|
|
87
|
+
if (!result)
|
|
88
|
+
return 'undefined';
|
|
89
|
+
return JSON.stringify(result[prop] ?? result.output);
|
|
90
|
+
});
|
|
91
|
+
// eslint-disable-next-line no-new-func
|
|
92
|
+
return new Function(`return ${resolved}`)();
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return true; // default to true if condition can't be evaluated
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// ── Executor ──────────────────────────────────────────────────
|
|
99
|
+
async function executeRecipe(recipe, params) {
|
|
100
|
+
console.log(`[Recipe] Executing: ${recipe.name}`);
|
|
101
|
+
// Apply defaults for missing optional params
|
|
102
|
+
for (const [name, def] of Object.entries(recipe.params || {})) {
|
|
103
|
+
if (!(name in params) && 'default' in def) {
|
|
104
|
+
params[name] = def.default;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const stepResults = {};
|
|
108
|
+
for (const step of recipe.steps) {
|
|
109
|
+
// Evaluate condition gate
|
|
110
|
+
if (step.condition) {
|
|
111
|
+
try {
|
|
112
|
+
if (!evaluateCondition(step.condition, stepResults)) {
|
|
113
|
+
console.log(`[Recipe] Skipping ${step.id}: condition not met`);
|
|
114
|
+
stepResults[step.id] = { skipped: true };
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch { }
|
|
119
|
+
}
|
|
120
|
+
const resolvedArgs = resolveArgs(step.args || {}, params, stepResults);
|
|
121
|
+
console.log(`[Recipe] Step ${step.id}: ${step.tool}`);
|
|
122
|
+
const maxAttempts = step.retries ?? 1;
|
|
123
|
+
let result = null;
|
|
124
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
125
|
+
try {
|
|
126
|
+
result = await (0, toolRegistry_1.executeTool)(step.tool, resolvedArgs);
|
|
127
|
+
if (result?.success)
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
if (attempt === maxAttempts - 1) {
|
|
132
|
+
if (step.onFail === 'abort') {
|
|
133
|
+
return { success: false, output: `Recipe aborted at step ${step.id}: ${err}` };
|
|
134
|
+
}
|
|
135
|
+
// 'skip' or no onFail — mark skipped and continue
|
|
136
|
+
result = { skipped: true, error: String(err), output: '' };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
stepResults[step.id] = result;
|
|
141
|
+
}
|
|
142
|
+
const output = resolveTemplate(recipe.output || '', params, stepResults);
|
|
143
|
+
return { success: true, output };
|
|
144
|
+
}
|
|
145
|
+
function matchRecipe(message, recipes) {
|
|
146
|
+
for (const recipe of recipes) {
|
|
147
|
+
for (const trigger of (recipe.trigger || [])) {
|
|
148
|
+
// Extract param names from {{param}} placeholders
|
|
149
|
+
const paramNames = (trigger.match(/\{\{(\w+)\}\}/g) || [])
|
|
150
|
+
.map(p => p.replace(/[{}]/g, ''));
|
|
151
|
+
// Build a regex by replacing {{param}} with capture groups
|
|
152
|
+
const escaped = trigger.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
153
|
+
const pattern = escaped.replace(/\\\{\\\{(\w+)\\\}\\\}/g, '(.+)');
|
|
154
|
+
const match = message.match(new RegExp(`^${pattern}$`, 'i'));
|
|
155
|
+
if (match) {
|
|
156
|
+
console.log(`[Recipe] Matched: ${recipe.name} via trigger: "${trigger}"`);
|
|
157
|
+
const params = {};
|
|
158
|
+
paramNames.forEach((name, i) => {
|
|
159
|
+
params[name] = match[i + 1]?.trim();
|
|
160
|
+
});
|
|
161
|
+
return { recipe, params };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// DevOS — Autonomous AI Execution System
|
|
4
|
+
// Copyright (c) 2026 Shiva Deore. All rights reserved.
|
|
5
|
+
// ============================================================
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.responseCache = exports.ResponseCache = void 0;
|
|
11
|
+
// core/responseCache.ts — TTL-based response cache for tool results.
|
|
12
|
+
// Tools with defined TTLs get their outputs cached and reused within
|
|
13
|
+
// the TTL window. Side-effectful tools (file_write, shell_exec, etc.)
|
|
14
|
+
// are explicitly excluded via NO_CACHE_TOOLS.
|
|
15
|
+
const fs_1 = __importDefault(require("fs"));
|
|
16
|
+
const path_1 = __importDefault(require("path"));
|
|
17
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
18
|
+
const CACHE_PATH = path_1.default.join(process.cwd(), 'workspace', 'cache', 'response-cache.json');
|
|
19
|
+
// TTL per tool type (milliseconds)
|
|
20
|
+
const TOOL_TTL = {
|
|
21
|
+
system_info: 30 * 1000, // 30 seconds — hardware changes rarely
|
|
22
|
+
get_market_data: 5 * 60 * 1000, // 5 minutes — prices update frequently
|
|
23
|
+
get_company_info: 60 * 60 * 1000, // 1 hour — fundamentals change slowly
|
|
24
|
+
get_stocks: 5 * 60 * 1000, // 5 minutes
|
|
25
|
+
social_research: 30 * 60 * 1000, // 30 minutes
|
|
26
|
+
web_search: 10 * 60 * 1000, // 10 minutes
|
|
27
|
+
fetch_url: 15 * 60 * 1000, // 15 minutes
|
|
28
|
+
fetch_page: 15 * 60 * 1000, // 15 minutes
|
|
29
|
+
};
|
|
30
|
+
// Tools that should NEVER be cached (side-effectful or time-sensitive)
|
|
31
|
+
const NO_CACHE_TOOLS = new Set([
|
|
32
|
+
'file_write', 'file_read', 'shell_exec', 'run_python',
|
|
33
|
+
'run_node', 'screenshot', 'notify', 'open_browser',
|
|
34
|
+
'browser_click', 'browser_type', 'mouse_click', 'keyboard_type',
|
|
35
|
+
'code_interpreter_python', 'code_interpreter_node',
|
|
36
|
+
]);
|
|
37
|
+
class ResponseCache {
|
|
38
|
+
constructor() {
|
|
39
|
+
this.cache = new Map();
|
|
40
|
+
this.load();
|
|
41
|
+
// Cleanup expired entries every 5 minutes
|
|
42
|
+
setInterval(() => this.cleanup(), 5 * 60 * 1000);
|
|
43
|
+
}
|
|
44
|
+
// ── Key hashing ───────────────────────────────────────────────
|
|
45
|
+
hashKey(tool, input) {
|
|
46
|
+
const str = `${tool}:${JSON.stringify(input, Object.keys(input).sort())}`;
|
|
47
|
+
return crypto_1.default.createHash('md5').update(str).digest('hex');
|
|
48
|
+
}
|
|
49
|
+
// ── Cache read ────────────────────────────────────────────────
|
|
50
|
+
get(tool, input) {
|
|
51
|
+
if (NO_CACHE_TOOLS.has(tool))
|
|
52
|
+
return null;
|
|
53
|
+
const key = this.hashKey(tool, input);
|
|
54
|
+
const entry = this.cache.get(key);
|
|
55
|
+
if (!entry)
|
|
56
|
+
return null;
|
|
57
|
+
if (Date.now() > entry.expiresAt) {
|
|
58
|
+
this.cache.delete(key);
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
entry.hitCount++;
|
|
62
|
+
console.log(`[Cache] HIT: ${tool} (hits: ${entry.hitCount})`);
|
|
63
|
+
return entry.output;
|
|
64
|
+
}
|
|
65
|
+
// ── Cache write ───────────────────────────────────────────────
|
|
66
|
+
set(tool, input, output) {
|
|
67
|
+
if (NO_CACHE_TOOLS.has(tool))
|
|
68
|
+
return;
|
|
69
|
+
const ttl = TOOL_TTL[tool];
|
|
70
|
+
if (!ttl)
|
|
71
|
+
return; // Only cache tools with a defined TTL
|
|
72
|
+
const key = this.hashKey(tool, input);
|
|
73
|
+
this.cache.set(key, {
|
|
74
|
+
key,
|
|
75
|
+
output,
|
|
76
|
+
tool,
|
|
77
|
+
input,
|
|
78
|
+
createdAt: Date.now(),
|
|
79
|
+
expiresAt: Date.now() + ttl,
|
|
80
|
+
hitCount: 0,
|
|
81
|
+
});
|
|
82
|
+
this.save();
|
|
83
|
+
}
|
|
84
|
+
// ── Stats ─────────────────────────────────────────────────────
|
|
85
|
+
getStats() {
|
|
86
|
+
const tools = {};
|
|
87
|
+
let totalHits = 0;
|
|
88
|
+
for (const entry of this.cache.values()) {
|
|
89
|
+
tools[entry.tool] = (tools[entry.tool] || 0) + 1;
|
|
90
|
+
totalHits += entry.hitCount;
|
|
91
|
+
}
|
|
92
|
+
return { totalEntries: this.cache.size, totalHits, tools };
|
|
93
|
+
}
|
|
94
|
+
// ── Clear all ─────────────────────────────────────────────────
|
|
95
|
+
clear() {
|
|
96
|
+
this.cache.clear();
|
|
97
|
+
this.save();
|
|
98
|
+
}
|
|
99
|
+
// ── Expired entry cleanup ─────────────────────────────────────
|
|
100
|
+
cleanup() {
|
|
101
|
+
const now = Date.now();
|
|
102
|
+
for (const [key, entry] of this.cache) {
|
|
103
|
+
if (now > entry.expiresAt)
|
|
104
|
+
this.cache.delete(key);
|
|
105
|
+
}
|
|
106
|
+
this.save();
|
|
107
|
+
}
|
|
108
|
+
// ── Persistence ───────────────────────────────────────────────
|
|
109
|
+
load() {
|
|
110
|
+
try {
|
|
111
|
+
if (!fs_1.default.existsSync(CACHE_PATH))
|
|
112
|
+
return;
|
|
113
|
+
const data = JSON.parse(fs_1.default.readFileSync(CACHE_PATH, 'utf-8'));
|
|
114
|
+
this.cache = new Map(Object.entries(data));
|
|
115
|
+
this.cleanup(); // Remove expired entries on load
|
|
116
|
+
}
|
|
117
|
+
catch { }
|
|
118
|
+
}
|
|
119
|
+
save() {
|
|
120
|
+
try {
|
|
121
|
+
fs_1.default.mkdirSync(path_1.default.dirname(CACHE_PATH), { recursive: true });
|
|
122
|
+
fs_1.default.writeFileSync(CACHE_PATH, JSON.stringify(Object.fromEntries(this.cache), null, 2));
|
|
123
|
+
}
|
|
124
|
+
catch { }
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
exports.ResponseCache = ResponseCache;
|
|
128
|
+
exports.responseCache = new ResponseCache();
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// DevOS — Autonomous AI Execution System
|
|
4
|
+
// Copyright (c) 2026 Shiva Deore. All rights reserved.
|
|
5
|
+
// ============================================================
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.runInSandbox = runInSandbox;
|
|
8
|
+
// core/runSandbox.ts — Node.js VM-based sandbox for the ▲ run tool.
|
|
9
|
+
//
|
|
10
|
+
// Injects the Aiden SDK (`aiden` namespace) into a fresh VM context.
|
|
11
|
+
// All tool calls are tracked. Timeout and max-tool-call limits enforced.
|
|
12
|
+
// No new dependencies — uses built-in `vm` module only.
|
|
13
|
+
const vm_1 = require("vm");
|
|
14
|
+
const aidenSdk_1 = require("./aidenSdk");
|
|
15
|
+
// ── Sandbox ───────────────────────────────────────────────────────────────────
|
|
16
|
+
const DEFAULT_TIMEOUT = 30000; // 30 s
|
|
17
|
+
const DEFAULT_MAX_TOOL_CALLS = 20;
|
|
18
|
+
async function runInSandbox(code, options = {}) {
|
|
19
|
+
const timeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
20
|
+
const maxToolCalls = options.maxToolCalls ?? DEFAULT_MAX_TOOL_CALLS;
|
|
21
|
+
const start = Date.now();
|
|
22
|
+
const outputLog = [];
|
|
23
|
+
const toolCalls = [];
|
|
24
|
+
// ── Build SDK runtime with tracking ───────────────────────────────────────
|
|
25
|
+
const sdk = (0, aidenSdk_1.buildSdkRuntime)((toolName, args) => {
|
|
26
|
+
if (toolCalls.length >= maxToolCalls) {
|
|
27
|
+
throw new Error(`Tool call limit exceeded (max ${maxToolCalls}). Aborting.`);
|
|
28
|
+
}
|
|
29
|
+
const callStart = Date.now();
|
|
30
|
+
// We push after the fact — duration set to 0 here, patched in wrapper below
|
|
31
|
+
toolCalls.push({ tool: toolName, args, durationMs: 0 });
|
|
32
|
+
const idx = toolCalls.length - 1;
|
|
33
|
+
// Patch the duration after the call completes (via a post-hook approach)
|
|
34
|
+
const origTime = callStart;
|
|
35
|
+
// We'll update durationMs via the wrapper in makeTrackedSdk
|
|
36
|
+
void origTime; // suppress unused warning; timing done differently below
|
|
37
|
+
void idx;
|
|
38
|
+
});
|
|
39
|
+
// ── Patch SDK to record timing per-call ───────────────────────────────────
|
|
40
|
+
const trackedSdk = patchSdkTiming(sdk, toolCalls, maxToolCalls);
|
|
41
|
+
// ── Sandboxed console ─────────────────────────────────────────────────────
|
|
42
|
+
const sandboxConsole = {
|
|
43
|
+
log: (...a) => outputLog.push(a.map(String).join(' ')),
|
|
44
|
+
warn: (...a) => outputLog.push('[warn] ' + a.map(String).join(' ')),
|
|
45
|
+
error: (...a) => outputLog.push('[error] ' + a.map(String).join(' ')),
|
|
46
|
+
info: (...a) => outputLog.push(a.map(String).join(' ')),
|
|
47
|
+
};
|
|
48
|
+
// ── VM context — minimal globals ─────────────────────────────────────────
|
|
49
|
+
const ctx = (0, vm_1.createContext)({
|
|
50
|
+
aiden: trackedSdk,
|
|
51
|
+
console: sandboxConsole,
|
|
52
|
+
JSON,
|
|
53
|
+
Math,
|
|
54
|
+
Date,
|
|
55
|
+
parseInt,
|
|
56
|
+
parseFloat,
|
|
57
|
+
isNaN,
|
|
58
|
+
isFinite,
|
|
59
|
+
String,
|
|
60
|
+
Number,
|
|
61
|
+
Boolean,
|
|
62
|
+
Array,
|
|
63
|
+
Object,
|
|
64
|
+
Promise,
|
|
65
|
+
setTimeout,
|
|
66
|
+
clearTimeout,
|
|
67
|
+
setInterval,
|
|
68
|
+
clearInterval,
|
|
69
|
+
});
|
|
70
|
+
// ── Wrap user code in async IIFE ─────────────────────────────────────────
|
|
71
|
+
const wrappedCode = `
|
|
72
|
+
(async () => {
|
|
73
|
+
${code}
|
|
74
|
+
})()
|
|
75
|
+
`;
|
|
76
|
+
try {
|
|
77
|
+
const script = new vm_1.Script(wrappedCode);
|
|
78
|
+
const resultPromise = script.runInContext(ctx, { timeout: Math.min(timeout, 5000) });
|
|
79
|
+
// The script.runInContext timeout only applies to sync execution.
|
|
80
|
+
// For async, we race with a wall-clock timeout.
|
|
81
|
+
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error(`Sandbox timeout after ${timeout}ms`)), timeout));
|
|
82
|
+
const finalValue = await Promise.race([resultPromise, timeoutPromise]);
|
|
83
|
+
return {
|
|
84
|
+
success: true,
|
|
85
|
+
output: outputLog,
|
|
86
|
+
result: finalValue,
|
|
87
|
+
toolCalls,
|
|
88
|
+
durationMs: Date.now() - start,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
return {
|
|
93
|
+
success: false,
|
|
94
|
+
output: outputLog,
|
|
95
|
+
error: err?.message ?? String(err),
|
|
96
|
+
toolCalls,
|
|
97
|
+
durationMs: Date.now() - start,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// ── Timing patcher ────────────────────────────────────────────────────────────
|
|
102
|
+
/**
|
|
103
|
+
* Walks the SDK object tree and wraps every async function to record
|
|
104
|
+
* actual call duration in the toolCalls array.
|
|
105
|
+
*/
|
|
106
|
+
function patchSdkTiming(sdkNode, toolCalls, maxToolCalls) {
|
|
107
|
+
if (typeof sdkNode === 'function') {
|
|
108
|
+
return async (...args) => {
|
|
109
|
+
if (toolCalls.length >= maxToolCalls) {
|
|
110
|
+
throw new Error(`Tool call limit exceeded (max ${maxToolCalls}). Aborting.`);
|
|
111
|
+
}
|
|
112
|
+
const t0 = Date.now();
|
|
113
|
+
try {
|
|
114
|
+
return await sdkNode(...args);
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
// Find the last entry that was pushed by the inner onToolCall handler
|
|
118
|
+
const last = toolCalls[toolCalls.length - 1];
|
|
119
|
+
if (last)
|
|
120
|
+
last.durationMs = Date.now() - t0;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
if (typeof sdkNode === 'object' && sdkNode !== null) {
|
|
125
|
+
const patched = {};
|
|
126
|
+
for (const [k, v] of Object.entries(sdkNode)) {
|
|
127
|
+
patched[k] = patchSdkTiming(v, toolCalls, maxToolCalls);
|
|
128
|
+
}
|
|
129
|
+
return patched;
|
|
130
|
+
}
|
|
131
|
+
return sdkNode;
|
|
132
|
+
}
|