@rubytech/taskmaster 1.9.2 → 1.9.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.
@@ -134,7 +134,9 @@ function mergeAuthProfileStores(base, override) {
134
134
  !override.usageStats) {
135
135
  return base;
136
136
  }
137
- // Merge profiles, preferring the fresher credential for OAuth/token types
137
+ // Merge profiles, preferring the fresher credential for OAuth/token types.
138
+ // For api_key profiles, prefer base (main store) — centralized API key
139
+ // management writes there, and agent stores only have inherited copies.
138
140
  const mergedProfiles = { ...base.profiles };
139
141
  for (const [profileId, overrideCred] of Object.entries(override.profiles)) {
140
142
  const baseCred = base.profiles[profileId];
@@ -142,8 +144,14 @@ function mergeAuthProfileStores(base, override) {
142
144
  // No conflict — use override
143
145
  mergedProfiles[profileId] = overrideCred;
144
146
  }
147
+ else if (baseCred.type === "api_key" && overrideCred.type === "api_key") {
148
+ // API keys: base (main store) is authoritative — applyApiKeys() writes
149
+ // the centralized key there. Agent stores only have stale inherited
150
+ // copies. Always prefer base so key updates propagate immediately.
151
+ // (keep base — already in mergedProfiles)
152
+ }
145
153
  else {
146
- // Both have this profile — prefer the one with later expiry (fresher token)
154
+ // OAuth/token: prefer the one with later expiry (fresher token)
147
155
  const baseExpiry = getCredentialExpiry(baseCred);
148
156
  const overrideExpiry = getCredentialExpiry(overrideCred);
149
157
  if (overrideExpiry >= baseExpiry) {
@@ -1,15 +1,18 @@
1
1
  import { createSubsystemLogger } from "../../logging/subsystem.js";
2
- import { fetchWithTimeout, normalizeBaseUrl, readErrorResponse } from "../../media-understanding/providers/shared.js";
2
+ import { fetchWithTimeout, normalizeBaseUrl, readErrorResponse, } from "../../media-understanding/providers/shared.js";
3
3
  const log = createSubsystemLogger("image-gen");
4
+ /** Show first 8 and last 4 characters of a key, mask everything in between. */
5
+ function maskKey(key) {
6
+ if (key.length <= 16)
7
+ return `${key.slice(0, 4)}...`;
8
+ return `${key.slice(0, 8)}...${key.slice(-4)}`;
9
+ }
4
10
  /* ------------------------------------------------------------------ */
5
11
  /* Constants */
6
12
  /* ------------------------------------------------------------------ */
7
13
  const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
8
14
  const DEFAULT_TIMEOUT_MS = 120_000;
9
- const GEMINI_IMAGE_MODELS = new Set([
10
- "gemini-2.5-flash-image",
11
- "gemini-3-pro-image-preview",
12
- ]);
15
+ const GEMINI_IMAGE_MODELS = new Set(["gemini-2.5-flash-image", "gemini-3-pro-image-preview"]);
13
16
  /* ------------------------------------------------------------------ */
14
17
  /* Model detection */
15
18
  /* ------------------------------------------------------------------ */
@@ -47,7 +50,7 @@ export async function generateImageGemini(params) {
47
50
  const res = await fetchWithTimeout(url, { method: "POST", headers, body: JSON.stringify(body) }, timeoutMs, fetchFn);
48
51
  if (!res.ok) {
49
52
  const detail = await readErrorResponse(res);
50
- log.error("gemini HTTP error", { status: res.status, detail });
53
+ log.error("gemini HTTP error", { status: res.status, key: maskKey(params.apiKey), detail });
51
54
  const suffix = detail ? `: ${detail}` : "";
52
55
  throw new Error(`Image generation failed (HTTP ${res.status})${suffix}`);
53
56
  }
@@ -125,7 +128,7 @@ export async function generateImageImagen(params) {
125
128
  const res = await fetchWithTimeout(url, { method: "POST", headers, body: JSON.stringify(body) }, timeoutMs, fetchFn);
126
129
  if (!res.ok) {
127
130
  const detail = await readErrorResponse(res);
128
- log.error("imagen HTTP error", { status: res.status, detail });
131
+ log.error("imagen HTTP error", { status: res.status, key: maskKey(params.apiKey), detail });
129
132
  const suffix = detail ? `: ${detail}` : "";
130
133
  throw new Error(`Image generation failed (HTTP ${res.status})${suffix}`);
131
134
  }
@@ -8,6 +8,15 @@ import { sanitizeToolResultImages } from "../tool-images.js";
8
8
  import { readNumberParam, readStringParam } from "./common.js";
9
9
  const log = createSubsystemLogger("image-gen");
10
10
  /* ------------------------------------------------------------------ */
11
+ /* Helpers */
12
+ /* ------------------------------------------------------------------ */
13
+ /** Show first 8 and last 4 characters of a key, mask everything in between. */
14
+ function maskKey(key) {
15
+ if (key.length <= 16)
16
+ return `${key.slice(0, 4)}...`;
17
+ return `${key.slice(0, 8)}...${key.slice(-4)}`;
18
+ }
19
+ /* ------------------------------------------------------------------ */
11
20
  /* Constants */
12
21
  /* ------------------------------------------------------------------ */
13
22
  const SUPPORTED_MODELS = new Set([
@@ -47,7 +56,9 @@ export function createImageGenerateTool(options) {
47
56
  parameters: Type.Object({
48
57
  prompt: Type.String({ description: "The image generation prompt." }),
49
58
  model: Type.Optional(Type.String({ description: "Model ID. Default: gemini-2.5-flash-image." })),
50
- aspectRatio: Type.Optional(Type.String({ description: "e.g. 1:1, 16:9, 9:16, 3:4, 4:3. Gemini also supports 2:3, 3:2, 4:5, 5:4, 21:9." })),
59
+ aspectRatio: Type.Optional(Type.String({
60
+ description: "e.g. 1:1, 16:9, 9:16, 3:4, 4:3. Gemini also supports 2:3, 3:2, 4:5, 5:4, 21:9.",
61
+ })),
51
62
  resolution: Type.Optional(Type.String({ description: "1K, 2K, or 4K (4K: gemini-3-pro-image-preview only)." })),
52
63
  numberOfImages: Type.Optional(Type.Number({ description: "1-4 images per request. Imagen models only." })),
53
64
  personGeneration: Type.Optional(Type.String({ description: "dont_allow, allow_adult, allow_all. Imagen models only." })),
@@ -73,6 +84,7 @@ export function createImageGenerateTool(options) {
73
84
  agentDir: options?.agentDir,
74
85
  });
75
86
  const apiKey = requireApiKey(auth, "google");
87
+ log.info("resolved google API key", { source: auth.source, key: maskKey(apiKey) });
76
88
  /* --- Call the appropriate backend ---------------------------- */
77
89
  const isGemini = isGeminiImageModel(modelRaw);
78
90
  const result = isGemini
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.9.2",
3
- "commit": "1780753dfd111891f360a6f6d8e0490437acde9b",
4
- "builtAt": "2026-02-27T07:27:50.330Z"
2
+ "version": "1.9.4",
3
+ "commit": "e9bf5d4257f77aff7d7c7c4e63905bd36b515877",
4
+ "builtAt": "2026-02-27T08:18:37.555Z"
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/taskmaster",
3
- "version": "1.9.2",
3
+ "version": "1.9.4",
4
4
  "description": "AI-powered business assistant for small businesses",
5
5
  "publishConfig": {
6
6
  "access": "public"