melchizedek-agents 1.6.0 → 1.8.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +113 -0
  2. package/DOCUMENTATION.md +16 -7
  3. package/config/agents/syndicate.schema.json +63 -0
  4. package/config/agents/syndicateSchema.yaml +50 -0
  5. package/dist/lib/a2a/executor.js +21 -0
  6. package/dist/lib/compile.d.ts +4 -3
  7. package/dist/lib/compile.js +8 -3
  8. package/dist/lib/doctor.d.ts +10 -0
  9. package/dist/lib/doctor.js +33 -4
  10. package/dist/lib/index.d.ts +9 -0
  11. package/dist/lib/index.js +7 -0
  12. package/dist/lib/loadSyndicate.d.ts +13 -0
  13. package/dist/lib/loadSyndicate.js +14 -5
  14. package/dist/lib/observability/supabaseSpanExporter.js +2 -1
  15. package/dist/lib/observability/tracer.d.ts +2 -2
  16. package/dist/lib/observability/tracer.js +2 -2
  17. package/dist/lib/project.d.ts +116 -0
  18. package/dist/lib/project.js +274 -0
  19. package/dist/lib/runtime/native/checkpoint.d.ts +2 -1
  20. package/dist/lib/runtime/native/checkpoint.js +6 -2
  21. package/dist/lib/runtime/native/interrupts.js +9 -2
  22. package/dist/lib/runtime/nativeTurn.d.ts +2 -1
  23. package/dist/lib/runtime/nativeTurn.js +5 -1
  24. package/dist/lib/runtime/syndicateTurn.d.ts +11 -1
  25. package/dist/lib/runtime/syndicateTurn.js +383 -5
  26. package/dist/lib/sandbox/sandbox.d.ts +121 -0
  27. package/dist/lib/sandbox/sandbox.js +276 -0
  28. package/dist/lib/sandbox/worker.d.ts +29 -0
  29. package/dist/lib/sandbox/worker.js +280 -0
  30. package/dist/lib/script/config.d.ts +35 -0
  31. package/dist/lib/script/config.js +17 -0
  32. package/dist/lib/script/journal.d.ts +137 -0
  33. package/dist/lib/script/journal.js +193 -0
  34. package/dist/lib/script/run.d.ts +138 -0
  35. package/dist/lib/script/run.js +364 -0
  36. package/dist/lib/syndicateSchema.d.ts +20 -0
  37. package/dist/lib/syndicateSchema.js +98 -6
  38. package/dist/lib/toolRegistry.js +4 -0
  39. package/dist/lib/tools/runCodeTool.d.ts +50 -0
  40. package/dist/lib/tools/runCodeTool.js +59 -0
  41. package/dist/scripts/a2a_server.js +3 -3
  42. package/dist/scripts/doctor.js +2 -1
  43. package/dist/scripts/mcp_server.js +2 -1
  44. package/package.json +6 -1
