zidane 5.4.2 → 5.5.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/README.md +45 -1
- package/dist/{agent-DxBoKDba.d.ts → agent-CvImMxMQ.d.ts} +256 -5
- package/dist/agent-CvImMxMQ.d.ts.map +1 -0
- package/dist/chat.d.ts +137 -16
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +3 -2
- package/dist/contexts/docker.d.ts +1 -1
- package/dist/contexts-DhmMlT2W.js +472 -0
- package/dist/contexts-DhmMlT2W.js.map +1 -0
- package/dist/contexts.d.ts +3 -3
- package/dist/contexts.js +1 -1
- package/dist/{errors-Byb0F8B9.js → errors-CDwtPIMX.js} +4 -2
- package/dist/{errors-Byb0F8B9.js.map → errors-CDwtPIMX.js.map} +1 -1
- package/dist/{index-BOtXdQkW.d.ts → index-B0uc2C5x.d.ts} +9 -3
- package/dist/index-B0uc2C5x.d.ts.map +1 -0
- package/dist/{index-BiO_5Hm4.d.ts → index-CbS75MD3.d.ts} +2 -2
- package/dist/index-CbS75MD3.d.ts.map +1 -0
- package/dist/{index-B2VOOijU.d.ts → index-CtXksgqb.d.ts} +73 -4
- package/dist/index-CtXksgqb.d.ts.map +1 -0
- package/dist/index.d.ts +6 -6
- package/dist/index.js +11 -11
- package/dist/{interpolate-ERgZUxgg.js → interpolate-BaaKaKzN.js} +156 -19
- package/dist/interpolate-BaaKaKzN.js.map +1 -0
- package/dist/{login-CJbeAadS.js → login-iTy-0wYz.js} +3 -3
- package/dist/{login-CJbeAadS.js.map → login-iTy-0wYz.js.map} +1 -1
- package/dist/{mcp-DhmmJfxK.js → mcp-CNUbvbsy.js} +2 -2
- package/dist/{mcp-DhmmJfxK.js.map → mcp-CNUbvbsy.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{messages-D0xT979U.js → messages-fTR19Ga6.js} +2 -2
- package/dist/{messages-D0xT979U.js.map → messages-fTR19Ga6.js.map} +1 -1
- package/dist/{presets-MCcvxiNT.js → presets-h6UWhghO.js} +3 -2
- package/dist/presets-h6UWhghO.js.map +1 -0
- package/dist/presets.d.ts +2 -2
- package/dist/presets.js +1 -1
- package/dist/{providers-x3LZByR5.js → providers-G0VBZK9j.js} +4 -4
- package/dist/{providers-x3LZByR5.js.map → providers-G0VBZK9j.js.map} +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +2 -2
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session/sqlite.d.ts.map +1 -1
- package/dist/session/sqlite.js +2 -1
- package/dist/session/sqlite.js.map +1 -1
- package/dist/{session-BHZwxmfr.js → session-CbkiJDlH.js} +3 -2
- package/dist/session-CbkiJDlH.js.map +1 -0
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/skills.d.ts +2 -2
- package/dist/skills.js +1 -1
- package/dist/{tools-BNfyY14s.js → tools-D_icxa-V.js} +813 -284
- package/dist/tools-D_icxa-V.js.map +1 -0
- package/dist/tools.d.ts +3 -3
- package/dist/tools.js +2 -2
- package/dist/{transcript-anchors-DonKvoh4.d.ts → transcript-anchors-3FFw2xuk.d.ts} +98 -15
- package/dist/transcript-anchors-3FFw2xuk.d.ts.map +1 -0
- package/dist/tui.d.ts +29 -5
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +879 -70
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-TKvy0q29.js → turn-operations-CtgBlBHn.js} +412 -125
- package/dist/turn-operations-CtgBlBHn.js.map +1 -0
- package/dist/types-IcokUOyC.js.map +1 -1
- package/dist/types-KukEp-mi.d.ts +253 -0
- package/dist/types-KukEp-mi.d.ts.map +1 -0
- package/dist/types.d.ts +4 -4
- package/dist/types.js +1 -1
- package/docs/ARCHITECTURE.md +37 -3
- package/docs/CHAT.md +4 -2
- package/docs/RUN_IN_BACKGROUND.md +612 -0
- package/docs/SKILL.md +83 -14
- package/docs/TUI.md +40 -2
- package/package.json +4 -4
- package/dist/agent-DxBoKDba.d.ts.map +0 -1
- package/dist/contexts-BwiHIr2w.js +0 -129
- package/dist/contexts-BwiHIr2w.js.map +0 -1
- package/dist/index-B2VOOijU.d.ts.map +0 -1
- package/dist/index-BOtXdQkW.d.ts.map +0 -1
- package/dist/index-BiO_5Hm4.d.ts.map +0 -1
- package/dist/interpolate-ERgZUxgg.js.map +0 -1
- package/dist/presets-MCcvxiNT.js.map +0 -1
- package/dist/session-BHZwxmfr.js.map +0 -1
- package/dist/tools-BNfyY14s.js.map +0 -1
- package/dist/transcript-anchors-DonKvoh4.d.ts.map +0 -1
- package/dist/turn-operations-TKvy0q29.js.map +0 -1
- package/dist/types-Ce78ds4h.d.ts +0 -88
- package/dist/types-Ce78ds4h.d.ts.map +0 -1
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -142,7 +142,9 @@ flowchart TB
|
|
|
142
142
|
VC -->|yes| VCH["validation:coerce hook\n(observational)"]
|
|
143
143
|
VC -->|no| T7
|
|
144
144
|
VCH --> T7["tool:before hook\n(ctx.coercions when present, runToolCounts)"]
|
|
145
|
-
T7 --> T8["toolDef.execute(coercedInput, ctx)"]
|
|
145
|
+
T7 --> T8["toolDef.execute(coercedInput, ctx)\nracing against perCallAbort.signal"]
|
|
146
|
+
T8 -->|cancelled by\nagent.cancelTool| TC["tool:cancelled hook\n(reason, runToolCounts)"]
|
|
147
|
+
TC --> TCR["Return TOOL_USE_CANCELLED_MESSAGE\n(isError: true; skips tool:transform/after)"]
|
|
146
148
|
T8 -->|error| T9["tool:error hook\n(can substitute result)"]
|
|
147
149
|
T8 -->|ok| T10["tool:transform hook\n(can mutate result;\nctx.outputBytes pre-mutation)"]
|
|
148
150
|
T9 --> T10
|
|
@@ -158,6 +160,8 @@ flowchart TB
|
|
|
158
160
|
|
|
159
161
|
**`tool:gate` mutations.** Set `block` to refuse (`Blocked: reason`), `result` to substitute and skip execute, or neither to run normally. `block` wins over `result` so a policy gate always beats a consumer cache. The substitute path skips `tool:before` + validate + execute but fires `tool:transform` + `tool:after` — budgets and telemetry stay consistent with executed calls.
|
|
160
162
|
|
|
163
|
+
**Per-call cancellation.** Each dispatch registers an `AbortController` in `LoopContext.pendingToolCancels` keyed by `callId`; the tool body's `ctx.signal` is `AbortSignal.any([ctx.signal, perCallAbort.signal])`. `agent.cancelTool(callId, reason?)` flips the per-call signal, the body promise races against the cancellation, the loop emits `tool:cancelled`, and the wire result becomes `TOOL_USE_CANCELLED_MESSAGE` with `isError: true`. Other in-flight tools in the same batch keep running — distinct from `agent.abort()` (whole-run abort → `INTERRUPT_MESSAGE_FOR_TOOL_USE` at the batch layer) and `TOOL_USE_SKIPPED_MESSAGE` (steered out before dispatch). The map entry is removed in a `finally` so it only ever holds currently-dispatching calls; the body promise gets a no-op `.catch` so misbehaving tools that ignore the signal can't surface as unhandled rejections after the loop has moved on.
|
|
164
|
+
|
|
161
165
|
**`runToolCounts`** is a frozen pre-call snapshot, scoped to `runId`. Includes dedup + gate-`result` substitutes; excludes blocked calls. Resumed sessions reset the counter. Concurrent fleets see the same pre-batch snapshot — built-in budget/dedup middleware uses its own gate-time reservation counter so `behavior.toolBudgets` stays atomic across the fleet.
|
|
162
166
|
|
|
163
167
|
**Output shape.** `string | ToolResultContent[]`. Text tools return strings; multimodal tools (MCP browsers, screenshots) return `[{ type: 'text' }, { type: 'image', mediaType, data }]`. Providers with `capabilities.imageInToolResult: true` (Anthropic, OpenAI Codex) route arrays natively; OpenAI-compat emits a companion `user` message with `image_url` parts. Non-vision providers swap image blocks for a text marker before the hook fires.
|
|
@@ -359,15 +363,22 @@ per run():
|
|
|
359
363
|
skills_use({ name }) → state.activate(skill, 'model')
|
|
360
364
|
→ skills:activate (via: 'model')
|
|
361
365
|
→ <skill_content> wrapper
|
|
366
|
+
skills_use({ name, mode: 'deactivate' }) → state.deactivate(skill)
|
|
367
|
+
→ skills:deactivate (reason: 'model')
|
|
368
|
+
→ "Skill X deactivated" confirmation
|
|
362
369
|
skills_read / skills_run_script → gated on state.isActive + path sandbox
|
|
363
370
|
run end: deactivateAllSkills() → skills:deactivate (reason: 'run-end')
|
|
364
371
|
|
|
365
372
|
agent.activateSkill(name) → skills:activate (via: 'explicit')
|
|
366
373
|
agent.deactivateSkill(name) → skills:deactivate (reason: 'explicit')
|
|
367
374
|
agent.reset() → skills:deactivate (reason: 'reset')
|
|
375
|
+
|
|
376
|
+
session-resume rehydration:
|
|
377
|
+
last-mode-wins per skill across history's skills_use blocks
|
|
378
|
+
→ trailing mode:'deactivate' STAYS deactivated; activate/deactivate/activate ends active
|
|
368
379
|
```
|
|
369
380
|
|
|
370
|
-
**allowed-tools.** When any active skill declares `allowed-tools`, a `tool:gate` handler blocks calls outside the union (the three skills tools are always implicitly allowed).
|
|
381
|
+
**allowed-tools.** When any active skill declares `allowed-tools`, a `tool:gate` handler blocks calls outside the union (the three skills tools are always implicitly allowed). Block-list (`- entry`) and flow-list (`[a, b]`) authoring shapes both normalize to `string[]` via the frontmatter parser's array-aware path; `AgentToolNotAllowedError.message` carries a recovery hint pointing the model at `skills_use({ mode: 'deactivate', name })` to release the offending skill without waiting for a run boundary.
|
|
371
382
|
|
|
372
383
|
**Body-only delivery.** `skills_use` returns the body (frontmatter stripped) wrapped in `<skill_content>` for host-SDK context protection. Body includes shell-interpolated instructions (`` !`cmd` ``, fresh per activation), the skill directory, the resource listing (not eagerly loaded), and compatibility + allowed-tools when present.
|
|
373
384
|
|
|
@@ -420,7 +431,7 @@ run end: uninstallLazyDisclosureGate; unlocked GC-eligible on next run()
|
|
|
420
431
|
skills:resolve ← once per agent, after discovery (warmup / run / activateSkill — first wins)
|
|
421
432
|
skills:catalog [mutable: catalog] ← once per agent, after system-prompt catalog built (same trigger)
|
|
422
433
|
skills:activate ← per activation; { skill, via: 'model' | 'explicit' | 'resume' }
|
|
423
|
-
skills:deactivate ← per deactivation; { skill, reason: 'run-end' | 'explicit' | 'reset' }
|
|
434
|
+
skills:deactivate ← per deactivation; { skill, reason: 'run-end' | 'explicit' | 'reset' | 'model' }
|
|
424
435
|
```
|
|
425
436
|
|
|
426
437
|
Every run ends with an implicit deactivate-all pass (`reason: 'run-end'`); activation state never leaks across runs unless re-asserted via `agent.activateSkill()`.
|
|
@@ -453,6 +464,7 @@ turn:after ← always fires (incl. errors)
|
|
|
453
464
|
tool:transform [mutable: result, isError] ← + outputBytes pre-mutation
|
|
454
465
|
tool:after ← + outputBytes post-mutation
|
|
455
466
|
tool:error [mutable: result?] ← on execute throw; ctx.result substitutes
|
|
467
|
+
tool:cancelled ← agent.cancelTool(callId); skips transform/after; wire = TOOL_USE_CANCELLED_MESSAGE
|
|
456
468
|
tool-results:after ← after the tool-results user turn is pushed (persistence seam)
|
|
457
469
|
usage ← running totals
|
|
458
470
|
output ← when behavior.schema set
|
|
@@ -481,6 +493,27 @@ session:end ← run finished (completed | aborted | err
|
|
|
481
493
|
includes turnRange [start, end] for cleanup
|
|
482
494
|
```
|
|
483
495
|
|
|
496
|
+
### Background task lifecycle
|
|
497
|
+
|
|
498
|
+
Fires when the model invokes `shell({ run_in_background: true })`. The shell tool dispatches via `ExecutionContext.execBackground`, which spawns a process group, opens a `WriteStream` to `<userDir>/<sessionId>/tasks/<task-id>.<context-timestamp>.log` (the timestamp segment is `YYYYMMDD-HHMMSS-mmm` UTC, shared by every task in the same `ExecutionContext` so two contexts on the same session never collide), and returns immediately with a `TaskHandle`. The agent listens to `background:exit` and queues a `<task-notification>` block for injection into the next user turn (see "Notification queue" below).
|
|
499
|
+
|
|
500
|
+
**Host opt-in / opt-out.** Background mode is auto-disabled at the schema level (the `run_in_background` field is dropped from the `shell` tool's input schema and the related description paragraphs are stripped) when either `behavior.tasksDir` is unset or `behavior.disableBackgroundTasks: true` is set. `createAgent` performs the rewrite once per run as it indexes tools by spec name — only for the identity-equal framework `shell` constant, so host-customized shell-named tools are left untouched. The runtime check in `runBackground` stays as defense-in-depth (forged inputs that smuggle the flag in past the schema fall through to a clean error, not a silent fallthrough to foreground). Hosts that want explicit control should call `createShellTool({ allowBackground })` and register the tailored variant directly.
|
|
501
|
+
|
|
502
|
+
```
|
|
503
|
+
background:start ← spawn succeeded; payload carries taskId + pid + outputPath
|
|
504
|
+
(observational; tools don't wait on listeners)
|
|
505
|
+
background:exit ← at-most-once per task; the context's settle latch
|
|
506
|
+
guarantees no duplicate fires (close-or-error race-safe)
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
**Notification queue.** The agent owns a `Map<taskId, TaskExitInfo>` populated by the `background:exit` listener. At the start of every `agent.run()`, the queue drains and each entry becomes a leading `text` block on the seeded user turn (`<task-notification>…</task-notification>` XML — see `renderTaskNotificationXml` in `src/agent.ts`). The same agent listens to `tool:after` for `shell_kill` (deletes by `task_id` from input) and `read_file` / `read` (deletes by matching the `path` argument against any pending entry's `outputPath`) so the model never receives a redundant signal when it already learned about the exit through other means. The drain happens once per entry — `Map.set` overwrites by id, so a defensive double-enqueue can't multiply notifications.
|
|
510
|
+
|
|
511
|
+
**Replay synthesis.** `eventsFromTurns` detects the `<task-notification>` XML in persisted user-turn text blocks (anchored regex — no false positives mid-prompt) and emits a structured `'task-notification'` `StreamEvent` instead of the generic `user-prompt` event. The XML text block is dropped from the rendered stream so the banner doesn't double-paint alongside the raw XML. Field decoding is tolerant — every tag falls back to a safe default if the persisted block predates a later schema addition.
|
|
512
|
+
|
|
513
|
+
**Destroy ordering.** `agent.destroy()` runs cleanup INSIDE-the-session first (notification queue, per-call cancels) then NEEDED-by-the-session (MCP connection, execution handle, skills cache). The execution context's own `destroy(handle)` walks its background-task registry, SIGTERMs each survivor's process group, and only resolves once every output stream has flushed + closed. The reverse order would tear down the handle underneath still-flushing tasks and corrupt log files. The queue is re-cleared AFTER `executionContext.destroy()` because the `background:exit` hook listener stays bound for the agent's lifetime and may re-populate the map mid-teardown.
|
|
514
|
+
|
|
515
|
+
**Orphan reaper.** `ProcessContext` lazy-registers a `process.on('exit')` handler on the first `execBackground` call that synchronously SIGTERMs every still-running task's process group. Without it, the `detached: true` spawn flag (required for the kill-tree semantics) would leak children when the host process exits without calling `destroy()` (Ctrl+C, uncaught exception, OOM kill). The handler deregisters in `destroy()` so reconstructed contexts don't accumulate listeners.
|
|
516
|
+
|
|
484
517
|
### Once (lazy on first `warmup()` / `run()` / `activateSkill()` — or eager when `eager: true`)
|
|
485
518
|
|
|
486
519
|
```
|
|
@@ -509,6 +542,7 @@ The child's lifecycle also bubbles to the parent hook surface with `childId` + `
|
|
|
509
542
|
```
|
|
510
543
|
child:stream:text / child:stream:thinking / child:stream:end / child:stream:error
|
|
511
544
|
child:tool:gate / child:mcp:tool:gate ← share the child's ctx — parent mutations propagate
|
|
545
|
+
child:tool:cancelled ← bubbled per-call cancel from a subagent's tool
|
|
512
546
|
child:tool:transform ← share the child's ctx — parent mutations propagate
|
|
513
547
|
child:tool:before / child:tool:after / child:tool:error
|
|
514
548
|
child:turn:after
|
package/docs/CHAT.md
CHANGED
|
@@ -419,7 +419,7 @@ type ChipColorMap = { default: ChipColor } & Partial<Record<string, ChipColor>>
|
|
|
419
419
|
|
|
420
420
|
Per-tool approval gate with a per-project safelist. The agent emits a `tool:gate` hook; the chat layer's wired handler:
|
|
421
421
|
|
|
422
|
-
1. If `IMPLICITLY_SAFE_TOOLS` covers the call → allow.
|
|
422
|
+
1. If `IMPLICITLY_SAFE_TOOLS` covers the call → allow. The list is `['read_file', 'list_files', 'glob', 'grep', 'ask_user', 'present_plan', 'todowrite', 'todoread', 'skills_use']` — pure reads, interaction prompts (the picker IS the gate), todo metadata, and skill activation (mutates a per-agent Map; no shell/disk/network — gating it would also break the model's recovery path when it deactivates a skill after an `AgentToolNotAllowedError`). `skills_read` (touches disk) and `skills_run_script` (executes scripts) are intentionally NOT on the list.
|
|
423
423
|
2. If the project safelist (`~/.zidane/projects.json`) matches → allow.
|
|
424
424
|
3. Else push an `ApprovalRequest` onto `SafeModeProvider`'s queue and await.
|
|
425
425
|
|
|
@@ -958,6 +958,7 @@ Render path expectations on the `'tool'` `StreamEvent`:
|
|
|
958
958
|
- Tags every child-run event with `{ childId: 'child-N', depth }` where `child-N` is chronologically assigned by `startedAt`. Same labels the live spawn tool emits.
|
|
959
959
|
- Strips the redundant `Tokens: …` line from spawn tool-result bodies — only when guarded by the `[sub-agent <id>] …` header, so a body line that legitimately starts with `Tokens:` is preserved.
|
|
960
960
|
- Emits a `'compact-summary'` event for every `compact-summary` content block, with `compact` metadata (`replacedCount`, `model`, `compactedAt`, `inputTokens`, `outputTokens`, `cacheReadTokens`, `cacheCreationTokens`).
|
|
961
|
+
- Detects leading `<task-notification>` text blocks (the wire format `renderTaskNotificationXml` emits) on user turns and synthesizes a `'task-notification'` event with structured `task` metadata (`taskId`, `status`, `exitCode`, `outputPath`, `command`, `durationMs`). The raw text block is suppressed from the `'user-prompt'` stream so the banner doesn't double-paint. Detection is anchored — a stray notification-shaped phrase mid-prompt won't trigger.
|
|
961
962
|
|
|
962
963
|
`lastContextSizeFromTurns(turns, runs)` returns the most recent **depth-0** assistant turn's input token total (cache-aware: `input + cacheRead + cacheCreation`) — the next prompt feeds the parent, so a session that ended mid-spawn doesn't surface the child's window in the footer.
|
|
963
964
|
|
|
@@ -1016,7 +1017,7 @@ The discriminated union the renderer consumes. `kind` values:
|
|
|
1016
1017
|
'thinking' | 'tool' | 'tool-result' | 'error'
|
|
1017
1018
|
| 'user-prompt' | 'info' | 'separator'
|
|
1018
1019
|
| 'markdown' | 'spawn-start' | 'spawn-end'
|
|
1019
|
-
| 'compact-summary'
|
|
1020
|
+
| 'compact-summary' | 'task-notification'
|
|
1020
1021
|
```
|
|
1021
1022
|
|
|
1022
1023
|
Notable fields:
|
|
@@ -1030,6 +1031,7 @@ Notable fields:
|
|
|
1030
1031
|
- `refs?: { start, end, providerId }[]` — chip-highlight spans on `'user-prompt'` events.
|
|
1031
1032
|
- `turnId?: string` — the `SessionTurn.id` the event was emitted by; drives select-turn highlighting.
|
|
1032
1033
|
- `compact?: { replacedCount, model, compactedAt, inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens }` — metadata for `'compact-summary'` boundary cards.
|
|
1034
|
+
- `task?: { taskId, status: 'exited' | 'killed', exitCode, outputPath, command, durationMs }` — metadata for `'task-notification'` banners. Synthesized in `eventsFromTurns` from persisted `<task-notification>` XML in user-turn text blocks; the raw XML block is dropped from the user-prompt stream so the banner doesn't double-paint alongside it.
|
|
1033
1035
|
|
|
1034
1036
|
`'user-prompt'` carries the **raw** user prompt without a `❯` prefix; renderers add their own chevron. `refs` offsets are aligned to the raw text.
|
|
1035
1037
|
|