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.
- package/.env.example +9 -0
- package/CHANGELOG.md +174 -0
- package/DOCUMENTATION.md +107 -10
- package/ONBOARDING.md +50 -11
- package/QUICKSTART.md +1 -1
- package/README.md +22 -2
- package/config/agents/examples/connectors.yaml +82 -0
- package/config/agents/syndicate.schema.json +324 -26
- package/dist/lib/a2a/app.js +3 -0
- package/dist/lib/a2a/oauthSetup.js +20 -5
- package/dist/lib/chatgpt/adapter.d.ts +50 -0
- package/dist/lib/chatgpt/adapter.js +107 -0
- package/dist/lib/chatgpt/cli.d.ts +23 -0
- package/dist/lib/chatgpt/cli.js +77 -0
- package/dist/lib/chatgpt/oauth.d.ts +104 -0
- package/dist/lib/chatgpt/oauth.js +472 -0
- package/dist/lib/chatgpt/state.d.ts +98 -0
- package/dist/lib/chatgpt/state.js +145 -0
- package/dist/lib/chatgpt/store.d.ts +24 -0
- package/dist/lib/chatgpt/store.js +94 -0
- package/dist/lib/compile.js +40 -25
- package/dist/lib/dispatch.js +6 -2
- package/dist/lib/doctor.d.ts +34 -3
- package/dist/lib/doctor.js +63 -5
- package/dist/lib/loadSyndicate.d.ts +6 -0
- package/dist/lib/mcp/server.d.ts +195 -0
- package/dist/lib/mcp/server.js +564 -0
- package/dist/lib/memory/supabaseMemoryService.js +2 -2
- package/dist/lib/models/adapterResolver.js +6 -1
- package/dist/lib/models/gateway.d.ts +1 -1
- package/dist/lib/models/gateway.js +4 -2
- package/dist/lib/models/gptAdapter.js +11 -1
- package/dist/lib/onboarding.d.ts +4 -3
- package/dist/lib/onboarding.js +48 -9
- package/dist/lib/runtime/syndicateTurn.js +30 -16
- package/dist/lib/syndicateSchema.d.ts +146 -8
- package/dist/lib/syndicateSchema.js +118 -11
- package/dist/lib/tools/credentialUses.d.ts +7 -1
- package/dist/lib/tools/credentialUses.js +10 -2
- package/dist/lib/tools/generateImageTool.js +2 -1
- package/dist/lib/tools/inspectImageTool.js +1 -1
- package/dist/lib/tools/mcpToolFactory.d.ts +29 -1
- package/dist/lib/tools/mcpToolFactory.js +71 -25
- package/dist/lib/tools/mcpTransports.d.ts +12 -0
- package/dist/lib/tools/mcpTransports.js +11 -0
- package/dist/lib/tools/oauthConsent.d.ts +18 -3
- package/dist/lib/tools/oauthConsent.js +49 -14
- package/dist/lib/tools/oauthDiscovery.d.ts +202 -0
- package/dist/lib/tools/oauthDiscovery.js +616 -0
- package/dist/lib/tools/oauthTools.d.ts +44 -12
- package/dist/lib/tools/oauthTools.js +79 -22
- package/dist/lib/tools/xApiSearchTool.js +2 -2
- package/dist/scripts/a2a_server.js +8 -0
- package/dist/scripts/assistant_worker.js +11 -1
- package/dist/scripts/doctor.d.ts +1 -1
- package/dist/scripts/doctor.js +1 -1
- package/dist/scripts/mcp_server.d.ts +12 -0
- package/dist/scripts/mcp_server.js +345 -0
- package/dist/scripts/setup.d.ts +2 -0
- package/dist/scripts/setup.js +27 -0
- package/dist/scripts/syndicate_chat.js +12 -3
- package/package.json +15 -12
- package/skills/README.md +2 -2
- package/skills/briefs/skills-readme.md +1 -1
- package/skills/melchizedek-onboard/SKILL.md +28 -6
- 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
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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(
|
|
301
|
+
const blocked = blockedHostReason(new URL(tokenUrl).hostname);
|
|
293
302
|
if (blocked)
|
|
294
|
-
throw new Error(`${where}: refusing token endpoint ${new URL(
|
|
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,
|
|
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
|
|
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(
|
|
315
|
-
if (credentialCallProblem(secretEnv,
|
|
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
|
-
|
|
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
|
|
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,
|
|
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
|
|
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]
|
|
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
|
-
|
|
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;
|
package/dist/scripts/doctor.d.ts
CHANGED
|
@@ -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
|
package/dist/scripts/doctor.js
CHANGED
|
@@ -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>;
|