feat(goose): add local v1.49 canary routing, smoke gates, and migration docs

Wire Portal and TKMind proxy to loopback Goose v1.49 via canary env blocks,
with verification scripts, Phase 2/3 evidence baselines, and rollback runbooks
so local upgrade stays isolated from stable 1.41 and production.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-09-08 21:10:20 +08:00
parent d2d463b1db
commit 716ef407fe
68 changed files with 48162 additions and 2 deletions
File diff suppressed because it is too large Load Diff
+14
View File
@@ -0,0 +1,14 @@
# Goose v1.49 Phase 0 本地基线
本目录存放 loopback 升级对照用的 JSON 基线,**不含生产密钥**。
| 文件 | 生成命令 |
|---|---|
| `goose-v149-memory-latest.json` | `node scripts/export-goose-v149-memory-manifest.mjs --output docs/baselines/goose-v149-memory-latest.json` |
| `goose-v149-runtime-baseline-*.json` | `node scripts/capture-goose-v149-baseline.mjs` |
| `goose-v149-cost-evidence-*.json` | `node scripts/check-goosed-v149-cost-smoke.mjs` |
| `goose-v149-deepseek-tools-evidence-*.json` | `node scripts/check-goosed-v149-deepseek-tools.mjs` |
| `goose-v149-thinking-preservation-latest.json` | `node scripts/check-goosed-v149-thinking-preservation.mjs` |
| `goose-v149-message-sanitize-latest.json` | `node scripts/compare-goose-v149-message-sanitize.mjs` |
Phase 3 收口:[goose-v149-phase3-closeout.md](../goose-v149-phase3-closeout.md) · 聚合:`node scripts/run-goosed-v149-phase3.mjs`
@@ -0,0 +1,22 @@
{
"schemaVersion": "goose-v149-cost-evidence-v1",
"capturedAt": "2026-09-08T10:06:59.003Z",
"sessionId": "20260908_30",
"requestId": "b0ee6f3f-cedc-470c-84cd-925b83c3c131",
"finishReason": "stop",
"tokenState": {
"inputTokens": 8339,
"outputTokens": 2,
"totalTokens": 8341,
"accumulatedInputTokens": 8339,
"accumulatedOutputTokens": 2,
"accumulatedTotalTokens": 8341,
"accumulatedCost": null
},
"billingState": {
"accumulatedInputTokens": 8339,
"accumulatedOutputTokens": 2,
"accumulatedCost": 0.0022537300000000002
},
"enrichedCost": 0.0022537300000000002
}
@@ -0,0 +1,22 @@
{
"schemaVersion": "goose-v149-cost-evidence-v1",
"capturedAt": "2026-09-08T10:07:14.546Z",
"sessionId": "20260908_37",
"requestId": "4c558b69-64e3-4909-9e33-1cb598a00cc2",
"finishReason": "stop",
"tokenState": {
"inputTokens": 8339,
"outputTokens": 2,
"totalTokens": 8341,
"accumulatedInputTokens": 8339,
"accumulatedOutputTokens": 2,
"accumulatedTotalTokens": 8341,
"accumulatedCost": null
},
"billingState": {
"accumulatedInputTokens": 8339,
"accumulatedOutputTokens": 2,
"accumulatedCost": 0.0022537300000000002
},
"enrichedCost": 0.0022537300000000002
}
@@ -0,0 +1,22 @@
{
"schemaVersion": "goose-v149-cost-evidence-v1",
"capturedAt": "2026-09-08T10:30:13.352Z",
"sessionId": "20260908_50",
"requestId": "752da410-df43-44c6-9958-2c23f3d948ae",
"finishReason": "stop",
"tokenState": {
"inputTokens": 8339,
"outputTokens": 2,
"totalTokens": 8341,
"accumulatedInputTokens": 8339,
"accumulatedOutputTokens": 2,
"accumulatedTotalTokens": 8341,
"accumulatedCost": null
},
"billingState": {
"accumulatedInputTokens": 8339,
"accumulatedOutputTokens": 2,
"accumulatedCost": 0.0022537300000000002
},
"enrichedCost": 0.0022537300000000002
}
@@ -0,0 +1,22 @@
{
"schemaVersion": "goose-v149-cost-evidence-v1",
"capturedAt": "2026-09-08T11:12:36.359Z",
"sessionId": "20260908_66",
"requestId": "ba50d32c-5024-4b77-8372-cf6ea1c5eaf0",
"finishReason": "stop",
"tokenState": {
"inputTokens": 8339,
"outputTokens": 2,
"totalTokens": 8341,
"accumulatedInputTokens": 8339,
"accumulatedOutputTokens": 2,
"accumulatedTotalTokens": 8341,
"accumulatedCost": null
},
"billingState": {
"accumulatedInputTokens": 8339,
"accumulatedOutputTokens": 2,
"accumulatedCost": 0.0022537300000000002
},
"enrichedCost": 0.0022537300000000002
}
@@ -0,0 +1,22 @@
{
"schemaVersion": "goose-v149-cost-evidence-v1",
"capturedAt": "2026-09-08T11:19:29.723Z",
"sessionId": "20260908_79",
"requestId": "388a1d9e-6edb-413a-bd49-9d602e3132d0",
"finishReason": "stop",
"tokenState": {
"inputTokens": 8586,
"outputTokens": 2,
"totalTokens": 8588,
"accumulatedInputTokens": 8586,
"accumulatedOutputTokens": 2,
"accumulatedTotalTokens": 8588,
"accumulatedCost": null
},
"billingState": {
"accumulatedInputTokens": 8586,
"accumulatedOutputTokens": 2,
"accumulatedCost": 0.00232042
},
"enrichedCost": 0.00232042
}
@@ -0,0 +1,22 @@
{
"schemaVersion": "goose-v149-cost-evidence-v1",
"capturedAt": "2026-09-08T12:07:15.664Z",
"sessionId": "20260908_94",
"requestId": "f72ff3fd-413f-4c80-b671-1d4f15d1ecf1",
"finishReason": "stop",
"tokenState": {
"inputTokens": 8586,
"outputTokens": 2,
"totalTokens": 8588,
"accumulatedInputTokens": 8586,
"accumulatedOutputTokens": 2,
"accumulatedTotalTokens": 8588,
"accumulatedCost": null
},
"billingState": {
"accumulatedInputTokens": 8586,
"accumulatedOutputTokens": 2,
"accumulatedCost": 0.00232042
},
"enrichedCost": 0.00232042
}
@@ -0,0 +1,22 @@
{
"schemaVersion": "goose-v149-cost-evidence-v1",
"capturedAt": "2026-09-08T12:23:16.640Z",
"sessionId": "20260908_109",
"requestId": "a9365f33-add9-45f3-8512-6a5554e85194",
"finishReason": "stop",
"tokenState": {
"inputTokens": 8586,
"outputTokens": 2,
"totalTokens": 8588,
"accumulatedInputTokens": 8586,
"accumulatedOutputTokens": 2,
"accumulatedTotalTokens": 8588,
"accumulatedCost": null
},
"billingState": {
"accumulatedInputTokens": 8586,
"accumulatedOutputTokens": 2,
"accumulatedCost": 0.00232042
},
"enrichedCost": 0.00232042
}
@@ -0,0 +1,22 @@
{
"schemaVersion": "goose-v149-cost-evidence-v1",
"capturedAt": "2026-09-08T12:32:49.137Z",
"sessionId": "20260908_124",
"requestId": "b31d65ea-e68a-4a7b-a47b-89b3d365afa2",
"finishReason": "stop",
"tokenState": {
"inputTokens": 8586,
"outputTokens": 2,
"totalTokens": 8588,
"accumulatedInputTokens": 8586,
"accumulatedOutputTokens": 2,
"accumulatedTotalTokens": 8588,
"accumulatedCost": null
},
"billingState": {
"accumulatedInputTokens": 8586,
"accumulatedOutputTokens": 2,
"accumulatedCost": 0.00232042
},
"enrichedCost": 0.00232042
}
@@ -0,0 +1,22 @@
{
"schemaVersion": "goose-v149-cost-evidence-v1",
"capturedAt": "2026-09-08T13:06:15.865Z",
"sessionId": "20260908_141",
"requestId": "d85df70e-9d1d-45d1-9408-96dbff9d5c96",
"finishReason": "stop",
"tokenState": {
"inputTokens": 8586,
"outputTokens": 2,
"totalTokens": 8588,
"accumulatedInputTokens": 8586,
"accumulatedOutputTokens": 2,
"accumulatedTotalTokens": 8588,
"accumulatedCost": null
},
"billingState": {
"accumulatedInputTokens": 8586,
"accumulatedOutputTokens": 2,
"accumulatedCost": 0.00232042
},
"enrichedCost": 0.00232042
}
@@ -0,0 +1,18 @@
{
"schemaVersion": "goose-v149-deepseek-tools-v1",
"capturedAt": "2026-09-08T12:07:03.092Z",
"sessionId": "20260908_84",
"requestId": "24118cbb-ebe9-4f0f-9b8c-59a71cbbad28",
"providerId": "custom_tkmind_relay_deepseek",
"model": "deepseek-chat",
"toolNames": [
"todo__todo_write"
],
"finishReason": "stop",
"eventTypes": [
"Message",
"Message",
"Message",
"Finish"
]
}
@@ -0,0 +1,18 @@
{
"schemaVersion": "goose-v149-deepseek-tools-v1",
"capturedAt": "2026-09-08T12:07:06.888Z",
"sessionId": "20260908_89",
"requestId": "cbdfc7d5-48ee-4fe9-acf1-dd08bbba27b2",
"providerId": "custom_tkmind_relay_deepseek",
"model": "deepseek-chat",
"toolNames": [
"todo__todo_write"
],
"finishReason": "stop",
"eventTypes": [
"Message",
"Message",
"Message",
"Finish"
]
}
@@ -0,0 +1,18 @@
{
"schemaVersion": "goose-v149-deepseek-tools-v1",
"capturedAt": "2026-09-08T12:07:18.952Z",
"sessionId": "20260908_96",
"requestId": "23c5fcf6-e15a-4880-b0d1-c9fd0073c419",
"providerId": "custom_tkmind_relay_deepseek",
"model": "deepseek-chat",
"toolNames": [
"todo__todo_write"
],
"finishReason": "stop",
"eventTypes": [
"Message",
"Message",
"Message",
"Finish"
]
}
@@ -0,0 +1,18 @@
{
"schemaVersion": "goose-v149-deepseek-tools-v1",
"capturedAt": "2026-09-08T12:23:05.066Z",
"sessionId": "20260908_104",
"requestId": "c5f423b5-9dc0-474e-898c-a472a935386e",
"providerId": "custom_tkmind_relay_deepseek",
"model": "deepseek-chat",
"toolNames": [
"todo__todo_write"
],
"finishReason": "stop",
"eventTypes": [
"Message",
"Message",
"Message",
"Finish"
]
}
@@ -0,0 +1,18 @@
{
"schemaVersion": "goose-v149-deepseek-tools-v1",
"capturedAt": "2026-09-08T12:23:19.758Z",
"sessionId": "20260908_112",
"requestId": "0f40ec62-b124-4288-9518-040c4550e329",
"providerId": "custom_tkmind_relay_deepseek",
"model": "deepseek-chat",
"toolNames": [
"todo__todo_write"
],
"finishReason": "stop",
"eventTypes": [
"Message",
"Message",
"Message",
"Finish"
]
}
@@ -0,0 +1,18 @@
{
"schemaVersion": "goose-v149-deepseek-tools-v1",
"capturedAt": "2026-09-08T12:32:37.309Z",
"sessionId": "20260908_119",
"requestId": "3e3c39f4-6165-4e40-9b0a-d4785c969851",
"providerId": "custom_tkmind_relay_deepseek",
"model": "deepseek-chat",
"toolNames": [
"todo__todo_write"
],
"finishReason": "stop",
"eventTypes": [
"Message",
"Message",
"Message",
"Finish"
]
}
@@ -0,0 +1,18 @@
{
"schemaVersion": "goose-v149-deepseek-tools-v1",
"capturedAt": "2026-09-08T12:32:51.717Z",
"sessionId": "20260908_127",
"requestId": "8c1f27ac-e8c1-47c8-8835-9ae27d217adb",
"providerId": "custom_tkmind_relay_deepseek",
"model": "deepseek-chat",
"toolNames": [
"todo__todo_write"
],
"finishReason": "stop",
"eventTypes": [
"Message",
"Message",
"Message",
"Finish"
]
}
@@ -0,0 +1,18 @@
{
"schemaVersion": "goose-v149-deepseek-tools-v1",
"capturedAt": "2026-09-08T13:04:37.865Z",
"sessionId": "20260908_135",
"requestId": "c1360188-7f59-41e3-ba43-689fa8f7be1d",
"providerId": "custom_tkmind_relay_deepseek",
"model": "deepseek-chat",
"toolNames": [
"todo__todo_write"
],
"finishReason": "stop",
"eventTypes": [
"Message",
"Message",
"Message",
"Finish"
]
}
@@ -0,0 +1,18 @@
{
"schemaVersion": "goose-v149-deepseek-tools-v1",
"capturedAt": "2026-09-08T13:06:18.709Z",
"sessionId": "20260908_144",
"requestId": "328356c3-25b6-4d15-bd18-206dd7c803aa",
"providerId": "custom_tkmind_relay_deepseek",
"model": "deepseek-chat",
"toolNames": [
"todo__todo_write"
],
"finishReason": "stop",
"eventTypes": [
"Message",
"Message",
"Message",
"Finish"
]
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,14 @@
{
"schemaVersion": "goose-v149-message-sanitize-v1",
"capturedAt": "2026-09-08T13:06:26.641Z",
"v149": {
"passed": 11,
"root": "/Users/john/Project/tkmind_go-v149"
},
"v141": {
"passed": 11,
"ok": true,
"root": "/Users/john/Project/tkmind_go-v141-prod-prep"
},
"equivalent": true
}
@@ -0,0 +1,283 @@
{
"schemaVersion": "goose-v149-baseline-v1",
"capturedAt": "2026-09-08T10:02:06.825Z",
"target": "https://127.0.0.1:18049",
"status": {
"status": "ok"
},
"sessionsSummary": {
"count": 20,
"sample": [
{
"id": "20260908_20",
"working_dir": "/Users/john/Project/Memind",
"name": "pong",
"user_set_name": false,
"session_type": "user",
"created_at": "2026-09-08T10:02:00.279689Z",
"updated_at": "2026-09-08T10:02:02.146310Z",
"extension_data": {
"enabled_extensions.v0": {
"extensions": [
{
"type": "platform",
"name": "tom",
"description": "Inject custom context into every turn via GOOSE_MOIM_MESSAGE_TEXT and GOOSE_MOIM_MESSAGE_FILE environment variables",
"display_name": "Top Of Mind",
"bundled": true
},
{
"type": "platform",
"name": "skills",
"description": "Discover and provide skill instructions from filesystem and builtins",
"display_name": "Skills",
"bundled": true
},
{
"type": "platform",
"name": "aider",
"description": "Delegate multi-file coding tasks to Aider instead of developer write/edit",
"display_name": "Aider",
"bundled": true
},
{
"type": "platform",
"name": "Extension Manager",
"description": "Enable extension management tools for discovering, enabling, and disabling extensions",
"display_name": "Extension Manager",
"bundled": true
},
{
"type": "platform",
"name": "developer",
"description": "Write and edit files, and execute shell commands",
"display_name": "Developer",
"bundled": true
},
{
"type": "platform",
"name": "summon",
"description": "Load knowledge and delegate tasks to subagents",
"display_name": "Summon",
"bundled": true
},
{
"type": "platform",
"name": "openhands",
"description": "Delegate multi-file coding tasks to OpenHands instead of developer write/edit",
"display_name": "OpenHands",
"bundled": true
},
{
"type": "platform",
"name": "apps",
"description": "Create and manage custom Goose apps through chat. Apps are HTML/CSS/JavaScript and run in sandboxed windows.",
"display_name": "Apps",
"bundled": true
},
{
"type": "platform",
"name": "analyze",
"description": "Analyze code structure with tree-sitter: directory overviews, file details, symbol call graphs",
"display_name": "Analyze",
"bundled": true
},
{
"type": "platform",
"name": "todo",
"description": "Enable a todo list for goose so it can keep track of what it is doing",
"display_name": "Todo",
"bundled": true
}
]
}
},
"usage": {
"input_tokens": 8339,
"output_tokens": 2,
"total_tokens": 8341,
"cache_read_input_tokens": 0,
"cache_write_input_tokens": null
},
"accumulated_usage": {
"input_tokens": 8339,
"output_tokens": 2,
"total_tokens": 8341,
"cache_read_input_tokens": 0,
"cache_write_input_tokens": 0
},
"accumulated_cost": null,
"schedule_id": null,
"recipe": null,
"user_recipe_values": null,
"conversation": null,
"message_count": 3,
"last_message_at": "2026-09-08T10:02:01Z",
"provider_name": "custom_tkmind_relay_deepseek",
"model_config": {
"model_name": "deepseek-chat",
"temperature": null,
"max_tokens": null,
"toolshim": false,
"toolshim_model": null
},
"goose_mode": "auto",
"archived_at": null,
"project_id": null,
"parent_session_id": null,
"last_message_snippet": null
},
{
"id": "20260908_19",
"working_dir": "/Users/john/Project/Memind",
"name": "New Chat",
"user_set_name": false,
"session_type": "user",
"created_at": "2026-09-08T10:02:00.057531Z",
"updated_at": "2026-09-08T10:02:00.064842Z",
"extension_data": {
"enabled_extensions.v0": {
"extensions": [
{
"type": "platform",
"name": "openhands",
"description": "OpenHands coding delegate",
"display_name": null,
"bundled": null,
"available_tools": [
"code"
]
},
{
"type": "platform",
"name": "aider",
"description": "Aider coding delegate",
"display_name": null,
"bundled": null,
"available_tools": [
"code"
]
}
]
}
},
"usage": {
"input_tokens": null,
"output_tokens": null,
"total_tokens": null,
"cache_read_input_tokens": null,
"cache_write_input_tokens": null
},
"accumulated_usage": {
"input_tokens": null,
"output_tokens": null,
"total_tokens": null,
"cache_read_input_tokens": null,
"cache_write_input_tokens": null
},
"accumulated_cost": null,
"schedule_id": null,
"recipe": null,
"user_recipe_values": null,
"conversation": null,
"message_count": 0,
"last_message_at": null,
"provider_name": null,
"model_config": null,
"goose_mode": "auto",
"archived_at": null,
"project_id": null,
"parent_session_id": null,
"last_message_snippet": null
},
{
"id": "20260908_18",
"working_dir": "/Users/john/Project/Memind",
"name": "New Chat",
"user_set_name": false,
"session_type": "user",
"created_at": "2026-09-08T10:01:59.691171Z",
"updated_at": "2026-09-08T10:01:59.885622Z",
"extension_data": {
"enabled_extensions.v0": {
"extensions": [
{
"type": "stdio",
"name": "sandbox-fs",
"description": "MindSpace sandbox filesystem MCP (local v1.49 smoke)",
"cmd": "/opt/homebrew/Cellar/node/26.0.0/bin/node",
"args": [
"/Users/john/Project/Memind/mindspace-sandbox-mcp.mjs",
"/var/folders/wv/y43yzlmn7gd1bcsq1fcwx3b00000gn/T/goose-v149-sandbox-3ADPMS"
],
"envs": {
"ALLOWED_TOOLS": "write_file,read_file,edit_file,create_dir,list_dir",
"SANDBOX_ROOT": "/var/folders/wv/y43yzlmn7gd1bcsq1fcwx3b00000gn/T/goose-v149-sandbox-3ADPMS"
},
"env_keys": [],
"timeout": null,
"cwd": null,
"bundled": null,
"available_tools": [
"write_file",
"read_file",
"edit_file",
"create_dir",
"list_dir"
]
},
{
"type": "platform",
"name": "skills",
"description": "skills",
"display_name": null,
"bundled": null
}
]
}
},
"usage": {
"input_tokens": null,
"output_tokens": null,
"total_tokens": null,
"cache_read_input_tokens": null,
"cache_write_input_tokens": null
},
"accumulated_usage": {
"input_tokens": null,
"output_tokens": null,
"total_tokens": null,
"cache_read_input_tokens": null,
"cache_write_input_tokens": null
},
"accumulated_cost": null,
"schedule_id": null,
"recipe": null,
"user_recipe_values": null,
"conversation": null,
"message_count": 0,
"last_message_at": null,
"provider_name": null,
"model_config": null,
"goose_mode": "auto",
"archived_at": null,
"project_id": null,
"parent_session_id": null,
"last_message_snippet": null
}
]
},
"reply": {
"requestId": "a53113c2-8462-4b6d-94f6-0303f6eebea5",
"outcome": "error",
"events": [
{
"type": "Error",
"error": "Provider not set",
"chat_request_id": "a53113c2-8462-4b6d-94f6-0303f6eebea5",
"request_id": "a53113c2-8462-4b6d-94f6-0303f6eebea5"
}
],
"sessionId": "20260908_21"
}
}
@@ -0,0 +1,12 @@
{
"schemaVersion": "goose-v149-thinking-preservation-v1",
"capturedAt": "2026-09-08T13:06:25.818Z",
"worktree": "/Users/john/Project/tkmind_go-v149",
"tests": [
"test_deepseek_thinking_preserved_in_tool_call_message",
"test_kimi_thinking_preserved_in_tool_call_message",
"test_reasoning_preserved_when_combined_with_tool_call",
"test_multi_tool_response_preserves_reasoning_and_message_id_correlation"
],
"passed": 4
}
+109
View File
@@ -0,0 +1,109 @@
# Goose v1.49 本地 Canary 运行手册
> 范围:仅本机 loopback`:18049` vs `:18006`),**禁止**连接 103/105 或修改生产 launchd。
> 决策表:[TKMIND_V1_49_MIGRATION.md](../TKMIND_V1_49_MIGRATION.md)
## 架构
```text
Portal (pnpm dev :8081)
├─ TKMIND_GOOSE_CANARY=off → TKMIND_API_TARGET=https://127.0.0.1:18006 (stable 1.41)
├─ TKMIND_GOOSE_CANARY=all → TKMIND_API_TARGET=https://127.0.0.1:18049 (v1.49 候选)
└─ TKMIND_GOOSE_CANARY=users → 双 targetcanary 用户路由到 v1.49(需 USER_IDS
```
Memory 在 canary 期间保持 legacy ownerMySQL `h5_user_memory_items`),upstream goosed **不得**写入业务记忆。
## 前置条件
```bash
bash scripts/build-goosed-v149-local.sh
bash scripts/ensure-goose-v149-local-db.sh
bash scripts/run-goosed-v149-local.sh # 另开终端,监听 :18049
curl -sk https://127.0.0.1:18049/status # → ok
```
## 一键开启 Canary(全量切到 v1.49
```bash
bash scripts/switch-goose-v149-canary.sh on all
pnpm dev # 重启 Portal 以加载 .env.local
node scripts/check-goosed-v149-canary-portal.mjs # 隔离 :8083 验证(不干扰主 Portal
```
> 已写入 `.env.local` canary 块。主 Portal `:8081` 须重启后才路由到 `:18049`。
> v1.49 已移植 `platform/web``web_search` / `fetch_url`);验证:`node scripts/check-goosed-v149-web-smoke.mjs`。
脚本会在 `.env.local` 写入 `# GOOSE_V149_CANARY_BEGIN … END` 块,设置:
- `TKMIND_GOOSE_CANARY=all`
- `TKMIND_API_TARGET=https://127.0.0.1:18049`
- Memory 安全开关(legacy / vector off
- `H5_PUBLIC_BASE_URL=http://127.0.0.1:8081`(避免 Vite `:5173` 未起时页面链接失效)
## 按用户 Canary(双 target
```bash
export TKMIND_GOOSE_CANARY_USER_IDS="<your-user-uuid>"
bash scripts/switch-goose-v149-canary.sh on users
pnpm dev
```
Portal 会注册 `[18006, 18049]` 双 targetcanary 用户的新 session 应 pin 到 v1.49(见 `scripts/goose-v149-canary.mjs` + `release-gate/canary-routing.mjs`)。
## 一键回滚(回落 stable
```bash
bash scripts/switch-goose-v149-canary.sh rollback
pnpm dev
```
回滚仅移除 canary 块,不删 v1.49 进程或 PG 库;stable `:18006` 保持可用。
## 验证
```bash
# Phase 2 聚合(推荐日常)
node scripts/run-goosed-v149-phase2.mjs
# Phase 3 收口(合并前)
node scripts/run-goosed-v149-phase3.mjs
# 单项 / 全量 smoke
node scripts/check-goosed-v149-all.mjs
node scripts/check-goosed-v149-memory-loop.mjs
node scripts/check-goosed-v149-portal-smoke.mjs # 主 Portal 重启后
node scripts/check-goosed-v149-canary-portal.mjs # 隔离 :8083
```
## Phase 0 基线采集
```bash
# Runtime 样本(status / sessions / 可选 SSE reply
node scripts/capture-goose-v149-baseline.mjs
# Memory manifest(每用户 ID/hash/evidence/source session
node scripts/export-goose-v149-memory-manifest.mjs --output docs/baselines/goose-v149-memory-latest.json
```
输出目录:`docs/baselines/`(git 可跟踪的本地基线 JSON)。
## 环境变量参考
见 [goose-v149-canary.env.example](./goose-v149-canary.env.example)。
| 变量 | 说明 |
|---|---|
| `TKMIND_GOOSE_CANARY` | `off` / `all` / `users` |
| `TKMIND_API_TARGET_STABLE` | 默认 `https://127.0.0.1:18006` |
| `TKMIND_API_TARGET_V149` | 默认 `https://127.0.0.1:18049` |
| `TKMIND_GOOSE_CANARY_USER_IDS` | 逗号分隔 user UUIDusers 模式) |
| `GOOSE_V149_CANARY_ENV_FILE` | 默认 `.env.local` |
## 禁止项
- 不得把 `TKMIND_API_TARGET` 指向 103/105 公网或生产端口
- 不得在本阶段修改 103 launchd、105 nginx
- 不得启用 upstream Memory backend 写入 MySQL 业务表
- 生产发布仍须走 `main` + `check-release-ready.sh` + Gate report
+25
View File
@@ -0,0 +1,25 @@
# Goose v1.49 local canary example.
# Copy into .env.local via: bash scripts/switch-goose-v149-canary.sh on all
# Rollback: bash scripts/switch-goose-v149-canary.sh rollback
# --- Full canary (all Portal traffic → v1.49) ---
# TKMIND_GOOSE_CANARY=all
# TKMIND_API_TARGET=https://127.0.0.1:18049
# TKMIND_API_TARGET_STABLE=https://127.0.0.1:18006
# TKMIND_API_TARGET_V149=https://127.0.0.1:18049
# --- Per-user canary ---
# TKMIND_GOOSE_CANARY=users
# TKMIND_API_TARGET=https://127.0.0.1:18006
# TKMIND_API_TARGETS=https://127.0.0.1:18006,https://127.0.0.1:18049
# TKMIND_GOOSE_CANARY_USER_IDS=replace-with-user-uuid
# Memory safety (Phase 01)
MEMORY_BACKEND=legacy
MEMORY_VECTOR_ENABLED=0
MEMORY_AGENT_INJECTION_MODE=off
MEMORY_LIFECYCLE_ENABLED=0
MEMORY_CANDIDATE_ENABLED=0
# Goose v1.49 local session DB (isolated from production memind_sessions)
# GOOSE_SESSION_DB_URL=postgresql://john@127.0.0.1:5432/goose_sessions_v149_dev
+77
View File
@@ -0,0 +1,77 @@
# Goose v1.49 本地升级 Phase 0 基线
> 范围:仅本机 loopback,禁止连接 103/105 或生产库。
> 决策表:[TKMIND_V1_49_MIGRATION.md](../TKMIND_V1_49_MIGRATION.md)
## Stable 基线(103 生产,只读对照)
| 项 | 值 |
|---|---|
| goosed 版本 | `goose-server 1.41.0` |
| 二进制 | `tkmind_go-native/releases/goosed-20260729-v141-m1` |
| 源码分支 | `upgrade/goose-1.47`TKMind 补丁) |
| 端口 | `18006``18014`native launchd × 9 |
| Session PG | `memind_sessions` @ `127.0.0.1:5432`103 |
| 记录时间 | 2026-09-08 |
## v1.49 候选(本地隔离)
| 项 | 值 |
|---|---|
| worktree | `/Users/john/Project/tkmind_go-v149` |
| 分支 | `upgrade/v1.49-tkmind` |
| upstream tag | `v1.49.0` |
| 端口 | `18049` |
| 启动命令 | `goose serve --tls`**v1.49 已移除 `goosed`/`goose-server`**,改为 ACP HTTP/WS |
| Session PG | `goose_sessions_v149_dev` @ `127.0.0.1:5432` |
| XDG 隔离 | `/tmp/goose-v149-runtime/{config,data,state,cache}` |
| Portal 目标 | `TKMIND_API_TARGET_V149=https://127.0.0.1:18049`(独立 H5 进程时使用) |
## Memory 本地策略(Phase 1 初始)
```bash
MEMORY_BACKEND=legacy
MEMORY_VECTOR_ENABLED=0
MEMORY_AGENT_INJECTION_MODE=off
MEMORY_LIFECYCLE_ENABLED=0
MEMORY_CANDIDATE_ENABLED=0
```
upstream / v1.49 goosed 不得写入 MySQL 业务记忆;只走现有 Portal `conversation-memory` owner。
## Phase 0 检查项
- [x] 记录 stable 1.41 基线
- [x] 拉取 upstream `v1.49.0` tag
- [x] 创建 `upgrade/v1.49-tkmind` worktree
- [x] 创建隔离 PG 库 `goose_sessions_v149_dev`
- [x] 编译 v1.49 goosed 并通过 `GET /status``goose 1.49.0` @ `:18049``ok`
- [x] 移植 legacy REST`/agent/start``/sessions``/sessions/{id}/events|reply|cancel` 等,见 `tkmind_compat`
- [x] 导出 Memory manifest`node scripts/export-goose-v149-memory-manifest.mjs``docs/baselines/goose-v149-memory-latest.json`
- [x] 保存 runtime 基线样本(`node scripts/capture-goose-v149-baseline.mjs``docs/baselines/`
- [x] Canary 开关与一键回滚(`scripts/switch-goose-v149-canary.sh`,见 [goose-v149-canary-runbook.md](./goose-v149-canary-runbook.md)
## Phase 1 验证
```bash
node scripts/run-goosed-v149-phase2.mjs # Phase 2 聚合(cost + drift + phase1
node scripts/check-goosed-v149-cost-smoke.mjs # Finish token_state / billing
node scripts/compare-goose-v149-memory-manifest.mjs
bash scripts/switch-goose-v149-canary.sh on all # 全量 canary 后重启 pnpm dev
```
```bash
bash scripts/ensure-goose-v149-vendor.sh # 离线 vendorGitHub 不可用时)
bash scripts/build-goosed-v149-local.sh # 编译 goose CLI(首次较久)
bash scripts/ensure-goose-v149-local-db.sh
bash scripts/run-goosed-v149-local.sh # goose serve @ 18049
curl -sk https://127.0.0.1:18049/status
```
> v1.49 已通过 `crates/goose/src/acp/tkmind/` 重移植 legacy REST;聚合验证:`node scripts/check-goosed-v149-all.mjs`。
## 禁止项
- 不得使用生产 `DATABASE_URL``GOOSE_SESSION_DB_URL`(含 RDS / 103 / 105 地址)
- 不得修改 103 launchd、105 nginx、Portal `:8081`
- 不得在本阶段删除 1.41 定制或 `:18036` DeepSeek 代理
+87
View File
@@ -0,0 +1,87 @@
# Goose v1.49 本地编译所需下载链接
> TKMind 需要 **code-mode**(含 v8)。本机 GitHub SSL 不稳定时,可手动下载后放入 cargo 缓存。
## 默认编译(含 code-mode,推荐)
```bash
bash scripts/build-goosed-v149-local.sh
# 等价 GOOSE_V149_BUILD_MODE=codemode
```
会先执行 `scripts/ensure-goose-v149-v8.sh` 确保 v8 缓存就绪。
## 方案 A:仅跳过 local-inference 的 code-mode 编译
```bash
cd /Users/john/Project/tkmind_go-v149
bash ../Memind/scripts/ensure-goose-v149-v8.sh
rustup run 1.96.1 cargo build --release --package goose-cli --bin goose \
--no-default-features --features rustls-tls,aws-providers,system-keyring,code-mode,telemetry
```
## 方案 B:完整 default features(含 v8 + local-inference
```bash
GOOSE_V149_BUILD_MODE=full bash scripts/build-goosed-v149-local.sh
```
### rusty_v8 静态库(code-mode 必需)
| 项 | 值 |
|---|---|
| 文件 | `librusty_v8_release_aarch64-apple-darwin.a.gz` |
| 直链 | https://github.com/denoland/rusty_v8/releases/download/v145.0.0/librusty_v8_release_aarch64-apple-darwin.a.gz |
| Release 页 | https://github.com/denoland/rusty_v8/releases/tag/v145.0.0 |
**重要**cargo/rusty_v8 要求缓存路径是**单个 gzip 文件**,不是目录:
```text
~/.cargo/.rusty_v8/https___github_com_denoland_rusty_v8_releases_download_v145_0_0_librusty_v8_release_aarch64_apple_darwin_a_gz
```
(该路径本身就是文件,不要再套一层子目录。)
手动下载命令:
```bash
bash scripts/ensure-goose-v149-v8.sh
```
或:
```bash
CACHE_FILE="$HOME/.cargo/.rusty_v8/https___github_com_denoland_rusty_v8_releases_download_v145_0_0_librusty_v8_release_aarch64_apple_darwin_a_gz"
mkdir -p "$(dirname "$CACHE_FILE")"
curl -fL --retry 5 -o "$CACHE_FILE" \
"https://github.com/denoland/rusty_v8/releases/download/v145.0.0/librusty_v8_release_aarch64-apple-darwin.a.gz"
```
## 方案 Cminimal(无 code-mode,仅调试 serve 路由)
```bash
GOOSE_V149_BUILD_MODE=minimal bash scripts/build-goosed-v149-local.sh
```
### 2. Git 依赖(已用 file:// 本地 hermit 缓存,一般不需再下)
| 依赖 | 本地路径 |
|---|---|
| agent-client-protocol rust-sdk | `tkmind_go-v141-prod-prep/.hermit/rust/git/checkouts/rust-sdk-.../c97a520` |
| cudaforge | `tkmind_go-v141-prod-prep/.hermit/rust/git/checkouts/cudaforge-.../e7c1967` |
已在 `tkmind_go-v149/Cargo.toml``[patch.crates-io]` 指向 `file://`
### 3. upstream Goose 源码
| 项 | 链接 |
|---|---|
| Tag v1.49.0 | https://github.com/aaif-goose/goose/releases/tag/v1.49.0 |
## 验证
```bash
bash scripts/build-goosed-v149-local.sh # 或方案 A 命令
bash scripts/run-goosed-v149-local.sh
curl -sk https://127.0.0.1:18049/status
```
+45
View File
@@ -0,0 +1,45 @@
# Goose v1.49 Phase 2 证据台账
> 本地 loopback only。每项记录 **通过 / 保留 / 回滚**,禁止批量删除定制。
## 执行入口
```bash
node scripts/run-goosed-v149-phase2.mjs
node scripts/check-goosed-v149-cost-smoke.mjs # 需 DEEPSEEK_API_KEY
node scripts/compare-goose-v149-memory-manifest.mjs # 对比 baseline drift
node scripts/check-goosed-v149-deepseek-tools.mjs # 需 DEEPSEEK_API_KEY
node scripts/compare-goose-v149-message-sanitize.mjs # provider sanitize 单测对照
node scripts/check-goosed-v149-thinking-preservation.mjs
node scripts/check-goosed-v149-skills-smoke.mjs
```
## 证据项
| 项 | 脚本/产物 | 状态 | 备注 |
|---|---|---|---|
| Phase 1 聚合 smoke | `check-goosed-v149-all.mjs` | 通过 | 2026-09-08 |
| Finish token_state + cost | `check-goosed-v149-cost-smoke.mjs` | 通过 | in=8339 out=2 cost≈0.00225 USD |
| Memory manifest 无 drift | `compare-goose-v149-memory-manifest.mjs` | 通过 | digest=69b48723… |
| Memory fail-open (legacy) | `check-goosed-v149-memory-failopen.mjs` | 通过 | 超时 degrade 不抛错 |
| Portal canary 联调 | `check-goosed-v149-canary-portal.mjs` | 通过 | 隔离 Portal :8083 → v149 :18049 |
| Session reconcile 路由 | `check-goosed-v149-session-reconcile.mjs` | 通过 | GET /sessions/{id}, add/remove/restart |
| platform/web 扩展 | `check-goosed-v149-web-smoke.mjs` | 通过 | web_search + fetch_url 工具挂载 |
| DeepSeek 工具轮次 | `check-goosed-v149-deepseek-tools.mjs` | 通过/可选 | 需 DEEPSEEK_API_KEYtodo_write Finish |
| DeepSeek reasoning 保留 | `check-goosed-v149-thinking-preservation.mjs` | 通过 | agent 集成测试 4 项 |
| upstream web-search 路径 | `check-goosed-v149-skills-smoke.mjs` | 通过 | skills/load_skill(与 platform/web 并存) |
| message sanitize 对照 | `compare-goose-v149-message-sanitize.mjs` | 通过 | goose-providers sanitize 单测 v1.41≈v1.49 |
| DeepSeek no-think 代理 `:18036` | — | 保留 | 生产 parityPortal session-reply-wait 仍启用 |
## 回滚
```bash
bash scripts/switch-goose-v149-canary.sh rollback
pnpm dev
```
## 判定规则
- **通过**:脚本 OK + 证据 JSON 已写入 `docs/baselines/`
- **保留**:行为不等价或缺证据,维持 1.41 / 定制实现
- **回滚**:canary 一切换即异常,执行 rollback 脚本
+62
View File
@@ -0,0 +1,62 @@
# Goose v1.49 Phase 3 收口清单
> **本阶段不授权 103/105 操作。** 仅整理证据、回滚路径与合并前闸门。
> 决策表:[TKMIND_V1_49_MIGRATION.md](../TKMIND_V1_49_MIGRATION.md) · Phase 2 台账:[goose-v149-phase2-evidence.md](./goose-v149-phase2-evidence.md)
## 聚合验证入口
```bash
bash scripts/run-goosed-v149-local.sh # :18049 另开终端
node scripts/run-goosed-v149-phase3.mjs # Phase 2 + parity + drift
```
通过时应输出 **`GOOSE_V149_PHASE3_OK`**。
## 已取得证据(可进入合并讨论,不可直接删代码)
| 能力 | 证据 | Phase 3 结论 |
|---|---|---|
| tkmind_compat / PG Session | `check-goosed-v149-all.mjs` | **Keep** — 已适配 v1.49 |
| sandbox-fs / harness | harness + sandbox-fs smoke | **Keep** |
| platform/web | `check-goosed-v149-web-smoke.mjs` | **Keep** — 已移植;upstream skill 并存 |
| skills / web-search | `check-goosed-v149-skills-smoke.mjs` | **Wrapper** — 不替换 platform/web |
| DeepSeek Finish + 工具轮次 | provider + deepseek-tools smoke | **Keep** relay + v1.49 runtime |
| reasoning 保留 | `check-goosed-v149-thinking-preservation.mjs` | **保留** `:18036` 直至生产 parity |
| message sanitize | `compare-goose-v149-message-sanitize.mjs` | provider 层等价;Portal 展示仍 Keep |
| Memory legacy owner | memory-loop + drift + failopen | **Keep** — 禁止 upstream 写 MySQL |
| 计费 token_state | cost-smoke JSON | **Wrapper** — 观察 v1.49 原生 cost |
## 明确禁止在本阶段删除
- `session-reply-wait.mjs`、H5 SSE 游标映射、Finish merge
- `deepseek-no-think-proxy.mjs` / `:18036`(生产 parity 未退役)
- MindSpace / Page Data / 微信 / 计费 / capabilities 策略
- 1.41 stable 端口池 `:18006``:18014` 与 launchd 配置
## 合并前闸门(Memind 仓库)
1. [ ] `upgrade/v1.49-tkmind` worktree 变更已 review`tkmind_go-v149` 独立仓库/worktree
2. [ ] Memind 侧脚本/文档/canary 开关 PR 就绪
3. [ ] `node scripts/run-goosed-v149-phase3.mjs` 连续通过
4. [ ] Memory manifest 无 drift`compare-goose-v149-memory-manifest.mjs`
5. [x] 主 Portal 重启后 canary 联调:`check-goosed-v149-portal-smoke.mjs`targets 含 `:18049`);canary 块强制 `H5_PUBLIC_BASE_URL=http://127.0.0.1:8081` 避免 5173 未起时链接失效
6. [ ] **用户明确批准** 后才:`git push`、合并 `main`、任何 103 构建
## 合并后 / 生产前(仍须单独审批)
- `bash scripts/check-release-ready.sh`
- 生产 Gate reportCore + Impact
- `npm run verify:mindspace-publish-guards:full` 等受保护路径 verify
- Memory owner 切换:**不在** v1.49 运行时升级包内默认开启
## 回滚(任意时刻)
```bash
bash scripts/switch-goose-v149-canary.sh rollback
pnpm dev
# stable :18006 继续服务;v1.49 :18049 可单独停进程
```
## 基线产物索引
见 [docs/baselines/README.md](./baselines/README.md)。