@f5-sales-demo/xcsh 20.13.0 → 20.13.2
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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5-sales-demo/xcsh",
|
|
4
|
-
"version": "20.13.
|
|
4
|
+
"version": "20.13.2",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://github.com/f5-sales-demo/xcsh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -61,20 +61,21 @@
|
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@agentclientprotocol/sdk": "1.3.0",
|
|
64
|
+
"@f5-sales-demo/pi-agent-core": "20.13.2",
|
|
65
|
+
"@f5-sales-demo/pi-ai": "20.13.2",
|
|
66
|
+
"@f5-sales-demo/pi-natives": "20.13.2",
|
|
67
|
+
"@f5-sales-demo/pi-resource-management": "20.13.2",
|
|
68
|
+
"@f5-sales-demo/pi-tui": "20.13.2",
|
|
69
|
+
"@f5-sales-demo/pi-utils": "20.13.2",
|
|
70
|
+
"@f5-sales-demo/xcsh-stats": "20.13.2",
|
|
64
71
|
"@mozilla/readability": "^0.6",
|
|
65
|
-
"@f5-sales-demo/xcsh-stats": "20.13.0",
|
|
66
|
-
"@f5-sales-demo/pi-agent-core": "20.13.0",
|
|
67
|
-
"@f5-sales-demo/pi-ai": "20.13.0",
|
|
68
|
-
"@f5-sales-demo/pi-natives": "20.13.0",
|
|
69
|
-
"@f5-sales-demo/pi-resource-management": "20.13.0",
|
|
70
|
-
"@f5-sales-demo/pi-tui": "20.13.0",
|
|
71
|
-
"@f5-sales-demo/pi-utils": "20.13.0",
|
|
72
72
|
"@sinclair/typebox": "^0.34",
|
|
73
73
|
"@xterm/headless": "^6.0",
|
|
74
74
|
"ajv": "^8.20",
|
|
75
75
|
"chalk": "^5.6",
|
|
76
76
|
"diff": "^9.0",
|
|
77
77
|
"fflate": "0.8.3",
|
|
78
|
+
"google-auth-library": "10.9.1",
|
|
78
79
|
"linkedom": "^0.18",
|
|
79
80
|
"lru-cache": "11.5.2",
|
|
80
81
|
"markit-ai": "0.5.3",
|
|
@@ -17,17 +17,17 @@ export interface BuildInfo {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const BUILD_INFO: BuildInfo = {
|
|
20
|
-
"version": "20.13.
|
|
21
|
-
"commit": "
|
|
22
|
-
"shortCommit": "
|
|
20
|
+
"version": "20.13.2",
|
|
21
|
+
"commit": "2c8c472faa5935f368999d276bc0b877b437c43c",
|
|
22
|
+
"shortCommit": "2c8c472",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v20.13.
|
|
25
|
-
"commitDate": "2026-08-
|
|
26
|
-
"buildDate": "2026-08-
|
|
24
|
+
"tag": "v20.13.2",
|
|
25
|
+
"commitDate": "2026-08-11T14:56:07Z",
|
|
26
|
+
"buildDate": "2026-08-11T15:23:40.081Z",
|
|
27
27
|
"dirty": true,
|
|
28
28
|
"prNumber": "",
|
|
29
29
|
"repoUrl": "https://github.com/f5-sales-demo/xcsh",
|
|
30
30
|
"repoSlug": "f5-sales-demo/xcsh",
|
|
31
|
-
"commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/
|
|
32
|
-
"releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v20.13.
|
|
31
|
+
"commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/2c8c472faa5935f368999d276bc0b877b437c43c",
|
|
32
|
+
"releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v20.13.2"
|
|
33
33
|
};
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import type { ConsoleCatalogData } from "./console-catalog-types";
|
|
4
4
|
|
|
5
|
-
export const CONSOLE_CATALOG_VERSION = "
|
|
5
|
+
export const CONSOLE_CATALOG_VERSION = "4450d0ec7d164aaee226271a6b4cd69aca408f28";
|
|
6
6
|
|
|
7
7
|
export const CONSOLE_CATALOG_DATA: ConsoleCatalogData = {
|
|
8
|
-
version: "
|
|
8
|
+
version: "4450d0ec7d164aaee226271a6b4cd69aca408f28",
|
|
9
9
|
workflows: {
|
|
10
10
|
"address-allocator/create":
|
|
11
11
|
'---\nschema: urn:xcsh:console:workflow:v1\nid: address-allocator-create\nlabel: Create IP Address Allocators\nresource: address-allocator\noperation: create\npreconditions:\n - user_logged_in\n - "role_minimum: admin"\nparams:\n name:\n required: true\n description: IP Address Allocators name (lowercase alphanumeric and hyphens)\n example: example-address-allocator\n address_allocator_mode:\n required: true\n description: Address Allocator Mode\n allocation_unit:\n required: false\n description: Allocation Unit\n default: 0\n address_pool:\n required: false\n description: Address Pool\n default: value\n address_allocation_scheme:\n required: false\n description: "Server-required: Field should be not nil"\n default: value\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/multi-cloud-network-connect/manage/networking/legacy_network_configuration/address_allocators\n wait_for: text(\'IP Address Allocators\')\n description: Navigate to IP Address Allocators list page\n - id: click-add-tab\n action: click\n selector: text(\'Add IP Address Allocator\')\n wait_for: textbox[name=\'Name\']\n description: Click Add IP Address Allocator to open the create form\n - id: fill-name\n action: fill\n selector: textbox[name=\'Name\']\n value: "{name}"\n description: Enter Name\n - id: select-address_allocator_mode\n action: select\n selector: listbox\n context: Address Allocator Mode section\n value: "{address_allocator_mode}"\n description: Select Address Allocator Mode\n - id: fill-allocation_unit\n action: fill\n selector: spinbutton[name=\'Allocation Unit\']\n value: "{allocation_unit}"\n description: Set Allocation Unit\n - id: fill-address_pool\n action: fill\n selector: ngx-datatable input.form-control\n context: Address Pool table\n value: "{address_pool}"\n description: Enter Address Pool in the existing table row (no Add Item needed — the table ships one empty row)\n - id: select-address_allocation_scheme\n action: select\n selector: listbox\n context: Address Allocation Scheme section\n value: "{address_allocation_scheme}"\n description: Select Address Allocation Scheme\n - id: save\n action: click\n selector: "[class*=\'save-bt\'],[class*=\'submit-button\']"\n context: footer\n wait_for: text(\'{name}\')\n wait_timeout_ms: 30000\n description: Save/submit the form (union selector matches save-bt OR submit-button)\npostconditions:\n - resource_list_page_visible\n - "resource_name_in_list: {name}"\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: "2025.06"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n',
|
|
@@ -481,7 +481,7 @@ export const EMBEDDED_DOCS: Readonly<Record<string, string>> = {
|
|
|
481
481
|
"ko/tui/tree.md": "---\ntitle: Tree 명령어 레퍼런스\ndescription: 세션 기록과 대화 분기를 시각화하기 위한 /tree 명령어 레퍼런스.\nsidebar:\n order: 4\n label: /tree 명령어\ni18n:\n sourceHash: ee0e412fe993\n translator: machine\n---\n\n# `/tree` 명령어 레퍼런스\n\n`/tree`는 대화형 **세션 트리** 탐색기를 엽니다. 현재 세션 파일의 모든 항목으로 이동하여 해당 지점부터 계속할 수 있습니다.\n\n이것은 파일 내 리프 이동이며, 새로운 세션 내보내기가 아닙니다.\n\n## `/tree`의 기능\n\n- 현재 세션 항목에서 트리를 구축합니다 (`SessionManager.getTree()`)\n- 키보드 탐색, 필터, 검색 기능이 포함된 `TreeSelectorComponent`를 엽니다\n- 선택 시 `AgentSession.navigateTree(targetId, { summarize, customInstructions })`를 호출합니다\n- 새로운 리프 경로에서 보이는 채팅을 재구축합니다\n- user/custom 메시지를 선택할 때 선택적으로 에디터 텍스트를 미리 채웁니다\n\n주요 구현:\n\n- `src/modes/controllers/input-controller.ts` (`/tree`, 키바인딩 연결, 이중 Escape 동작)\n- `src/modes/controllers/selector-controller.ts` (트리 UI 실행 + 요약 프롬프트 흐름)\n- `src/modes/components/tree-selector.ts` (탐색, 필터, 검색, 레이블, 렌더링)\n- `src/session/agent-session.ts` (`navigateTree` 리프 전환 + 선택적 요약)\n- `src/session/session-manager.ts` (`getTree`, `branch`, `branchWithSummary`, `resetLeaf`, 레이블 영속성)\n\n## 여는 방법\n\n다음 중 하나로 동일한 선택기를 열 수 있습니다:\n\n- `/tree`\n- 설정된 키바인딩 액션 `tree`\n- 빈 에디터에서 이중 Escape (`doubleEscapeAction = \"tree\"`인 경우, 기본값)\n- `/branch` (`doubleEscapeAction = \"tree\"`인 경우, 사용자 전용 분기 선택기 대신 트리 선택기로 라우팅)\n\n## 트리 UI 모델\n\n트리는 세션 항목의 부모 포인터(`id` / `parentId`)에서 렌더링됩니다.\n\n- 자식은 타임스탬프 오름차순으로 정렬됩니다 (오래된 것이 먼저, 새로운 것이 아래)\n- 활성 분기(루트에서 현재 리프까지의 경로)는 불릿으로 표시됩니다\n- 레이블(있는 경우)은 노드 텍스트 앞에 `[label]`로 렌더링됩니다\n- 여러 루트가 존재하는 경우(고아/끊어진 부모 체인), 가상 분기 루트 아래에 표시됩니다\n\n```text\n트리 뷰 예시 (활성 경로는 •로 표시):\n\n├─ user: \"작업 시작\"\n│ └─ assistant: \"계획\"\n│ ├─ • user: \"접근법 A 시도\"\n│ │ └─ • assistant: \"A 결과\"\n│ │ └─ • [milestone] user: \"A 계속\"\n│ └─ user: \"접근법 B 시도\"\n│ └─ assistant: \"B 결과\"\n```\n\n선택기는 현재 선택 항목을 중심으로 재배치되며 최대 다음만큼의 행을 표시합니다:\n\n- `max(5, floor(terminalHeight / 2))` 행\n\n## 트리 선택기 내 키바인딩\n\n- `Up` / `Down`: 선택 이동 (순환)\n- `Left` / `Right`: 페이지 위 / 페이지 아래\n- `Enter`: 노드 선택\n- `Esc`: 검색이 활성화된 경우 검색 지우기; 그렇지 않으면 선택기 닫기\n- `Ctrl+C`: 선택기 닫기\n- `Type`: 검색 쿼리에 추가\n- `Backspace`: 검색 문자 삭제\n- `Shift+L`: 선택된 항목의 레이블 편집/지우기\n- `Ctrl+O`: 필터를 앞으로 순환\n- `Shift+Ctrl+O`: 필터를 뒤로 순환\n- `Alt+D/T/U/L/A`: 특정 필터 모드로 직접 이동\n\n## 필터 및 검색 의미론\n\n필터 모드 (`TreeList`):\n\n1. `default`\n2. `no-tools`\n3. `user-only`\n4. `labeled-only`\n5. `all`\n\n### `default`\n\n대부분의 대화 노드를 표시하지만 관리용 항목 유형은 숨깁니다:\n\n- `label`\n- `custom`\n- `model_change`\n- `thinking_level_change`\n\n### `no-tools`\n\n`default`와 동일하며, 추가로 `toolResult` 메시지를 숨깁니다.\n\n### `user-only`\n\n역할이 `user`인 `message` 항목만 표시합니다.\n\n### `labeled-only`\n\n현재 레이블로 확인되는 항목만 표시합니다.\n\n### `all`\n\n관리용/custom 항목을 포함하여 세션 트리의 모든 것을 표시합니다.\n\n### 도구 전용 어시스턴트 노드 동작\n\n**도구 호출만** 포함하고 텍스트가 없는 어시스턴트 메시지는 다음의 경우를 제외하고 모든 필터 뷰에서 기본적으로 숨겨집니다:\n\n- 메시지가 오류/중단됨 (`stopReason`이 `stop`/`toolUse`가 아닌 경우), 또는\n- 현재 리프인 경우 (항상 표시 유지)\n\n### 검색 동작\n\n- 쿼리는 공백으로 토큰화됩니다\n- 매칭은 대소문자를 구분하지 않습니다\n- 모든 토큰이 일치해야 합니다 (AND 의미론)\n- 검색 가능한 텍스트에는 레이블, 역할, 유형별 콘텐츠(메시지 텍스트, 분기 요약 텍스트, custom 유형, 도구 명령어 스니펫 등)가 포함됩니다\n\n## 선택 결과 (중요)\n\n`navigateTree`는 선택된 항목 유형에 따라 새로운 리프 동작을 계산합니다:\n\n### `user` 메시지 선택\n\n- 새로운 리프는 선택된 항목의 `parentId`가 됩니다\n- 부모가 `null`인 경우(루트 사용자 메시지), 리프는 루트로 재설정됩니다 (`resetLeaf()`)\n- 선택된 메시지 텍스트가 편집/재제출을 위해 에디터에 복사됩니다\n\n### `custom_message` 선택\n\n- 사용자 메시지와 동일한 리프 규칙 (`parentId`)\n- 텍스트 콘텐츠가 추출되어 에디터에 복사됩니다\n\n### 비사용자 노드 선택 (assistant/tool/summary/compaction/custom 관리용 등)\n\n- 새로운 리프는 선택된 노드 id가 됩니다\n- 에디터에 미리 채워지지 않습니다\n\n### 현재 리프 선택\n\n- 무동작; 선택기가 \"이미 이 지점에 있습니다\" 메시지와 함께 닫힙니다\n\n```text\n선택 결정 (간략화):\n\n선택된 노드\n │\n ├─ 현재 리프인가? ── 예 ──> 선택기 닫기 (무동작)\n │\n ├─ user/custom_message인가? ── 예 ──> leaf := parentId (루트의 경우 resetLeaf)\n │ + 에디터 텍스트 미리 채우기\n │\n └─ 그 외 ──> leaf := 선택된 노드 id\n + 에디터 미리 채우기 없음\n```\n\n## 전환 시 요약 흐름\n\n요약 프롬프트는 `branchSummary.enabled`로 제어됩니다 (기본값: `false`).\n\n활성화된 경우, 노드를 선택한 후 UI가 다음을 묻습니다:\n\n- `요약 없음`\n- `요약`\n- `사용자 정의 프롬프트로 요약`\n\n흐름 세부 사항:\n\n- 요약 프롬프트에서 Escape를 누르면 트리 선택기가 다시 열립니다\n- 사용자 정의 프롬프트 취소 시 요약 선택 루프로 돌아갑니다\n- 요약 중에 UI는 로더를 표시하고 `Esc`를 `abortBranchSummary()`에 바인딩합니다\n- 요약이 중단되면 트리 선택기가 다시 열리고 이동이 적용되지 않습니다\n\n`navigateTree` 내부 동작:\n\n- 이전 리프에서 공통 조상까지 버려진 분기 항목을 수집합니다\n- `session_before_tree`를 발생시킵니다 (확장 기능이 취소하거나 요약을 삽입할 수 있음)\n- 요청되고 필요한 경우에만 기본 요약기를 사용합니다\n- 다음을 통해 이동을 적용합니다:\n - 요약이 있는 경우 `branchWithSummary(...)`\n - 요약 없는 비루트 이동의 경우 `branch(newLeafId)`\n - 요약 없는 루트 이동의 경우 `resetLeaf()`\n- 에이전트 대화를 재구축된 세션 컨텍스트로 교체합니다\n- `session_tree`를 발생시킵니다\n\n참고: 사용자가 요약을 요청했지만 요약할 내용이 없는 경우, 요약 항목을 생성하지 않고 탐색이 진행됩니다.\n\n## 레이블\n\n트리 UI에서의 레이블 편집은 `appendLabelChange(targetId, label)`을 호출합니다.\n\n- 비어 있지 않은 레이블은 확인된 레이블을 설정/업데이트합니다\n- 빈 레이블은 이를 지웁니다\n- 레이블은 추가 전용 `label` 항목으로 저장됩니다\n- 트리 노드는 원시 레이블 항목 기록이 아닌 확인된 레이블 상태를 표시합니다\n\n## `/tree` vs 인접 작업\n\n| 작업 | 범위 | 결과 |\n|---|---|---|\n| `/tree` | 현재 세션 파일 | 선택된 지점으로 리프를 이동 (동일 파일) |\n| `/branch` | 보통 현재 세션 파일 -> 새 세션 파일 | 기본적으로 선택된 **사용자** 메시지에서 새 세션 파일로 분기; `doubleEscapeAction = \"tree\"`인 경우, `/branch`는 대신 트리 탐색 UI를 엽니다 |\n| `/fork` | 전체 현재 세션 | 세션을 새로운 영속 세션 파일로 복제 |\n| `/resume` | 세션 목록 | 다른 세션 파일로 전환 |\n\n핵심 구분: `/tree`는 하나의 세션 파일 내에서의 탐색/위치 변경 도구입니다. `/branch`, `/fork`, `/resume`는 모두 세션 파일 컨텍스트를 변경합니다.\n\n## 운영자 워크플로우\n\n### 현재 분기를 잃지 않고 이전 사용자 프롬프트에서 다시 실행\n\n1. `/tree`\n2. 이전 사용자 메시지를 검색/선택\n3. `요약 없음` 선택 (필요한 경우 요약)\n4. 에디터에 미리 채워진 텍스트 편집\n5. 제출\n\n효과: 동일 세션 파일 내에서 선택된 지점으로부터 새 분기가 성장합니다.\n\n### 컨텍스트 브레드크럼과 함께 현재 분기 떠나기\n\n1. `branchSummary.enabled` 활성화\n2. `/tree`로 대상 노드 선택\n3. `요약` 선택 (또는 사용자 정의 프롬프트)\n\n효과: 계속하기 전에 대상 위치에 `branch_summary` 항목이 추가됩니다.\n\n### 숨겨진 관리용 항목 조사\n\n1. `/tree`\n2. `Alt+A` 누르기 (all)\n3. `model`, `thinking`, `custom` 또는 레이블 검색\n\n효과: 대화 노드뿐만 아니라 전체 내부 타임라인을 검사합니다.\n\n### 나중에 이동할 피벗 포인트 북마크\n\n1. `/tree`\n2. 항목으로 이동\n3. `Shift+L`을 누르고 레이블 설정\n4. 나중에 `Alt+L` (`labeled-only`)을 사용하여 빠르게 이동\n\n효과: 지속적인 분기 랜드마크 간 빠른 탐색이 가능합니다.\n",
|
|
482
482
|
"ko/tui/tui-runtime-internals.md": "---\ntitle: TUI 런타임 내부 구조\ndescription: '렌더링 파이프라인, 입력 처리, 상태 관리를 포함한 터미널 UI 런타임 내부 구조.'\nsidebar:\n order: 2\n label: 런타임 내부 구조\ni18n:\n sourceHash: 67e79fc1e1f3\n translator: machine\n---\n\n# TUI 런타임 내부 구조\n\n이 문서는 대화형 모드에서 터미널 입력부터 렌더링된 출력까지의 테마 외 런타임 경로를 설명합니다. `packages/tui`의 동작과 `packages/coding-agent` 컨트롤러의 통합에 초점을 맞춥니다.\n\n## 런타임 계층 및 소유권\n\n- **`packages/tui` 엔진**: 터미널 생명주기, stdin 정규화, 포커스 라우팅, 렌더 스케줄링, 차등 페인팅, 오버레이 합성, 하드웨어 커서 배치.\n- **`packages/coding-agent` 대화형 모드**: 컴포넌트 트리 구성, 에디터 콜백 및 키맵 바인딩, 에이전트/세션 이벤트 반응, 도메인 상태(스트리밍, 도구 실행, 재시도, 플랜 모드)를 UI 구성 요소로 변환.\n\n경계 규칙: TUI 엔진은 메시지에 독립적입니다. `Component.render(width)`, `handleInput(data)`, 포커스, 오버레이만 알고 있습니다. 에이전트 시맨틱은 대화형 컨트롤러에 유지됩니다.\n\n## 구현 파일\n\n- [`../src/modes/interactive-mode.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/coding-agent/src/modes/interactive-mode.ts)\n- [`../src/modes/controllers/event-controller.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/coding-agent/src/modes/controllers/event-controller.ts)\n- [`../src/modes/controllers/input-controller.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/coding-agent/src/modes/controllers/input-controller.ts)\n- [`../src/modes/components/custom-editor.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/coding-agent/src/modes/components/custom-editor.ts)\n- [`../../tui/src/tui.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/tui/src/tui.ts)\n- [`../../tui/src/terminal.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/tui/src/terminal.ts)\n- [`../../tui/src/editor-component.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/tui/src/editor-component.ts)\n- [`../../tui/src/stdin-buffer.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/tui/src/stdin-buffer.ts)\n- [`../../tui/src/components/loader.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/tui/src/components/loader.ts)\n\n## 부팅 및 컴포넌트 트리 조립\n\n`InteractiveMode`는 `TUI(new ProcessTerminal(), showHardwareCursor)`를 생성하고 다음과 같은 영구 컨테이너를 만듭니다:\n\n- `chatContainer`\n- `pendingMessagesContainer`\n- `statusContainer`\n- `todoContainer`\n- `statusLine`\n- `editorContainer` (`CustomEditor` 포함)\n\n`init()`은 해당 순서로 트리를 연결하고, 에디터에 포커스를 설정하며, `InputController`를 통해 입력 핸들러를 등록하고, TUI를 시작한 후 강제 렌더를 요청합니다.\n\n강제 렌더(`requestRender(true)`)는 다시 페인팅하기 전에 이전 줄 캐시와 커서 북키핑을 초기화합니다.\n\n## 터미널 생명주기 및 stdin 정규화\n\n`ProcessTerminal.start()`:\n\n1. 원시 모드 및 브래킷 붙여넣기를 활성화합니다.\n2. 리사이즈 핸들러를 연결합니다.\n3. 부분 이스케이프 청크를 완전한 시퀀스로 분리하기 위해 `StdinBuffer`를 생성합니다.\n4. Kitty 키보드 프로토콜 지원을 쿼리(`CSI ? u`)한 후, 지원되면 프로토콜 플래그를 활성화합니다.\n5. Windows에서는 `kernel32` 모드 플래그를 통해 VT 입력 활성화를 시도합니다.\n\n`StdinBuffer` 동작:\n\n- 분열된 이스케이프 시퀀스(CSI/OSC/DCS/APC/SS3)를 버퍼링합니다.\n- 시퀀스가 완료되거나 타임아웃으로 플러시될 때만 `data`를 방출합니다.\n- 브래킷 붙여넣기를 감지하고 원시 붙여넣기 텍스트와 함께 `paste` 이벤트를 방출합니다.\n\n이를 통해 부분 이스케이프 청크가 일반 키 입력으로 잘못 해석되는 것을 방지합니다.\n\n## 입력 라우팅 및 포커스 모델\n\n입력 경로:\n\n`stdin -> ProcessTerminal -> StdinBuffer -> TUI.#handleInput -> focusedComponent.handleInput`\n\n라우팅 세부 사항:\n\n1. TUI는 등록된 입력 리스너를 먼저 실행하여(`addInputListener`) 소비/변환 동작을 허용합니다.\n2. TUI는 컴포넌트 디스패치 전에 전역 디버그 단축키(`shift+ctrl+d`)를 처리합니다.\n3. 포커스된 컴포넌트가 이제 숨겨지거나 보이지 않는 오버레이에 속하면, TUI는 포커스를 다음 보이는 오버레이 또는 저장된 오버레이 이전 포커스로 재할당합니다.\n4. 포커스된 컴포넌트가 `wantsKeyRelease = true`로 설정하지 않는 한 키 해제 이벤트는 필터링됩니다.\n5. 디스패치 후 TUI는 렌더를 스케줄링합니다.\n\n`setFocus()`는 `Focusable.focused`도 전환하여 컴포넌트가 하드웨어 커서 배치를 위해 `CURSOR_MARKER`를 방출할지 여부를 제어합니다.\n\n## 키 처리 분리: 에디터 vs 컨트롤러\n\n`CustomEditor`는 우선순위가 높은 조합(escape, ctrl-c/d/z, ctrl-v, ctrl-p 변형, ctrl-t, alt-up, 확장 사용자 지정 키)을 먼저 가로채고, 나머지는 기본 `Editor` 동작(텍스트 편집, 히스토리, 자동 완성, 커서 이동)에 위임합니다.\n\n`InputController.setupKeyHandlers()`는 에디터 콜백을 모드 액션에 바인딩합니다:\n\n- `Escape`에서 취소/모드 종료\n- 이중 `Ctrl+C` 또는 빈 에디터 `Ctrl+D`에서 종료\n- `Ctrl+Z`에서 일시 중단/재개\n- 슬래시 명령 및 선택기 단축키\n- 후속/대기열 제거 토글 및 확장 토글\n\n이를 통해 키 파싱/에디터 메커니즘은 `packages/tui`에 유지되고 모드 시맨틱은 coding-agent 컨트롤러에 유지됩니다.\n\n## 렌더 루프 및 diff 전략\n\n`TUI.requestRender()`는 `process.nextTick`을 사용하여 틱당 하나의 렌더로 디바운싱됩니다. 동일한 턴에서 여러 상태 변경이 병합됩니다.\n\n`#doRender()` 파이프라인:\n\n1. 루트 컴포넌트 트리를 `newLines`로 렌더링합니다.\n2. 보이는 오버레이가 있으면 합성합니다.\n3. 보이는 뷰포트 줄에서 `CURSOR_MARKER`를 추출하고 제거합니다.\n4. 비이미지 줄에 세그먼트 리셋 접미사를 추가합니다.\n5. 전체 재페인트와 차등 패치 중에서 선택합니다:\n - 첫 번째 프레임\n - 너비 변경\n - `clearOnShrink`가 활성화되고 오버레이가 없는 상태에서 축소\n - 이전 뷰포트 위의 편집\n6. 차등 업데이트의 경우 변경된 줄 범위만 패치하고, 필요한 경우 오래된 후행 줄을 지웁니다.\n7. IME 지원을 위해 하드웨어 커서를 재배치합니다.\n\n렌더 쓰기는 플리커/티어링을 줄이기 위해 동기화된 출력 모드(`CSI ? 2026 h/l`)를 사용합니다.\n\n## 렌더 안전 제약\n\n`TUI`의 중요 안전 검사:\n\n- 비이미지 렌더링 줄은 터미널 너비를 초과해서는 안 됩니다. 오버플로우 시 예외를 발생시키고 크래시 진단을 작성합니다.\n- 오버레이 합성에는 방어적 잘라내기와 합성 후 너비 검증이 포함됩니다.\n- 너비 변경은 줄 바꿈 시맨틱이 변경되기 때문에 전체 재그리기를 강제합니다.\n- 커서 위치는 이동 전에 클램핑됩니다.\n\n이러한 제약은 단순한 관례가 아닌 런타임 적용 사항입니다.\n\n## 리사이즈 처리\n\n리사이즈 이벤트는 `ProcessTerminal`에서 `TUI.requestRender()`로 이벤트 기반으로 처리됩니다.\n\n효과:\n\n- 너비 변경 시 전체 재그리기가 트리거됩니다.\n- 뷰포트/상단 추적(`#previousViewportTop`, `#maxLinesRendered`)은 콘텐츠나 터미널 크기가 변경될 때 잘못된 상대 커서 계산을 방지합니다.\n- 오버레이 가시성은 터미널 크기에 의존할 수 있으며(`OverlayOptions.visible`), 리사이즈 후 오버레이가 보이지 않게 되면 포커스가 수정됩니다.\n\n## 스트리밍 및 증분 UI 업데이트\n\n`EventController`는 `AgentSessionEvent`를 구독하고 UI를 증분 방식으로 업데이트합니다:\n\n- `agent_start`: `statusContainer`에서 로더를 시작합니다.\n- `message_start` 어시스턴트: `streamingComponent`를 생성하고 마운트합니다.\n- `message_update`: 스트리밍 어시스턴트 콘텐츠를 업데이트하고, 도구 호출이 나타날 때 도구 실행 구성 요소를 생성/업데이트합니다.\n- `tool_execution_update/end`: 도구 결과 구성 요소와 완료 상태를 업데이트합니다.\n- `message_end`: 어시스턴트 스트림을 완료하고, 중단/오류 주석을 처리하며, 정상 중지 시 보류 중인 도구 인수를 완료로 표시합니다.\n- `agent_end`: 로더를 중지하고, 임시 스트림 상태를 지우며, 지연된 모델 전환을 플러시하고, 백그라운드 상태이면 완료 알림을 발행합니다.\n\n읽기 도구 그룹화는 의도적으로 상태를 유지하며(`#lastReadGroup`), 비읽기 중단이 발생할 때까지 연속적인 읽기 도구 호출을 하나의 시각적 블록으로 병합합니다.\n\n## 상태 및 로더 오케스트레이션\n\n상태 레인 소유권:\n\n- `statusContainer`는 임시 로더(`loadingAnimation`, `autoCompactionLoader`, `retryLoader`)를 보유합니다.\n- `statusLine`은 영구 상태/훅/플랜 표시기를 렌더링하고 에디터 상단 테두리 업데이트를 구동합니다.\n\n로더 동작:\n\n- `Loader`는 인터벌을 통해 80ms마다 업데이트하고 각 프레임에서 렌더를 요청합니다.\n- 자동 압축 및 자동 재시도 중에는 이스케이프 핸들러가 해당 작업을 취소하기 위해 일시적으로 재정의됩니다.\n- 종료/취소 경로에서 컨트롤러는 이전 이스케이프 핸들러를 복원하고 로더 구성 요소를 중지/지웁니다.\n\n## 모드 전환 및 백그라운드 처리\n\n### Bash/Python 입력 모드\n\n입력 텍스트 접두사가 에디터 테두리 모드 플래그를 전환합니다:\n\n- `!` -> bash 모드\n- `$` (비템플릿 리터럴 접두사) -> python 모드\n\nEscape는 에디터 텍스트를 지우고 테두리 색상을 복원하여 비활성 모드를 종료합니다. 실행이 활성 상태일 때 Escape는 실행 중인 작업을 대신 중단합니다.\n\n### 플랜 모드\n\n`InteractiveMode`는 플랜 모드 플래그, 상태 줄 상태, 활성 도구, 모델 전환을 추적합니다. 진입/종료 시 세션 모드 항목과 상태/UI 상태를 업데이트하며, 스트리밍이 활성 상태이면 지연된 모델 전환을 포함합니다.\n\n### 일시 중단/재개 (`Ctrl+Z`)\n\n`InputController.handleCtrlZ()`:\n\n1. TUI를 다시 시작하고 강제 렌더하기 위해 일회성 `SIGCONT` 핸들러를 등록합니다.\n2. 일시 중단 전에 TUI를 중지합니다.\n3. 프로세스 그룹에 `SIGTSTP`를 전송합니다.\n\n### 백그라운드 모드 (`/background` 또는 `/bg`)\n\n`handleBackgroundCommand()`:\n\n- 유휴 상태일 때 거부합니다.\n- 도구 UI 컨텍스트를 비대화형(`hasUI=false`)으로 전환하여 대화형 UI 도구가 빠르게 실패하도록 합니다.\n- 로더/상태 줄을 중지하고 포그라운드 이벤트 핸들러의 구독을 취소합니다.\n- 백그라운드 이벤트 핸들러를 구독합니다(주로 `agent_end`를 기다림).\n- TUI를 중지하고 `SIGTSTP`를 전송합니다(POSIX 작업 제어 경로).\n\n대기 중인 작업 없이 백그라운드에서 `agent_end` 발생 시, 컨트롤러는 완료 알림을 전송하고 종료합니다.\n\n## 취소 경로\n\n주요 취소 입력:\n\n- 활성 스트림 로더 중 `Escape`: 대기 중인 메시지를 에디터에 복원하고 에이전트를 중단합니다.\n- bash/python 실행 중 `Escape`: 실행 중인 명령을 중단합니다.\n- 자동 압축/재시도 중 `Escape`: 임시 이스케이프 핸들러를 통해 전용 중단 메서드를 호출합니다.\n- `Ctrl+C` 단일 누름: 에디터 지우기; 500ms 내 두 번 누름: 종료.\n\n취소는 상태 조건부입니다. 동일한 키가 런타임 상태에 따라 중단, 모드 종료, 선택기 트리거, 또는 아무 동작 없음을 의미할 수 있습니다.\n\n## 이벤트 기반 vs 스로틀 동작\n\n이벤트 기반 업데이트:\n\n- 에이전트 세션 이벤트(`EventController`)\n- 키 입력 콜백(`InputController`)\n- 터미널 리사이즈 콜백\n- `InteractiveMode`의 테마/브랜치 감시자\n\n스로틀/디바운스 경로:\n\n- TUI 렌더링은 틱 디바운싱됩니다(`requestRender` 병합).\n- 로더 애니메이션은 고정 인터벌(80ms)로, 각 프레임에서 렌더를 요청합니다.\n- 에디터 자동 완성 업데이트(`Editor` 내부)는 디바운스 타이머를 사용하여 타이핑 중 재계산 오버헤드를 줄입니다.\n\n따라서 런타임은 이벤트 기반 상태 전환과 제한된 렌더 주기를 혼합하여 재페인트 폭풍 없이 대화형 응답성을 유지합니다.\n",
|
|
483
483
|
"ko/tui/tui.md": "---\ntitle: 확장 기능 및 커스텀 도구를 위한 TUI 통합\ndescription: '확장 기능, 커스텀 도구, 커스텀 렌더러를 위한 TUI 통합 계약.'\nsidebar:\n order: 1\n label: 확장 기능 통합\ni18n:\n sourceHash: 47f8f2b2045e\n translator: machine\n---\n\n# 확장 기능 및 커스텀 도구를 위한 TUI 통합\n\n이 문서는 확장 UI, 커스텀 도구 UI, 커스텀 렌더러를 위해 `packages/coding-agent`와 `packages/tui`에서 사용하는 **현재** TUI 계약을 다룹니다.\n\n## 이 서브시스템이란\n\n런타임은 두 개의 레이어로 구성됩니다:\n\n- **렌더링 엔진 (`packages/tui`)**: 차분 터미널 렌더러, 입력 디스패치, 포커스, 오버레이, 커서 배치.\n- **통합 레이어 (`packages/coding-agent`)**: 확장/커스텀 도구 컴포넌트를 마운트하고, 키바인딩/테마를 연결하며, 에디터 상태를 복원합니다.\n\n## 모드별 런타임 동작\n\n| 모드 | `ctx.ui.custom(...)` 사용 가능 여부 | 비고 |\n| --- | --- | --- |\n| 인터랙티브 TUI | 지원됨 | 컴포넌트가 에디터 영역에 마운트되고 포커스되며, 반드시 `done(result)`를 호출하여 resolve해야 합니다. |\n| 백그라운드/헤드리스 | 비인터랙티브 | UI 컨텍스트는 no-op입니다 (`hasUI === false`). |\n| RPC 모드 | 지원되지 않음 | `custom()`은 `Promise<never>`를 반환하며 TUI 컴포넌트를 마운트하지 않습니다. |\n\n확장/도구가 비인터랙티브 모드에서 실행될 수 있다면, `ctx.hasUI` / `pi.hasUI`로 가드하세요.\n\n## 핵심 컴포넌트 계약 (`@f5-sales-demo/pi-tui`)\n\n`packages/tui/src/tui.ts`에서 정의합니다:\n\n```ts\nexport interface Component {\n render(width: number): string[];\n handleInput?(data: string): void;\n wantsKeyRelease?: boolean;\n invalidate(): void;\n}\n```\n\n`Focusable`은 별도입니다:\n\n```ts\nexport interface Focusable {\n focused: boolean;\n}\n```\n\n커서 동작은 `CURSOR_MARKER`를 사용합니다 (`getCursorPosition`이 아님). 포커스된 컴포넌트는 렌더링된 텍스트에 마커를 출력하고, `TUI`가 이를 추출하여 하드웨어 커서를 위치시킵니다.\n\n## 렌더링 제약 조건 (터미널 안전성)\n\n`render(width)` 출력은 반드시 터미널에 안전해야 합니다:\n\n1. **어떤 라인에서도 `width`를 초과하지 마세요**. 이미지가 아닌 라인이 오버플로우되면 렌더러가 에러를 던집니다.\n2. **문자열 길이가 아닌 시각적 너비를 측정하세요**: `visibleWidth()`를 사용합니다.\n3. **ANSI 인식 텍스트를 잘라내거나 줄바꿈하세요**: `truncateToWidth()` / `wrapTextWithAnsi()`를 사용합니다.\n4. **외부 소스의 탭/콘텐츠를 정제하세요**: `replaceTabs()`를 사용합니다 (그리고 coding-agent 렌더 경로의 상위 레벨 정제기도 사용합니다).\n\n최소 패턴:\n\n```ts\nimport { replaceTabs, truncateToWidth } from \"@f5-sales-demo/pi-tui\";\n\nrender(width: number): string[] {\n return this.lines.map(line => truncateToWidth(replaceTabs(line), width));\n}\n```\n\n## 입력 처리 및 키바인딩\n\n### 원시 키 매칭\n\n탐색 키와 조합에는 `matchesKey(data, \"...\")`를 사용하세요.\n\n### 사용자 구성 앱 키바인딩 존중\n\n확장 UI 팩토리는 `KeybindingsManager`(인터랙티브 모드)를 수신하므로, 키를 하드코딩하는 대신 매핑된 액션을 사용할 수 있습니다:\n\n```ts\nif (keybindings.matches(data, \"interrupt\")) {\n done(undefined);\n return;\n}\n```\n\n### 키 릴리스/반복 이벤트\n\n키 릴리스 이벤트는 컴포넌트에서 다음을 설정하지 않으면 필터링됩니다:\n\n```ts\nwantsKeyRelease = true;\n```\n\n필요한 경우 `isKeyRelease()` / `isKeyRepeat()`를 사용하세요.\n\n## 포커스, 오버레이, 커서\n\n- `TUI.setFocus(component)`는 해당 컴포넌트로 입력을 라우팅합니다.\n- 오버레이 API는 `TUI`에 존재하지만 (`showOverlay`, `OverlayHandle`), 인터랙티브 모드에서 확장 `ctx.ui.custom` 마운팅은 현재 에디터 컴포넌트 영역을 직접 교체합니다.\n- `custom(..., options?: { overlay?: boolean })` 옵션은 확장 타입에 존재하지만, 현재 인터랙티브 확장 마운팅에서는 이 옵션을 무시합니다.\n\n## 마운트 포인트 및 반환 계약\n\n## 1) 확장 UI (`ExtensionUIContext`)\n\n현재 시그니처 (`extensibility/extensions/types.ts`):\n\n```ts\ncustom<T>(\n factory: (\n tui: TUI,\n theme: Theme,\n keybindings: KeybindingsManager,\n done: (result: T) => void,\n ) => (Component & { dispose?(): void }) | Promise<Component & { dispose?(): void }>,\n options?: { overlay?: boolean },\n): Promise<T>\n```\n\n인터랙티브 모드에서의 동작 (`extension-ui-controller.ts`):\n\n- 에디터 텍스트를 저장합니다.\n- 에디터 컴포넌트를 사용자의 컴포넌트로 교체합니다.\n- 사용자의 컴포넌트에 포커스를 줍니다.\n- `done(result)` 호출 시: `component.dispose?.()`를 호출하고, 에디터와 텍스트를 복원하며, 에디터에 포커스를 주고, 프로미스를 resolve합니다.\n\n따라서 `done(...)`은 완료를 위해 필수입니다.\n\n## 2) 훅/커스텀 도구 UI 컨텍스트 (레거시 타이핑)\n\n`HookUIContext.custom`은 훅/커스텀 도구 타입에서 `(tui, theme, done)`으로 타이핑되어 있습니다.\n내부 인터랙티브 구현은 팩토리를 `(tui, theme, keybindings, done)`으로 호출합니다. JS 소비자는 추가 인자를 사용할 수 있으며, 타입 레벨 호환성은 여전히 3인자 레거시 시그니처를 반영합니다.\n\n커스텀 도구는 일반적으로 팩토리 스코프의 `pi.ui` 객체를 통해 동일한 UI 진입점을 사용한 다음, 선택된 값을 일반 도구 콘텐츠로 반환합니다:\n\n```ts\nasync execute(toolCallId, params, onUpdate, ctx, signal) {\n if (!pi.hasUI) {\n return { content: [{ type: \"text\", text: \"UI unavailable\" }] };\n }\n\n const picked = await pi.ui.custom<string | undefined>((tui, theme, done) => {\n const component = new MyPickerComponent(done, signal);\n return component;\n });\n\n return { content: [{ type: \"text\", text: picked ? `Picked: ${picked}` : \"Cancelled\" }] };\n}\n```\n\n## 3) 커스텀 도구 호출/결과 렌더러\n\n커스텀 도구와 확장 도구는 다음에서 컴포넌트를 반환할 수 있습니다:\n\n- `renderCall(args, theme)`\n- `renderResult(result, options, theme, args?)`\n\n`options`에는 현재 다음이 포함됩니다:\n\n- `expanded: boolean`\n- `isPartial: boolean`\n- `spinnerFrame?: number`\n\n이러한 렌더러는 `ToolExecutionComponent`에 의해 마운트됩니다.\n\n## 생명주기 및 취소\n\n- `dispose()`는 타입 레벨에서 선택 사항이지만, 타이머, 서브프로세스, 워처, 소켓, 또는 오버레이를 소유하는 경우 구현해야 합니다.\n- `done(...)`은 컴포넌트 플로우에서 정확히 한 번 호출되어야 합니다.\n- 취소 가능한 장시간 실행 UI의 경우, `CancellableLoader`와 `AbortSignal`을 결합하고 `onAbort`에서 `done(...)`을 호출하세요.\n\n취소 패턴 예시:\n\n```ts\nconst loader = new CancellableLoader(tui, theme.fg(\"accent\"), theme.fg(\"muted\"), \"Working...\");\nloader.onAbort = () => done(undefined);\nvoid doWork(loader.signal).then(result => done(result));\nreturn loader;\n```\n\n## 실제 커스텀 컴포넌트 예시 (확장 명령)\n\n```ts\nimport type { Component } from \"@f5-sales-demo/pi-tui\";\nimport { SelectList, matchesKey, replaceTabs, truncateToWidth } from \"@f5-sales-demo/pi-tui\";\nimport { getSelectListTheme, type ExtensionAPI } from \"@f5-sales-demo/xcsh\";\n\nclass Picker implements Component {\n list: SelectList;\n keybindings: any;\n done: (value: string | undefined) => void;\n\n constructor(\n items: Array<{ value: string; label: string }>,\n keybindings: any,\n done: (value: string | undefined) => void,\n ) {\n this.list = new SelectList(items, 8, getSelectListTheme());\n this.keybindings = keybindings;\n this.done = done;\n this.list.onSelect = item => this.done(item.value);\n this.list.onCancel = () => this.done(undefined);\n }\n\n handleInput(data: string): void {\n if (this.keybindings.matches(data, \"interrupt\")) {\n this.done(undefined);\n return;\n }\n this.list.handleInput(data);\n }\n\n render(width: number): string[] {\n return this.list.render(width).map(line => truncateToWidth(replaceTabs(line), width));\n }\n\n invalidate(): void {\n this.list.invalidate();\n }\n}\n\nexport default function extension(pi: ExtensionAPI): void {\n pi.registerCommand(\"pick-model\", {\n description: \"Pick a model profile\",\n handler: async (_args, ctx) => {\n if (!ctx.hasUI) return;\n\n const selected = await ctx.ui.custom<string | undefined>((tui, theme, keybindings, done) => {\n const items = [\n { value: \"fast\", label: theme.fg(\"accent\", \"Fast\") },\n { value: \"balanced\", label: \"Balanced\" },\n { value: \"quality\", label: \"Quality\" },\n ];\n return new Picker(items, keybindings, done);\n });\n\n if (selected) ctx.ui.notify(`Selected profile: ${selected}`, \"info\");\n },\n });\n}\n```\n\n## 주요 구현 파일\n\n- `packages/tui/src/tui.ts` — `Component`, `Focusable`, 커서 마커, 포커스, 오버레이, 입력 디스패치.\n- `packages/tui/src/utils.ts` — 너비/잘라내기/정제 프리미티브.\n- `packages/tui/src/keys.ts` / `keybindings.ts` — 키 파싱 및 구성 가능한 액션 매핑.\n- `packages/coding-agent/src/modes/controllers/extension-ui-controller.ts` — 확장/훅/커스텀 도구 UI의 인터랙티브 마운팅/언마운팅.\n- `packages/coding-agent/src/extensibility/extensions/types.ts` — 확장 UI 및 렌더러 계약.\n- `packages/coding-agent/src/extensibility/hooks/types.ts` — 훅 UI 계약 (레거시 custom 시그니처).\n- `packages/coding-agent/src/extensibility/custom-tools/types.ts` — 커스텀 도구 execute/render 계약.\n- `packages/coding-agent/src/modes/components/tool-execution.ts` — `renderCall`/`renderResult` 컴포넌트 마운팅 및 부분 상태 옵션.\n- `packages/coding-agent/src/tools/context.ts` — 도구 UI 컨텍스트 전파 (`hasUI`, `ui`).\n",
|
|
484
|
-
"plans/provider-agnostic-dynamic-model-routing.md": "# Provider-Agnostic Dynamic Model Routing for xcsh\n\n## 1. Summary and decisions\n\nImplement a routing coordinator at the AgentSession boundary. It will profile each top-level task, select an appropriate model tier, account for context capacity, optionally dispatch bounded read-only subagents, and escalate only from validated evidence.\n\nThe router will complement—not replace—existing model roles, retry fallbacks, context promotion, compaction, and task execution.\n\nKey decisions:\n\n- Routing applies to any provider with an explicit tier-pool definition.\n- Ship reviewed presets; never infer tiers from arbitrary model names.\n- Initial presets:\n - OpenAI: Luna → utility, Terra → balanced, Sol → frontier.\n - Anthropic: Haiku → utility, Sonnet → balanced, Opus → frontier.\n - LiteLLM: separate OpenAI and Anthropic pools under the same litellm provider.\n\n- Direct OpenAI and Anthropic use the same router through provider-specific pools.\n- Untiered providers and models outside a pool pass through unchanged.\n- Routing is family-sticky by default. Cross-family/provider routing requires an explicitly declared mixed pool or the existing retry fallback mechanism.\n- Default mode is off; rollout proceeds through explicit shadow, then opt-in auto.\n- A manual model selection is a hard pin until `/route auto`.\n- Upgrades occur immediately; downshifts require two consecutive lower-tier profiles.\n- The router chooses once before a tool loop and remains fixed during it. Existing retry fallback and context-overflow promotion remain emergency exceptions.\n- Autonomous delegation is limited to read-only work, at most three subtasks, with no recursive autonomous delegation.\n- No separate routing dollar budget. Existing concurrency, recursion, authentication, safety, and approval controls remain authoritative.\n\n## 2. Architecture and public interfaces\n\n### Capability and configuration model\n\nAdd a routing settings group:\n\n```yaml\nrouting:\n mode: off # off | shadow | auto\n profiler: hybrid # rules | hybrid\n familyPolicy: sticky # sticky | configured-mixed\n delegation: read-only # off | read-only\n delegationMaxTasks: 3\n downshiftAfterTurns: 2\n tierEffort:\n utility: low\n balanced: medium\n frontier: high\n pools: {} # Overrides or additional explicit pools\n disabledPresets: []\n```\n\nEach pool contains ordered, fully qualified model selectors for utility, balanced, and frontier. Selectors must be unique within a pool. Non-mixed pools must use one provider; mixed pools must explicitly opt in.\n\nBuilt-in pools:\n\n- `openai/gpt-5.6`: direct OpenAI models (e.g. `gpt-4o-mini` for utility, `gpt-4o` for balanced, `o3-mini`/`gpt-4.5-preview` for frontier).\n- `anthropic/claude`: direct Anthropic models (e.g. `claude-3-5-haiku-latest` for utility, `claude-3-5-sonnet-latest` for balanced, `claude-3-opus-latest` for frontier).\n- `litellm/openai`: internal LiteLLM Luna, Terra, and Sol (`gpt-5.6-luna`, `gpt-5.6-terra`, `gpt-5.6-sol`).\n- `litellm/anthropic`: internal LiteLLM Haiku, Sonnet, and Opus.\n\nAt implementation start, pin the exact Anthropic and LiteLLM IDs from authenticated model inventories and official documentation. Missing models degrade the pool; a pool with fewer than two available tiers is ineligible and passes through.\n\nConfiguration precedence:\n\n1. Explicit user pool override.\n2. Reviewed built-in preset.\n3. No pool and no name inference.\n\nCandidate selection intersects the pool with authenticated, enabled, runtime-discovered, and `--models`-scoped models.\n\n### Core types\n\nIntroduce public routing types:\n\n```typescript\ntype RoutingTier = \"utility\" | \"balanced\" | \"frontier\";\ntype RoutingMode = \"off\" | \"shadow\" | \"auto\";\ntype RoutingDecisionSource = \"rules\" | \"classifier\" | \"hybrid\";\n\ninterface TaskProfile {\n complexityScore: number;\n desiredTier: RoutingTier;\n confidence: number;\n reasons: RoutingReasonCode[];\n requiredCapabilities: {\n vision: boolean;\n tools: boolean;\n minimumContextTokens: number;\n };\n delegation?: ReadOnlyDelegationPlan;\n}\n\ninterface RoutingDecision {\n epochId: string;\n mode: RoutingMode;\n poolId?: string;\n anchorModel: string;\n desiredTier?: RoutingTier;\n effectiveTier?: RoutingTier;\n selectedModel?: string;\n source?: RoutingDecisionSource;\n applied: boolean;\n reasons: RoutingReasonCode[];\n}\n\ninterface RoutingOutcome {\n epochId: string;\n status: \"accepted\" | \"rejected\";\n evidence: RoutingOutcomeEvidence[];\n safeToContinue?: boolean;\n}\n```\n\nExtend `AgentSession` with:\n\n- `getRoutingStatus()`\n- `setRoutingMode(mode)`\n- `clearRoutingPin()`\n- `recordRoutingOutcome(outcome)`\n- model-switch source metadata distinguishing manual, routing, retry fallback, and context promotion.\n\nExpose session events:\n\n- `routing_decision`\n- `routing_applied`\n- `routing_delegated`\n- `routing_escalated`\n- `routing_skipped`\n\nEvents include provider, pool, tier, model, sanitized reason codes, context estimate, decision duration, classifier usage, and token usage. They must never contain prompt text, credentials, headers, or tool output.\n\nPersist mode overrides, pins, active pool, tier, downshift streak, and escalation floor as non-context session custom entries. On session resume, reset, or branch switching, downshift streak counters and escalation floors are contextually re-evaluated against the active turn branch history rather than relying on flat global custom entries.\n\n### Profiling and resolution\n\nDeterministic profiling starts at score 30:\n\n- +25: prior validated rejection.\n- +20: architecture, migration, security analysis, or explicit deep-review intent.\n- +15: mutation spanning multiple targets/repositories or at least three independent deliverables.\n- +10: image/special capability requirement.\n- +10: context usage at least 60%; +20 at least 80%.\n- +10: material ambiguity or missing acceptance conditions.\n- -20: exact, single-step read, extraction, classification, summarization, or mechanical operation involving at most one target.\n\nClamp to 0–100:\n\n- 0–30: utility\n- 31–69: balanced\n- 70–100: frontier\n\nHard capability, context, safety, and validated-outcome floors cannot be lowered by the classifier.\n\nIn hybrid mode, an ambiguous balanced profile invokes a one-shot structured classifier through the utility model in the active pool. It receives the bounded current request and structured metadata—not the conversation transcript—and has no tools. Confidence below 0.75, timeout, malformed output, or unavailable utility tier resolves to balanced.\n\nContext resolution uses the existing context estimator and compaction reserve. A candidate is eligible only when:\n\n`estimated input + max(existing reserveTokens, 15% of candidate context) < candidate contextWindow`\n\nThe resolver searches the desired tier, then higher tiers. It never selects a lower tier than the required quality/capability floor.\n\n### Runtime flow\n\n```mermaid\nflowchart LR\n A[Top-level prompt] --> B{Mode, pin, pool and fallback gate}\n B -->|Ineligible| C[Pass through and emit skipped]\n B -->|Eligible| D[Deterministic task profile]\n D --> E{Ambiguous and hybrid?}\n E -->|Yes| F[Utility structured classifier]\n E -->|No| G[Capability and context floors]\n F --> G\n G --> H[Pool resolver and downshift hysteresis]\n H --> I{Mode}\n I -->|Shadow| J[Record proposed route]\n I -->|Auto| K[Temporary sourced model switch]\n K --> L[Optional read-only delegation]\n J --> M[Normal agent loop]\n L --> M\n M --> N[Validated outcome]\n N -->|Accepted| O[Clear escalation floor]\n N -->|Rejected and safe| P[One higher-tier continuation]\n N -->|Rejected and unsafe| Q[Record next-turn tier floor]\n```\n\nThe coordinator runs after retry-fallback restoration but before API-key validation and compaction. It skips while an existing retry fallback remains active.\n\nA rejected, trusted outcome may cause at most one post-loop escalation continuation. It switches one tier upward, continues from the existing transcript and tool results, and does not replay the prompt or completed actions. Unsafe continuations only set a floor for the next turn. Free-form model self-assessment cannot trigger escalation.\n\nAutonomous delegation requires at least two independent read-only information targets. The classifier may return two or three schema-validated subtasks. Delegates:\n\n- Use only read, grep, find, ls, lsp, and approved read-only search tools.\n- Receive a minimal task/context pack.\n- Use utility by default and balanced only when their own profile requires it.\n- Cannot spawn children.\n- Run through existing task concurrency and cancellation controls.\n- Return results as attributed context for the parent.\n- Never run in shadow mode.\n\nCommands:\n\n- `/route status`: display effective mode, eligibility, pool, pin, active tier/model, downshift streak, and last decision.\n- `/route off`: stop future routing and retain the current model.\n- `/route shadow`: calculate and report decisions without switching or delegating.\n- `/route auto`: clear the manual model pin and route within the pool containing the current model.\n\n## 3. Acceptance matrix and rollout\n\nRequired behavior:\n\n- LiteLLM can independently route its OpenAI and Anthropic families without crossing them.\n- Direct OpenAI and Anthropic use the same generic coordinator and pool contract.\n- An untiered provider, unknown model, unavailable pool, or single-tier pool never changes models.\n- Off and shadow modes never change the active model or launch delegates.\n- Manual model selection remains fixed until `/route auto`.\n- Context and capability requirements can raise but never lower the selected tier.\n- A downshift requires two consecutive qualifying turns.\n- Auxiliary classification is skipped for deterministic profiles.\n- Router-controlled model choice remains fixed through the normal tool loop.\n- Retry fallback and context promotion retain their existing behavior.\n- Autonomous delegates are read-only, bounded, non-recursive, cancellable, and accounted for.\n- Escalation requires trusted validation evidence and never blindly replays a turn.\n- Session resume reproduces the prior routing state without adding routing metadata to model context.\n- All route decisions are observable without leaking prompt or credential data.\n",
|
|
484
|
+
"plans/provider-agnostic-dynamic-model-routing.md": "# Provider-Agnostic Dynamic Model Routing\n\n## Executive status\n\nThe production router is implemented at the `AgentSession` boundary. It supports explicit provider-qualified pools, utility/balanced/frontier tiers, off/shadow/auto modes, deterministic and hybrid classification, context eligibility, hysteresis, manual pins, escalation and rollback, read-only delegation, persistence, telemetry, and route commands.\n\nThe authenticated routing-matrix harness has been redesigned under issue #3114. Its deterministic and mocked-network evidence is authoritative for code paths, but the project is not empirically complete until a clean exact-`origin/main` report proves all five required lanes through real authenticated inference.\n\nCI, unit tests, dry runs, bundled catalog entries, missing-credential BLOCKED results, and completion-auditor statements are not live acceptance evidence.\n\n## Scope and non-goals\n\nThe canonical profile requires direct OpenAI, direct Anthropic, LiteLLM OpenAI-family, LiteLLM Anthropic-family, and an explicitly configured Google Vertex pool. Four scenarios and three repetitions produce 60 measured rows; one warmup per lane produces five warmup rows.\n\nOther providers may opt in only through explicit capability and tier-pool configuration. Untiered providers remain on their selected model. Model names never imply tiers.\n\nThis work does not add translations, infer gateway upstream providers, treat Azure credentials as direct OpenAI credentials, substitute unavailable tier models, or run paid inference before deterministic gates pass.\n\n## Runtime architecture\n\n```text\nAgentSession\n -> RoutingCoordinator\n -> deterministic/hybrid profiler\n -> explicit pool and live model candidates\n -> capability/context resolver\n -> state machine and hysteresis\n -> model switch or shadow decision\n -> bounded read-only delegation\n -> outcome, escalation, rollback\n -> persistence and sanitized telemetry\n```\n\nManual selection is a hard pin until `/route auto`. Context and capability floors can promote but not demote. Downshifts require consecutive lower-tier profiles. Retry fallback and context-overflow handling remain emergency mechanisms. Delegation is read-only, bounded, non-recursive, cancellable, and token-accounted.\n\nThe harness is a separate evidence pipeline:\n\n```text\nCLI profile\n -> lane capabilities\n -> credential resolvers\n -> provider inventory adapters\n -> per-lane tier reconciliation\n -> warmup rows\n -> measured routing and inference rows\n -> evidence classifier\n -> schema validation, recursive redaction, secret scan\n -> external report and hash receipt\n```\n\n## Provider capability model\n\n| Lane | Client transport | Family | Pool | Inventory | Authentication | Attribution |\n| --- | --- | --- | --- | --- | --- | --- |\n| `openai` | OpenAI Responses | OpenAI | `openai/gpt-5.6` | Authenticated OpenAI `/v1/models` | Existing xcsh direct OpenAI resolver | Endpoint, request, client, raw response model |\n| `anthropic` | Anthropic Messages | Anthropic | `anthropic/claude` | Authenticated Anthropic `/v1/models` | Existing xcsh API-key/OAuth resolver with LiteLLM fallback disabled | Endpoint, request, client, raw response model |\n| `litellm-openai` | OpenAI-compatible | OpenAI | `litellm/openai` | Its own authenticated LiteLLM endpoint | Lane-specific key/base URL, then xcsh LiteLLM resolver | Endpoint, request, client, raw response model; upstream provider may be unproven |\n| `litellm-anthropic` | Anthropic Messages-compatible | Anthropic | `litellm/anthropic` | Its own authenticated LiteLLM endpoint | Separate Anthropic-compatible base URL and LiteLLM credential | Endpoint, request, client, raw response model; upstream provider may be unproven |\n| `google-vertex` | Vertex | Google | `google-vertex/gemini` | Authenticated Model Garden publisher list | Real ADC access token and project/location | Endpoint, request, and client; the current SDK stream does not expose a response-reported model |\n\nLane identity is independent of provider name. This keeps direct Anthropic and Anthropic-over-LiteLLM distinct.\n\n`AssistantMessage.provider` and `model` remain client/request fields for compatibility. Optional `responseAttribution` records server evidence only. Missing server evidence remains absent and fails lanes that declare response-model proof mandatory.\n\n## Inventory architecture\n\nFour inventories remain distinct:\n\n1. Bundled catalog metadata, which can inform display, context, and cost only.\n2. Explicit configured utility/balanced/frontier models.\n3. Models returned by the lane's authenticated live endpoint.\n4. Eligible candidates: configured tiers intersected with that same lane's live inventory and runtime constraints.\n\nAll three tiers must exist for every canonical lane. Candidate inventories are never combined across endpoints.\n\nInventory states are `AVAILABLE`, `BLOCKED_AUTH`, `BLOCKED_NETWORK`, `BLOCKED_RATE_LIMIT`, `UNSUPPORTED_DISCOVERY`, `FAIL_SCHEMA`, `FAIL_EMPTY_INVENTORY`, and `FAIL_MISSING_TIERS`. Dry-run inventory is `SIMULATED`. No failed live state falls back to bundled success.\n\n## Evidence and benchmark contract\n\nEvidence is recorded separately for requested model, routing-selected tier/model, client provider, endpoint fingerprint, server-reported response model, server-reported upstream provider when available, stop reason, usage, exact content, and multimodal consumption.\n\nLiteLLM authority is capability-relative: a report may establish endpoint, request, client, response model, tier, usage, and content while stating that the true gateway upstream provider is unproven. The report must never infer it.\n\nStatuses:\n\n- `PASS`: every required assertion for the row passed.\n- `FAIL`: deterministic routing, schema, attribution, stop, usage, content, report, or security behavior failed.\n- `BLOCKED`: authentication, network, rate limit, or provider availability prevented evaluation.\n- `SKIPPED_UNTIERED`: optional provider has no configured pool; illegal for canonical required lanes.\n- `SIMULATED`: dry-run row; never counted as PASS.\n\nDefault counts are five warmups and 60 measured rows. `matrixComplete` requires exact counts and every live inventory, warmup, and measured row PASS. `authoritative` additionally requires non-dry execution, clean exact final HEAD, positive usage, declared response attribution, schema validation, recursive redaction, and secret-scan success.\n\nExit codes are 0 for successful requested-mode execution, 1 for behavior/schema/security failure, 2 for an environmentally BLOCKED or incomplete required matrix, and 64 for invalid CLI configuration. A successful dry run may exit 0 but remains non-complete and non-authoritative.\n\nThe utility, balanced, and frontier prompts contain deterministic profiler signals and require marker-only output. The multimodal fixture contains a visible code absent from the prompt; the model must inspect the image to produce the expected answer.\n\n## TDD and staged UAT\n\nEvery behavior is introduced with a failing focused test, minimal implementation, focused green run, coding-agent suite, type check, lint, and dry run.\n\nMocked HTTP coverage includes successful provider schemas, empty inventory, missing tiers, 401, 403, 404, 429, 500, malformed JSON, DNS/network failure, timeout/abort, ADC, OAuth/API-key headers, no bundled fallback, redaction, attribution gaps, warmup failures, and partial/all-BLOCKED contracts.\n\nPaid UAT is staged:\n\n1. Stage A: unit tests, mocked HTTP, type/lint checks, dry run, schema validation, and secret tests.\n2. Stage B: LiteLLM OpenAI utility with one warmup and one repetition; then balanced/frontier only after success.\n3. Stage C: both LiteLLM lanes, one warmup and one repetition per scenario.\n4. Stage D: final clean `origin/main`, all five lanes, five warmups, 60 measured rows, recursive scan.\n\nThe complete paid matrix is never used as a debugging loop.\n\n## Reporting, security, and rollout\n\nSchema-v2 reports record Git state, parameters, capability declarations, sanitized endpoint fingerprints, inventory reconciliation, first-class warmups and measurements, timestamps, durations, usage, attribution sources, counts, authority, and security state. Reports are written outside the repository with mode 0600.\n\nThe writer recursively redacts resolved secrets, credential-shaped fields, authorization values, URL credentials, query tokens, and credential paths. It validates the final candidate against the checked-in schema, scans the exact bytes with Gitleaks, atomically publishes unchanged bytes, and writes a SHA-256 receipt. Scan failure publishes no report and exits 1.\n\nOperational rollout remains off by default, then shadow, then per-lane automatic enablement beginning with the two LiteLLM lanes. Monitor decisions, reason codes, latency, token/cost distribution, failures, BLOCKED rate, escalation, and rollback. Provider outages never create inferred substitutes. `/route off` or per-lane disablement is the safe rollback.\n\n## Tracked completion ledger\n\n- [x] RM-01 provider-specific inventory adapters and credential resolvers\n - Implementation target: capability registry, OpenAI-compatible, Anthropic, LiteLLM, and Vertex Model Garden adapters; AuthStorage/API-key/OAuth/ADC resolution.\n - Failing test: provider schema, header, missing credential, and ADC cases in `bench-routing-matrix.test.ts`.\n - Verification command: `bun test test/bench-routing-matrix.test.ts --max-concurrency 2`.\n - Required artifact: mocked request/response assertions with no real inference.\n - Completion claim: all adapters use provider-specific URLs, schemas, and authentication; none substitutes a bundled inventory.\n - Reviewer evidence: 22 focused tests pass, including OpenAI, Anthropic, two independent LiteLLM endpoints, and Vertex.\n- [x] RM-02 live, bundled, configured, and eligible inventory separation\n - Implementation target: explicit inventory states, configured-tier reconciliation, and lane-qualified candidate construction.\n - Failing test: empty inventory, missing tier, separate endpoint, and failed-discovery cases.\n - Verification command: `bun test test/bench-routing-matrix.test.ts --max-concurrency 2`.\n - Required artifact: report inventory rows with discovered IDs, missing tiers, eligible candidates, and endpoint fingerprints.\n - Completion claim: only the authenticated lane inventory can make a configured tier eligible.\n - Reviewer evidence: focused tests prove missing tiers fail and failed discovery never falls back.\n- [x] RM-03 typed response extraction and exact-output scenarios\n - Implementation target: content-block parser and utility/balanced/frontier prompts that require silent reasoning and one exact marker.\n - Failing test: text, thinking, tool/error, invalid block, whitespace, and task-profile cases.\n - Verification command: `bun test test/bench-routing-matrix.test.ts --max-concurrency 2`.\n - Required artifact: measured rows with sanitized reason codes and exact marker results.\n - Completion claim: extraction deterministically joins text blocks and rejects unexpected behavioral blocks.\n - Reviewer evidence: content extraction and all four tier-profile tests pass.\n- [x] RM-04 genuine response attribution\n - Implementation target: optional server-evidence fields on `AssistantMessage`, populated from OpenAI and Anthropic response bodies only.\n - Failing test: missing response model and a server model different from the request.\n - Verification command: `bun test test/response-attribution.test.ts test/anthropic-stream-envelope.test.ts --max-concurrency 2` in `packages/ai`.\n - Required artifact: report fields identify evidence source or explicitly omit unavailable evidence.\n - Completion claim: requested model is never reused as response-reported model; Vertex and gateway upstream limitations are declared.\n - Reviewer evidence: six focused transport tests pass and capability-relative classifier tests reject missing required evidence.\n- [x] RM-05 first-class warmup and contract integration\n - Implementation target: one row per warmup with model/provider/stop/usage checks, expected counts, completeness, authority, and exit status.\n - Failing test: behavioral warmup failure plus partial/all-BLOCKED matrices.\n - Verification command: `bun test test/bench-routing-matrix.test.ts --max-concurrency 2`.\n - Required artifact: five default warmup rows and 60 default measured rows.\n - Completion claim: any required warmup FAIL/BLOCKED makes the matrix incomplete, non-authoritative, and nonzero for live execution.\n - Reviewer evidence: contract tests and the 5/60 dry-run count pass.\n- [x] RM-06 image-derived multimodal validation\n - Implementation target: deterministic embedded PNG bearing `ROUTE-7C` and a prompt whose expected marker is absent from its text.\n - Failing test: assert typed image content, expected MIME type, and marker absence from the prompt.\n - Verification command: `bun test test/bench-routing-matrix.test.ts --max-concurrency 2`.\n - Required artifact: visual scenario row with the exact image-derived marker.\n - Completion claim: a passing visual response must obtain the answer from image content.\n - Reviewer evidence: fixture contract and multimodal tier profiling tests pass.\n- [x] RM-07 mocked network and failure taxonomy\n - Implementation target: status mapping for 401/403/404/429/500, malformed/empty schemas, DNS/network, timeout/abort, and redacted diagnostics.\n - Failing test: one deterministic mocked-network case per state and authentication variant.\n - Verification command: `bun test test/bench-routing-matrix.test.ts --max-concurrency 2`.\n - Required artifact: focused test output containing 22 PASS and zero FAIL.\n - Completion claim: environmental blocks and behavioral/schema failures remain distinguishable without leaking request details.\n - Reviewer evidence: focused suite passes with 67 assertions.\n- [x] RM-08 report schema and security publication gate\n - Implementation target: schema v2, recursive redaction, 0600 temporary file, Gitleaks scan, atomic rename, and SHA-256 receipt.\n - Failing test: malformed report shape and nested secret/header/URL/query/path values.\n - Verification command: `bun run bench:routing-matrix --dry-run` and `git diff --check`.\n - Required artifact: out-of-repository report plus `.sha256` receipt.\n - Completion claim: unvalidated or secret-scan-failing bytes are never published as the final report.\n - Reviewer evidence: dry run published `/tmp/routing-matrix-reports/2026-08-11T13-44-10-446Z/routing-matrix-report.json`; schema and scan passed.\n- [x] RM-09 deterministic Stage A gate\n - Implementation target: focused tests, full AI and coding-agent suites, formatting, type checking, dry run, and secret scan.\n - Failing test: the pre-implementation harness tests failed until live-path exports and behavior existed.\n - Verification command: `bun run check`, AI focused tests, and `bun run test` in `packages/coding-agent`.\n - Required artifact: command logs and dry-run report.\n - Completion claim: Stage A is green before any paid call is attempted.\n - Reviewer evidence: coding-agent 6,529 pass/559 skip/0 fail; AI package and focused suites pass; check and dry run exit zero.\n- [ ] RM-10 Stage B authenticated LiteLLM OpenAI smoke\n - Implementation target: `litellm-openai`, one warmup, utility at one repetition, then balanced/frontier only after utility passes.\n - Failing test: the first authenticated smoke must expose any endpoint, inventory, attribution, or inference defect.\n - Verification command: `bun run bench:routing-matrix --lanes litellm-openai --scenarios utility-greeting --warmups 1 --repetitions 1`.\n - Required artifact: clean, redacted smoke report outside the repository.\n - Completion claim: live inventory, warmup, and utility measurement all PASS with valid usage and required attribution.\n - Reviewer evidence: blocked on 2026-08-11 because neither LiteLLM endpoint nor credential is configured; no paid call was made.\n- [ ] RM-11 Stage C two-family LiteLLM matrix\n - Implementation target: both LiteLLM lanes, all required scenarios, one warmup and one repetition.\n - Failing test: cross-family pool/model selection, independent inventory, marker, or attribution mismatch fails its row.\n - Verification command: `bun run bench:routing-matrix --lanes litellm-openai,litellm-anthropic --warmups 1 --repetitions 1`.\n - Required artifact: redacted two-lane report and hash receipt.\n - Completion claim: 2/2 warmups and 8/8 measured rows PASS with no FAIL/BLOCKED.\n - Reviewer evidence: pending RM-10 and authorized credentials.\n- [ ] RM-12 Stage D authoritative five-lane matrix\n - Implementation target: all canonical required lanes, one warmup, four scenarios, and three measured repetitions.\n - Failing test: any missing inventory tier, warmup, row, usage, required attribution, or scan makes the run non-authoritative.\n - Verification command: `bun run bench:routing-matrix` on clean current `origin/main`.\n - Required artifact: redacted exact-head report with five warmups, 60 measurements, and hash receipt.\n - Completion claim: `passedWarmups === 5`, `passedMeasured === 60`, `matrixComplete === true`, `authoritative === true`, exit 0.\n - Reviewer evidence: pending RM-10/RM-11, all five authorized credentials, and merge to current main.\n- [ ] RM-13 final exact-head empirical review\n - Implementation target: independently compare report SHA, Git SHA/cleanliness, schema, counts, evidence limitations, and recursive secret scan.\n - Failing test: any mismatch between report claims and current main rejects completion.\n - Verification command: schema validation, `sha256sum -c`, Gitleaks directory scan, and GitHub required-check review.\n - Required artifact: reviewer acceptance linked to the exact authoritative report.\n - Completion claim: project is empirically complete only after the reviewer accepts the clean exact-head authenticated evidence.\n - Reviewer evidence: pending RM-12.\n\nThe unchecked items require configured authorized credentials and real inference. Until they pass, the project remains implemented but empirically incomplete.\n\n## Risks and unresolved product decisions\n\n- Canonical availability risk: any configured tier absent from a live inventory blocks that entire required lane; changing the canonical tier is a reviewed product decision, not a harness fallback.\n- Attribution limitation: LiteLLM may not expose its true upstream provider and the current Vertex SDK stream does not report a serving model. Authority is therefore capability-relative and must retain these explicit omissions.\n- Credential topology: the two LiteLLM families require independently addressable inventory/inference configuration even if an installation chooses to share one key.\n- Vertex inventory compatibility: Model Garden permissions and regional availability may differ from inference permissions; authenticated UAT must confirm the chosen project/location.\n- Cost control: Stage D is prohibited as a debugging loop and remains gated on successful Stages B and C.\n- Final product decision: reviewers must explicitly accept capability-relative attribution, or require gateway/SDK changes that expose stronger upstream evidence before declaring RM-13 complete.\n",
|
|
485
485
|
"pt-br/configuration/blob-artifact-architecture.md": "---\ntitle: Arquitetura de Armazenamento de Blobs e Artefatos\ndescription: >-\n Armazenamento de blobs endereçável por conteúdo e registro de artefatos para\n mídia de sessão, capturas de tela e saídas de ferramentas.\nsidebar:\n order: 7\n label: Armazenamento de blobs e artefatos\ni18n:\n sourceHash: 7a8855b81324\n translator: machine\n---\n\n# Arquitetura de armazenamento de blobs e artefatos\n\nEste documento descreve como o coding-agent armazena payloads grandes/binários fora do JSONL de sessão, como a saída truncada de ferramentas é persistida e como as URLs internas (`artifact://`, `agent://`) resolvem de volta para os dados armazenados.\n\n## Por que dois sistemas de armazenamento existem\n\nO runtime utiliza dois mecanismos de persistência diferentes para diferentes formatos de dados:\n\n- **Blobs endereçados por conteúdo** (`blob:sha256:<hash>`): armazenamento global orientado a binários, usado para externalizar payloads base64 de imagens grandes das entradas de sessão persistidas.\n- **Artefatos com escopo de sessão** (arquivos sob `<arquivoDeSessão-sem-.jsonl>/`): arquivos de texto por sessão usados para saídas completas de ferramentas e saídas de subagentes.\n\nEles são intencionalmente separados:\n\n- o armazenamento de blobs otimiza a deduplicação e referências estáveis por hash de conteúdo,\n- o armazenamento de artefatos otimiza ferramentas de sessão append-only e recuperação por humanos/ferramentas através de IDs locais.\n\n## Limites de armazenamento e layout em disco\n\n## Limite do armazenamento de blobs (global)\n\n`SessionManager` constrói `BlobStore(getBlobsDir())`, então os arquivos de blob ficam em um diretório global compartilhado de blobs (não em uma pasta de sessão).\n\nNomenclatura de arquivos de blob:\n\n- caminho do arquivo: `<blobsDir>/<sha256-hex>`\n- sem extensão\n- string de referência armazenada nas entradas: `blob:sha256:<sha256-hex>`\n\nImplicações:\n\n- o mesmo conteúdo binário entre sessões resolve para o mesmo hash/caminho,\n- escritas são idempotentes no nível do conteúdo,\n- blobs podem sobreviver a qualquer arquivo de sessão individual.\n\n## Limite de artefatos (local à sessão)\n\n`ArtifactManager` deriva o diretório de artefatos a partir do caminho do arquivo de sessão:\n\n- arquivo de sessão: `.../<timestamp>_<sessionId>.jsonl`\n- diretório de artefatos: `.../<timestamp>_<sessionId>/` (remove `.jsonl`)\n\nOs tipos de artefatos compartilham este diretório:\n\n- arquivos de saída de ferramenta truncada: `<numericId>.<toolType>.log` (para `artifact://`)\n- arquivos de saída de subagente: `<outputId>.md` (para `agent://`)\n\n## Esquemas de alocação de IDs e nomes\n\n## IDs de blob: hash de conteúdo\n\n`BlobStore.put()` computa SHA-256 sobre os bytes binários brutos e retorna:\n\n- `hash`: digest hexadecimal,\n- `path`: `<blobsDir>/<hash>`,\n- `ref`: `blob:sha256:<hash>`.\n\nNenhum contador local de sessão é utilizado.\n\n## IDs de artefato: inteiro monotônico local à sessão\n\n`ArtifactManager` escaneia os arquivos de artefato `*.log` existentes no primeiro uso para encontrar o ID numérico máximo existente e define `nextId = max + 1`.\n\nComportamento de alocação:\n\n- formato do arquivo: `{id}.{toolType}.log`\n- IDs são strings sequenciais (`\"0\"`, `\"1\"`, ...)\n- a retomada não sobrescreve artefatos existentes porque o escaneamento acontece antes da alocação.\n\nSe o diretório de artefatos estiver ausente, o escaneamento retorna lista vazia e a alocação começa do `0`.\n\n## IDs de saída de agente (`agent://`)\n\n`AgentOutputManager` aloca IDs para saídas de subagentes como `<index>-<requestedId>` (opcionalmente aninhado sob prefixo pai, por exemplo, `0-Parent.1-Child`). Ele escaneia arquivos `.md` existentes na inicialização para continuar a partir do próximo índice na retomada.\n\n## Fluxo de dados de persistência\n\n## 1) Caminho de reescrita na persistência de entradas de sessão\n\nAntes que as entradas de sessão sejam escritas (`#rewriteFile` / persistência incremental), `SessionManager` chama `prepareEntryForPersistence()` (via `truncateForPersistence`).\n\nComportamentos-chave:\n\n1. **Truncamento de strings grandes**: strings superdimensionadas são cortadas e sufixadas com `\"[Session persistence truncated large content]\"`.\n2. **Remoção de campos transientes**: `partialJson` e `jsonlEvents` são removidos das entradas persistidas.\n3. **Externalização de imagens para blobs**:\n - aplica-se apenas a blocos de imagem em arrays `content`,\n - apenas quando `data` não é já uma referência de blob,\n - apenas quando o comprimento do base64 é pelo menos o limite (`BLOB_EXTERNALIZE_THRESHOLD = 1024`),\n - substitui base64 inline por `blob:sha256:<hash>`.\n\nIsso mantém o JSONL de sessão compacto enquanto preserva a recuperabilidade.\n\n## 2) Caminho de reidratação no carregamento de sessão\n\nAo abrir uma sessão (`setSessionFile`), após as migrações, `SessionManager` executa `resolveBlobRefsInEntries()`.\n\nPara cada bloco de imagem de message/custom-message com `blob:sha256:<hash>`:\n\n- lê os bytes do blob a partir do armazenamento de blobs,\n- converte os bytes de volta para base64,\n- modifica a entrada em memória para inline base64 para consumidores em tempo de execução.\n\nSe o blob estiver ausente:\n\n- `resolveImageData()` registra um aviso,\n- retorna a string de referência original sem alteração,\n- o carregamento continua (sem crash).\n\n## 3) Caminho de despejo/truncamento de saída de ferramenta\n\n`OutputSink` alimenta a saída em streaming no bash/python/ssh e executores relacionados.\n\nComportamento:\n\n1. Cada chunk é sanitizado e adicionado ao buffer de cauda em memória.\n2. Quando os bytes em memória excedem o limite de despejo (`DEFAULT_MAX_BYTES`, 50KB), o sink marca a saída como truncada.\n3. Se um caminho de artefato está disponível, o sink abre um escritor de arquivo e escreve:\n - o conteúdo já em buffer uma vez,\n - todos os chunks subsequentes.\n4. O buffer em memória é sempre aparado para a janela de cauda para exibição.\n5. `dump()` retorna um resumo incluindo `artifactId` apenas quando o file sink foi criado com sucesso.\n\nEfeito prático:\n\n- UI/retorno de ferramenta mostra a cauda truncada,\n- a saída completa é preservada no arquivo de artefato e referenciada como `artifact://<id>`.\n\nSe a criação do file sink falhar (erro de I/O, caminho ausente, etc.), o sink silenciosamente faz fallback para truncamento somente em memória; a saída completa não é persistida.\n\n## Modelo de acesso por URL\n\n## Referências `blob:`\n\n`blob:sha256:<hash>` é uma referência de persistência dentro dos payloads de entradas de sessão, não um esquema de URL interno tratado pelo roteador. A resolução é feita pelo `SessionManager` durante o carregamento da sessão.\n\n## `artifact://<id>`\n\nTratado pelo `ArtifactProtocolHandler`:\n\n- requer diretório de artefatos de sessão ativo,\n- o ID deve ser numérico,\n- resolve por correspondência do prefixo do nome de arquivo `<id>.`,\n- retorna texto bruto (`text/plain`) do arquivo `.log` correspondente,\n- quando ausente, o erro inclui lista de IDs de artefatos disponíveis.\n\nComportamento com diretório ausente:\n\n- se o diretório de artefatos não existir, lança `No artifacts directory found`.\n\n## `agent://<id>`\n\nTratado pelo `AgentProtocolHandler` sobre `<artifactsDir>/<id>.md`:\n\n- a forma simples retorna texto markdown,\n- as formas `/path` ou `?q=` realizam extração JSON,\n- extração por path e query não podem ser combinadas,\n- se extração é solicitada, o conteúdo do arquivo deve ser parseado como JSON.\n\nComportamento com diretório ausente:\n\n- lança `No artifacts directory found`.\n\nComportamento com saída ausente:\n\n- lança `Not found: <id>` com IDs disponíveis dos arquivos `.md` existentes.\n\nIntegração com a ferramenta read:\n\n- `read` suporta paginação com offset/limit para leituras de URL interna sem extração,\n- rejeita `offset/limit` quando extração `agent://` é utilizada.\n\n## Semânticas de retomada, fork e movimentação\n\n## Retomada\n\n- `ArtifactManager` escaneia arquivos `{id}.*.log` existentes na primeira alocação e continua a numeração.\n- `AgentOutputManager` escaneia IDs de saída `.md` existentes e continua a numeração.\n- `SessionManager` reidrata referências de blob para base64 no carregamento.\n\n## Fork\n\n`SessionManager.fork()` cria um novo arquivo de sessão com novo ID de sessão e link `parentSession`, então retorna os caminhos de arquivo antigo/novo. A cópia de artefatos é tratada pelo `AgentSession.fork()`:\n\n- tenta cópia recursiva do diretório de artefatos antigo para o novo diretório de artefatos,\n- diretório antigo ausente é tolerado,\n- erros de cópia que não são ENOENT são registrados como avisos e o fork ainda é concluído.\n\nImplicações de ID após o fork:\n\n- se a cópia foi bem-sucedida, os contadores de artefatos na nova sessão continuam após o ID máximo copiado,\n- se a cópia falhou/foi ignorada, os IDs de artefatos da nova sessão começam do `0`.\n\nImplicações de blob após o fork:\n\n- blobs são globais e endereçados por conteúdo, então nenhuma cópia de diretório de blobs é necessária.\n\n## Mover para novo cwd\n\n`SessionManager.moveTo()` renomeia tanto o arquivo de sessão quanto o diretório de artefatos para o novo diretório de sessão padrão, com lógica de rollback se uma etapa posterior falhar. Isso preserva a identidade dos artefatos enquanto realoca o escopo da sessão.\n\n## Tratamento de falhas e caminhos de fallback\n\n| Caso | Comportamento |\n| --- | --- |\n| Arquivo de blob ausente durante reidratação | Avisa e mantém a string de referência `blob:sha256:` em memória |\n| Blob read ENOENT via `BlobStore.get` | Retorna `null` |\n| Diretório de artefatos ausente (`ArtifactManager.listFiles`) | Retorna lista vazia (alocação pode começar do zero) |\n| Diretório de artefatos ausente (`artifact://` / `agent://`) | Lança explicitamente `No artifacts directory found` |\n| ID de artefato não encontrado | Lança com listagem de IDs disponíveis |\n| Falha na inicialização do escritor de artefato do OutputSink | Continua com truncamento somente de cauda (sem artefato de saída completa) |\n| Sem arquivo de sessão (alguns caminhos de tarefa) | Ferramenta Task faz fallback para diretório de artefatos temporário para saídas de subagente |\n\n## Externalização de blob binário vs artefatos de saída de texto\n\n- **Externalização de blob** é para payloads de imagens binárias dentro do conteúdo de entradas de sessão persistidas; substitui base64 inline no JSONL por referências de conteúdo estáveis.\n- **Artefatos** são arquivos de texto simples para saída de execução e saída de subagente; são endereçáveis por IDs locais à sessão através de URLs internas.\n\nOs dois sistemas se intersectam apenas indiretamente (ambos reduzem o inchaço do JSONL de sessão) mas possuem caminhos diferentes de identidade, tempo de vida e recuperação.\n\n## Arquivos de implementação\n\n- [`src/session/blob-store.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/coding-agent/src/session/blob-store.ts) — formato de referência de blob, hashing, put/get, helpers de externalização/resolução.\n- [`src/session/artifacts.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/coding-agent/src/session/artifacts.ts) — modelo de diretório de artefatos de sessão e alocação de ID numérico de artefato.\n- [`src/session/streaming-output.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/coding-agent/src/session/streaming-output.ts) — comportamento de truncamento/despejo-para-arquivo do `OutputSink` e metadados de resumo.\n- [`src/session/session-manager.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/coding-agent/src/session/session-manager.ts) — transformações de persistência, reidratação de blob no carregamento, interações de fork/movimentação de sessão.\n- [`src/session/agent-session.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/coding-agent/src/session/agent-session.ts) — cópia de diretório de artefatos durante fork interativo.\n- [`src/tools/output-utils.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/coding-agent/src/tools/output-utils.ts) — bootstrap do gerenciador de artefatos de ferramentas e alocação de caminho de artefato por ferramenta.\n- [`src/internal-urls/artifact-protocol.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/coding-agent/src/internal-urls/artifact-protocol.ts) — resolver de `artifact://`.\n- [`src/internal-urls/agent-protocol.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/coding-agent/src/internal-urls/agent-protocol.ts) — resolver de `agent://` + extração JSON.\n- [`src/sdk.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/coding-agent/src/sdk.ts) — wiring do roteador de URLs internas e resolver de diretório de artefatos.\n- [`src/task/output-manager.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/coding-agent/src/task/output-manager.ts) — alocação de ID de saída de agente com escopo de sessão para `agent://`.\n- [`src/task/executor.ts`](https://github.com/f5-sales-demo/xcsh/blob/main/packages/coding-agent/src/task/executor.ts) — escritas de artefatos de saída de subagente (`<id>.md`) e fallback para diretório de artefatos temporário.\n",
|
|
486
486
|
"pt-br/configuration/config-usage.md": "---\ntitle: Descoberta e Resolução de Configuração\ndescription: >-\n Como o xcsh descobre, resolve e organiza configurações a partir das raízes de\n projeto, usuário e empresa.\nsidebar:\n order: 1\n label: Configuração\ni18n:\n sourceHash: e38bd9792499\n translator: machine\n---\n\n# Descoberta e Resolução de Configuração\n\nEste documento descreve como o coding-agent resolve a configuração atualmente: quais raízes são escaneadas, como a precedência funciona e como a configuração resolvida é consumida por settings, skills, hooks, tools e extensões.\n\n## Escopo\n\nImplementação principal:\n\n- `src/config.ts`\n- `src/config/settings.ts`\n- `src/config/settings-schema.ts`\n- `src/discovery/builtin.ts`\n- `src/discovery/helpers.ts`\n\nPontos-chave de integração:\n\n- `src/capability/index.ts`\n- `src/discovery/index.ts`\n- `src/extensibility/skills.ts`\n- `src/extensibility/hooks/loader.ts`\n- `src/extensibility/custom-tools/loader.ts`\n- `src/extensibility/extensions/loader.ts`\n\n---\n\n## Fluxo de resolução (visual)\n\n```text\n Config roots (ordered)\n┌───────────────────────────────────────┐\n│ 1) ~/.xcsh/agent + <cwd>/.xcsh │\n│ 2) ~/.claude + <cwd>/.claude │\n│ 3) ~/.codex + <cwd>/.codex │\n│ 4) ~/.gemini + <cwd>/.gemini │\n└───────────────────────────────────────┘\n │\n ▼\n config.ts helper resolution\n (getConfigDirs/findConfigFile/findNearest...)\n │\n ▼\n capability providers enumerate items\n (native, claude, codex, gemini, agents, etc.)\n │\n ▼\n priority sort + per-capability dedup\n │\n ▼\n subsystem-specific consumption\n (settings, skills, hooks, tools, extensions)\n```\n\n## 1) Raízes de configuração e ordem de fontes\n\n## Raízes canônicas\n\n`src/config.ts` define uma lista fixa de prioridade de fontes:\n\n1. `.xcsh` (nativo)\n2. `.claude`\n3. `.codex`\n4. `.gemini`\n\nBases de nível de usuário:\n\n- `~/.xcsh/agent`\n- `~/.claude`\n- `~/.codex`\n- `~/.gemini`\n\nBases de nível de projeto:\n\n- `<cwd>/.xcsh`\n- `<cwd>/.claude`\n- `<cwd>/.codex`\n- `<cwd>/.gemini`\n\n`CONFIG_DIR_NAME` é `.xcsh` (`packages/utils/src/dirs.ts`).\n\n## Restrição importante\n\nOs helpers genéricos em `src/config.ts` **não** incluem `.pi` na ordem de descoberta de fontes.\n\n---\n\n## 2) Helpers principais de descoberta (`src/config.ts`)\n\n## `getConfigDirs(subpath, options)`\n\nRetorna entradas ordenadas:\n\n- Entradas de nível de usuário primeiro (por prioridade de fonte)\n- Depois entradas de nível de projeto (pela mesma prioridade de fonte)\n\nOpções:\n\n- `user` (padrão `true`)\n- `project` (padrão `true`)\n- `cwd` (padrão `getProjectDir()`)\n- `existingOnly` (padrão `false`)\n\nEsta API é utilizada para buscas de configuração baseadas em diretórios (commands, hooks, tools, agents, etc.).\n\n## `findConfigFile(subpath, options)` / `findConfigFileWithMeta(...)`\n\nBusca o primeiro arquivo existente entre as bases ordenadas, retorna a primeira correspondência (apenas o caminho ou caminho+metadados).\n\n## `findAllNearestProjectConfigDirs(subpath, cwd)`\n\nPercorre os diretórios ancestrais para cima e retorna o **diretório existente mais próximo por base de fonte** (`.xcsh`, `.claude`, `.codex`, `.gemini`), depois ordena os resultados por prioridade de fonte.\n\nUse isso quando a configuração de projeto deve ser herdada de diretórios ancestrais (comportamento de monorepo/workspace aninhado).\n\n---\n\n## 3) Wrapper de arquivo de configuração (`ConfigFile<T>` em `src/config.ts`)\n\n`ConfigFile<T>` é o carregador com validação de schema para arquivos de configuração únicos.\n\nFormatos suportados:\n\n- `.yml` / `.yaml`\n- `.json` / `.jsonc`\n\nComportamento:\n\n- Valida os dados parseados com AJV contra um schema TypeBox fornecido.\n- Armazena em cache o resultado do carregamento até `invalidate()`.\n- Retorna resultado de três estados via `tryLoad()`:\n - `ok`\n - `not-found`\n - `error` (`ConfigError` com contexto de schema/parse)\n\nMigração legada ainda suportada:\n\n- Se o caminho alvo é `.yml`/`.yaml`, um `.json` adjacente é migrado automaticamente uma vez (`migrateJsonToYml`).\n\n---\n\n## 4) Modelo de resolução de settings (`src/config/settings.ts`)\n\nO modelo de settings em tempo de execução é organizado em camadas:\n\n1. Settings globais: `~/.xcsh/agent/config.yml`\n2. Settings de projeto: descobertas via capability de settings (`settings.json` dos providers)\n3. Overrides em tempo de execução: em memória, não persistentes\n4. Valores padrão do schema: do `SETTINGS_SCHEMA`\n\nCaminho efetivo de leitura:\n\n`defaults <- global <- project <- overrides`\n\nComportamento de escrita:\n\n- `settings.set(...)` escreve na camada **global** (`config.yml`) e enfileira salvamento em background.\n- Settings de projeto são somente leitura a partir da descoberta de capabilities.\n\n## Comportamento de migração ainda ativo\n\nNa inicialização, se `config.yml` não existe:\n\n1. Migra de `~/.xcsh/agent/settings.json` (renomeado para `.bak` em caso de sucesso)\n2. Mescla com settings legadas do DB de `agent.db`\n3. Escreve o resultado mesclado em `config.yml`\n\nMigrações em nível de campo em `#migrateRawSettings`:\n\n- `queueMode` -> `steeringMode`\n- `ask.timeout` milissegundos -> segundos quando o valor antigo parece ser ms (`> 1000`)\n- `theme: \"...\"` flat legado -> estrutura `theme.dark/theme.light`\n\n---\n\n## 5) Integração capability/discovery\n\nA maioria dos fluxos de carregamento de configuração não-core passa pelo registro de capabilities (`src/capability/index.ts` + `src/discovery/index.ts`).\n\n## Ordenação de providers\n\nProviders são ordenados por prioridade numérica (maior primeiro). Exemplos de prioridades:\n\n- Native OMP (`builtin.ts`): `100`\n- Claude: `80`\n- Codex / agents / Claude marketplace: `70`\n- Gemini: `60`\n\n```text\nProvider precedence (higher wins)\n\nnative (.xcsh) priority 100\nclaude priority 80\ncodex / agents / ... priority 70\ngemini priority 60\n```\n\n## Semântica de deduplicação\n\nCapabilities definem uma `key(item)`:\n\n- mesma chave => primeiro item vence (item de maior prioridade/carregado primeiro)\n- sem chave (`undefined`) => sem deduplicação, todos os itens são mantidos\n\nChaves relevantes:\n\n- skills: `name`\n- tools: `name`\n- hooks: `${type}:${tool}:${name}`\n- extension modules: `name`\n- extensions: `name`\n- settings: sem deduplicação (todos os itens são preservados)\n\n---\n\n## 6) Comportamento do provider nativo `.xcsh` (`src/discovery/builtin.ts`)\n\nO provider nativo (`id: native`) lê de:\n\n- projeto: `<cwd>/.xcsh/...`\n- usuário: `~/.xcsh/agent/...`\n\n### Regra de admissão de diretório\n\n`builtin.ts` só inclui uma raiz de configuração se o diretório existir **e não estiver vazio** (`ifNonEmptyDir`).\n\n### Carregamento específico por escopo\n\n- Skills: `skills/*/SKILL.md`\n- Slash commands: `commands/*.md`\n- Rules: `rules/*.{md,mdc}`\n- Prompts: `prompts/*.md`\n- Instructions: `instructions/*.md`\n- Hooks: `hooks/pre/*`, `hooks/post/*`\n- Tools: `tools/*.json|*.md` e `tools/<name>/index.ts`\n- Extension modules: descobertos em `extensions/` (+ array de strings legado `settings.json.extensions`)\n- Extensions: `extensions/<name>/gemini-extension.json`\n- Settings capability: `settings.json`\n\n### Nuance de busca de projeto mais próximo\n\nPara `SYSTEM.md` e `XCSH.md`, o provider nativo usa a busca de diretório `.xcsh` de projeto no ancestral mais próximo (subindo a árvore) mas ainda exige que o diretório `.xcsh` não esteja vazio.\n\n---\n\n## 7) Como os principais subsistemas consomem a configuração\n\n## Subsistema de settings\n\n- `Settings.init()` carrega o `config.yml` global + itens descobertos da capability de settings do projeto.\n- Apenas itens de capability com `level === \"project\"` são mesclados na camada de projeto.\n\n## Subsistema de skills\n\n- `extensibility/skills.ts` carrega via `loadCapability(skillCapability.id, { cwd })`.\n- Aplica toggles e filtros de fonte (`ignoredSkills`, `includeSkills`, diretórios customizados).\n- Toggles com nomes legados ainda existem (`skills.enablePiUser`, `skills.enablePiProject`) mas controlam o provider nativo (`provider === \"native\"`).\n\n## Subsistema de hooks\n\n- `discoverAndLoadHooks()` resolve caminhos de hooks a partir da capability de hooks + caminhos configurados explicitamente.\n- Depois carrega módulos via importação do Bun.\n\n## Subsistema de tools\n\n- `discoverAndLoadCustomTools()` resolve caminhos de tools a partir da capability de tools + caminhos de tools de plugins + caminhos configurados explicitamente.\n- Arquivos de tools declarativos `.md/.json` são apenas metadados; o carregamento executável espera módulos de código.\n\n## Subsistema de extensões\n\n- `discoverAndLoadExtensions()` resolve módulos de extensão a partir da capability de extension-module mais caminhos explícitos.\n- A implementação atual intencionalmente mantém apenas itens de capability com `_source.provider === \"native\"` antes do carregamento.\n\n---\n\n## 8) Regras de precedência nas quais confiar\n\nUse este modelo mental:\n\n1. A ordenação de diretórios de fonte do `config.ts` determina a ordem dos caminhos candidatos.\n2. A prioridade do provider de capability determina a precedência entre providers.\n3. A deduplicação por chave de capability determina o comportamento de colisão (primeiro vence para capabilities com chave).\n4. A lógica de merge específica do subsistema pode alterar ainda mais a precedência efetiva (especialmente settings).\n\n### Ressalva específica de settings\n\nItens de capability de settings não são deduplicados; `Settings.#loadProjectSettings()` faz deep-merge dos itens de projeto na ordem retornada. Como o merge aplica valores de itens posteriores sobre valores anteriores, o comportamento efetivo de override depende da ordem de emissão do provider, não apenas da semântica de chave de capability.\n\n---\n\n## 9) Comportamentos de legado/compatibilidade ainda presentes\n\n- Migração de JSON -> YAML do `ConfigFile` para arquivos destinados a YAML.\n- Migração de settings de `settings.json` e `agent.db` para `config.yml`.\n- Migrações de chaves de settings (`queueMode`, `ask.timeout`, `theme` flat).\n- Compatibilidade de manifesto de extensão: o loader aceita tanto seções de manifesto `package.json.xcsh` quanto `package.json.pi`.\n- Nomes de settings legados `skills.enablePiUser` / `skills.enablePiProject` ainda são gates ativos para a fonte nativa de skills.\n\nSe esses caminhos de compatibilidade forem removidos no código, atualize este documento imediatamente; vários comportamentos em tempo de execução ainda dependem deles hoje.\n",
|
|
487
487
|
"pt-br/configuration/environment-variables.md": "---\ntitle: Variáveis de Ambiente\ndescription: >-\n Referência de variáveis de ambiente de runtime para configuração e controle de\n comportamento do xcsh.\nsidebar:\n order: 2\n label: Variáveis de ambiente\ni18n:\n sourceHash: e2890f963c02\n translator: machine\n---\n\n# Variáveis de Ambiente (Referência de Runtime Atual)\n\nEsta referência é derivada dos caminhos de código atuais em:\n\n- `packages/coding-agent/src/**`\n- `packages/ai/src/**` (resolução de provedor/autenticação utilizada pelo coding-agent)\n- `packages/utils/src/**` e `packages/tui/src/**` onde essas variáveis afetam diretamente o runtime do coding-agent\n\nDocumenta apenas o comportamento ativo.\n\n## Modelo de resolução e precedência\n\nA maioria das consultas em runtime utiliza `$env` de `@f5-sales-demo/pi-utils` (`packages/utils/src/env.ts`).\n\nOrdem de carregamento do `$env`:\n\n1. Ambiente de processo existente (`Bun.env`)\n2. `.env` do projeto (`$PWD/.env`) para chaves ainda não definidas\n3. `.env` do diretório home (`~/.env`) para chaves ainda não definidas\n\nRegra adicional em arquivos `.env`: chaves `XCSH_*` são espelhadas para chaves `PI_*` durante o parse.\n\n---\n\n## 1) Autenticação de modelo/provedor\n\nEstas são consumidas via `getEnvApiKey()` (`packages/ai/src/stream.ts`), salvo indicação contrária.\n\n### Credenciais principais de provedor\n\n| Variável | Usada para | Necessária quando | Notas / precedência |\n|---------------------------------|---|---------------------------------------------------------------|-----------------------------------------------------------------------------------------------------|\n| `ANTHROPIC_OAUTH_TOKEN` | Autenticação na API Anthropic | Usando Anthropic com autenticação por token OAuth | Tem precedência sobre `ANTHROPIC_API_KEY` na resolução de autenticação do provedor |\n| `ANTHROPIC_API_KEY` | Autenticação na API Anthropic | Usando Anthropic sem token OAuth | Fallback após `ANTHROPIC_OAUTH_TOKEN` |\n| `ANTHROPIC_FOUNDRY_API_KEY` | Anthropic via Azure Foundry / gateway empresarial | `CLAUDE_CODE_USE_FOUNDRY` habilitado | Tem precedência sobre `ANTHROPIC_OAUTH_TOKEN` e `ANTHROPIC_API_KEY` quando o modo Foundry está habilitado |\n| `OPENAI_API_KEY` | Autenticação OpenAI | Usando provedores da família OpenAI sem argumento apiKey explícito | Usado pelos provedores OpenAI Completions/Responses |\n| `GEMINI_API_KEY` | Autenticação Google Gemini | Usando modelos do provedor `google` | Chave principal para mapeamento do provedor Gemini |\n| `GOOGLE_API_KEY` | Fallback de autenticação da ferramenta de imagem Gemini | Usando a ferramenta `gemini_image` sem `GEMINI_API_KEY` | Usado pelo caminho de fallback da ferramenta de imagem do coding-agent |\n| `GROQ_API_KEY` | Autenticação Groq | Usando modelos Groq | |\n| `CEREBRAS_API_KEY` | Autenticação Cerebras | Usando modelos Cerebras | |\n| `TOGETHER_API_KEY` | Autenticação Together | Usando provedor `together` | |\n| `HUGGINGFACE_HUB_TOKEN` | Autenticação Hugging Face | Usando provedor `huggingface` | Variável de ambiente principal do token Hugging Face |\n| `HF_TOKEN` | Autenticação Hugging Face | Usando provedor `huggingface` | Fallback quando `HUGGINGFACE_HUB_TOKEN` não está definido |\n| `SYNTHETIC_API_KEY` | Autenticação Synthetic | Usando modelos Synthetic | |\n| `NVIDIA_API_KEY` | Autenticação NVIDIA | Usando provedor `nvidia` | |\n| `NANO_GPT_API_KEY` | Autenticação NanoGPT | Usando provedor `nanogpt` | |\n| `VENICE_API_KEY` | Autenticação Venice | Usando provedor `venice` | |\n| `LITELLM_API_KEY` | Autenticação LiteLLM | Usando provedor `litellm` | Chave de proxy LiteLLM compatível com OpenAI. Quando definido com `LITELLM_BASE_URL`, habilita a auto-configuração do `models.yml` |\n| `LM_STUDIO_API_KEY` | Autenticação LM Studio (opcional) | Usando provedor `lm-studio` com hosts autenticados | LM Studio local geralmente roda sem autenticação; qualquer token não vazio funciona quando uma chave é necessária |\n| `OLLAMA_API_KEY` | Autenticação Ollama (opcional) | Usando provedor `ollama` com hosts autenticados | Ollama local geralmente roda sem autenticação; qualquer token não vazio funciona quando uma chave é necessária |\n| `LLAMA_CPP_API_KEY` | Autenticação Ollama (opcional) | Usando `llama-server` com parâmetro `--api-key` | llama.cpp local geralmente roda sem autenticação; qualquer token não vazio funciona quando uma chave é configurada |\n| `XIAOMI_API_KEY` | Autenticação Xiaomi MiMo | Usando provedor `xiaomi` | |\n| `MOONSHOT_API_KEY` | Autenticação Moonshot | Usando provedor `moonshot` | |\n| `XAI_API_KEY` | Autenticação xAI | Usando modelos xAI | |\n| `OPENROUTER_API_KEY` | Autenticação OpenRouter | Usando modelos OpenRouter | Também usado pela ferramenta de imagem quando o provedor preferido/auto é OpenRouter |\n| `MISTRAL_API_KEY` | Autenticação Mistral | Usando modelos Mistral | |\n| `ZAI_API_KEY` | Autenticação z.ai | Usando modelos z.ai | Também usado pelo provedor de busca web z.ai |\n| `MINIMAX_API_KEY` | Autenticação MiniMax | Usando provedor `minimax` | |\n| `MINIMAX_CODE_API_KEY` | Autenticação MiniMax Code | Usando provedor `minimax-code` | |\n| `MINIMAX_CODE_CN_API_KEY` | Autenticação MiniMax Code CN | Usando provedor `minimax-code-cn` | |\n| `OPENCODE_API_KEY` | Autenticação OpenCode | Usando modelos OpenCode | |\n| `QIANFAN_API_KEY` | Autenticação Qianfan | Usando provedor `qianfan` | |\n| `QWEN_OAUTH_TOKEN` | Autenticação Qwen Portal | Usando `qwen-portal` com token OAuth | Tem precedência sobre `QWEN_PORTAL_API_KEY` |\n| `QWEN_PORTAL_API_KEY` | Autenticação Qwen Portal | Usando `qwen-portal` com chave API | Fallback após `QWEN_OAUTH_TOKEN` |\n| `ZENMUX_API_KEY` | Autenticação ZenMux | Usando provedor `zenmux` | Usado para rotas compatíveis com OpenAI e Anthropic do ZenMux |\n| `VLLM_API_KEY` | Autenticação/descoberta opt-in do vLLM | Usando provedor `vllm` (servidores locais compatíveis com OpenAI) | Qualquer valor não vazio funciona para servidores locais sem autenticação |\n| `CURSOR_ACCESS_TOKEN` | Autenticação do provedor Cursor | Usando provedor Cursor | |\n| `AI_GATEWAY_API_KEY` | Autenticação Vercel AI Gateway | Usando provedor `vercel-ai-gateway` | |\n| `CLOUDFLARE_AI_GATEWAY_API_KEY` | Autenticação Cloudflare AI Gateway | Usando provedor `cloudflare-ai-gateway` | A URL base deve ser configurada como `https://gateway.ai.cloudflare.com/v1/<account>/<gateway>/anthropic` |\n\n### Cadeias de token GitHub/Copilot\n\n| Variável | Usada para | Cadeia |\n|---|---|---|\n| `COPILOT_GITHUB_TOKEN` | Autenticação do provedor GitHub Copilot | `COPILOT_GITHUB_TOKEN` → `GH_TOKEN` → `GITHUB_TOKEN` |\n| `GH_TOKEN` | Fallback do Copilot; autenticação na API GitHub no web scraper | No web scraper: `GITHUB_TOKEN` → `GH_TOKEN` |\n| `GITHUB_TOKEN` | Fallback do Copilot; autenticação na API GitHub no web scraper | No web scraper: verificado antes de `GH_TOKEN` |\n\n---\n\n## 2) Configuração de runtime específica por provedor\n\n### Anthropic Foundry Gateway (Azure / proxy empresarial)\n\nQuando `CLAUDE_CODE_USE_FOUNDRY` está habilitado, as requisições Anthropic mudam para o modo Foundry:\n\n- A URL base é resolvida a partir de `FOUNDRY_BASE_URL` (o fallback permanece como a URL base padrão/do modelo se não definida).\n- A resolução da chave API para o provedor `anthropic` torna-se:\n `ANTHROPIC_FOUNDRY_API_KEY` → `ANTHROPIC_OAUTH_TOKEN` → `ANTHROPIC_API_KEY`.\n- `ANTHROPIC_CUSTOM_HEADERS` é interpretado como pares `chave: valor` separados por vírgula/nova linha e mesclados nos cabeçalhos da requisição.\n- Material TLS de cliente/servidor pode ser injetado a partir de valores de ambiente:\n `NODE_EXTRA_CA_CERTS`, `CLAUDE_CODE_CLIENT_CERT`, `CLAUDE_CODE_CLIENT_KEY`.\n Cada um aceita:\n - um caminho de sistema de arquivos para conteúdo PEM, ou\n - PEM inline (incluindo sequências `\\n` escapadas).\n\n| Variável | Tipo de valor | Comportamento |\n|---|---|---|\n| `CLAUDE_CODE_USE_FOUNDRY` | String tipo booleano (`1`, `true`, `yes`, `on`) | Habilita o modo Foundry para o provedor Anthropic |\n| `FOUNDRY_BASE_URL` | String URL | URL base do endpoint Anthropic no modo Foundry |\n| `ANTHROPIC_FOUNDRY_API_KEY` | String de token | Usado para `Authorization: Bearer <token>` |\n| `ANTHROPIC_CUSTOM_HEADERS` | String de lista de cabeçalhos | Cabeçalhos extras; formato `header-a: valor, header-b: valor` ou separados por nova linha |\n| `NODE_EXTRA_CA_CERTS` | Caminho PEM ou PEM inline | Cadeia CA extra para validação de certificado do servidor |\n| `CLAUDE_CODE_CLIENT_CERT` | Caminho PEM ou PEM inline | Certificado de cliente mTLS |\n| `CLAUDE_CODE_CLIENT_KEY` | Caminho PEM ou PEM inline | Chave privada do cliente mTLS (deve ser pareada com o certificado) |\n\n### Amazon Bedrock\n\n| Variável | Padrão / comportamento |\n|---|---|\n| `AWS_REGION` | Fonte principal de região |\n| `AWS_DEFAULT_REGION` | Fallback se `AWS_REGION` não estiver definida |\n| `AWS_PROFILE` | Habilita o caminho de autenticação por perfil nomeado |\n| `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY` | Habilita o caminho de autenticação por chave IAM |\n| `AWS_BEARER_TOKEN_BEDROCK` | Habilita o caminho de autenticação por bearer token |\n| `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI` / `AWS_CONTAINER_CREDENTIALS_FULL_URI` | Habilita o caminho de credencial de tarefa ECS |\n| `AWS_WEB_IDENTITY_TOKEN_FILE` + `AWS_ROLE_ARN` | Habilita o caminho de autenticação por web identity |\n| `AWS_BEDROCK_SKIP_AUTH` | Se `1`, injeta credenciais fictícias (cenários de proxy/sem autenticação) |\n| `AWS_BEDROCK_FORCE_HTTP1` | Se `1`, força o handler de requisição Node HTTP/1 |\n\nFallback de região no código do provedor: `options.region` → `AWS_REGION` → `AWS_DEFAULT_REGION` → `us-east-1`.\n\n### Azure OpenAI Responses\n\n| Variável | Padrão / comportamento |\n|---|---|\n| `AZURE_OPENAI_API_KEY` | Obrigatória a menos que a chave API seja passada como opção |\n| `AZURE_OPENAI_API_VERSION` | Padrão `v1` |\n| `AZURE_OPENAI_BASE_URL` | Override direto da URL base |\n| `AZURE_OPENAI_RESOURCE_NAME` | Usado para construir a URL base: `https://<resource>.openai.azure.com/openai/v1` |\n| `AZURE_OPENAI_DEPLOYMENT_NAME_MAP` | String de mapeamento opcional: `modelId=deploymentName,model2=deployment2` |\n\nResolução da URL base: opção `azureBaseUrl` → env `AZURE_OPENAI_BASE_URL` → opção/env resource name → `model.baseUrl`.\n\n### Google Vertex AI\n\n| Variável | Obrigatória? | Notas |\n|---|---|---|\n| `GOOGLE_CLOUD_PROJECT` | Sim (a menos que passada nas opções) | Fallback: `GCLOUD_PROJECT` |\n| `GCLOUD_PROJECT` | Fallback | Usada como fonte alternativa de ID do projeto |\n| `GOOGLE_CLOUD_LOCATION` | Sim (a menos que passada nas opções) | Sem padrão no provedor |\n| `GOOGLE_APPLICATION_CREDENTIALS` | Condicional | Se definida, o arquivo deve existir; caso contrário, o caminho de fallback ADC é verificado (`~/.config/gcloud/application_default_credentials.json`) |\n\n### Kimi\n\n| Variável | Padrão / comportamento |\n|---|---|\n| `KIMI_CODE_OAUTH_HOST` | Override principal do host OAuth |\n| `KIMI_OAUTH_HOST` | Override de fallback do host OAuth |\n| `KIMI_CODE_BASE_URL` | Substitui a URL base do endpoint de uso do Kimi (`usage/kimi.ts`) |\n\nCadeia do host OAuth: `KIMI_CODE_OAUTH_HOST` → `KIMI_OAUTH_HOST` → `https://auth.kimi.com`.\n\n### Compatibilidade Antigravity/Gemini image\n\n| Variável | Padrão / comportamento |\n|---|---|\n| `PI_AI_ANTIGRAVITY_VERSION` | Substitui a tag de versão do user-agent Antigravity no provedor Gemini CLI |\n\n### OpenAI Codex responses (controles de funcionalidade/debug)\n\n| Variável | Comportamento |\n|---|---|\n| `PI_CODEX_DEBUG` | `1`/`true` habilita logs de debug do provedor Codex |\n| `PI_CODEX_WEBSOCKET` | `1`/`true` habilita preferência de transporte websocket |\n| `PI_CODEX_WEBSOCKET_V2` | `1`/`true` habilita caminho websocket v2 |\n| `PI_CODEX_WEBSOCKET_IDLE_TIMEOUT_MS` | Override de inteiro positivo (padrão 300000) |\n| `PI_CODEX_WEBSOCKET_RETRY_BUDGET` | Override de inteiro não negativo (padrão 5) |\n| `PI_CODEX_WEBSOCKET_RETRY_DELAY_MS` | Override de backoff base em inteiro positivo (padrão 500) |\n\n### Debug do provedor Cursor\n\n| Variável | Comportamento |\n|---|---|\n| `DEBUG_CURSOR` | Habilita logs de debug do provedor; `2`/`verbose` para trechos detalhados de payload |\n| `DEBUG_CURSOR_LOG` | Caminho de arquivo opcional para saída de log de debug JSONL |\n\n### Chave de compatibilidade de cache de prompt\n\n| Variável | Comportamento |\n|---|---|\n| `PI_CACHE_RETENTION` | Se `long`, habilita retenção longa onde suportado (`anthropic`, `openai-responses`, resolução de retenção Bedrock) |\n\n---\n\n## 3) Subsistema de busca web\n\n### Credenciais de provedor de busca\n\n| Variável | Usada por |\n|---|---|\n| `EXA_API_KEY` | Provedor de busca Exa e ferramentas MCP Exa |\n| `BRAVE_API_KEY` | Provedor de busca Brave |\n| `PERPLEXITY_API_KEY` | Modo chave API do provedor de busca Perplexity |\n| `TAVILY_API_KEY` | Provedor de busca Tavily |\n| `ZAI_API_KEY` | Provedor de busca z.ai (também verifica OAuth armazenado em `agent.db`) |\n| `OPENAI_API_KEY` / OAuth Codex no DB | Disponibilidade/autenticação do provedor de busca Codex |\n\n### Cadeia de autenticação de busca web Anthropic\n\n`packages/coding-agent/src/web/search/auth.ts` resolve credenciais de busca web Anthropic nesta ordem:\n\n1. `ANTHROPIC_SEARCH_API_KEY` (+ opcional `ANTHROPIC_SEARCH_BASE_URL`)\n2. Entrada de provedor em `models.json` com `api: \"anthropic-messages\"`\n3. Credenciais OAuth Anthropic de `agent.db` (não deve expirar dentro do buffer de 5 minutos)\n4. Fallback genérico de env Anthropic: chave do provedor (`ANTHROPIC_FOUNDRY_API_KEY`/`ANTHROPIC_OAUTH_TOKEN`/`ANTHROPIC_API_KEY`) + opcional `ANTHROPIC_BASE_URL` (`FOUNDRY_BASE_URL` quando o modo Foundry está habilitado)\n\nVariáveis relacionadas:\n\n| Variável | Padrão / comportamento |\n|---|---|\n| `ANTHROPIC_SEARCH_API_KEY` | Chave de busca explícita de maior prioridade |\n| `ANTHROPIC_SEARCH_BASE_URL` | Padrão `https://api.anthropic.com` quando omitida |\n| `ANTHROPIC_SEARCH_MODEL` | Padrão `claude-haiku-4-5` |\n| `ANTHROPIC_BASE_URL` | URL base de fallback genérica para o caminho de autenticação nível 4 |\n\n### Flag de comportamento do fluxo OAuth Perplexity\n\n| Variável | Comportamento |\n|---|---|\n| `PI_AUTH_NO_BORROW` | Se definida, desabilita o caminho de empréstimo de token de aplicativo nativo macOS no fluxo de login Perplexity |\n\n---\n\n## 4) Ferramentas Python e runtime de kernel\n\n| Variável | Padrão / comportamento |\n|---|---|\n| `PI_PY` | Override do modo de ferramenta Python: `0`/`bash`=`bash-only`, `1`/`py`=`ipy-only`, `mix`/`both`=`both`; valores inválidos são ignorados |\n| `PI_PYTHON_SKIP_CHECK` | Se `1`, pula verificações de disponibilidade/aquecimento do kernel Python |\n| `PI_PYTHON_GATEWAY_URL` | Se definida, usa gateway de kernel externo em vez do gateway compartilhado local |\n| `PI_PYTHON_GATEWAY_TOKEN` | Token de autenticação opcional para gateway externo (`Authorization: token <value>`) |\n| `PI_PYTHON_IPC_TRACE` | Se `1`, habilita caminho de rastreamento IPC de baixo nível no módulo de kernel |\n| `VIRTUAL_ENV` | Caminho de venv de maior prioridade para resolução do runtime Python |\n\nComportamento condicional extra:\n\n- Se `BUN_ENV=test` ou `NODE_ENV=test`, as verificações de disponibilidade do Python são tratadas como OK e o aquecimento é ignorado.\n- A filtragem de ambiente Python nega chaves API comuns e permite variáveis base seguras + prefixos `LC_`, `XDG_`, `PI_`.\n\n---\n\n## 5) Toggles de comportamento do agente/runtime\n\n| Variável | Padrão / comportamento |\n|----------------------------|----------------------------------------------------------------------------------------------|\n| `PI_SMOL_MODEL` | Override efêmero de model-role para `smol` (CLI `--smol` tem precedência) |\n| `PI_SLOW_MODEL` | Override efêmero de model-role para `slow` (CLI `--slow` tem precedência) |\n| `PI_PLAN_MODEL` | Override efêmero de model-role para `plan` (CLI `--plan` tem precedência) |\n| `PI_NO_TITLE` | Se definida (qualquer valor não vazio), desabilita a geração automática de título de sessão na primeira mensagem do usuário |\n| `NULL_PROMPT` | Se `true`, o construtor de prompt de sistema retorna string vazia |\n| `PI_BLOCKED_AGENT` | Bloqueia um tipo específico de subagente na ferramenta de tarefa |\n| `PI_SUBPROCESS_CMD` | Substitui o comando de spawn do subagente (bypass da resolução `xcsh` / `xcsh.cmd`) |\n| `PI_TASK_MAX_OUTPUT_BYTES` | Máximo de bytes de saída capturados por subagente (padrão `500000`) |\n| `PI_TASK_MAX_OUTPUT_LINES` | Máximo de linhas de saída capturadas por subagente (padrão `5000`) |\n| `PI_TIMING` | Se `1`, habilita logs de instrumentação de timing de startup/ferramenta |\n| `PI_DEBUG_STARTUP` | Habilita prints de debug de estágio de startup para stderr em múltiplos caminhos de startup |\n| `PI_PACKAGE_DIR` | Substitui a resolução do diretório base de assets do pacote (busca de caminhos de docs/exemplos/changelog) |\n| `PI_DISABLE_LSPMUX` | Se `1`, desabilita detecção/integração do lspmux e força o spawn direto do servidor LSP |\n| `LITELLM_BASE_URL` | URL base do proxy LiteLLM. Quando definida com `LITELLM_API_KEY`, dispara a auto-geração do `models.yml` na primeira execução e auto-reparo em cada startup |\n| `LM_STUDIO_BASE_URL` | Override da URL base de descoberta implícita padrão do LM Studio (`http://127.0.0.1:1234/v1` se não definida) |\n| `OLLAMA_BASE_URL` | Override da URL base de descoberta implícita padrão do Ollama (`http://127.0.0.1:11434` se não definida) |\n| `LLAMA_CPP_BASE_URL` | Override da URL base de descoberta implícita padrão do Llama.cpp (`http://127.0.0.1:8080` se não definida) |\n| `PI_EDIT_VARIANT` | Se `hashline`, força o modo de exibição hashline read/grep quando a ferramenta de edição está disponível |\n| `PI_NO_PTY` | Se `1`, desabilita o caminho PTY interativo para a ferramenta bash |\n\n`PI_NO_PTY` também é definida internamente quando o CLI `--no-pty` é usado.\n\n---\n\n## 6) Caminhos raiz de armazenamento e configuração\n\nEstas são consumidas via `@f5-sales-demo/pi-utils/dirs` e afetam onde o coding-agent armazena dados.\n\n| Variável | Padrão / comportamento |\n|---|---|\n| `PI_CONFIG_DIR` | Nome do diretório raiz de configuração sob o home (padrão `.xcsh`) |\n| `PI_CODING_AGENT_DIR` | Override completo para o diretório do agente (padrão `~/<PI_CONFIG_DIR ou .xcsh>/agent`) |\n| `PWD` | Usado ao fazer correspondência do diretório de trabalho atual canônico em helpers de caminho |\n\n---\n\n## 7) Ambiente de execução de shell/ferramentas\n\n(De `packages/utils/src/procmgr.ts` e integração da ferramenta bash do coding-agent.)\n\n| Variável | Comportamento |\n|---|---|\n| `PI_BASH_NO_CI` | Suprime a injeção automática de `CI=true` no ambiente de shell gerado |\n| `CLAUDE_BASH_NO_CI` | Alias legado de fallback para `PI_BASH_NO_CI` |\n| `PI_BASH_NO_LOGIN` | Destinada a desabilitar o modo de shell de login |\n| `CLAUDE_BASH_NO_LOGIN` | Alias legado de fallback para `PI_BASH_NO_LOGIN` |\n| `PI_SHELL_PREFIX` | Wrapper de prefixo de comando opcional |\n| `CLAUDE_CODE_SHELL_PREFIX` | Alias legado de fallback para `PI_SHELL_PREFIX` |\n| `VISUAL` | Comando de editor externo preferido |\n| `EDITOR` | Comando de editor externo de fallback |\n\nNota da implementação atual: `PI_BASH_NO_LOGIN`/`CLAUDE_BASH_NO_LOGIN` são lidas, mas a implementação atual de `getShellArgs()` retorna `['-l','-c']` em ambas as ramificações (efetivamente sem efeito hoje).\n\n---\n\n## 8) Detecção de UI/tema/sessão (env auto-detectado)\n\nEstas são lidas como sinais de runtime; geralmente são definidas pelo terminal/SO em vez de configuradas manualmente.\n\n| Variável | Usada para |\n|---|---|\n| `COLORTERM`, `TERM`, `WT_SESSION` | Detecção de capacidade de cor (modo de cor do tema) |\n| `COLORFGBG` | Auto-detecção de fundo claro/escuro do terminal |\n| `TERM_PROGRAM`, `TERM_PROGRAM_VERSION`, `TERMINAL_EMULATOR` | Identidade do terminal no prompt/contexto do sistema |\n| `KDE_FULL_SESSION`, `XDG_CURRENT_DESKTOP`, `DESKTOP_SESSION`, `XDG_SESSION_DESKTOP`, `GDMSESSION`, `WINDOWMANAGER` | Detecção de desktop/gerenciador de janelas no prompt/contexto do sistema |\n| `KITTY_WINDOW_ID`, `TMUX_PANE`, `TERM_SESSION_ID`, `WT_SESSION` | IDs de breadcrumb de sessão estáveis por terminal |\n| `SHELL`, `ComSpec`, `TERM_PROGRAM`, `TERM` | Diagnósticos de informações do sistema |\n| `APPDATA`, `XDG_CONFIG_HOME` | Resolução de caminho de configuração do lspmux |\n| `HOME` | Encurtamento de caminho na UI de comando MCP |\n\n---\n\n## 9) Flags de carregamento nativo/debug\n\n| Variável | Comportamento |\n|---|---|\n| `PI_DEV` | Habilita diagnósticos verbosos de carregamento de addon nativo em `packages/natives` |\n\n## 10) Flags de runtime da TUI (pacote compartilhado, afeta a UX do coding-agent)\n\n| Variável | Comportamento |\n|---|---|\n| `PI_NOTIFICATIONS` | `off` / `0` / `false` suprimem notificações de desktop |\n| `PI_TUI_WRITE_LOG` | Se definida, registra escritas da TUI em arquivo |\n| `PI_HARDWARE_CURSOR` | Se `1`, habilita modo de cursor de hardware |\n| `PI_CLEAR_ON_SHRINK` | Se `1`, limpa linhas vazias quando o conteúdo encolhe |\n| `PI_DEBUG_REDRAW` | Se `1`, habilita log de debug de redesenho |\n| `PI_TUI_DEBUG` | Se `1`, habilita caminho de dump de debug profundo da TUI |\n\n---\n\n## 11) Controles de geração de commit\n\n| Variável | Comportamento |\n|---|---|\n| `PI_COMMIT_TEST_FALLBACK` | Se `true` (case-insensitive), força o caminho de geração de commit por fallback |\n| `PI_COMMIT_NO_FALLBACK` | Se `true`, desabilita fallback quando o agente não retorna nenhuma proposta |\n| `PI_COMMIT_MAP_REDUCE` | Se `false`, desabilita o caminho de análise de commit por map-reduce |\n| `DEBUG` | Se definida, stack traces de erro do agente de commit são impressos |\n\n---\n\n## Variáveis sensíveis à segurança\n\nTrate estas como segredos; não as registre em logs nem as commit:\n\n- Chaves de provedor/API e credenciais OAuth/bearer (todas as `*_API_KEY`, `*_TOKEN`, tokens de acesso/refresh OAuth)\n- Credenciais de nuvem (`AWS_*`, o caminho de `GOOGLE_APPLICATION_CREDENTIALS` pode expor material de conta de serviço)\n- Variáveis de autenticação de busca/provedor (`EXA_API_KEY`, `BRAVE_API_KEY`, `PERPLEXITY_API_KEY`, chaves de busca Anthropic)\n- Material mTLS Foundry (`CLAUDE_CODE_CLIENT_CERT`, `CLAUDE_CODE_CLIENT_KEY`, `NODE_EXTRA_CA_CERTS` quando aponta para bundles de CA privados)\n\nO runtime Python também remove explicitamente muitas variáveis de chave comuns antes de gerar subprocessos de kernel (`packages/coding-agent/src/ipy/runtime.ts`).\n",
|