melchizedek-agents 1.2.0 → 1.3.1

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 (66) hide show
  1. package/.env.example +9 -0
  2. package/CHANGELOG.md +174 -0
  3. package/DOCUMENTATION.md +107 -10
  4. package/ONBOARDING.md +50 -11
  5. package/QUICKSTART.md +1 -1
  6. package/README.md +22 -2
  7. package/config/agents/examples/connectors.yaml +82 -0
  8. package/config/agents/syndicate.schema.json +324 -26
  9. package/dist/lib/a2a/app.js +3 -0
  10. package/dist/lib/a2a/oauthSetup.js +20 -5
  11. package/dist/lib/chatgpt/adapter.d.ts +50 -0
  12. package/dist/lib/chatgpt/adapter.js +107 -0
  13. package/dist/lib/chatgpt/cli.d.ts +23 -0
  14. package/dist/lib/chatgpt/cli.js +77 -0
  15. package/dist/lib/chatgpt/oauth.d.ts +104 -0
  16. package/dist/lib/chatgpt/oauth.js +472 -0
  17. package/dist/lib/chatgpt/state.d.ts +98 -0
  18. package/dist/lib/chatgpt/state.js +145 -0
  19. package/dist/lib/chatgpt/store.d.ts +24 -0
  20. package/dist/lib/chatgpt/store.js +94 -0
  21. package/dist/lib/compile.js +40 -25
  22. package/dist/lib/dispatch.js +6 -2
  23. package/dist/lib/doctor.d.ts +34 -3
  24. package/dist/lib/doctor.js +63 -5
  25. package/dist/lib/loadSyndicate.d.ts +6 -0
  26. package/dist/lib/mcp/server.d.ts +195 -0
  27. package/dist/lib/mcp/server.js +564 -0
  28. package/dist/lib/memory/supabaseMemoryService.js +2 -2
  29. package/dist/lib/models/adapterResolver.js +6 -1
  30. package/dist/lib/models/gateway.d.ts +1 -1
  31. package/dist/lib/models/gateway.js +4 -2
  32. package/dist/lib/models/gptAdapter.js +11 -1
  33. package/dist/lib/onboarding.d.ts +4 -3
  34. package/dist/lib/onboarding.js +48 -9
  35. package/dist/lib/runtime/syndicateTurn.js +30 -16
  36. package/dist/lib/syndicateSchema.d.ts +146 -8
  37. package/dist/lib/syndicateSchema.js +118 -11
  38. package/dist/lib/tools/credentialUses.d.ts +7 -1
  39. package/dist/lib/tools/credentialUses.js +10 -2
  40. package/dist/lib/tools/generateImageTool.js +2 -1
  41. package/dist/lib/tools/inspectImageTool.js +1 -1
  42. package/dist/lib/tools/mcpToolFactory.d.ts +29 -1
  43. package/dist/lib/tools/mcpToolFactory.js +71 -25
  44. package/dist/lib/tools/mcpTransports.d.ts +12 -0
  45. package/dist/lib/tools/mcpTransports.js +11 -0
  46. package/dist/lib/tools/oauthConsent.d.ts +18 -3
  47. package/dist/lib/tools/oauthConsent.js +49 -14
  48. package/dist/lib/tools/oauthDiscovery.d.ts +202 -0
  49. package/dist/lib/tools/oauthDiscovery.js +616 -0
  50. package/dist/lib/tools/oauthTools.d.ts +44 -12
  51. package/dist/lib/tools/oauthTools.js +79 -22
  52. package/dist/lib/tools/xApiSearchTool.js +2 -2
  53. package/dist/scripts/a2a_server.js +8 -0
  54. package/dist/scripts/assistant_worker.js +11 -1
  55. package/dist/scripts/doctor.d.ts +1 -1
  56. package/dist/scripts/doctor.js +1 -1
  57. package/dist/scripts/mcp_server.d.ts +12 -0
  58. package/dist/scripts/mcp_server.js +345 -0
  59. package/dist/scripts/setup.d.ts +2 -0
  60. package/dist/scripts/setup.js +27 -0
  61. package/dist/scripts/syndicate_chat.js +12 -3
  62. package/package.json +15 -12
  63. package/skills/README.md +2 -2
  64. package/skills/briefs/skills-readme.md +1 -1
  65. package/skills/melchizedek-onboard/SKILL.md +28 -6
  66. package/skills/melchizedek-onboard-keys/SKILL.md +17 -5
