@vincemakes/kiso-code 0.40.3 → 0.40.4

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.
@@ -18,5 +18,11 @@ export interface AdapterOptions {
18
18
  readonly accountId: string;
19
19
  }>;
20
20
  readonly promptCacheKey?: string;
21
+ readonly headers?: Readonly<Record<string, string>>;
21
22
  }
23
+ /** The profile's headers with `{session}` replaced by the session id. A
24
+ * header that names `{session}` when there is no session (`kiso sessions`,
25
+ * which streams nothing) is left out, never sent with the placeholder in
26
+ * it. Undefined when nothing is left. */
27
+ export declare function expandProfileHeaders(headers: Readonly<Record<string, string>> | undefined, sessionId: string | undefined): Readonly<Record<string, string>> | undefined;
22
28
  export declare function adapterOptionsFor(profile: ModelProfile, auth: AdapterAuth, sessionId?: string): AdapterOptions;
@@ -33,6 +33,25 @@
33
33
  */
34
34
  import { effectiveBaseUrl } from "./credentials.js";
35
35
  import { oauthTokenThunk } from "./token.js";
36
+ /** The profile's headers with `{session}` replaced by the session id. A
37
+ * header that names `{session}` when there is no session (`kiso sessions`,
38
+ * which streams nothing) is left out, never sent with the placeholder in
39
+ * it. Undefined when nothing is left. */
40
+ export function expandProfileHeaders(headers, sessionId) {
41
+ if (headers === undefined)
42
+ return undefined;
43
+ const out = {};
44
+ for (const [name, value] of Object.entries(headers)) {
45
+ if (value.includes("{session}")) {
46
+ if (sessionId === undefined)
47
+ continue;
48
+ out[name] = value.split("{session}").join(sessionId);
49
+ }
50
+ else
51
+ out[name] = value;
52
+ }
53
+ return Object.keys(out).length === 0 ? undefined : out;
54
+ }
36
55
  export function adapterOptionsFor(profile, auth, sessionId) {
37
56
  // OR-1: exactly one sign-in shape reaches the adapter. An OAuth profile
38
57
  // has no key to pass — it passes the thunk the adapter re-resolves per
@@ -43,6 +62,7 @@ export function adapterOptionsFor(profile, auth, sessionId) {
43
62
  // own environment variable, and a profile with no baseUrl then sent the
44
63
  // stored vendor key wherever that variable pointed.
45
64
  const url = effectiveBaseUrl(profile.kind, profile.baseUrl);
65
+ const headers = expandProfileHeaders(profile.headers, sessionId);
46
66
  return {
47
67
  ...credential,
48
68
  ...(url !== undefined ? { baseUrl: url } : {}),
@@ -53,5 +73,9 @@ export function adapterOptionsFor(profile, auth, sessionId) {
53
73
  // cache.
54
74
  ...(sessionId !== undefined ? { promptCacheKey: sessionId } : {}),
55
75
  ...(profile.promptCaching !== undefined ? { promptCaching: profile.promptCaching } : {}),
76
+ // The endpoint's own headers (a gateway's session header, say), with
77
+ // the session id where the profile wrote `{session}` — the same id
78
+ // as the cache key above, so one conversation is one lane end to end.
79
+ ...(headers !== undefined ? { headers } : {}),
56
80
  };
57
81
  }
package/dist/config.d.ts CHANGED
@@ -75,6 +75,16 @@ export interface ModelProfile {
75
75
  * the registry looks for this model's window when `baseUrl` has no row.
76
76
  */
77
77
  readonly upstream?: string;
78
+ /**
79
+ * Headers the endpoint needs on every request — a gateway that routes a
80
+ * conversation by a session header, say. `{session}` in a value becomes
81
+ * the kiso session's id, so one conversation keeps one id across /model
82
+ * and /resume, and two conversations never share one. Names are
83
+ * lower-cased. A credential header is refused by name: a key never lives
84
+ * in this file (`apiKeyEnv` and `kiso login` are the doors), and the
85
+ * framing headers belong to the adapter.
86
+ */
87
+ readonly headers?: Readonly<Record<string, string>>;
78
88
  }
79
89
  export interface AutoCompactConfig {
80
90
  readonly thresholdRatio: number;
package/dist/config.js CHANGED
@@ -34,6 +34,27 @@ import { join } from "node:path";
34
34
  import { kisoHome } from "./state.js";
35
35
  import { AuthError, getCredential, providerIdOf } from "./auth/credentials.js";
36
36
  import { BUILTIN_MANIFESTS } from "@vincemakes/kiso-runtime/internal";
37
+ /** Header names a profile may not set: credentials (a key never lives in the
38
+ * config file) and the framing the adapter owns. */
39
+ const REFUSED_HEADERS = new Set(["authorization", "proxy-authorization", "x-api-key", "api-key", "cookie", "host", "content-length", "content-type", "transfer-encoding", "connection"]);
40
+ function parseHeaders(field, raw, fail) {
41
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw))
42
+ fail(field, "expected an object of header names to string values");
43
+ const out = {};
44
+ for (const [name, value] of Object.entries(raw)) {
45
+ if (!/^[A-Za-z0-9-]+$/.test(name))
46
+ fail(`${field}.${name}`, "expected a header name of letters, digits and dashes");
47
+ const lower = name.toLowerCase();
48
+ if (REFUSED_HEADERS.has(lower))
49
+ fail(`${field}.${name}`, "a credential or framing header cannot be set here — the key comes from apiKeyEnv or kiso login");
50
+ if (lower in out)
51
+ fail(`${field}.${name}`, "named twice (header names are case-insensitive)");
52
+ if (typeof value !== "string" || /[\r\n\0]/.test(value))
53
+ fail(`${field}.${name}`, "expected a one-line string value");
54
+ out[lower] = value;
55
+ }
56
+ return out;
57
+ }
37
58
  const KINDS = ["openai-compat", "anthropic", "openai-responses"];
38
59
  const MODES_LIST = ["manual", "default", "accept-edits", "plan", "bypass", "dontAsk"];
39
60
  export class ConfigError extends Error {
@@ -149,6 +170,7 @@ export function parseConfig(text, source) {
149
170
  fail(`models.${name}.contextWindow`, "expected a positive token count");
150
171
  if (p.upstream !== undefined && (typeof p.upstream !== "string" || p.upstream.trim() === ""))
151
172
  fail(`models.${name}.upstream`, "expected the URL or name of what this endpoint forwards to");
173
+ const headers = p.headers === undefined ? undefined : parseHeaders(`models.${name}.headers`, p.headers, fail);
152
174
  models[name] = {
153
175
  kind: p.kind,
154
176
  model: p.model,
@@ -158,6 +180,7 @@ export function parseConfig(text, source) {
158
180
  ...(typeof p.streamIdleMs === "number" ? { streamIdleMs: p.streamIdleMs } : {}),
159
181
  ...(typeof p.contextWindow === "number" ? { contextWindow: p.contextWindow } : {}),
160
182
  ...(typeof p.upstream === "string" ? { upstream: p.upstream.trim() } : {}),
183
+ ...(headers !== undefined ? { headers } : {}),
161
184
  };
162
185
  }
163
186
  out.models = models;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-code",
3
- "version": "0.40.3",
3
+ "version": "0.40.4",
4
4
  "description": "kiso-code \u2014 the coding agent on kiso, the durable runtime for AI agents: kiso chat / kiso resume / kiso sessions.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -18,20 +18,20 @@
18
18
  "test": "vitest run"
19
19
  },
20
20
  "dependencies": {
21
- "@vincemakes/kiso-ask-ext": "0.40.3",
22
- "@vincemakes/kiso-core": "0.40.3",
23
- "@vincemakes/kiso-evals": "0.40.3",
24
- "@vincemakes/kiso-mcp-ext": "0.40.3",
25
- "@vincemakes/kiso-provider-anthropic": "0.40.3",
26
- "@vincemakes/kiso-provider-openai": "0.40.3",
27
- "@vincemakes/kiso-provider-openai-responses": "0.40.3",
28
- "@vincemakes/kiso-runtime": "0.40.3",
29
- "@vincemakes/kiso-skills-ext": "0.40.3",
30
- "@vincemakes/kiso-subagent-ext": "0.40.3",
31
- "@vincemakes/kiso-task-ext": "0.40.3",
32
- "@vincemakes/kiso-tools-node": "0.40.3",
33
- "@vincemakes/kiso-tui": "0.40.3",
34
- "@vincemakes/kiso-tui-cells": "0.40.3"
21
+ "@vincemakes/kiso-ask-ext": "0.40.4",
22
+ "@vincemakes/kiso-core": "0.40.4",
23
+ "@vincemakes/kiso-evals": "0.40.4",
24
+ "@vincemakes/kiso-mcp-ext": "0.40.4",
25
+ "@vincemakes/kiso-provider-anthropic": "0.40.4",
26
+ "@vincemakes/kiso-provider-openai": "0.40.4",
27
+ "@vincemakes/kiso-provider-openai-responses": "0.40.4",
28
+ "@vincemakes/kiso-runtime": "0.40.4",
29
+ "@vincemakes/kiso-skills-ext": "0.40.4",
30
+ "@vincemakes/kiso-subagent-ext": "0.40.4",
31
+ "@vincemakes/kiso-task-ext": "0.40.4",
32
+ "@vincemakes/kiso-tools-node": "0.40.4",
33
+ "@vincemakes/kiso-tui": "0.40.4",
34
+ "@vincemakes/kiso-tui-cells": "0.40.4"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/node": "^26.1.2",