@@ -0,0 +1,59 @@
1
+ /**
2
+ * lib/tools/runCodeTool.ts — `run_code`: an agent runs JavaScript in the
3
+ * engine's sandbox and rests its answer on what the code returned
4
+ * (ADR 0137 items 2 to 4).
5
+ *
6
+ * WHY a registry tool: any agent on any provider can list it
7
+ * (`tools: [run_code]`), since the model only writes text, and it gets what
8
+ * every registry tool gets: `require_approval`, MCP exposure (ADR 0125), the
9
+ * result bound and `retries.tool_errors`. `code_execution: gemini` stays,
10
+ * for Python in Gemini's own sandbox on Gemini models (ADR 0033).
11
+ *
12
+ * THE CONTRACT: `{ code, input? }`. The code is a JavaScript module whose
13
+ * default export is called with `input`; the tool returns
14
+ * `{ result, logs, error }`: the export's JSON-serializable return, the
15
+ * `console.log` output, and what stopped it, if anything. Each is bounded at
16
+ * MAX_RESULT_CHARS. The code reaches nothing but `input` and `console`: no
17
+ * network, no host, no keys, no clock, no randomness (lib/sandbox/).
18
+ *
19
+ * THE SWITCH: nothing runs unless the operator set MELCHIZEDEK_CODE_SANDBOX=on;
20
+ * a syndicate listing run_code without it is refused at load (refuseOnNative),
21
+ * and a call that reaches here anyway answers with the reason.
22
+ *
23
+ * ATTACH: an agent's `run_code: { attach: true }` puts each run's code and
24
+ * result on the reply as a `code_run` data part (lib/runtime/syndicateTurn.ts
25
+ * collects them, the A2A executor publishes them). Off by default.
26
+ */
27
+ import { z } from 'zod';
28
+ import { codeSandboxEnabled, codeSandboxOffMessage, runInSandbox } from '../sandbox/sandbox.js';
29
+ import { MAX_RESULT_CHARS } from './tool.js';
30
+ import { defineTool } from './toolContract.js';
31
+ /** The registry name. */
32
+ export const RUN_CODE = 'run_code';
33
+ /** The largest program a call may send. */
34
+ export const MAX_CODE_CHARS = 64_000;
35
+ export const runCodeTool = defineTool({
36
+ name: RUN_CODE,
37
+ description: 'Run JavaScript and get back what it returns, so an answer can rest on a computation instead of mental arithmetic: totals, conversions, statistics, dates from given timestamps, parsing, sorting, checking a formula. '
38
+ + '`code` is an ES module whose default export is called with `input` and returns a JSON-serializable value, e.g. `export default ({ xs }) => xs.reduce((a, b) => a + b, 0)`. '
39
+ + 'console.log output comes back in `logs`. The code has no network, no files, no clock and no randomness (Date.now, new Date() and Math.random throw); pass any data it needs in `input`. '
40
+ + 'It returns { result, logs, error }; when error is set, fix the code and run it again.',
41
+ schema: z.object({
42
+ code: z.string().min(1).max(MAX_CODE_CHARS).describe('An ES module: `export default (input) => value`. May be async.'),
43
+ input: z.unknown().optional().describe('JSON the default export is called with.'),
44
+ }),
45
+ async execute({ code, input }, ctx) {
46
+ if (!codeSandboxEnabled())
47
+ return { result: null, logs: '', error: codeSandboxOffMessage(RUN_CODE) };
48
+ const run = await runInSandbox({
49
+ source: code,
50
+ value: input ?? null,
51
+ ...(ctx.signal ? { signal: ctx.signal } : {}),
52
+ limits: { maxLogChars: MAX_RESULT_CHARS, maxResultChars: MAX_RESULT_CHARS },
53
+ });
54
+ const logs = run.logsTruncated ? `${run.logs}\n[logs truncated at ${MAX_RESULT_CHARS.toLocaleString('en-US')} characters]` : run.logs;
55
+ if (!run.ok)
56
+ return { result: null, logs, error: `${run.error?.code ?? 'CODE_ERROR'}: ${run.error?.message ?? 'the code did not finish'}` };
57
+ return { result: run.result ?? null, logs, error: null };
58
+ },
59
+ });
@@ -97,7 +97,6 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
97
97
  */
98
98
  import { randomBytes } from 'node:crypto';
99
99
  import { readdirSync, realpathSync, writeFileSync } from 'node:fs';
100
- import path from 'node:path';
101
100
  import { pathToFileURL } from 'node:url';
102
101
  import { createA2AApp } from '../lib/a2a/app.js';
103
102
  import { callerTokens, firstOf, hashCallerToken, jwtIdentity, parseCallers, sharedSecret, trustedHeader, } from '../lib/a2a/identity.js';
@@ -116,6 +115,7 @@ import { callbackIdentity, callbackIdentityProblem, oauthServerSetup } from '../
116
115
  import { postgresCredentialRows } from '../lib/storage/postgres/credentialStore.js';
117
116
  import { setOAuthHosts } from '../lib/tools/oauthHosts.js';
118
117
  import { setCredentialHosts } from '../lib/tools/credentialHosts.js';
118
+ import { resolveAgentsDir } from '../lib/project.js';
119
119
  // Re-exported for existing importers (tests/failNarration.test.ts).
120
120
  export { describeTurnError } from '../lib/a2a/executor.js';
121
121
  /** Minimum bearer-secret length below which the server warns. */
@@ -181,7 +181,7 @@ export function servedFileSyndicates(syndicateName, servedAgents, registryAgents
181
181
  add(id);
182
182
  }
