mouaif 0.3.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 +21 -0
- package/README.md +140 -0
- package/bin/mouaif.js +281 -0
- package/frontend/dist/assets/AgentFilePicker-CcKLJorU.js +1 -0
- package/frontend/dist/assets/CliModal-Hs5phmNZ.js +7 -0
- package/frontend/dist/assets/DictationPage-BI23lp42.js +2 -0
- package/frontend/dist/assets/FileEditor-DDl31c6d.js +2 -0
- package/frontend/dist/assets/GitModal-3EC_gpJ5.js +2 -0
- package/frontend/dist/assets/Inspector-Ba3R1w04.js +73 -0
- package/frontend/dist/assets/SettingsAbout-bvZGDEDw.js +1 -0
- package/frontend/dist/assets/SettingsActions-Dk6WX9jv.js +1 -0
- package/frontend/dist/assets/SettingsAgents-BNV0MgDB.js +1 -0
- package/frontend/dist/assets/SettingsDefaults-DbMmQbzc.js +1 -0
- package/frontend/dist/assets/SettingsHiddenContent-BZ2sloH1.js +1 -0
- package/frontend/dist/assets/SettingsMcp-DOrfbQd1.js +1 -0
- package/frontend/dist/assets/SettingsMcpEdit-BGMQ2CWC.js +3 -0
- package/frontend/dist/assets/SettingsMcpRegistry-BywXee_A.js +1 -0
- package/frontend/dist/assets/SettingsNotifications-B0LEs11a.js +1 -0
- package/frontend/dist/assets/SettingsPricing-BAg33iVF.js +1 -0
- package/frontend/dist/assets/SettingsProject-DNrKhCcZ.js +14 -0
- package/frontend/dist/assets/SettingsProjects-IqkBfDcm.js +1 -0
- package/frontend/dist/assets/SettingsPrompts-BgeiASuk.js +1 -0
- package/frontend/dist/assets/SettingsProviders-k0xJN0IK.js +1 -0
- package/frontend/dist/assets/SettingsTags-B5kjFdQi.js +1 -0
- package/frontend/dist/assets/agentNavigation-BiiCpFz5.js +1 -0
- package/frontend/dist/assets/codemirror-Bp6CUUFk.js +30 -0
- package/frontend/dist/assets/index-BGvI4n0T.js +61 -0
- package/frontend/dist/assets/index-Bgg1gnDf.css +1 -0
- package/frontend/dist/assets/index-C1sQFIC-.css +1 -0
- package/frontend/dist/assets/index-CANPYzQg.css +1 -0
- package/frontend/dist/assets/index-Crn1LdzK.css +1 -0
- package/frontend/dist/assets/index-FbCWDPiB.css +1 -0
- package/frontend/dist/assets/projectQS-D1cSZ7Gr.js +1 -0
- package/frontend/dist/assets/virtual-list-6H9b4K51.js +1 -0
- package/frontend/dist/icons/favicon-32.png +0 -0
- package/frontend/dist/icons/icon-180-apple.png +0 -0
- package/frontend/dist/icons/icon-192.png +0 -0
- package/frontend/dist/icons/icon-512.png +0 -0
- package/frontend/dist/icons/icon-maskable-512.png +0 -0
- package/frontend/dist/index.html +83 -0
- package/frontend/dist/manifest.webmanifest +33 -0
- package/frontend/dist/sw.js +482 -0
- package/package.json +98 -0
- package/scripts/patch-zimmerframe.js +58 -0
- package/src/access-auth.js +515 -0
- package/src/agentFeatures.js +294 -0
- package/src/agentFiles.js +164 -0
- package/src/agentSkills.js +147 -0
- package/src/agents.js +230 -0
- package/src/ai-chat.js +21 -0
- package/src/ai-endpoints.js +1880 -0
- package/src/ai-stream.js +2048 -0
- package/src/ai.js +68 -0
- package/src/auth.js +391 -0
- package/src/chatdb.js +816 -0
- package/src/chats.js +275 -0
- package/src/custom-actions.js +65 -0
- package/src/files.js +431 -0
- package/src/hideFileContent.js +327 -0
- package/src/http-server.js +535 -0
- package/src/index.js +15 -0
- package/src/inspector.js +731 -0
- package/src/inspectorProfiles.js +503 -0
- package/src/live-chat.js +107 -0
- package/src/mcp.js +1517 -0
- package/src/messages.js +238 -0
- package/src/modelList.js +137 -0
- package/src/notifications.js +52 -0
- package/src/oauth-anthropic.js +280 -0
- package/src/oauth-github-copilot.js +417 -0
- package/src/oauth-mcp.js +216 -0
- package/src/oauth-openrouter.js +285 -0
- package/src/package-version.js +20 -0
- package/src/projects.js +285 -0
- package/src/promptProfiles.js +256 -0
- package/src/prompts.js +384 -0
- package/src/providerShapes.js +44 -0
- package/src/providers/base.js +41 -0
- package/src/providers/index.js +25 -0
- package/src/push.js +315 -0
- package/src/qr.js +192 -0
- package/src/restart.js +47 -0
- package/src/server-handlers-access.js +306 -0
- package/src/server-handlers-actions.js +100 -0
- package/src/server-handlers-ai.js +248 -0
- package/src/server-handlers-auth.js +273 -0
- package/src/server-handlers-chats.js +1436 -0
- package/src/server-handlers-git.js +467 -0
- package/src/server-handlers-mcp-oauth.js +56 -0
- package/src/server-handlers-misc.js +783 -0
- package/src/server-handlers-projects.js +289 -0
- package/src/server-handlers-prompts.js +259 -0
- package/src/server-handlers-push.js +102 -0
- package/src/server-handlers-settings.js +406 -0
- package/src/server-handlers-tools.js +654 -0
- package/src/server-handlers-transcribe.js +399 -0
- package/src/server-shared.js +780 -0
- package/src/server-web-static.js +191 -0
- package/src/settings.js +898 -0
- package/src/statusBar.js +541 -0
- package/src/tags.js +414 -0
- package/src/toolFeedback.js +225 -0
- package/src/tools/ask.js +154 -0
- package/src/tools/authorization.js +932 -0
- package/src/tools/files.js +1150 -0
- package/src/tools/progress.js +71 -0
- package/src/tools/restart.js +32 -0
- package/src/tools/searchEngine.js +957 -0
- package/src/tools/shell.js +341 -0
- package/src/tools/subagent.js +47 -0
- package/src/tools/task.js +234 -0
- package/src/tools/webpreview.js +448 -0
- package/src/trace.js +103 -0
- package/src/transcribe.js +683 -0
- package/src/usage.js +389 -0
- package/src/util.js +151 -0
package/src/agents.js
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Project agents — named personas stored in <projectDir>/.mouaif.json
|
|
4
|
+
// under `agents`. An agent is used exclusively as a delegation target
|
|
5
|
+
// for the native `subagent` tool: when the model calls
|
|
6
|
+
// subagent({ task, agent: "<name>" }), the nested call's system
|
|
7
|
+
// message is the agent's instructions, optionally restricted to the
|
|
8
|
+
// agent's tool allowlist.
|
|
9
|
+
//
|
|
10
|
+
// Each agent = { name, content, tools?, modelId?, providerId?, thinkingLevel?, createdAt, updatedAt }
|
|
11
|
+
// name: user-defined, unique per project, immutable. Matches
|
|
12
|
+
// NAME_RE. This is the value the subagent `agent` argument
|
|
13
|
+
// is matched against.
|
|
14
|
+
// content: the persona instructions. Injected as the nested call's
|
|
15
|
+
// system message. Capped at MAX_BYTES on write.
|
|
16
|
+
// tools: optional array of tool names the nested call may use.
|
|
17
|
+
// Absent/empty = inherit the parent's full tool surface.
|
|
18
|
+
// modelId: optional model id the nested call runs on. It may be a
|
|
19
|
+
// project model or a live-catalog model when providerId is set.
|
|
20
|
+
// Absent/empty = inherit the chat's model and provider.
|
|
21
|
+
// providerId: provider connection for the pinned model. Required for a
|
|
22
|
+
// live-catalog model; inferred for legacy project-model pins.
|
|
23
|
+
// thinkingLevel: optional reasoning-effort override for the nested
|
|
24
|
+
// call (same values as the chat's thinking dropdown).
|
|
25
|
+
// Absent/empty = inherit the chat's thinking level.
|
|
26
|
+
//
|
|
27
|
+
// Agents are NOT chat personas — a chat-level persona is what custom
|
|
28
|
+
// prompts are for. Nothing in the stream path, chat record, or project
|
|
29
|
+
// record references an agent.
|
|
30
|
+
//
|
|
31
|
+
// Storage migration: the legacy `agentPresets` key (entries carried
|
|
32
|
+
// extra fields like title/modelId/promptSize) is read as a fallback
|
|
33
|
+
// for one release; only { id->name, content, tools } survive.
|
|
34
|
+
|
|
35
|
+
const settings = require('./settings.js');
|
|
36
|
+
|
|
37
|
+
const MAX_BYTES = 64 * 1024;
|
|
38
|
+
const NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
39
|
+
|
|
40
|
+
function isValidName(name) {
|
|
41
|
+
return typeof name === 'string' && NAME_RE.test(name);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function capContent(content) {
|
|
45
|
+
let text = typeof content === 'string' ? content : '';
|
|
46
|
+
if (Buffer.byteLength(text, 'utf8') > MAX_BYTES) {
|
|
47
|
+
// Trim to the byte cap on a char boundary, then note the cut.
|
|
48
|
+
let buf = Buffer.from(text, 'utf8').subarray(0, MAX_BYTES);
|
|
49
|
+
text = buf.toString('utf8');
|
|
50
|
+
text += '\n\n[... truncated at ' + MAX_BYTES + ' bytes ...]';
|
|
51
|
+
}
|
|
52
|
+
return text;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function normalizeAgent(raw) {
|
|
56
|
+
if (!raw || typeof raw !== 'object') return null;
|
|
57
|
+
// Accept legacy { id, title, content, tools } entries: id becomes
|
|
58
|
+
// the name, everything else is dropped.
|
|
59
|
+
const name = typeof raw.name === 'string' && raw.name ? raw.name
|
|
60
|
+
: (typeof raw.id === 'string' && raw.id ? raw.id : null);
|
|
61
|
+
if (!name || !isValidName(name)) return null;
|
|
62
|
+
const tools = Array.isArray(raw.tools)
|
|
63
|
+
? raw.tools.map(String).map((s) => s.trim()).filter(Boolean)
|
|
64
|
+
: undefined;
|
|
65
|
+
const modelId = typeof raw.modelId === 'string' && raw.modelId.trim() ? raw.modelId.trim() : undefined;
|
|
66
|
+
const providerId = typeof raw.providerId === 'string' && raw.providerId.trim() ? raw.providerId.trim() : undefined;
|
|
67
|
+
const thinkingLevel = typeof raw.thinkingLevel === 'string' && raw.thinkingLevel.trim() ? raw.thinkingLevel.trim() : undefined;
|
|
68
|
+
return {
|
|
69
|
+
name,
|
|
70
|
+
content: capContent(raw.content),
|
|
71
|
+
tools: tools && tools.length ? tools : undefined,
|
|
72
|
+
modelId,
|
|
73
|
+
providerId,
|
|
74
|
+
thinkingLevel,
|
|
75
|
+
createdAt: raw.createdAt || new Date().toISOString(),
|
|
76
|
+
updatedAt: raw.updatedAt || raw.createdAt || new Date().toISOString()
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Resolve an agent's project or live-catalog model pin. A provider-qualified
|
|
81
|
+
// pin can be hydrated from its app-level connection even when the model is
|
|
82
|
+
// not duplicated in project settings; legacy unqualified pins stay strict.
|
|
83
|
+
function resolveModel(projectDir, agent) {
|
|
84
|
+
const modelId = agent && typeof agent.modelId === 'string' ? agent.modelId.trim() : '';
|
|
85
|
+
if (!modelId) return null;
|
|
86
|
+
const providerId = agent && typeof agent.providerId === 'string' ? agent.providerId.trim() : '';
|
|
87
|
+
const resolved = settings.getResolved(projectDir || null);
|
|
88
|
+
const models = Array.isArray(resolved.models) ? resolved.models : [];
|
|
89
|
+
const m = models.find((x) => x && x.id === modelId && (!providerId || x.provider === providerId));
|
|
90
|
+
if (m) return m;
|
|
91
|
+
// A provider-qualified pin may come from the live catalog rather than
|
|
92
|
+
// the project's saved models. The dispatcher hydrates this identity
|
|
93
|
+
// record with the app-level provider connection.
|
|
94
|
+
if (providerId) return { id: modelId, provider: providerId };
|
|
95
|
+
const error = new Error('Agent "' + (agent.name || '?') + '" references unknown model "' + modelId + '"');
|
|
96
|
+
error.code = 'EUNKNOWN_MODEL';
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function readRawList(project) {
|
|
101
|
+
if (!project || typeof project !== 'object') return [];
|
|
102
|
+
if (Array.isArray(project.agents)) return project.agents;
|
|
103
|
+
// One-release fallback for the legacy key.
|
|
104
|
+
if (Array.isArray(project.agentPresets)) return project.agentPresets;
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function list(projectDir) {
|
|
109
|
+
if (!projectDir) return [];
|
|
110
|
+
const project = settings.getProject(projectDir);
|
|
111
|
+
return readRawList(project).map(normalizeAgent).filter(Boolean);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function get(projectDir, name) {
|
|
115
|
+
if (!isValidName(name)) return null;
|
|
116
|
+
return list(projectDir).find((a) => a.name === name) || null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function writeList(projectDir, agents) {
|
|
120
|
+
settings.setProject(projectDir, { agents });
|
|
121
|
+
// Drop the legacy key once we write the new one.
|
|
122
|
+
try { settings.unsetProjectKeys(projectDir, ['agentPresets']); } catch { /* non-fatal */ }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function create(projectDir, opts) {
|
|
126
|
+
const name = opts && typeof opts.name === 'string' ? opts.name.trim() : '';
|
|
127
|
+
if (!isValidName(name)) {
|
|
128
|
+
const error = new Error('Agent name is required and must match ' + NAME_RE);
|
|
129
|
+
error.code = 'EBADINPUT';
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
const content = opts && typeof opts.content === 'string' ? opts.content : '';
|
|
133
|
+
const agents = list(projectDir);
|
|
134
|
+
if (agents.some((a) => a.name === name)) {
|
|
135
|
+
const error = new Error('Agent "' + name + '" already exists');
|
|
136
|
+
error.code = 'EBADINPUT';
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
const now = new Date().toISOString();
|
|
140
|
+
const agent = normalizeAgent({
|
|
141
|
+
name,
|
|
142
|
+
content,
|
|
143
|
+
tools: Array.isArray(opts.tools) ? opts.tools : undefined,
|
|
144
|
+
modelId: typeof opts.modelId === 'string' ? opts.modelId : undefined,
|
|
145
|
+
providerId: typeof opts.providerId === 'string' ? opts.providerId : undefined,
|
|
146
|
+
thinkingLevel: typeof opts.thinkingLevel === 'string' ? opts.thinkingLevel : undefined,
|
|
147
|
+
createdAt: now,
|
|
148
|
+
updatedAt: now
|
|
149
|
+
});
|
|
150
|
+
agents.push(agent);
|
|
151
|
+
writeList(projectDir, agents);
|
|
152
|
+
return agent;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function update(projectDir, name, patch) {
|
|
156
|
+
if (!isValidName(name)) return null;
|
|
157
|
+
const agents = list(projectDir);
|
|
158
|
+
const idx = agents.findIndex((a) => a.name === name);
|
|
159
|
+
if (idx < 0) return null;
|
|
160
|
+
const current = agents[idx];
|
|
161
|
+
// Name is now mutable. Validate and check duplicates (excluding self).
|
|
162
|
+
let newName = current.name;
|
|
163
|
+
if (Object.prototype.hasOwnProperty.call(patch || {}, 'name')) {
|
|
164
|
+
const candidate = typeof patch.name === 'string' ? patch.name.trim() : '';
|
|
165
|
+
if (!candidate) {
|
|
166
|
+
const error = new Error('Agent name must match ' + NAME_RE);
|
|
167
|
+
error.code = 'EBADINPUT';
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
if (!isValidName(candidate)) {
|
|
171
|
+
const error = new Error('Agent name must match ' + NAME_RE);
|
|
172
|
+
error.code = 'EBADINPUT';
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
if (candidate !== current.name && agents.some((a) => a.name === candidate)) {
|
|
176
|
+
const error = new Error('Agent "' + candidate + '" already exists');
|
|
177
|
+
error.code = 'EBADINPUT';
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
newName = candidate;
|
|
181
|
+
}
|
|
182
|
+
const merged = {
|
|
183
|
+
name: newName,
|
|
184
|
+
content: Object.prototype.hasOwnProperty.call(patch || {}, 'content')
|
|
185
|
+
? capContent(patch.content)
|
|
186
|
+
: current.content,
|
|
187
|
+
tools: Object.prototype.hasOwnProperty.call(patch || {}, 'tools')
|
|
188
|
+
? (Array.isArray(patch.tools)
|
|
189
|
+
? patch.tools.map(String).map((s) => s.trim()).filter(Boolean)
|
|
190
|
+
: undefined)
|
|
191
|
+
: current.tools,
|
|
192
|
+
modelId: Object.prototype.hasOwnProperty.call(patch || {}, 'modelId')
|
|
193
|
+
? (typeof patch.modelId === 'string' && patch.modelId.trim() ? patch.modelId.trim() : undefined)
|
|
194
|
+
: current.modelId,
|
|
195
|
+
providerId: Object.prototype.hasOwnProperty.call(patch || {}, 'providerId')
|
|
196
|
+
? (typeof patch.providerId === 'string' && patch.providerId.trim() ? patch.providerId.trim() : undefined)
|
|
197
|
+
: (Object.prototype.hasOwnProperty.call(patch || {}, 'modelId') && !patch.modelId ? undefined : current.providerId),
|
|
198
|
+
thinkingLevel: Object.prototype.hasOwnProperty.call(patch || {}, 'thinkingLevel')
|
|
199
|
+
? (typeof patch.thinkingLevel === 'string' && patch.thinkingLevel.trim() ? patch.thinkingLevel.trim() : undefined)
|
|
200
|
+
: current.thinkingLevel,
|
|
201
|
+
createdAt: current.createdAt,
|
|
202
|
+
updatedAt: new Date().toISOString()
|
|
203
|
+
};
|
|
204
|
+
if (merged.tools && !merged.tools.length) merged.tools = undefined;
|
|
205
|
+
agents[idx] = merged;
|
|
206
|
+
writeList(projectDir, agents);
|
|
207
|
+
return merged;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function remove(projectDir, name) {
|
|
211
|
+
if (!isValidName(name)) return false;
|
|
212
|
+
const agents = list(projectDir);
|
|
213
|
+
const idx = agents.findIndex((a) => a.name === name);
|
|
214
|
+
if (idx < 0) return false;
|
|
215
|
+
agents.splice(idx, 1);
|
|
216
|
+
writeList(projectDir, agents);
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
module.exports = {
|
|
221
|
+
MAX_BYTES,
|
|
222
|
+
NAME_RE,
|
|
223
|
+
isValidName,
|
|
224
|
+
list,
|
|
225
|
+
get,
|
|
226
|
+
create,
|
|
227
|
+
update,
|
|
228
|
+
remove,
|
|
229
|
+
resolveModel
|
|
230
|
+
};
|
package/src/ai-chat.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Non-streaming convenience wrapper over the streaming core.
|
|
4
|
+
// Used by tests and one-shot callers that want a plain `text`
|
|
5
|
+
// result instead of handling SSE events themselves.
|
|
6
|
+
|
|
7
|
+
const { streamChat } = require('./ai-stream.js');
|
|
8
|
+
|
|
9
|
+
async function chat(model, messages, opts) {
|
|
10
|
+
const events = [];
|
|
11
|
+
const r = await streamChat({
|
|
12
|
+
model, messages,
|
|
13
|
+
signal: opts && opts.signal,
|
|
14
|
+
onEvent: (name, data) => events.push({ name, data })
|
|
15
|
+
});
|
|
16
|
+
let text = '';
|
|
17
|
+
for (const e of events) if (e.name === 'message' && e.data && typeof e.data.delta === 'string') text += e.data.delta;
|
|
18
|
+
return Object.assign({ text }, r);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = { chat };
|