@byokit/accounts 0.10.0 → 0.11.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - Expose fresh subscription access to host-side capabilities using the app’s own sign-in.
6
+
7
+ ## 0.11.0 (2026-09-30)
8
+
9
+
10
+
11
+ - Add optional `parallelToolCalls` to `respond` and `Accounts.respond`, passed through to ChatGPT as `parallel_tool_calls`; omitted keeps the provider default.
12
+
5
13
  ## 0.10.0 (2026-09-30)
6
14
 
7
15
 
package/README.md CHANGED
@@ -260,6 +260,11 @@ already have shown partial words. This covers `response.incomplete` events and `
260
260
  whether fetch streams SSE, buffers it, or returns JSON. The account stays signed in and is not put to rest.
261
261
  Successful return values are unchanged.
262
262
 
263
+ Set `parallelToolCalls: false` to request one tool call at a time; `true` allows parallel calls. Both pass through as
264
+ `parallel_tool_calls`; omitting it sends nothing and keeps the provider default. `respond` supports only the ChatGPT
265
+ subscription route; OpenRouter, Grok and Copilot are credential routes without `respond` support, and there is no
266
+ Anthropic response route.
267
+
263
268
  The whole question passes through: `input` takes the turns so far (messages, with `input_image` where the person
264
269
  attached a picture), `tools` and `tool_choice` take the app's own function tools and built-ins (including
265
270
  `image_generation`), `reasoning.effort` how hard the model thinks, and `text` how long the answer is with the shape it
@@ -361,3 +366,5 @@ in N min/hours", and zero otherwise. For resting kinds, use
361
366
  `failure.until || nowMs + REST_MS[failure.kind]`; signed-out, not-included and network
362
367
  failures have no fallback rest. `classify` remains an alias. Pass a clock for deterministic
363
368
  classification; the default uses `Date.now()`. It stores and logs no error text.
369
+
370
+ For a capability running where the sign-in lives, `await accounts.access(member, signal)` returns fresh `{ access, accountId }` from the app’s own ChatGPT store. Keep these credentials in that process; proxy signaling or relay media for another device. This uses the same refresh and signed-out behavior as `respond`.
@@ -125,10 +125,11 @@ export declare class Accounts<R extends AuthHost = AuthHost, M extends Member =
125
125
  forget(member: M, key: string): void;
126
126
  /** 0 when the account is available; otherwise when it stops resting. */
127
127
  restingUntil(member: M, key: string): number;
128
- /** An account's error, acted on. A limit or overload rests it (until when it said, or a default). A plan without this
129
- * use is marked so. A refusal is checked: a sign-in that no longer refreshes is signed out for real, one that still
130
- * does was a passing refusal and rests a few minutes (kind `overloaded`) rather than loop. Returns the kind acted on,
131
- * or null for an error that is not about the account; `network` changes nothing. */
128
+ /** Fresh ChatGPT access for a host-side capability. Never send this to another device. */
129
+ access(member: M, signal?: AbortSignal): Promise<{
130
+ access: string;
131
+ accountId: string;
132
+ }>;
132
133
  /** Ask ChatGPT with this member's own sign-in, the answer streaming into `onText`; refreshed first when due. A
133
134
  * failure about the account (a limit, a lapsed sign-in) is acted on as `failed()` does, then thrown as a
134
135
  * ResponseError with the words to show. Without `tools` the answer is the plain text, as before: pass `input` as
@@ -140,6 +141,10 @@ export declare class Accounts<R extends AuthHost = AuthHost, M extends Member =
140
141
  respond(member: M, ask: Ask & {
141
142
  tools: ResponseTool[];
142
143
  }): Promise<ResponseResult>;
144
+ /** An account's error, acted on. A limit or overload rests it (until when it said, or a default). A plan without this
145
+ * use is marked so. A refusal is checked: a sign-in that no longer refreshes is signed out for real, one that still
146
+ * does was a passing refusal and rests a few minutes (kind `overloaded`) rather than loop. Returns the kind acted on,
147
+ * or null for an error that is not about the account; `network` changes nothing. */
143
148
  failed(member: M, key: string, error: string | ResponseError): Promise<import("./limits.ts").Failure | null>;
144
149
  /** The first choice whose account is neither resting nor known to be unusable: the fallback ladder. */
145
150
  ladder<T>(member: M, choices: readonly T[], key?: (c: T) => string): T | undefined;
package/dist/accounts.js CHANGED
@@ -228,7 +228,9 @@ export class Accounts {
228
228
  const r = this.rests.get(`${member}:${key}`);
229
229
  return r && r.until > Date.now() ? r.until : 0;
230
230
  }
231
- async respond(member, ask) {
231
+ /** Fresh ChatGPT access for a host-side capability. Never send this to another device. */
232
+ async access(member, signal) {
233
+ signal?.throwIfAborted();
232
234
  const key = 'chatgpt';
233
235
  const p = this.offer(key);
234
236
  const rt = await this.runtime(member);
@@ -247,8 +249,15 @@ export class Accounts {
247
249
  const c = await rt.readCredential(p.pi).catch(() => undefined);
248
250
  if (!access || c?.type !== 'oauth')
249
251
  throw new ResponseError(say('status.signedOut', { name: p.name }), 'signed_out');
252
+ signal?.throwIfAborted();
253
+ return { access, accountId: String(c.accountId ?? '') };
254
+ }
255
+ async respond(member, ask) {
256
+ const key = 'chatgpt';
257
+ const p = this.offer(key);
258
+ const { access, accountId } = await this.access(member);
250
259
  try {
251
- const base = { ...ask, access, accountId: String(c.accountId ?? ''), model: ask.model ?? p.models.strong, base: this.opts.apiBase, fetch: this.opts.fetch, originator: ask.originator ?? this.opts.originator };
260
+ const base = { ...ask, access, accountId, model: ask.model ?? p.models.strong, base: this.opts.apiBase, fetch: this.opts.fetch, originator: ask.originator ?? this.opts.originator };
252
261
  return ask.tools ? await respond({ ...base, tools: ask.tools }) : await respond({ ...base, tools: undefined });
253
262
  }
254
263
  catch (e) {
@@ -260,6 +269,10 @@ export class Accounts {
260
269
  throw e;
261
270
  }
262
271
  }
272
+ /** An account's error, acted on. A limit or overload rests it (until when it said, or a default). A plan without this
273
+ * use is marked so. A refusal is checked: a sign-in that no longer refreshes is signed out for real, one that still
274
+ * does was a passing refusal and rests a few minutes (kind `overloaded`) rather than loop. Returns the kind acted on,
275
+ * or null for an error that is not about the account; `network` changes nothing. */
263
276
  async failed(member, key, error) {
264
277
  const c = error instanceof ResponseError ? error.kind && { kind: error.kind, until: error.until } : classify(error);
265
278
  if (!c || c.kind === 'network')
@@ -175,6 +175,8 @@ export type Ask = {
175
175
  model?: string;
176
176
  /** The tools the model may call. Passed, the result carries the output items next to the text. */
177
177
  tools?: ResponseTool[];
178
+ /** Whether ChatGPT may call tools in parallel. Omitted: the provider's default. */
179
+ parallelToolCalls?: boolean;
178
180
  /** Which tool the model must use. Default: whatever it wants. */
179
181
  tool_choice?: ResponseToolChoice;
180
182
  /** How hard the model thinks. Default: none. */
package/dist/responses.js CHANGED
@@ -182,6 +182,7 @@ export async function respond(o) {
182
182
  body: JSON.stringify({
183
183
  model: o.model, store: false, stream: true, instructions: o.instructions, input,
184
184
  ...(o.tools ? { tools: o.tools } : {}),
185
+ ...(o.parallelToolCalls !== undefined ? { parallel_tool_calls: o.parallelToolCalls } : {}),
185
186
  ...(o.tool_choice !== undefined ? { tool_choice: o.tool_choice } : {}),
186
187
  text: { verbosity, ...(format ? { format } : {}), ...textRest },
187
188
  reasoning: { effort, ...reasoningRest },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byokit/accounts",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Sign in with the AI plan you already pay for, into your app's own store.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",