melchizedek-agents 1.3.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/CHANGELOG.md +33 -0
- package/dist/lib/chatgpt/oauth.d.ts +4 -3
- package/dist/lib/chatgpt/oauth.js +36 -12
- package/dist/lib/models/gptAdapter.js +11 -1
- package/package.json +12 -12
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,39 @@ the starter pack and the templates), not the repo's full history.
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## 1.3.1 — 2026-10-11
|
|
10
|
+
|
|
11
|
+
A patch for Sign in with ChatGPT, from its first live run on a ChatGPT plan:
|
|
12
|
+
turns on the sign-in now answer, a first sign-in keeps one host id, and a
|
|
13
|
+
plan without plan usage for apps is named as the reason a sign-in is
|
|
14
|
+
refused. Bin paths are written as npm stores them.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **Sign in with ChatGPT answers again: a stream whose `response.completed`
|
|
19
|
+
carries an empty `output` builds its final from `response.output_item.done`.**
|
|
20
|
+
The plan-usage route (`store: false`) sends every finished item only as
|
|
21
|
+
`output_item.done` and closes with an empty `output`, so on 1.3.0 a turn on
|
|
22
|
+
the sign-in completed with no text and no tool calls. The Responses stream
|
|
23
|
+
now keeps each finished item by `output_index` and uses them when the
|
|
24
|
+
completed reply has none; a populated `output` (every keyed call) still
|
|
25
|
+
wins. Found in the first live run on a ChatGPT plan (`gpt-5.5`,
|
|
26
|
+
`gpt-6.1-sol`).
|
|
27
|
+
- **Bin paths are written as npm stores them** (`dist/scripts/<bin>.js`, no
|
|
28
|
+
leading `./`). Same bin names and targets; npm 11 no longer warns at publish
|
|
29
|
+
that each bin "was invalid and removed".
|
|
30
|
+
- **Sign in with ChatGPT keeps one host id from the first attempt.** The
|
|
31
|
+
installation's `ext_agent_host_id` is written to the credential file (mode
|
|
32
|
+
600) before the browser opens, as OpenAI's docs ask, so a first sign-in that
|
|
33
|
+
times out or is cancelled no longer sends a new host id on the retry. A
|
|
34
|
+
stored registration is never overwritten.
|
|
35
|
+
- **A refused ChatGPT sign-in says why.** `invalid_grant` on the
|
|
36
|
+
authorization-code exchange, and `access_denied` (or another refusal) at
|
|
37
|
+
the consent page, now print one line: the sign-in was refused, the most
|
|
38
|
+
common cause is a ChatGPT plan or workspace that does not include plan
|
|
39
|
+
usage for apps, and how to retry. The error codes are unchanged; a
|
|
40
|
+
refresh's `invalid_grant` still means sign in again.
|
|
41
|
+
|
|
9
42
|
## 1.3.0 — 2026-10-10
|
|
10
43
|
|
|
11
44
|
Release 1.3.0 connects syndicates to the MCP ecosystem in both directions
|
|
@@ -75,9 +75,10 @@ export interface SignInResult {
|
|
|
75
75
|
planUsage: boolean;
|
|
76
76
|
}
|
|
77
77
|
/**
|
|
78
|
-
* The browser sign-in. Stores the
|
|
79
|
-
*
|
|
80
|
-
* the
|
|
78
|
+
* The browser sign-in. Stores the host id before the browser opens when no
|
|
79
|
+
* file exists yet, the registration (host id, issued client id) before the
|
|
80
|
+
* code exchange, and the tokens after the ID token verifies and the plan
|
|
81
|
+
* scope is confirmed.
|
|
81
82
|
*/
|
|
82
83
|
export declare function signIn(opts: SignInOptions): Promise<SignInResult>;
|
|
83
84
|
export interface AccessTokenOptions extends OAuthOptions {
|
|
@@ -50,6 +50,14 @@ const UNUSABLE_REFRESH = new Set([
|
|
|
50
50
|
'refresh_token_invalidated',
|
|
51
51
|
'refresh_token_reused',
|
|
52
52
|
]);
|
|
53
|
+
/**
|
|
54
|
+
* Callback error codes that mean the consent page refused or the person
|
|
55
|
+
* stopped there: OpenAI's page for a plan or workspace without plan usage for
|
|
56
|
+
* apps offers no way on, so leaving it is the usual way these arrive.
|
|
57
|
+
*/
|
|
58
|
+
const REFUSED_AT_CONSENT = new Set(['access_denied', 'unauthorized_client', 'consent_required', 'interaction_required']);
|
|
59
|
+
/** One line for a sign-in OpenAI refused at the consent page or the code exchange. */
|
|
60
|
+
const signInRefused = (code) => `ChatGPT refused the sign-in (${code}); the most common cause is a ChatGPT plan or workspace that does not include plan usage for apps. Retry with \`npm run setup -- --chatgpt-signin\` (\`npx melchizedek-setup --chatgpt-signin\` from an installed package).`;
|
|
53
61
|
/** A sign-in failure: a code for the program, a message for the person, never a token. */
|
|
54
62
|
export class SignInError extends Error {
|
|
55
63
|
code;
|
|
@@ -236,7 +244,7 @@ async function listen(port, state, savedClientId, timeoutMs, signal) {
|
|
|
236
244
|
res.writeHead(200, PAGE_HEADERS).end(page('Sign-in cancelled', 'You can close this tab and return to the terminal.'));
|
|
237
245
|
const raw = url.searchParams.get('error') ?? '';
|
|
238
246
|
const code = /^[a-z0-9_.-]{1,64}$/i.test(raw) ? raw : 'authorization_failed';
|
|
239
|
-
settle(new SignInError(code, `ChatGPT did not authorize the sign-in (${code}).`));
|
|
247
|
+
settle(new SignInError(code, REFUSED_AT_CONSENT.has(code) ? signInRefused(code) : `ChatGPT did not authorize the sign-in (${code}).`));
|
|
240
248
|
return;
|
|
241
249
|
}
|
|
242
250
|
const codes = url.searchParams.getAll('code');
|
|
@@ -284,9 +292,10 @@ export async function openInBrowser(url) {
|
|
|
284
292
|
});
|
|
285
293
|
}
|
|
286
294
|
/**
|
|
287
|
-
* The browser sign-in. Stores the
|
|
288
|
-
*
|
|
289
|
-
* the
|
|
295
|
+
* The browser sign-in. Stores the host id before the browser opens when no
|
|
296
|
+
* file exists yet, the registration (host id, issued client id) before the
|
|
297
|
+
* code exchange, and the tokens after the ID token verifies and the plan
|
|
298
|
+
* scope is confirmed.
|
|
290
299
|
*/
|
|
291
300
|
export async function signIn(opts) {
|
|
292
301
|
const issuer = issuerOf(opts);
|
|
@@ -295,6 +304,11 @@ export async function signIn(opts) {
|
|
|
295
304
|
const sameIssuer = previous?.issuer === issuer;
|
|
296
305
|
const hostId = previous?.hostId ?? `urn:uuid:${randomUUID()}`;
|
|
297
306
|
const savedClientId = sameIssuer ? previous?.clientId : undefined;
|
|
307
|
+
// OpenAI asks for the host id to be chosen and persisted before the first
|
|
308
|
+
// sign-in, so a timed-out or cancelled attempt does not send a new one next
|
|
309
|
+
// time. Only written when nothing is stored: a stored registration is kept.
|
|
310
|
+
if (!previous)
|
|
311
|
+
writeStoredSignIn(opts.file, { version: 1, issuer, hostId });
|
|
298
312
|
const state = randomValue();
|
|
299
313
|
const nonce = randomValue();
|
|
300
314
|
const verifier = randomValue();
|
|
@@ -326,14 +340,24 @@ export async function signIn(opts) {
|
|
|
326
340
|
? { ...previous, clientId: cb.clientId, hostId }
|
|
327
341
|
: { version: 1, issuer, hostId, clientId: cb.clientId };
|
|
328
342
|
writeStoredSignIn(opts.file, registration);
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
343
|
+
let data;
|
|
344
|
+
try {
|
|
345
|
+
data = await tokenRequest(d.token_endpoint, {
|
|
346
|
+
grant_type: 'authorization_code',
|
|
347
|
+
client_id: cb.clientId,
|
|
348
|
+
code: cb.code,
|
|
349
|
+
code_verifier: verifier,
|
|
350
|
+
redirect_uri: listener.redirectUri,
|
|
351
|
+
resource: OPENAI_API_RESOURCE,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
catch (err) {
|
|
355
|
+
// OpenAI's consent page refuses a plan or workspace without plan usage
|
|
356
|
+
// for apps, and the exchange then answers invalid_grant.
|
|
357
|
+
if (err instanceof SignInError && err.code === 'invalid_grant')
|
|
358
|
+
throw new SignInError('invalid_grant', signInRefused('invalid_grant'));
|
|
359
|
+
throw err;
|
|
360
|
+
}
|
|
337
361
|
const subject = await verifyIdToken(data.id_token, d, cb.clientId, nonce);
|
|
338
362
|
const fields = tokenFields(data);
|
|
339
363
|
const planUsage = fields.scopes.includes(PLAN_SCOPE);
|
|
@@ -755,6 +755,10 @@ export class GptAdapter {
|
|
|
755
755
|
let completed;
|
|
756
756
|
let streamedThoughts = false;
|
|
757
757
|
const text = [];
|
|
758
|
+
// Each finished output item, by its index. The plan-usage route (Sign in
|
|
759
|
+
// with ChatGPT, store: false) sends response.completed with an empty
|
|
760
|
+
// output: the items arrive only here, so the final is built from them.
|
|
761
|
+
const doneItems = [];
|
|
758
762
|
for await (const ev of untilAborted(events, signal)) {
|
|
759
763
|
const delta = streamEventDelta(ev);
|
|
760
764
|
if (delta) {
|
|
@@ -765,7 +769,10 @@ export class GptAdapter {
|
|
|
765
769
|
yield { partial: true, parts: [delta.thought ? { type: 'thinking', text: delta.text } : { type: 'text', text: delta.text }] };
|
|
766
770
|
continue;
|
|
767
771
|
}
|
|
768
|
-
if (ev?.type === 'response.
|
|
772
|
+
if (ev?.type === 'response.output_item.done' && ev.item && typeof ev.item === 'object') {
|
|
773
|
+
doneItems.push({ index: typeof ev.output_index === 'number' ? ev.output_index : doneItems.length, item: ev.item });
|
|
774
|
+
}
|
|
775
|
+
else if (ev?.type === 'response.completed' && ev.response) {
|
|
769
776
|
completed = ev.response;
|
|
770
777
|
}
|
|
771
778
|
else if (ev?.type === 'response.failed' || ev?.type === 'error') {
|
|
@@ -775,6 +782,9 @@ export class GptAdapter {
|
|
|
775
782
|
}
|
|
776
783
|
}
|
|
777
784
|
if (completed) {
|
|
785
|
+
if ((!Array.isArray(completed.output) || completed.output.length === 0) && doneItems.length > 0) {
|
|
786
|
+
completed = { ...completed, output: doneItems.sort((x, y) => x.index - y.index).map((d) => d.item) };
|
|
787
|
+
}
|
|
778
788
|
const { thinking, final } = this.finalOf(completed, model, { skipThoughts: streamedThoughts });
|
|
779
789
|
if (thinking)
|
|
780
790
|
yield thinking;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "melchizedek-agents",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.1",
|
|
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",
|
|
@@ -48,17 +48,17 @@
|
|
|
48
48
|
"./package.json": "./package.json"
|
|
49
49
|
},
|
|
50
50
|
"bin": {
|
|
51
|
-
"melchizedek-serve": "
|
|
52
|
-
"melchizedek-mcp": "
|
|
53
|
-
"melchizedek-chat": "
|
|
54
|
-
"melchizedek-doctor": "
|
|
55
|
-
"melchizedek-skills": "
|
|
56
|
-
"melchizedek-worker": "
|
|
57
|
-
"melchizedek-db": "
|
|
58
|
-
"melchizedek-init": "
|
|
59
|
-
"melchizedek-setup": "
|
|
60
|
-
"melchizedek-registry": "
|
|
61
|
-
"melchizedek-codemod": "
|
|
51
|
+
"melchizedek-serve": "dist/scripts/a2a_server.js",
|
|
52
|
+
"melchizedek-mcp": "dist/scripts/mcp_server.js",
|
|
53
|
+
"melchizedek-chat": "dist/scripts/syndicate_chat.js",
|
|
54
|
+
"melchizedek-doctor": "dist/scripts/doctor.js",
|
|
55
|
+
"melchizedek-skills": "dist/scripts/skills_install.js",
|
|
56
|
+
"melchizedek-worker": "dist/scripts/assistant_worker.js",
|
|
57
|
+
"melchizedek-db": "dist/scripts/db.js",
|
|
58
|
+
"melchizedek-init": "dist/scripts/init.js",
|
|
59
|
+
"melchizedek-setup": "dist/scripts/setup.js",
|
|
60
|
+
"melchizedek-registry": "dist/scripts/registry.js",
|
|
61
|
+
"melchizedek-codemod": "dist/scripts/yaml_codemod.js"
|
|
62
62
|
},
|
|
63
63
|
"files": [
|
|
64
64
|
"dist",
|