@@ -61,7 +61,8 @@
61
61
  */
62
62
  import type { OAuthProvider } from './auth.ts';
63
63
  import type { OAuthHostAllowlist } from './oauthHosts.ts';
64
- import type { OAuthClientConfig } from './oauthConsent.ts';
64
+ import type { OAuthClientConfig, OAuthClientSource } from './oauthConsent.ts';
65
+ import type { DynamicOAuthGrant } from './oauthDiscovery.ts';
65
66
  import type { ToolContext } from './tool.ts';
66
67
  /** The `oauth2` block of an `auth` (OpenAPI) or an `mcp_auth` (MCP). */
67
68
  export interface OAuth2AuthConfig {
@@ -70,8 +71,14 @@ export interface OAuth2AuthConfig {
70
71
  grant: 'authorization_code' | 'client_credentials';
71
72
  /** The provider's authorization endpoint (authorization_code only). */
72
73
  authorization_url?: string;
73
- /** The provider's token endpoint. */
74
- token_url: string;
74
+ /** The provider's token endpoint. Absent only with `client_registration: 'dynamic'`, which discovers it. */
75
+ token_url?: string;
76
+ /**
77
+ * `'dynamic'` (MCP servers, authorization_code only, ADR 0124): no
78
+ * pre-issued client. The authorization server is discovered from the MCP
79
+ * server and the deployment registers itself (lib/tools/oauthDiscovery.ts).
80
+ */
81
+ client_registration?: 'dynamic';
75
82
  /** The client id, written out (it is not a secret) … */
76
83
  client_id?: string;
77
84
  /** … or the environment variable holding it. */
@@ -94,7 +101,7 @@ export interface OAuthGrantUse {
94
101
  tools: string;
95
102
  oauth2: OAuth2AuthConfig;
96
103
  }
97
- /** An agent's declared OAuth grants, OpenAPI entries first, then its MCP server. */
104
+ /** An agent's declared OAuth grants, OpenAPI entries first, then its MCP server, then each of its `mcp_servers` (ADR 0124). */
98
105
  export declare function agentOAuthGrants(agent: {
99
106
  name?: string;
100
107
  openapi?: Array<{
@@ -107,6 +114,12 @@ export declare function agentOAuthGrants(agent: {
107
114
  mcp_auth?: {
108
115
  oauth2?: OAuth2AuthConfig;
109
116
  };
117
+ mcp_servers?: Array<{
118
+ url?: string;
119
+ auth?: {
120
+ oauth2?: OAuth2AuthConfig;
121
+ };
122
+ }>;
110
123
  }, prefix?: string): OAuthGrantUse[];
111
124
  /** The environment variables a grant reads, by role. */
112
125
  export declare function oauthEnvNames(oauth2: OAuth2AuthConfig): string[];
@@ -187,12 +200,24 @@ interface AgentLike {
187
200
  mcp_auth?: {
188
201
  oauth2?: OAuth2AuthConfig;
189
202
  };
203
+ mcp_servers?: Array<{
204
+ url?: string;
205
+ auth?: {
206
+ oauth2?: OAuth2AuthConfig;
207
+ };
208
+ }>;
190
209
  yaml_reference?: string;
191
210
  }
192
211
  interface SyndicateLike {
193
212
  orchestrator?: AgentLike;
194
213
  subagents?: AgentLike[];
195
214
  }
215
+ /** The options oauthClientsFor and dynamicOAuthGrantsFor share. */
216
+ interface ClientsForOptions {
217
+ env?: NodeJS.ProcessEnv;
218
+ load?: (ref: string) => SyndicateLike;
219
+ allowlist?: OAuthHostAllowlist | null;
220
+ }
196
221
  /**
197
222
  * The OAuth clients the consent step needs for these syndicates' declared
198
223
  * authorization-code grants, by provider: pass them as
@@ -200,13 +225,19 @@ interface SyndicateLike {
200
225
  * secrets are read from the variables the YAML names. Two declarations of
201
226
  * one provider must agree on its endpoints, client and scopes, or this
202
227
  * throws: a provider's grant is one grant, whichever tool asks for it.
203
- * `load` reads a nested `yaml_reference:` file, when given.
228
+ * `load` reads a nested `yaml_reference:` file, when given. A grant with
229
+ * `client_registration: dynamic` has no client until it is registered: it
230
+ * is in `dynamicOAuthGrantsFor`, not here.
204
231
  */
205
- export declare function oauthClientsFor(configs: readonly SyndicateLike[], options?: {
206
- env?: NodeJS.ProcessEnv;
207
- load?: (ref: string) => SyndicateLike;
208
- allowlist?: OAuthHostAllowlist | null;
209
- }): Record<string, OAuthClientConfig>;
232
+ export declare function oauthClientsFor(configs: readonly SyndicateLike[], options?: ClientsForOptions): Record<string, OAuthClientConfig>;
233
+ /**
234
+ * The dynamic grants these syndicates declare (`client_registration:
235
+ * dynamic`, ADR 0124), by provider, checked as oauthClientsFor checks the
236
+ * others (one grant per provider, the MCP server on the allowlist): each
237
+ * becomes a consent client through `dynamicOAuthClient`
238
+ * (lib/tools/oauthDiscovery.ts).
239
+ */
240
+ export declare function dynamicOAuthGrantsFor(configs: readonly SyndicateLike[], options?: ClientsForOptions): Record<string, DynamicOAuthGrant>;
210
241
  /**
211
242
  * Every OAuth grant these syndicates declare, orchestrators and subagents,
212
243
  * following a nested `yaml_reference:` through `load` when given (once per
@@ -230,10 +261,11 @@ export declare function syndicateOAuthHostProblems(configs: readonly SyndicateLi
230
261
  * (`credentialStore({ providers })`, lib/tools/credentialStore.ts): an
231
262
  * expired user token is renewed at the provider's token endpoint with its
232
263
  * refresh token (RFC 6749 6) instead of asking the person again. Built from
233
- * `oauthClientsFor`'s clients; `allowPrivate` lets a private or loopback
264
+ * `oauthClientsFor`'s clients, and from `dynamicOAuthClient`'s sources (a
265
+ * discovered client, ADR 0124); `allowPrivate` lets a private or loopback
234
266
  * token endpoint through the guard (development).
235
267
  */
236
- export declare function oauthRefreshProviders(clients: Record<string, OAuthClientConfig>, options?: {
268
+ export declare function oauthRefreshProviders(clients: Record<string, OAuthClientConfig | OAuthClientSource>, options?: {
237
269
  allowPrivate?: boolean;
238
270
  fetch?: typeof fetch;
239
271
  timeoutMs?: number;
@@ -64,7 +64,7 @@ import { PROVIDER_NAME, ToolCredentialError } from './auth.js';
64
64
  import { readCredentialEnv } from './credentialEnv.js';
65
65
  import { CLIENT_SECRET_ENV, clientSecretEnvOf, credentialCallProblem, credentialHostProblem } from './credentialHosts.js';
66
66
  import { oauthHostProblem, oauthHosts } from './oauthHosts.js';
67
- /** An agent's declared OAuth grants, OpenAPI entries first, then its MCP server. */
67
+ /** An agent's declared OAuth grants, OpenAPI entries first, then its MCP server, then each of its `mcp_servers` (ADR 0124). */
68
68
  export function agentOAuthGrants(agent, prefix = '') {
69
69
  const name = `${prefix}${agent.name ?? ''}`;
70
70
  const out = [];
@@ -74,6 +74,10 @@ export function agentOAuthGrants(agent, prefix = '') {
74
74
  }
75
75
  if (agent.mcp_auth?.oauth2)
76
76
  out.push({ agent: name, tools: `mcp ${agent.mcp_server_url ?? ''}`, oauth2: agent.mcp_auth.oauth2 });
77
+ for (const server of agent.mcp_servers ?? []) {
78
+ if (server?.auth?.oauth2)
79
+ out.push({ agent: name, tools: `mcp ${server.url ?? ''}`, oauth2: server.auth.oauth2 });
80
+ }
77
81
  return out;
78
82
  }
79
83
  /** The environment variables a grant reads, by role. */
@@ -110,7 +114,9 @@ export function tokenTransportProblem(raw) {
110
114
  export function oauthGrantHostProblems(oauth2, servers, allowlist) {
111
115
  const list = allowlist === undefined ? oauthHosts() ?? null : allowlist;
112
116
  const urls = servers.map((s) => ['server', s]);
113
- urls.push(['token_url', oauth2.token_url]);
117
+ // A dynamic grant names no endpoint: discovery checks each one it finds (lib/tools/oauthDiscovery.ts).
118
+ if (oauth2.token_url)
119
+ urls.push(['token_url', oauth2.token_url]);
114
120
  if (oauth2.authorization_url)
115
121
  urls.push(['authorization_url', oauth2.authorization_url]);
116
122
  const out = new Set();
@@ -285,22 +291,25 @@ export function oauthTokenSource(oauth2, where, server, options = {}) {
285
291
  const env = options.env ?? process.env;
286
292
  if (!oauth2.client_secret_env)
287
293
  throw new Error(`${where}: a client_credentials grant needs client_secret_env`);
288
- const transport = tokenTransportProblem(oauth2.token_url);
294
+ const tokenUrl = oauth2.token_url;
295
+ if (!tokenUrl)
296
+ throw new Error(`${where}: a client_credentials grant needs token_url`);
297
+ const transport = tokenTransportProblem(tokenUrl);
289
298
  if (transport)
290
299
  throw new Error(`${where}: token_url: ${transport}`);
291
300
  if (options.allowPrivate !== true) {
292
- const blocked = blockedHostReason(new URL(oauth2.token_url).hostname);
301
+ const blocked = blockedHostReason(new URL(tokenUrl).hostname);
293
302
  if (blocked)
294
- throw new Error(`${where}: refusing token endpoint ${new URL(oauth2.token_url).hostname}: ${blocked}`);
303
+ throw new Error(`${where}: refusing token endpoint ${new URL(tokenUrl).hostname}: ${blocked}`);
295
304
  }
296
305
  // The client secret goes only to a host the operator binds its variable to (ADR 0122).
297
306
  const secretEnv = oauth2.client_secret_env;
298
- const unbound = credentialHostProblem(secretEnv, oauth2.token_url, 'token_url');
307
+ const unbound = credentialHostProblem(secretEnv, tokenUrl, 'token_url');
299
308
  if (unbound)
300
309
  throw new Error(`${where}: ${unbound}`);
301
310
  const grant = clientCredentialsGrant({
302
311
  provider,
303
- tokenUrl: oauth2.token_url,
312
+ tokenUrl,
304
313
  clientId: oauth2.client_id ?? readCredentialEnv(oauth2.client_id_env ?? '', where, env),
305
314
  clientSecret: readCredentialEnv(oauth2.client_secret_env, where, env),
306
315
  ...(oauth2.scopes?.length ? { scopes: [...oauth2.scopes] } : {}),
@@ -311,33 +320,47 @@ export function oauthTokenSource(oauth2, where, server, options = {}) {
311
320
  refuse(destination);
312
321
  // The client secret goes to the token endpoint: held to the same rule,
313
322
  // and to the credential host allowlist (ADR 0122).
314
- refuse(oauth2.token_url);
315
- if (credentialCallProblem(secretEnv, oauth2.token_url))
323
+ refuse(tokenUrl);
324
+ if (credentialCallProblem(secretEnv, tokenUrl))
316
325
  throw new ToolCredentialError('host_refused', provider, 'credential');
317
326
  return grant.token(ctx?.signal);
318
327
  };
319
328
  }
320
- /**
321
- * The OAuth clients the consent step needs for these syndicates' declared
322
- * authorization-code grants, by provider: pass them as
323
- * `oauthConsent({ providers })` (lib/tools/oauthConsent.ts). Client ids and
324
- * secrets are read from the variables the YAML names. Two declarations of
325
- * one provider must agree on its endpoints, client and scopes, or this
326
- * throws: a provider's grant is one grant, whichever tool asks for it.
327
- * `load` reads a nested `yaml_reference:` file, when given.
328
- */
329
- export function oauthClientsFor(configs, options = {}) {
329
+ /** Every declared authorization-code grant, by provider: configured clients and dynamic grants, one grant per provider. */
330
+ function collectOAuthClients(configs, options) {
330
331
  const env = options.env ?? process.env;
331
332
  const allowlist = options.allowlist === undefined ? oauthHosts(env) ?? null : options.allowlist;
332
333
  const clients = {};
334
+ const dynamic = {};
333
335
  const signature = new Map();
334
336
  for (const use of syndicateOAuthGrants(configs, options.load)) {
335
337
  const o = use.oauth2;
336
338
  if (o.grant !== 'authorization_code')
337
339
  continue;
338
340
  const where = `${use.agent} · ${use.tools} · oauth2`;
341
+ if (o.client_registration === 'dynamic') {
342
+ // Discovered and registered when first needed (ADR 0124): the MCP server is all the YAML names.
343
+ const server = use.tools.startsWith('mcp ') && use.tools.length > 4 ? use.tools.slice(4) : '';
344
+ if (!server)
345
+ throw new Error(`${where}: client_registration: dynamic is for an MCP server's grant`);
346
+ const hostProblems = oauthGrantHostProblems(o, [server], allowlist);
347
+ if (hostProblems.length)
348
+ throw new Error(`${where}: ${hostProblems.join('; ')}`);
349
+ const sig = JSON.stringify(['dynamic', server, [...(o.scopes ?? [])].sort(), o.authorization_params ?? {}]);
350
+ const before = signature.get(o.provider);
351
+ if (before !== undefined) {
352
+ if (before !== sig)
353
+ throw new Error(`${where}: provider "${o.provider}" is declared twice with different endpoints, client or scopes`);
354
+ continue;
355
+ }
356
+ signature.set(o.provider, sig);
357
+ dynamic[o.provider] = { provider: o.provider, server, scopes: [...(o.scopes ?? [])], ...(o.authorization_params ? { authorizationParams: { ...o.authorization_params } } : {}) };
358
+ continue;
359
+ }
339
360
  if (!o.authorization_url)
340
361
  throw new Error(`${where}: an authorization_code grant needs authorization_url`);
362
+ if (!o.token_url)
363
+ throw new Error(`${where}: an authorization_code grant needs token_url`);
341
364
  // The consent step sends the client secret, the code and later the refresh token to these endpoints (ADR 0114).
342
365
  const hostProblems = oauthGrantHostProblems(o, [], allowlist);
343
366
  if (hostProblems.length)
@@ -365,7 +388,31 @@ export function oauthClientsFor(configs, options = {}) {
365
388
  ...(o.client_secret_env ? { [CLIENT_SECRET_ENV]: o.client_secret_env } : {}),
366
389
  };
367
390
  }
368
- return clients;
391
+ return { clients, dynamic };
392
+ }
393
+ /**
394
+ * The OAuth clients the consent step needs for these syndicates' declared
395
+ * authorization-code grants, by provider: pass them as
396
+ * `oauthConsent({ providers })` (lib/tools/oauthConsent.ts). Client ids and
397
+ * secrets are read from the variables the YAML names. Two declarations of
398
+ * one provider must agree on its endpoints, client and scopes, or this
399
+ * throws: a provider's grant is one grant, whichever tool asks for it.
400
+ * `load` reads a nested `yaml_reference:` file, when given. A grant with
401
+ * `client_registration: dynamic` has no client until it is registered: it
402
+ * is in `dynamicOAuthGrantsFor`, not here.
403
+ */
404
+ export function oauthClientsFor(configs, options = {}) {
405
+ return collectOAuthClients(configs, options).clients;
406
+ }
407
+ /**
408
+ * The dynamic grants these syndicates declare (`client_registration:
409
+ * dynamic`, ADR 0124), by provider, checked as oauthClientsFor checks the
410
+ * others (one grant per provider, the MCP server on the allowlist): each
411
+ * becomes a consent client through `dynamicOAuthClient`
412
+ * (lib/tools/oauthDiscovery.ts).
413
+ */
414
+ export function dynamicOAuthGrantsFor(configs, options = {}) {
415
+ return collectOAuthClients(configs, options).dynamic;
369
416
  }
370
417
  /**
371
418
  * Every OAuth grant these syndicates declare, orchestrators and subagents,
@@ -415,14 +462,22 @@ export function syndicateOAuthHostProblems(configs, options = {}) {
415
462
  * (`credentialStore({ providers })`, lib/tools/credentialStore.ts): an
416
463
  * expired user token is renewed at the provider's token endpoint with its
417
464
  * refresh token (RFC 6749 6) instead of asking the person again. Built from
418
- * `oauthClientsFor`'s clients; `allowPrivate` lets a private or loopback
465
+ * `oauthClientsFor`'s clients, and from `dynamicOAuthClient`'s sources (a
466
+ * discovered client, ADR 0124); `allowPrivate` lets a private or loopback
419
467
  * token endpoint through the guard (development).
420
468
  */
421
469
  export function oauthRefreshProviders(clients, options = {}) {
422
470
  const providers = {};
423
- for (const [name, client] of Object.entries(clients)) {
471
+ for (const [name, entry] of Object.entries(clients)) {
424
472
  providers[name] = {
425
473
  refresh: async (refreshToken, context) => {
474
+ let client;
475
+ try {
476
+ client = typeof entry === 'function' ? await entry() : entry;
477
+ }
478
+ catch {
479
+ throw new ToolCredentialError('refresh_failed', name);
480
+ }
426
481
  // A refresh token goes only to a token endpoint the allowlist binds to its provider (ADR 0114).
427
482
  if (oauthCallProblem({ provider: name, grant: 'authorization_code' }, client.tokenUrl))
428
483
  throw new ToolCredentialError('host_refused', name, 'oauth');
@@ -433,6 +488,8 @@ export function oauthRefreshProviders(clients, options = {}) {
433
488
  const body = new URLSearchParams({ grant_type: 'refresh_token', refresh_token: refreshToken, client_id: client.clientId });
434
489
  if (client.clientSecret)
435
490
  body.set('client_secret', client.clientSecret);
491
+ if (client.resource)
492
+ body.set('resource', client.resource);
436
493
  return tokenRequest(client.tokenUrl, body, name, {
437
494
  allowPrivate: options.allowPrivate === true,
438
495
  doFetch: options.fetch ?? fetch,
@@ -442,7 +442,7 @@ async function lookupPost(id, token, input, deps) {
442
442
  const picked = wantImages ? pickImages(posts, imageMax(deps.env)) : [];
443
443
  const readings = picked.length ? await readImages(picked, deps) : new Map();
444
444
  const imagesRead = [...readings.values()].filter((r) => r.ok).length;
445
- console.log(`[x_api_search] lookup ${id} → ${posts.length} post (≈$${PRICE.postRead.toFixed(3)}), ${imagesRead}/${picked.length} image(s) transcribed on ${visionModel(deps.env)}${imageFailures(readings)}`);
445
+ console.log(`[x_api_search] lookup → ${posts.length} post (≈$${PRICE.postRead.toFixed(3)}), ${imagesRead}/${picked.length} image(s) transcribed on ${visionModel(deps.env)}${imageFailures(readings)}`);
446
446
  const photos = (posts[0].media ?? []).filter((m) => m.type === 'photo').length;
447
447
  return [
448
448
  `${head} — the post itself, as the API returns it.${photos ? ` ${photos} photo${photos === 1 ? '' : 's'} attached, ${imagesRead} transcribed beneath it.` : ''} For the replies and quotes around it, search conversation_id:${id}.`,
@@ -523,7 +523,7 @@ export async function runXApiSearch(input, deps = DEFAULT_DEPS) {
523
523
  const picked = wantImages ? pickImages(posts, imageMax(deps.env)) : [];
524
524
  const readings = picked.length ? await readImages(picked, deps) : new Map();
525
525
  const imagesRead = [...readings.values()].filter((r) => r.ok).length;
526
- console.log(`[x_api_search] "${query}" → ${posts.length} post(s) (≈$${(max * PRICE.postRead).toFixed(3)} at the ${max}-slot ceiling), ` +
526
+ console.log(`[x_api_search] search (${query.length} chars) → ${posts.length} post(s) (≈$${(max * PRICE.postRead).toFixed(3)} at the ${max}-slot ceiling), ` +
527
527
  `${imagesRead}/${picked.length} image(s) transcribed on ${visionModel(deps.env)}${imageFailures(readings)}`);
528
528
  return renderBlock({ ...outcome, ok: true, posts }, readings, new Set(picked.map((p) => p.media.key)), imagesRead);
529
529
  }
@@ -108,6 +108,7 @@ import { redisRateLimitStore } from '../lib/a2a/redisLimits.js';
108
108
  import { dbSchema } from '../lib/storage/schema.js';
109
109
  import { postgresStorage } from '../lib/storage/postgres/index.js';
110
110
  import { isPlaceholderValue, loadEnv } from '../lib/loadEnv.js';
111
+ import { refuseChatGptSignInOnServedSurface } from '../lib/chatgpt/state.js';
111
112
  import { flushTracing } from '../lib/observability/tracer.js';
112
113
  import { setLogLevel } from '../lib/runtime/logging.js';
113
114
  import { loadSyndicate } from '../lib/loadSyndicate.js';
@@ -246,6 +247,13 @@ export async function startServer(syndicateName = 'syndicate.yaml', extensions =
246
247
  process.env.OTEL_CONSOLE_SPANS = 'false';
247
248
  // The engine's level: INFO/DEBUG lines carry raw event JSON, so keep them quiet.
248
249
  setLogLevel('warn');
250
+ // Sign in with ChatGPT is local only (ADR 0126): stop before anything else starts.
251
+ try {
252
+ refuseChatGptSignInOnServedSurface('melchizedek-serve');
253
+ }
254
+ catch (err) {
255
+ fatal(err instanceof Error ? err.message : String(err));
256
+ }
249
257
  const publicUrl = process.env.PUBLIC_URL?.trim() || undefined;
250
258
  const port = envInt('PORT', 4000);
251
259
  // ── Authentication posture ─────────────────────────────────────────────────
@@ -48,6 +48,7 @@ import { loadEnv } from '../lib/loadEnv.js';
48
48
  import { loadSyndicate } from '../lib/loadSyndicate.js';
49
49
  import { runDurableTurn } from '../lib/runtime/native/checkpoint.js';
50
50
  import { runtimeSetting } from '../lib/runtime/runtimeFlag.js';
51
+ import { refuseChatGptSignInOnServedSurface } from '../lib/chatgpt/state.js';
51
52
  import { setLogLevel } from '../lib/runtime/logging.js';
52
53
  import { PROVIDERS, providerForModel, providerKeyPresent, } from '../lib/models/registry.js';
53
54
  import { CHECKPOINT_MAX_BYTES_ENV, applyInterrupted, checkpointMaxBytesSetting, fileTaskBackendWith, getTaskBackend, setTaskBackend, taskStorePath, } from '../lib/tools/taskTools.js';
@@ -58,6 +59,14 @@ loadEnv(import.meta.url);
58
59
  setLogLevel('warn');
59
60
  // MELCHIZEDEK_RUNTIME=adk (removed in 1.0.0) stops the worker here, naming the release.
60
61
  runtimeSetting();
62
+ // Sign in with ChatGPT is local only (ADR 0126): the worker runs jobs unattended, so it never runs on it.
63
+ try {
64
+ refuseChatGptSignInOnServedSurface('melchizedek-worker');
65
+ }
66
+ catch (err) {
67
+ console.error(`✗ ${err instanceof Error ? err.message : String(err)}`);
68
+ process.exit(1);
69
+ }
61
70
  // ── CLI ──────────────────────────────────────────────────────────────────────
62
71
  const argv = process.argv.slice(2).filter((a) => a !== '--');
63
72
  const flag = (name) => {
@@ -178,7 +187,8 @@ async function drain() {
178
187
  ran += 1;
179
188
  const started = Date.now();
180
189
  const label = job.owner ? `${job.id} (${job.owner})` : job.id;
181
- log(`${label} started: ${job.title}`);
190
+ // The title is the person's words: the log names the job, never its text.
191
+ log(`${label} started (${(job.instruction ?? job.title).length.toLocaleString()} chars)`);
182
192
  // Renew the lease while the job runs, so no other worker takes it back;
183
193
  // a renewal that finds the claim gone (the job was cancelled) stops the run.
184
194
  const held = job;
@@ -22,7 +22,7 @@
22
22
  * Usage:
23
23
  * npm run doctor
24
24
  * npm run doctor -- --json # machine-readable
25
- * npm run doctor -- --check # exit 1 on a blocked syndicate, a runtime problem, or an OAuth or credential host problem
25
+ * npm run doctor -- --check # exit 1 on a blocked syndicate, a runtime problem, an unrecognised MODEL_GATEWAY, or an OAuth or credential host problem
26
26
  * npm run doctor -- --matrix # the provider × capability matrix
27
27
  * npm run doctor -- --fix-namespaces [file…] # give long-term syndicates a memory_namespace
28
28
  * MELCHIZEDEK_AGENTS_DIR=/path npm run doctor
@@ -22,7 +22,7 @@
22
22
  * Usage:
23
23
  * npm run doctor
24
24
  * npm run doctor -- --json # machine-readable
25
- * npm run doctor -- --check # exit 1 on a blocked syndicate, a runtime problem, or an OAuth or credential host problem
25
+ * npm run doctor -- --check # exit 1 on a blocked syndicate, a runtime problem, an unrecognised MODEL_GATEWAY, or an OAuth or credential host problem
26
26
  * npm run doctor -- --matrix # the provider × capability matrix
27
27
  * npm run doctor -- --fix-namespaces [file…] # give long-term syndicates a memory_namespace
28
28
  * MELCHIZEDEK_AGENTS_DIR=/path npm run doctor
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ export interface McpCliArgs {
3
+ syndicates: string[];
4
+ agentsDir?: string;
5
+ http: boolean;
6
+ host?: string;
7
+ port?: number;
8
+ help: boolean;
9
+ }
10
+ /** The bin's flags. Throws, naming the flag, on one it does not know. */
11
+ export declare function parseMcpArgs(argv: string[]): McpCliArgs;
12
+ export declare function startMcpServer(argv?: string[]): Promise<void>;