183
183
  else {
184
- const agentsDir = path.resolve(process.env.MELCHIZEDEK_AGENTS_DIR ?? path.join(process.cwd(), 'config', 'agents'));
184
+ const agentsDir = resolveAgentsDir();
185
185
  let names = [];
186
186
  try {
187
187
  names = readdirSync(agentsDir);
@@ -633,7 +633,7 @@ if (invokedAsMain) {
633
633
  if (error?.code === 'ENOENT') {
634
634
  console.error(`[A2A] Syndicate not found: ${arg || 'syndicate.yaml'}.\n`
635
635
  + ' Pass your syndicate: melchizedek-serve <name>.yaml\n'
636
- + ' (agents directory: MELCHIZEDEK_AGENTS_DIR, or <cwd>/config/agents)');
636
+ + ' (agents directory: MELCHIZEDEK_AGENTS_DIR, else the agents folder in melchizedek.json, else <cwd>/config/agents)');
637
637
  process.exit(1);
638
638
  }
639
639
  console.error(error);
@@ -33,6 +33,7 @@ import { loadEnv } from '../lib/loadEnv.js';
33
33
  import { checkProblems, renderDoctor, runDoctor } from '../lib/doctor.js';
34
34
  import { renderCapabilityMatrix } from '../lib/models/capabilities.js';
35
35
  import { assignMemoryNamespace, LEGACY_MEMORY_APP_NAME } from '../lib/memory/namespace.js';
36
+ import { resolveAgentsDir } from '../lib/project.js';
36
37
  loadEnv();
37
38
  const args = process.argv.slice(2);
38
39
  const json = args.includes('--json');
@@ -52,7 +53,7 @@ function packageScripts() {
52
53
  if (fixAt !== -1) {
53
54
  // ADR 0020: give each named long-term syndicate its own memory namespace.
54
55
  // Only the files named are touched; with none named, list the candidates.
55
- const agentsDir = path.resolve(process.env.MELCHIZEDEK_AGENTS_DIR ?? path.join(process.cwd(), 'config', 'agents'));
56
+ const agentsDir = resolveAgentsDir();
56
57
  const files = args.slice(fixAt + 1).filter((a) => !a.startsWith('--'));
57
58
  if (files.length === 0) {
58
59
  const pending = runDoctor({ agentsDir }).syndicates.filter((s) => s.memory && !s.memory.declared && !s.file.includes('/'));
@@ -38,6 +38,7 @@
38
38
  import { readdirSync, realpathSync } from 'node:fs';
39
39
  import path from 'node:path';
40
40
  import { pathToFileURL } from 'node:url';
41
+ import { resolveAgentsDir } from '../lib/project.js';
41
42
  /** The bin's flags. Throws, naming the flag, on one it does not know. */
42
43
  export function parseMcpArgs(argv) {
43
44
  const out = { syndicates: [], http: false, help: false };
@@ -160,7 +161,7 @@ export async function startMcpServer(argv = process.argv.slice(2)) {
160
161
  const bindings = () => ({
161
162
  current_date: new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }),
162
163
  });
163
- const agentsDir = path.resolve(process.env.MELCHIZEDEK_AGENTS_DIR ?? path.join(process.cwd(), 'config', 'agents'));
164
+ const agentsDir = resolveAgentsDir();
164
165
  let files = args.syndicates.map((id) => (id.endsWith('.yaml') || id.endsWith('.yml') ? id : `${id}.yaml`));
165
166
  const named = files.length > 0;
166
167
  if (!named) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "melchizedek-agents",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "description": "A multi-model, multi-agent orchestration framework — YAML-defined syndicates on the engine's own agent loop, Supabase or Postgres session persistence, pgvector long-term memory. Companion repo to the lyceumagents.com curriculum.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -21,6 +21,8 @@
21
21
  "./loadSyndicate": "./dist/lib/loadSyndicate.js",
22
22
  "./loadEnv": "./dist/lib/loadEnv.js",
23
23
  "./dispatch": "./dist/lib/dispatch.js",
24
+ "./script": "./dist/lib/script/run.js",
25
+ "./sandbox": "./dist/lib/sandbox/sandbox.js",
24
26
  "./compile": "./dist/lib/compile.js",
25
27
  "./runtime": "./dist/lib/runtime/syndicateTurn.js",
26
28
  "./runtime/turnControl": "./dist/lib/runtime/turnControl.js",
@@ -31,6 +33,7 @@
31
33
  "./a2a/limits": "./dist/lib/a2a/redisLimits.js",
32
34
  "./mcp": "./dist/lib/mcp/server.js",
33
35
  "./registry": "./dist/lib/registry.js",
36
+ "./project": "./dist/lib/project.js",
34
37
  "./server": "./dist/scripts/a2a_server.js",
35
38
  "./guards": "./dist/lib/guards/index.js",
36
39
  "./storage/postgres": "./dist/lib/storage/postgres/index.js",
@@ -148,6 +151,7 @@
148
151
  "@a2a-js/sdk": "^1.3.0",
149
152
  "@anthropic-ai/sdk": "^0.131.0",
150
153
  "@google/genai": "2.27.0",
154
+ "@jitl/quickjs-wasmfile-release-sync": "0.32.0",
151
155
  "@modelcontextprotocol/sdk": "^1.32.1",
152
156
  "@opentelemetry/api": "^1.9.1",
153
157
  "@opentelemetry/core": "^2.8.0",
@@ -161,6 +165,7 @@
161
165
  "jose": "^6.2.12",
162
166
  "openai": "^7.28.0",
163
167
  "pg": "^8.23.1",
168
+ "quickjs-emscripten-core": "0.32.0",
164
169
  "yaml": "^2.9.0",
165
170
  "zod": "4.6.5"
166
171
  },