# Jiva v0.3.47 — Multimodal, Harmony, and Vertex AI Fixes

**Release Date:** May 19, 2026

---

## Summary

This release fixes a set of bugs that blocked or corrupted multimodal image analysis, Harmony-format tool-call sequences on Vertex AI MaaS, and model client construction. It also introduces Google Application Default Credentials (ADC) support so Vertex AI MaaS deployments on Cloud Run no longer require a static API key.

---

## Bug Fixes

### 1. Groq 400 error on multimodal image requests

**Symptom:** Asking the agent to analyse an image file (e.g. via `filesystem__read_media_file`) produced a cascade of identical `400` errors from the Groq API:

```
body.messages.5.tool.content : String should have at least 1 character
```

The Worker retried up to 20 times and failed every time.

**Root cause:** `filesystem__read_media_file` (and similar tools) return `{ text: "", images: [...] }` — an empty text body plus the raw image bytes. The Worker stored `toolResultText = typedResult.text` (an empty string `""`) and pushed a `role: "tool"` message with `content: ""` into conversation history. Groq requires at least one character in every message's content field.

**Fix:** `src/core/worker-agent.ts` — when a tool returns images with no accompanying text, `toolResultText` falls back to `'[Image content returned]'`. The image itself is still described by the multimodal model via the existing `pendingImages` path, which is unaffected.

---

### 2. Manager final response formatted as JSON instead of plain text

**Symptom:** After completing a task, the Manager's final reply to the user was raw JSON (e.g. `{"response": "...", "reasoning": "..."}`) instead of a readable markdown answer.

**Root cause:** The synthesis call included the full planning conversation history, which contains a `"Respond ONLY with valid JSON"` system instruction from the planning phase. The model correctly followed that instruction for the synthesis step, even though it was not intended for it.

**Fix:** `src/core/manager-agent.ts` — synthesis now uses a fresh context consisting only of the system messages and the synthesis prompt. The planning history is excluded. A safety fallback also extracts the `response` / `content` field if the model still returns JSON despite the updated prompt.

---

### 3. Harmony tool-call sequences broken on Vertex AI MaaS

**Symptom:** On Vertex AI MaaS (`gpt-oss-120b-maas`), tool calls in the second and subsequent turns failed — the model either ignored the tool result or repeated the same tool call in a loop.

**Root cause (a) — wrong conversation history format:** After receiving a Harmony response, the Worker stored the *cleaned* `response.content` (Harmony tokens stripped) as the assistant message in history. Harmony providers need to see their own raw token markers (`<|call|>`, `<|return|>`, `<|channel|>`) in history to continue a tool-call sequence. Without them, the model could not correlate the prior tool call with its result.

**Fix:** Added `rawHarmonyContent?: string` to `ModelResponse` (`src/models/base.ts`). `ModelClient` populates it whenever `useHarmonyFormat` is true. `WorkerAgent` detects the field and stores `rawHarmonyContent` as the assistant message content instead of the cleaned version. `tool_calls` are omitted from the history message in Harmony mode (the call is encoded in the raw content itself).

**Root cause (b) — unrecognised Vertex AI dialect:** Vertex AI MaaS emits tool calls in a different Harmony dialect (`<|channel|>commentary to=TOOL_NAME <|constrain|>json<|message|>JSON_ARGS<|call|>`) rather than the Krutrim pipe format (`<|call|>fn(args)<|return|>`). This dialect was not parsed, so tool calls from Vertex AI were silently dropped.

**Fix:** `src/models/harmony.ts` — added `vertexChannelRegex` to parse the Vertex AI dialect. The `functions.` namespace prefix emitted by Vertex AI is stripped so the tool name matches the registered MCP tool name. `extractAssistantMessage()` was also updated to clean both Krutrim and Vertex AI Harmony tokens from visible content, and to handle malformed `<|return|>` variants where the closing `|>` is missing.

---

### 4. `model` field ignored in CLI model client construction

**Symptom:** When a model config entry used the newer `model` field (instead of `defaultModel`), the CLI passed `undefined` to `createModelClient()`, causing the client to fall back to its default model rather than the configured one.

**Root cause:** `src/interfaces/cli/index.ts` passed `multimodalModelConfig.defaultModel` directly, ignoring `model`.

**Fix:** Updated to `model || defaultModel || ''` for both the multimodal and tool-calling model client construction paths.

---

## New Features

### Google Application Default Credentials (ADC) for Vertex AI MaaS

A new `src/models/google-adc.ts` module fetches short-lived GCP OAuth2 tokens automatically, removing the need for a static API key when running on Cloud Run or other GCP environments.

**Token source priority:**
1. GCP metadata server at `169.254.169.254` (Cloud Run, GCE, GKE) — uses the IP directly because musl libc (node:20-alpine) cannot resolve `metadata.google.internal`.
2. `google-auth-library` ADC — fallback for local development when `gcloud auth application-default` credentials are active.

Tokens are cached in-process and refreshed 5 minutes before expiry (at most one outbound metadata call per hour).

**Configuration:** Set `useGoogleADC: true` in the model config entry. The `apiKey` field is no longer required when ADC is enabled:

```json
{
  "models": {
    "reasoning": {
      "endpoint": "https://{REGION}-aiplatform.googleapis.com/v1/projects/{PROJECT}/locations/{REGION}/endpoints/{ENDPOINT_ID}/chat/completions",
      "useGoogleADC": true,
      "useHarmonyFormat": true,
      "model": "gpt-oss-120b-maas"
    }
  }
}
```

`useGoogleADC` is supported in `src/core/config.ts`, `ModelClientConfig`, `src/interfaces/cli/index.ts`, and `src/interfaces/http/session-manager.ts`.

---

### Sub-agent iteration limit increased

The default `maxIterations` for sub-agents spawned via `AgentSpawner` has been raised from 10 to 20. Tasks that require more back-and-forth tool use (e.g. multi-file edits, longer research tasks) no longer hit the iteration wall prematurely.

---

## Files Changed

| File | Change |
|------|--------|
| `src/core/worker-agent.ts` | Empty tool content fallback; `rawHarmonyContent` history handling |
| `src/core/manager-agent.ts` | Synthesis context isolation; JSON-response safety fallback |
| `src/core/agent-spawner.ts` | Default `maxIterations` 10 → 20 |
| `src/core/config.ts` | `useGoogleADC` flag; `model` field alias; validation error messages |
| `src/models/base.ts` | `rawHarmonyContent` field on `ModelResponse` |
| `src/models/google-adc.ts` | **New** — GCP ADC token provider |
| `src/models/harmony.ts` | Vertex AI dialect parser; malformed `<|return|>` fix; `extractAssistantMessage` cleanup |
| `src/models/model-client.ts` | ADC token injection; Harmony token cleanup in standard mode; tool call extraction improvements |
| `src/interfaces/cli/index.ts` | `model \|\| defaultModel` fallback |
| `src/interfaces/http/session-manager.ts` | `useGoogleADC` support; model config field handling |
| `Dockerfile` | `bash` added to apk install (required by `@mkusaka/mcp-shell-server`) |
| `cloud-run-deploy.yaml` | `JIVA_STORAGE_PROVIDER` updated to canonical `gcp-bucket` |

---

## Upgrade

```bash
npm install -g jiva-core@0.3.47
```

No breaking changes. All existing configs continue to work without modification.
