maqam 0.1.1 → 0.1.2
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/README.md +2 -0
- package/docs/usage.md +1094 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@ Maqam is an MIT-licensed Ajnas agent framework for governed workflows. It combin
|
|
|
6
6
|
|
|
7
7
|
The crawler is no longer the product center; it is the first governed connector. Maqam is meant for enterprise agent workflows that need inspectable runs, source-backed outputs, compliance-friendly defaults, and no required hosted service.
|
|
8
8
|
|
|
9
|
+
Full documentation: [docs/usage.md](https://github.com/AjnasNB/maqam/blob/main/docs/usage.md)
|
|
10
|
+
|
|
9
11
|

|
|
10
12
|
|
|
11
13
|
## What Ships
|
package/docs/usage.md
ADDED
|
@@ -0,0 +1,1094 @@
|
|
|
1
|
+
# Maqam Usage Guide
|
|
2
|
+
|
|
3
|
+
Maqam is an MIT-licensed Ajnas agent framework for governed workflows. It gives you a small local runtime for building agent systems that can be inspected, policy-checked, and connected to evidence.
|
|
4
|
+
|
|
5
|
+
This guide covers installation, CLI usage, SDK usage, the local console, crawler usage, API reference, common patterns, and troubleshooting.
|
|
6
|
+
|
|
7
|
+
## Table Of Contents
|
|
8
|
+
|
|
9
|
+
- [Install](#install)
|
|
10
|
+
- [Quick Start](#quick-start)
|
|
11
|
+
- [Local Console](#local-console)
|
|
12
|
+
- [Crawler CLI](#crawler-cli)
|
|
13
|
+
- [Framework SDK](#framework-sdk)
|
|
14
|
+
- [Architecture](#architecture)
|
|
15
|
+
- [API Reference](#api-reference)
|
|
16
|
+
- [Build A Custom Workflow](#build-a-custom-workflow)
|
|
17
|
+
- [Register A Custom Tool](#register-a-custom-tool)
|
|
18
|
+
- [Use Policy And Approvals](#use-policy-and-approvals)
|
|
19
|
+
- [Use Evidence And Claims](#use-evidence-and-claims)
|
|
20
|
+
- [Use The Skill Registry](#use-the-skill-registry)
|
|
21
|
+
- [HTTP API](#http-api)
|
|
22
|
+
- [Security And Compliance Notes](#security-and-compliance-notes)
|
|
23
|
+
- [Development](#development)
|
|
24
|
+
- [Troubleshooting](#troubleshooting)
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
Maqam requires Node.js 20 or newer.
|
|
29
|
+
|
|
30
|
+
Global install:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install -g maqam
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Project install:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install maqam
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Run from the cloned repository:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
git clone https://github.com/AjnasNB/maqam.git
|
|
46
|
+
cd maqam
|
|
47
|
+
npm install
|
|
48
|
+
npm test
|
|
49
|
+
npm run maqam
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
Start the local web console:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
maqam
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Open:
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
http://127.0.0.1:8787
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Run the crawler CLI:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
maqam-crawl https://example.com --max-pages 10 --jsonl
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Use the SDK:
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
import {
|
|
76
|
+
AgentRuntime,
|
|
77
|
+
EvidenceLedger,
|
|
78
|
+
PolicyEngine,
|
|
79
|
+
ToolGateway,
|
|
80
|
+
createCrawlerTool,
|
|
81
|
+
createResearchWorkflow
|
|
82
|
+
} from "maqam";
|
|
83
|
+
|
|
84
|
+
const evidenceLedger = new EvidenceLedger();
|
|
85
|
+
const policyEngine = new PolicyEngine({
|
|
86
|
+
allowedTools: ["crawler"],
|
|
87
|
+
allowedOrigins: ["https://github.com"]
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const toolGateway = new ToolGateway({ policyEngine, evidenceLedger });
|
|
91
|
+
toolGateway.registerTool("crawler", createCrawlerTool());
|
|
92
|
+
|
|
93
|
+
const runtime = new AgentRuntime({ policyEngine, evidenceLedger, toolGateway });
|
|
94
|
+
const result = await runtime.runWorkflow(
|
|
95
|
+
createResearchWorkflow({
|
|
96
|
+
seeds: ["https://github.com/AjnasNB/maqam"],
|
|
97
|
+
maxPages: 3
|
|
98
|
+
}),
|
|
99
|
+
{
|
|
100
|
+
objective: "Research Maqam from public sources",
|
|
101
|
+
allowedTools: ["crawler"],
|
|
102
|
+
allowedOrigins: ["https://github.com"],
|
|
103
|
+
budget: { maxToolCalls: 20, maxRuntimeMs: 120_000 }
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
console.log(result.status);
|
|
108
|
+
console.log(result.outputs.synthesize_report.candidates);
|
|
109
|
+
console.log(result.evidence);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Local Console
|
|
113
|
+
|
|
114
|
+
The `maqam` command starts a local browser console for governed research runs.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
maqam
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Use a custom port:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
maqam --port 8788
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The console lets you:
|
|
127
|
+
|
|
128
|
+
- Enter a seed URL.
|
|
129
|
+
- Choose the maximum pages to crawl.
|
|
130
|
+
- Keep crawling on the same origin or allow wider origin discovery.
|
|
131
|
+
- Run a governed workflow through policy, tool gateway, evidence, runtime, and synthesis.
|
|
132
|
+
- Inspect candidates, evidence records, claims, runtime trace, and tool trace.
|
|
133
|
+
|
|
134
|
+
The console is local by default and binds to `127.0.0.1`.
|
|
135
|
+
|
|
136
|
+
## Crawler CLI
|
|
137
|
+
|
|
138
|
+
Maqam includes a crawler because public-source collection is the first governed connector.
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
maqam-crawl https://example.com --max-pages 50 --jsonl --output crawl.jsonl
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Legacy aliases are also available:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
ajnas-crawl https://example.com
|
|
148
|
+
ajnas-agent-crawler https://example.com
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### CLI Options
|
|
152
|
+
|
|
153
|
+
| Option | Description | Default |
|
|
154
|
+
| --- | --- | --- |
|
|
155
|
+
| `--max-pages <n>` | Maximum pages to return. | `50` |
|
|
156
|
+
| `--concurrency <n>` | Concurrent workers. | `4` |
|
|
157
|
+
| `--delay <ms>` | Minimum delay per origin. | `250` |
|
|
158
|
+
| `--timeout <ms>` | Request timeout. | `15000` |
|
|
159
|
+
| `--sitemaps` | Discover URLs from `robots.txt` sitemaps and `/sitemap.xml`. | off |
|
|
160
|
+
| `--all-origins` | Allow crawling across discovered origins. | off |
|
|
161
|
+
| `--jsonl` | Output JSON Lines instead of a JSON array. | off |
|
|
162
|
+
| `--output <file>` | Write output to a file. | stdout |
|
|
163
|
+
| `--user-agent <ua>` | Use a custom user agent. | Maqam default |
|
|
164
|
+
| `--help` | Show CLI help. | off |
|
|
165
|
+
|
|
166
|
+
### Crawler Output
|
|
167
|
+
|
|
168
|
+
Each crawled page has this shape:
|
|
169
|
+
|
|
170
|
+
```json
|
|
171
|
+
{
|
|
172
|
+
"url": "https://example.com/",
|
|
173
|
+
"canonical": "https://example.com/",
|
|
174
|
+
"title": "Example",
|
|
175
|
+
"description": "Example description",
|
|
176
|
+
"h1": "Example",
|
|
177
|
+
"text": "Readable text...",
|
|
178
|
+
"markdown": "# Example\n\nReadable markdown...",
|
|
179
|
+
"links": ["https://example.com/about"],
|
|
180
|
+
"fetchedAt": "2026-06-30T00:00:00.000Z",
|
|
181
|
+
"status": 200,
|
|
182
|
+
"contentType": "text/html; charset=utf-8"
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Crawler Safety Defaults
|
|
187
|
+
|
|
188
|
+
The crawler:
|
|
189
|
+
|
|
190
|
+
- Uses `robots.txt` by default.
|
|
191
|
+
- Rate-limits per origin.
|
|
192
|
+
- Limits response size.
|
|
193
|
+
- Avoids non-HTTP URLs.
|
|
194
|
+
- Does not bypass login walls, paywalls, CAPTCHA, anti-bot systems, or authorization boundaries.
|
|
195
|
+
|
|
196
|
+
## Framework SDK
|
|
197
|
+
|
|
198
|
+
Install in a project:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
npm install maqam
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Import the public API:
|
|
205
|
+
|
|
206
|
+
```js
|
|
207
|
+
import {
|
|
208
|
+
AgentRuntime,
|
|
209
|
+
EvidenceLedger,
|
|
210
|
+
PolicyEngine,
|
|
211
|
+
ToolGateway,
|
|
212
|
+
SkillRegistry,
|
|
213
|
+
createCrawlerTool,
|
|
214
|
+
createResearchWorkflow,
|
|
215
|
+
crawl,
|
|
216
|
+
extractPage,
|
|
217
|
+
normalizeUrl,
|
|
218
|
+
discoverSitemapUrls,
|
|
219
|
+
AjnasFrameworkError,
|
|
220
|
+
PolicyDeniedError,
|
|
221
|
+
ApprovalRequiredError,
|
|
222
|
+
toErrorRecord
|
|
223
|
+
} from "maqam";
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Maqam is ESM-only. Use `import`, not `require`.
|
|
227
|
+
|
|
228
|
+
## Architecture
|
|
229
|
+
|
|
230
|
+
Maqam is composed from small framework primitives:
|
|
231
|
+
|
|
232
|
+
```text
|
|
233
|
+
User Goal
|
|
234
|
+
-> PolicyEngine.evaluateGoal()
|
|
235
|
+
-> AgentRuntime.runWorkflow()
|
|
236
|
+
-> ToolGateway.call()
|
|
237
|
+
-> EvidenceLedger.addEvidence()
|
|
238
|
+
-> EvidenceLedger.addClaim()
|
|
239
|
+
-> Quality checks
|
|
240
|
+
-> Auditable output
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Core objects:
|
|
244
|
+
|
|
245
|
+
- `AgentRuntime`: owns workflow execution.
|
|
246
|
+
- `PolicyEngine`: decides what is allowed, denied, or approval-gated.
|
|
247
|
+
- `ToolGateway`: routes all external tool calls through policy.
|
|
248
|
+
- `EvidenceLedger`: stores source evidence and claim support.
|
|
249
|
+
- `SkillRegistry`: stores skill metadata and selects matching skills.
|
|
250
|
+
- `createResearchWorkflow`: bundled workflow for public web research.
|
|
251
|
+
- `crawl`: lower-level crawler API used by `createCrawlerTool`.
|
|
252
|
+
|
|
253
|
+
## API Reference
|
|
254
|
+
|
|
255
|
+
### `new PolicyEngine(config)`
|
|
256
|
+
|
|
257
|
+
Creates a deterministic policy engine.
|
|
258
|
+
|
|
259
|
+
```js
|
|
260
|
+
const policyEngine = new PolicyEngine({
|
|
261
|
+
allowedTools: ["crawler", "github"],
|
|
262
|
+
deniedTools: ["email"],
|
|
263
|
+
allowedOrigins: ["https://github.com", "https://www.npmjs.com"],
|
|
264
|
+
deniedOrigins: ["https://example-private.local"],
|
|
265
|
+
approvalRequiredTools: ["github"],
|
|
266
|
+
maxToolCalls: 40,
|
|
267
|
+
defaultLimits: {
|
|
268
|
+
maxRuntimeMs: 600_000
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Config fields:
|
|
274
|
+
|
|
275
|
+
| Field | Type | Description |
|
|
276
|
+
| --- | --- | --- |
|
|
277
|
+
| `allowedTools` | `string[]` | If non-empty, only these tools may run. |
|
|
278
|
+
| `deniedTools` | `string[]` | Tools that must never run. |
|
|
279
|
+
| `allowedOrigins` | `string[]` | If non-empty, only these URL origins may be used. |
|
|
280
|
+
| `deniedOrigins` | `string[]` | URL origins that must never be used. |
|
|
281
|
+
| `approvalRequiredTools` | `string[]` | Tools that return `needs_approval`. |
|
|
282
|
+
| `maxToolCalls` | `number` | Shortcut for default `limits.maxToolCalls`. |
|
|
283
|
+
| `defaultLimits` | `object` | Default runtime and tool limits. |
|
|
284
|
+
|
|
285
|
+
Methods:
|
|
286
|
+
|
|
287
|
+
```js
|
|
288
|
+
policyEngine.evaluateGoal(goal);
|
|
289
|
+
policyEngine.authorizeToolCall({ toolName, input, context });
|
|
290
|
+
policyEngine.isToolAllowed("crawler");
|
|
291
|
+
policyEngine.isOriginAllowed("https://github.com");
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Decision shape:
|
|
295
|
+
|
|
296
|
+
```json
|
|
297
|
+
{
|
|
298
|
+
"status": "allow",
|
|
299
|
+
"reason": "Goal is allowed by policy.",
|
|
300
|
+
"limits": {
|
|
301
|
+
"maxToolCalls": 100,
|
|
302
|
+
"maxRuntimeMs": 600000
|
|
303
|
+
},
|
|
304
|
+
"requiredApprovals": []
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Possible statuses:
|
|
309
|
+
|
|
310
|
+
- `allow`: execution can continue.
|
|
311
|
+
- `deny`: execution must stop.
|
|
312
|
+
- `needs_approval`: a human approval step is required before continuing.
|
|
313
|
+
|
|
314
|
+
### `new EvidenceLedger(options)`
|
|
315
|
+
|
|
316
|
+
Creates an in-memory evidence and claim store.
|
|
317
|
+
|
|
318
|
+
```js
|
|
319
|
+
const evidenceLedger = new EvidenceLedger({
|
|
320
|
+
clock: () => new Date()
|
|
321
|
+
});
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Methods:
|
|
325
|
+
|
|
326
|
+
```js
|
|
327
|
+
const evidence = evidenceLedger.addEvidence({
|
|
328
|
+
runId: "run_1",
|
|
329
|
+
taskId: "collect_sources",
|
|
330
|
+
sourceType: "url",
|
|
331
|
+
source: "https://github.com/AjnasNB/maqam",
|
|
332
|
+
excerpt: "Repository metadata and README excerpt.",
|
|
333
|
+
tool: "crawler",
|
|
334
|
+
confidence: 0.85
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
const claim = evidenceLedger.addClaim({
|
|
338
|
+
runId: "run_1",
|
|
339
|
+
taskId: "synthesize_report",
|
|
340
|
+
text: "Maqam ships a policy engine.",
|
|
341
|
+
evidenceIds: [evidence.evidenceId],
|
|
342
|
+
confidence: 0.8
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
evidenceLedger.listEvidence();
|
|
346
|
+
evidenceLedger.listClaims();
|
|
347
|
+
evidenceLedger.unsupportedClaims();
|
|
348
|
+
evidenceLedger.toJSON();
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Evidence record shape:
|
|
352
|
+
|
|
353
|
+
```json
|
|
354
|
+
{
|
|
355
|
+
"evidenceId": "ev_1",
|
|
356
|
+
"runId": "run_1",
|
|
357
|
+
"taskId": "collect_sources",
|
|
358
|
+
"sourceType": "url",
|
|
359
|
+
"source": "https://github.com/AjnasNB/maqam",
|
|
360
|
+
"retrievedAt": "2026-06-30T00:00:00.000Z",
|
|
361
|
+
"excerpt": "Repository metadata and README excerpt.",
|
|
362
|
+
"hash": "sha256:...",
|
|
363
|
+
"tool": "crawler",
|
|
364
|
+
"confidence": 0.85
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
Unsupported claims are claims with no evidence IDs or with evidence IDs that do not exist in the ledger.
|
|
369
|
+
|
|
370
|
+
### `new ToolGateway(options)`
|
|
371
|
+
|
|
372
|
+
Creates a governed tool registry and execution path.
|
|
373
|
+
|
|
374
|
+
```js
|
|
375
|
+
const toolGateway = new ToolGateway({
|
|
376
|
+
policyEngine,
|
|
377
|
+
evidenceLedger
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
toolGateway.registerTool("echo", async (input) => {
|
|
381
|
+
return { value: input.value };
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
const result = await toolGateway.call("echo", { value: "ok" });
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Methods:
|
|
388
|
+
|
|
389
|
+
```js
|
|
390
|
+
toolGateway.registerTool(name, handler, metadata);
|
|
391
|
+
toolGateway.call(toolName, input, context);
|
|
392
|
+
toolGateway.trace;
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Tool handler signature:
|
|
396
|
+
|
|
397
|
+
```js
|
|
398
|
+
async function handler(input, context) {
|
|
399
|
+
return { ok: true };
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
The handler context includes:
|
|
404
|
+
|
|
405
|
+
- `toolName`
|
|
406
|
+
- `evidenceLedger`
|
|
407
|
+
- Any workflow context passed to `call`
|
|
408
|
+
|
|
409
|
+
If policy denies the call, `ToolGateway` throws `PolicyDeniedError`.
|
|
410
|
+
|
|
411
|
+
If policy requires approval, `ToolGateway` throws `ApprovalRequiredError`.
|
|
412
|
+
|
|
413
|
+
### `new AgentRuntime(options)`
|
|
414
|
+
|
|
415
|
+
Creates the workflow runner.
|
|
416
|
+
|
|
417
|
+
```js
|
|
418
|
+
const runtime = new AgentRuntime({
|
|
419
|
+
policyEngine,
|
|
420
|
+
evidenceLedger,
|
|
421
|
+
toolGateway
|
|
422
|
+
});
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
Run a workflow:
|
|
426
|
+
|
|
427
|
+
```js
|
|
428
|
+
const result = await runtime.runWorkflow(workflow, goal);
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
Workflow shape:
|
|
432
|
+
|
|
433
|
+
```js
|
|
434
|
+
const workflow = {
|
|
435
|
+
name: "my_workflow",
|
|
436
|
+
tasks: [
|
|
437
|
+
{
|
|
438
|
+
id: "first_task",
|
|
439
|
+
retries: 1,
|
|
440
|
+
timeoutMs: 5000,
|
|
441
|
+
run: async (context) => {
|
|
442
|
+
return { ok: true };
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
]
|
|
446
|
+
};
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Goal shape:
|
|
450
|
+
|
|
451
|
+
```js
|
|
452
|
+
const goal = {
|
|
453
|
+
runId: "run_custom_1",
|
|
454
|
+
objective: "Research public sources",
|
|
455
|
+
allowedTools: ["crawler"],
|
|
456
|
+
allowedOrigins: ["https://github.com"],
|
|
457
|
+
budget: {
|
|
458
|
+
maxToolCalls: 40,
|
|
459
|
+
maxRuntimeMs: 600_000
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
Runtime result shape:
|
|
465
|
+
|
|
466
|
+
```json
|
|
467
|
+
{
|
|
468
|
+
"runId": "run_123",
|
|
469
|
+
"status": "completed",
|
|
470
|
+
"trace": [
|
|
471
|
+
{
|
|
472
|
+
"taskId": "first_task",
|
|
473
|
+
"status": "completed",
|
|
474
|
+
"attempt": 1,
|
|
475
|
+
"startedAt": "2026-06-30T00:00:00.000Z",
|
|
476
|
+
"finishedAt": "2026-06-30T00:00:01.000Z"
|
|
477
|
+
}
|
|
478
|
+
],
|
|
479
|
+
"outputs": {
|
|
480
|
+
"first_task": {
|
|
481
|
+
"ok": true
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
"evidence": {
|
|
485
|
+
"evidence": [],
|
|
486
|
+
"claims": [],
|
|
487
|
+
"unsupportedClaims": []
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
Task context fields:
|
|
493
|
+
|
|
494
|
+
- `runId`
|
|
495
|
+
- `goal`
|
|
496
|
+
- `outputs`
|
|
497
|
+
- `evidence`
|
|
498
|
+
- `tools`
|
|
499
|
+
- `trace`
|
|
500
|
+
|
|
501
|
+
### `new SkillRegistry()`
|
|
502
|
+
|
|
503
|
+
Creates a lightweight registry for skill metadata.
|
|
504
|
+
|
|
505
|
+
```js
|
|
506
|
+
const registry = new SkillRegistry();
|
|
507
|
+
|
|
508
|
+
registry.register({
|
|
509
|
+
id: "oss-research",
|
|
510
|
+
name: "OSS Research",
|
|
511
|
+
version: "0.1.0",
|
|
512
|
+
triggers: ["oss", "github", "agent framework"],
|
|
513
|
+
capabilities: ["research", "synthesis"],
|
|
514
|
+
trustLevel: "verified",
|
|
515
|
+
evalScore: 0.9,
|
|
516
|
+
metadata: {
|
|
517
|
+
owner: "Ajnas"
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
const matches = registry.find({
|
|
522
|
+
text: "Research agent framework projects",
|
|
523
|
+
capabilities: ["research"]
|
|
524
|
+
});
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
Methods:
|
|
528
|
+
|
|
529
|
+
```js
|
|
530
|
+
registry.register(skill);
|
|
531
|
+
registry.get("oss-research");
|
|
532
|
+
registry.list();
|
|
533
|
+
registry.find({ text, capabilities });
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
Selection sorts by `evalScore` descending, then by `id`.
|
|
537
|
+
|
|
538
|
+
### `createCrawlerTool(defaultOptions)`
|
|
539
|
+
|
|
540
|
+
Wraps the low-level crawler as a `ToolGateway` handler.
|
|
541
|
+
|
|
542
|
+
```js
|
|
543
|
+
const crawlerTool = createCrawlerTool({
|
|
544
|
+
concurrency: 2,
|
|
545
|
+
delayMs: 250,
|
|
546
|
+
timeoutMs: 12_000
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
toolGateway.registerTool("crawler", crawlerTool);
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
When called through the gateway, input is passed to `crawl`:
|
|
553
|
+
|
|
554
|
+
```js
|
|
555
|
+
await toolGateway.call("crawler", {
|
|
556
|
+
seeds: ["https://example.com"],
|
|
557
|
+
maxPages: 5,
|
|
558
|
+
sameOrigin: true,
|
|
559
|
+
includeSitemaps: false
|
|
560
|
+
});
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### `createResearchWorkflow(options)`
|
|
564
|
+
|
|
565
|
+
Creates the bundled public research workflow.
|
|
566
|
+
|
|
567
|
+
```js
|
|
568
|
+
const workflow = createResearchWorkflow({
|
|
569
|
+
seeds: ["https://github.com/AjnasNB/maqam"],
|
|
570
|
+
maxPages: 5,
|
|
571
|
+
sameOrigin: true,
|
|
572
|
+
includeSitemaps: false
|
|
573
|
+
});
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
Tasks:
|
|
577
|
+
|
|
578
|
+
| Task ID | Purpose |
|
|
579
|
+
| --- | --- |
|
|
580
|
+
| `collect_sources` | Calls the crawler tool and records evidence for every page. |
|
|
581
|
+
| `synthesize_report` | Converts pages into candidate summaries and links claims to evidence. |
|
|
582
|
+
| `quality_checks` | Reports unsupported claims and evidence count. |
|
|
583
|
+
|
|
584
|
+
Candidate shape:
|
|
585
|
+
|
|
586
|
+
```json
|
|
587
|
+
{
|
|
588
|
+
"name": "Maqam",
|
|
589
|
+
"url": "https://github.com/AjnasNB/maqam",
|
|
590
|
+
"whatItDoes": "Summary excerpt...",
|
|
591
|
+
"whyUseful": "Potential source or reference for enterprise agent framework capabilities.",
|
|
592
|
+
"risks": ["Requires license and maintenance review before reuse."],
|
|
593
|
+
"recommendation": "inspiration_first",
|
|
594
|
+
"evidenceIds": ["ev_1"]
|
|
595
|
+
}
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
### `crawl(input)`
|
|
599
|
+
|
|
600
|
+
Runs the low-level crawler.
|
|
601
|
+
|
|
602
|
+
```js
|
|
603
|
+
const pages = await crawl({
|
|
604
|
+
seeds: ["https://example.com"],
|
|
605
|
+
maxPages: 25,
|
|
606
|
+
concurrency: 4,
|
|
607
|
+
sameOrigin: true,
|
|
608
|
+
includeSitemaps: false,
|
|
609
|
+
obeyRobots: true,
|
|
610
|
+
userAgent: "MyCrawler/1.0 (+https://example.com)",
|
|
611
|
+
delayMs: 250,
|
|
612
|
+
timeoutMs: 15_000,
|
|
613
|
+
maxBytes: 3 * 1024 * 1024,
|
|
614
|
+
onPage(page) {
|
|
615
|
+
console.log(page.url);
|
|
616
|
+
},
|
|
617
|
+
onError(error) {
|
|
618
|
+
console.error(error.url, error.error);
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
Input fields:
|
|
624
|
+
|
|
625
|
+
| Field | Type | Description |
|
|
626
|
+
| --- | --- | --- |
|
|
627
|
+
| `seeds` or `urls` | `string[]` | Starting URLs. At least one HTTP(S) URL is required. |
|
|
628
|
+
| `maxPages` | `number` | Maximum pages to return. |
|
|
629
|
+
| `concurrency` | `number` | Number of workers. |
|
|
630
|
+
| `sameOrigin` | `boolean` | Restrict discovered links to seed origins. |
|
|
631
|
+
| `includeSitemaps` | `boolean` | Discover URLs from sitemaps. |
|
|
632
|
+
| `obeyRobots` | `boolean` | Respect `robots.txt`. |
|
|
633
|
+
| `userAgent` | `string` | Custom user agent. |
|
|
634
|
+
| `delayMs` | `number` | Per-origin delay. |
|
|
635
|
+
| `timeoutMs` | `number` | Request timeout. |
|
|
636
|
+
| `maxBytes` | `number` | Maximum response body bytes. |
|
|
637
|
+
| `onPage` | `function` | Optional callback for each page. |
|
|
638
|
+
| `onError` | `function` | Optional callback for crawl failures. |
|
|
639
|
+
|
|
640
|
+
### Error Classes
|
|
641
|
+
|
|
642
|
+
```js
|
|
643
|
+
import {
|
|
644
|
+
AjnasFrameworkError,
|
|
645
|
+
PolicyDeniedError,
|
|
646
|
+
ApprovalRequiredError,
|
|
647
|
+
toErrorRecord
|
|
648
|
+
} from "maqam";
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
Use `PolicyDeniedError` when policy blocks execution and `ApprovalRequiredError` when a human decision is required.
|
|
652
|
+
|
|
653
|
+
```js
|
|
654
|
+
try {
|
|
655
|
+
await toolGateway.call("github", { action: "fork" });
|
|
656
|
+
} catch (error) {
|
|
657
|
+
if (error instanceof ApprovalRequiredError) {
|
|
658
|
+
console.log(error.details.requiredApprovals);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
`toErrorRecord(error)` converts framework and native errors into serializable records.
|
|
664
|
+
|
|
665
|
+
## Build A Custom Workflow
|
|
666
|
+
|
|
667
|
+
This example builds a two-task workflow that collects data from a custom tool and records a supported claim.
|
|
668
|
+
|
|
669
|
+
```js
|
|
670
|
+
import {
|
|
671
|
+
AgentRuntime,
|
|
672
|
+
EvidenceLedger,
|
|
673
|
+
PolicyEngine,
|
|
674
|
+
ToolGateway
|
|
675
|
+
} from "maqam";
|
|
676
|
+
|
|
677
|
+
const evidenceLedger = new EvidenceLedger();
|
|
678
|
+
const policyEngine = new PolicyEngine({
|
|
679
|
+
allowedTools: ["packageInfo"],
|
|
680
|
+
allowedOrigins: ["https://registry.npmjs.org"]
|
|
681
|
+
});
|
|
682
|
+
const toolGateway = new ToolGateway({ policyEngine, evidenceLedger });
|
|
683
|
+
|
|
684
|
+
toolGateway.registerTool("packageInfo", async ({ name }) => {
|
|
685
|
+
const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(name)}`);
|
|
686
|
+
if (!response.ok) throw new Error(`npm registry returned ${response.status}`);
|
|
687
|
+
return response.json();
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
const workflow = {
|
|
691
|
+
name: "npm_package_review",
|
|
692
|
+
tasks: [
|
|
693
|
+
{
|
|
694
|
+
id: "fetch_package",
|
|
695
|
+
retries: 1,
|
|
696
|
+
timeoutMs: 15_000,
|
|
697
|
+
run: async (context) => {
|
|
698
|
+
return context.tools.call("packageInfo", { name: "maqam" }, context);
|
|
699
|
+
}
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
id: "record_summary",
|
|
703
|
+
run: async (context) => {
|
|
704
|
+
const pkg = context.outputs.fetch_package;
|
|
705
|
+
const evidence = context.evidence.addEvidence({
|
|
706
|
+
runId: context.runId,
|
|
707
|
+
taskId: "record_summary",
|
|
708
|
+
sourceType: "registry",
|
|
709
|
+
source: "https://registry.npmjs.org/maqam",
|
|
710
|
+
excerpt: pkg.description,
|
|
711
|
+
tool: "packageInfo",
|
|
712
|
+
confidence: 0.9
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
context.evidence.addClaim({
|
|
716
|
+
runId: context.runId,
|
|
717
|
+
taskId: "record_summary",
|
|
718
|
+
text: "Maqam is published on npm.",
|
|
719
|
+
evidenceIds: [evidence.evidenceId],
|
|
720
|
+
confidence: 0.9
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
return {
|
|
724
|
+
name: pkg.name,
|
|
725
|
+
latest: pkg["dist-tags"]?.latest,
|
|
726
|
+
evidenceId: evidence.evidenceId
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
]
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
const runtime = new AgentRuntime({ policyEngine, evidenceLedger, toolGateway });
|
|
734
|
+
const result = await runtime.runWorkflow(workflow, {
|
|
735
|
+
objective: "Review an npm package",
|
|
736
|
+
allowedTools: ["packageInfo"],
|
|
737
|
+
allowedOrigins: ["https://registry.npmjs.org"]
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
console.log(result.outputs.record_summary);
|
|
741
|
+
console.log(result.evidence.unsupportedClaims);
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
## Register A Custom Tool
|
|
745
|
+
|
|
746
|
+
Tools should be small and explicit. The gateway handles policy and trace capture.
|
|
747
|
+
|
|
748
|
+
```js
|
|
749
|
+
toolGateway.registerTool("internalDocs", async ({ query }, context) => {
|
|
750
|
+
const records = await searchDocs(query);
|
|
751
|
+
|
|
752
|
+
for (const record of records) {
|
|
753
|
+
context.evidenceLedger?.addEvidence({
|
|
754
|
+
runId: context.runId,
|
|
755
|
+
taskId: context.taskId,
|
|
756
|
+
sourceType: "internal_doc",
|
|
757
|
+
source: record.id,
|
|
758
|
+
excerpt: record.snippet,
|
|
759
|
+
tool: "internalDocs",
|
|
760
|
+
confidence: 0.75
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
return records;
|
|
765
|
+
}, {
|
|
766
|
+
description: "Search internal documentation."
|
|
767
|
+
});
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
Good tool design:
|
|
771
|
+
|
|
772
|
+
- Accept structured input.
|
|
773
|
+
- Return structured output.
|
|
774
|
+
- Avoid side effects unless approval is required.
|
|
775
|
+
- Keep auth and secrets outside the tool input.
|
|
776
|
+
- Record evidence for source-backed results.
|
|
777
|
+
|
|
778
|
+
## Use Policy And Approvals
|
|
779
|
+
|
|
780
|
+
Deny a tool:
|
|
781
|
+
|
|
782
|
+
```js
|
|
783
|
+
const policyEngine = new PolicyEngine({
|
|
784
|
+
allowedTools: ["crawler"]
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
const decision = policyEngine.authorizeToolCall({
|
|
788
|
+
toolName: "email",
|
|
789
|
+
input: { to: "customer@example.com" }
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
console.log(decision.status); // "deny"
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
Require approval:
|
|
796
|
+
|
|
797
|
+
```js
|
|
798
|
+
const policyEngine = new PolicyEngine({
|
|
799
|
+
allowedTools: ["github"],
|
|
800
|
+
approvalRequiredTools: ["github"]
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
const toolGateway = new ToolGateway({ policyEngine });
|
|
804
|
+
toolGateway.registerTool("github", async () => ({ ok: true }));
|
|
805
|
+
|
|
806
|
+
await toolGateway.call("github", { action: "create_release" });
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
The call throws `ApprovalRequiredError`. Your application can catch it and place a human approval request in a queue.
|
|
810
|
+
|
|
811
|
+
## Use Evidence And Claims
|
|
812
|
+
|
|
813
|
+
Use evidence for source facts:
|
|
814
|
+
|
|
815
|
+
```js
|
|
816
|
+
const evidence = evidenceLedger.addEvidence({
|
|
817
|
+
sourceType: "url",
|
|
818
|
+
source: "https://github.com/AjnasNB/maqam",
|
|
819
|
+
excerpt: "Maqam is an MIT-licensed Ajnas agent framework...",
|
|
820
|
+
tool: "crawler",
|
|
821
|
+
confidence: 0.85
|
|
822
|
+
});
|
|
823
|
+
|
|
824
|
+
evidenceLedger.addClaim({
|
|
825
|
+
text: "Maqam is MIT licensed.",
|
|
826
|
+
evidenceIds: [evidence.evidenceId],
|
|
827
|
+
confidence: 0.8
|
|
828
|
+
});
|
|
829
|
+
|
|
830
|
+
const unsupported = evidenceLedger.unsupportedClaims();
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
Use unsupported-claim checks before publishing reports:
|
|
834
|
+
|
|
835
|
+
```js
|
|
836
|
+
if (evidenceLedger.unsupportedClaims().length > 0) {
|
|
837
|
+
throw new Error("Report contains unsupported claims.");
|
|
838
|
+
}
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
## Use The Skill Registry
|
|
842
|
+
|
|
843
|
+
The current registry is intentionally lightweight. It stores metadata and returns matching skills.
|
|
844
|
+
|
|
845
|
+
```js
|
|
846
|
+
const registry = new SkillRegistry();
|
|
847
|
+
|
|
848
|
+
registry.register({
|
|
849
|
+
id: "license-review",
|
|
850
|
+
name: "License Review",
|
|
851
|
+
version: "0.1.0",
|
|
852
|
+
triggers: ["license", "mit", "apache"],
|
|
853
|
+
capabilities: ["compliance", "research"],
|
|
854
|
+
trustLevel: "verified",
|
|
855
|
+
evalScore: 0.93
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
const skills = registry.find({
|
|
859
|
+
text: "Check whether this package is MIT licensed",
|
|
860
|
+
capabilities: ["compliance"]
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
console.log(skills[0].id);
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
Recommended metadata:
|
|
867
|
+
|
|
868
|
+
- `id`: stable machine identifier.
|
|
869
|
+
- `name`: human-readable name.
|
|
870
|
+
- `version`: semantic version.
|
|
871
|
+
- `triggers`: text phrases that should select the skill.
|
|
872
|
+
- `capabilities`: capability tags.
|
|
873
|
+
- `trustLevel`: `community`, `verified`, or tenant-specific labels.
|
|
874
|
+
- `evalScore`: numeric quality score from 0 to 1.
|
|
875
|
+
- `metadata`: owner, source, license, compatibility, or audit information.
|
|
876
|
+
|
|
877
|
+
## HTTP API
|
|
878
|
+
|
|
879
|
+
The local Maqam server exposes two API endpoints.
|
|
880
|
+
|
|
881
|
+
### `GET /api/health`
|
|
882
|
+
|
|
883
|
+
Response:
|
|
884
|
+
|
|
885
|
+
```json
|
|
886
|
+
{
|
|
887
|
+
"product": {
|
|
888
|
+
"name": "Maqam",
|
|
889
|
+
"tagline": "Compose governed agents",
|
|
890
|
+
"description": "Enterprise agent framework console for policy-bound research, evidence capture, and auditable workflow runs."
|
|
891
|
+
},
|
|
892
|
+
"status": "ok"
|
|
893
|
+
}
|
|
894
|
+
```
|
|
895
|
+
|
|
896
|
+
### `POST /api/runs/research`
|
|
897
|
+
|
|
898
|
+
Request:
|
|
899
|
+
|
|
900
|
+
```json
|
|
901
|
+
{
|
|
902
|
+
"seeds": ["https://github.com/AjnasNB/maqam"],
|
|
903
|
+
"maxPages": 2,
|
|
904
|
+
"sameOrigin": true,
|
|
905
|
+
"allowedOrigins": ["https://github.com"],
|
|
906
|
+
"objective": "Research Maqam from public sources"
|
|
907
|
+
}
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
Rules:
|
|
911
|
+
|
|
912
|
+
- `seeds` must be an array of HTTP(S) URLs.
|
|
913
|
+
- `maxPages` is clamped from 1 to 25.
|
|
914
|
+
- If `allowedOrigins` is omitted, Maqam derives origins from `seeds`.
|
|
915
|
+
- The server only registers the `crawler` tool for this endpoint.
|
|
916
|
+
|
|
917
|
+
Example:
|
|
918
|
+
|
|
919
|
+
```bash
|
|
920
|
+
curl -X POST http://127.0.0.1:8787/api/runs/research \
|
|
921
|
+
-H "content-type: application/json" \
|
|
922
|
+
-d "{\"seeds\":[\"https://github.com/AjnasNB/maqam\"],\"maxPages\":1}"
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
Response shape:
|
|
926
|
+
|
|
927
|
+
```json
|
|
928
|
+
{
|
|
929
|
+
"product": {
|
|
930
|
+
"name": "Maqam",
|
|
931
|
+
"tagline": "Compose governed agents"
|
|
932
|
+
},
|
|
933
|
+
"run": {
|
|
934
|
+
"runId": "run_123",
|
|
935
|
+
"status": "completed",
|
|
936
|
+
"trace": [],
|
|
937
|
+
"outputs": {},
|
|
938
|
+
"evidence": {}
|
|
939
|
+
},
|
|
940
|
+
"toolTrace": [],
|
|
941
|
+
"generatedAt": "2026-06-30T00:00:00.000Z"
|
|
942
|
+
}
|
|
943
|
+
```
|
|
944
|
+
|
|
945
|
+
## Security And Compliance Notes
|
|
946
|
+
|
|
947
|
+
Maqam is designed to make control points explicit, but it is not a complete compliance platform by itself.
|
|
948
|
+
|
|
949
|
+
Use these defaults:
|
|
950
|
+
|
|
951
|
+
- Keep `allowedTools` narrow.
|
|
952
|
+
- Keep `allowedOrigins` narrow.
|
|
953
|
+
- Require approval for write actions such as email, PR creation, release creation, publishing, customer data access, and production changes.
|
|
954
|
+
- Store secrets outside workflow input.
|
|
955
|
+
- Record evidence for every claim that may be used in a report.
|
|
956
|
+
- Run quality checks before publishing output.
|
|
957
|
+
- Keep crawler use limited to public, authorized content.
|
|
958
|
+
|
|
959
|
+
Do not use Maqam to:
|
|
960
|
+
|
|
961
|
+
- Bypass access controls.
|
|
962
|
+
- Evade CAPTCHA or anti-bot systems.
|
|
963
|
+
- Ignore robots.txt where it applies.
|
|
964
|
+
- Scrape private, gated, or paid content without permission.
|
|
965
|
+
- Publish generated reports without review when policy says approval is required.
|
|
966
|
+
|
|
967
|
+
## Development
|
|
968
|
+
|
|
969
|
+
Clone and test:
|
|
970
|
+
|
|
971
|
+
```bash
|
|
972
|
+
git clone https://github.com/AjnasNB/maqam.git
|
|
973
|
+
cd maqam
|
|
974
|
+
npm install
|
|
975
|
+
npm test
|
|
976
|
+
```
|
|
977
|
+
|
|
978
|
+
Run the console from source:
|
|
979
|
+
|
|
980
|
+
```bash
|
|
981
|
+
npm run maqam
|
|
982
|
+
```
|
|
983
|
+
|
|
984
|
+
Run the crawler from source:
|
|
985
|
+
|
|
986
|
+
```bash
|
|
987
|
+
npm run crawl -- https://example.com --max-pages 5
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
Check the package contents:
|
|
991
|
+
|
|
992
|
+
```bash
|
|
993
|
+
npm pack --dry-run
|
|
994
|
+
```
|
|
995
|
+
|
|
996
|
+
Run a specific test file:
|
|
997
|
+
|
|
998
|
+
```bash
|
|
999
|
+
npm test -- test/framework/policy.test.js
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
## Publishing
|
|
1003
|
+
|
|
1004
|
+
Package maintainers can publish with:
|
|
1005
|
+
|
|
1006
|
+
```bash
|
|
1007
|
+
npm publish --access public
|
|
1008
|
+
```
|
|
1009
|
+
|
|
1010
|
+
Before publishing:
|
|
1011
|
+
|
|
1012
|
+
```bash
|
|
1013
|
+
npm test
|
|
1014
|
+
npm pack --dry-run
|
|
1015
|
+
git status --short
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
## Troubleshooting
|
|
1019
|
+
|
|
1020
|
+
### `maqam` command not found
|
|
1021
|
+
|
|
1022
|
+
Install globally:
|
|
1023
|
+
|
|
1024
|
+
```bash
|
|
1025
|
+
npm install -g maqam
|
|
1026
|
+
```
|
|
1027
|
+
|
|
1028
|
+
Then restart your terminal so the npm global bin directory is on `PATH`.
|
|
1029
|
+
|
|
1030
|
+
### Port 8787 is already in use
|
|
1031
|
+
|
|
1032
|
+
Use another port:
|
|
1033
|
+
|
|
1034
|
+
```bash
|
|
1035
|
+
maqam --port 8788
|
|
1036
|
+
```
|
|
1037
|
+
|
|
1038
|
+
### Workflow returns `deny`
|
|
1039
|
+
|
|
1040
|
+
Check:
|
|
1041
|
+
|
|
1042
|
+
- The requested tool is present in `allowedTools`.
|
|
1043
|
+
- The URL origin is present in `allowedOrigins`.
|
|
1044
|
+
- The tool is not present in `deniedTools`.
|
|
1045
|
+
- The origin is not present in `deniedOrigins`.
|
|
1046
|
+
|
|
1047
|
+
### Tool call throws `ApprovalRequiredError`
|
|
1048
|
+
|
|
1049
|
+
The tool is configured in `approvalRequiredTools`. Catch the error and route it to a human approval queue.
|
|
1050
|
+
|
|
1051
|
+
### Crawler returns fewer pages than expected
|
|
1052
|
+
|
|
1053
|
+
Common causes:
|
|
1054
|
+
|
|
1055
|
+
- `robots.txt` disallows the page.
|
|
1056
|
+
- `sameOrigin` blocks off-origin links.
|
|
1057
|
+
- The page is not HTML/text/XML.
|
|
1058
|
+
- The response is too large.
|
|
1059
|
+
- The request timed out.
|
|
1060
|
+
- The site requires login or blocks automated access.
|
|
1061
|
+
|
|
1062
|
+
### `npm publish` asks for OTP
|
|
1063
|
+
|
|
1064
|
+
The npm account has two-factor authentication enabled. Re-run with a current OTP:
|
|
1065
|
+
|
|
1066
|
+
```bash
|
|
1067
|
+
npm publish --access public --otp=123456
|
|
1068
|
+
```
|
|
1069
|
+
|
|
1070
|
+
## Current Limitations
|
|
1071
|
+
|
|
1072
|
+
Maqam `0.1.x` is intentionally small:
|
|
1073
|
+
|
|
1074
|
+
- Evidence storage is in memory.
|
|
1075
|
+
- Workflow execution is sequential.
|
|
1076
|
+
- Human approval is represented by errors, not a full approval UI.
|
|
1077
|
+
- Skill registry is metadata-only.
|
|
1078
|
+
- The bundled console runs one research workflow.
|
|
1079
|
+
- No model provider is bundled.
|
|
1080
|
+
- No hosted control plane is included.
|
|
1081
|
+
|
|
1082
|
+
These constraints keep the package easy to inspect and extend.
|
|
1083
|
+
|
|
1084
|
+
## Next Extensions
|
|
1085
|
+
|
|
1086
|
+
Useful next packages or modules:
|
|
1087
|
+
|
|
1088
|
+
- Persistent evidence storage with SQLite or Postgres.
|
|
1089
|
+
- First-class human approval queue.
|
|
1090
|
+
- MCP-compatible connector framework.
|
|
1091
|
+
- Evaluation harness for policy and evidence quality.
|
|
1092
|
+
- Browser automation connector.
|
|
1093
|
+
- GitHub and npm metadata connectors.
|
|
1094
|
+
- Tenant-aware configuration and audit export.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "maqam",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Maqam is an MIT-licensed Ajnas agent framework for governed workflows, policy, evidence, skills, and crawler-backed research.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"files": [
|
|
22
22
|
"app/",
|
|
23
23
|
"bin/",
|
|
24
|
+
"docs/usage.md",
|
|
24
25
|
"src/",
|
|
25
26
|
"README.md",
|
|
26
27
|
"LICENSE"
|