AIObox
← Mọi hướng dẫn

AIObox × AkiMCP

Một cửa sổ được gọi tên ra sao

Handle, chatId và targetId: cách AI trong nhiều cửa sổ gọi nhau qua AkiMCP, vẽ đủ mọi nhánh.

Nhiều AI chạy song song trong nhiều cửa sổ, trên nhiều provider. Muốn chúng phối hợp, mỗi cửa sổ cần một cái tên bền. Tên đó không được đổi khi chat đổi, tab đổi hay Chrome khởi động lại.

Cửa sổ được gọi bằng handle P#·W#. chatId chỉ là chat đang mở trong cửa sổ đó. targetId chỉ là tab Chrome của lần chạy này. AkiMCP là bên duy nhất dịch tên ra tab thật.

1. Các tầng

Người dùng và AI nói bằng handle. AIObox giữ sổ tên, AkiMCP tra sổ rồi chạm vào đúng tab.

Các tầng giữa người dùng và một tabNgười dùng"nhắn P2·W2"AI chatchạy trong 1 cửa sổhandle P2·W1AkiMCPMCP server localaki__aioboxaki__aiobox_writewindows.refresh1 dòng idakimcp-pid-ms-nflags.jsonchat cutoff (Resting)AIObox desktopRust core · registryNumberingwindows.jsonprofiles · windows · tabsretired[] · epoch · generationghi atomichandles.jsonbộ đếm W · retiredchỉ AIObox đọc/ghiChrome profile P#port CDP riêngtitle: "P2·W1 · account · …"Tab providerwindow.akipanelNotion · ChatGPT · GrokClaude · Geminihandleop=… window= from= expect=đọcghi khi thiếuCDP vào targetId≤1 s xoá + đọc lạianswered=idgiữ sốghimở / adopt
AkiMCP đọc sổ tên do AIObox ghi, xin refresh khi thiếu, rồi CDP thẳng vào targetId. AI không bao giờ chạm file của AIObox, ngoài windows.refresh và các file yêu cầu trong requests/ (qua op).
  • AIObox desktop (Rust core) mở và quản lý Chrome, đặt handle vào tiêu đề trang, ghi sổ tên.
  • ~/.aki/aiobox/cdp/windows.json: sổ tên (profiles → windows → tabs, kèm retired[]). AIObox ghi atomic, AkiMCP đọc.
  • windows.refresh: AkiMCP ghi một dòng id để xin đọc lại; AIObox trả lời trong ≤1 s. handles.json: bộ đếm W, chỉ AIObox đọc ghi.

2. Ba loại id

Mỗi id có một vai. Nhầm vai là gửi nhầm chat. Các id dưới đây là ví dụ giả.

  • Handle, tên bền (P2·W1). Là gì: P = số profile, W = số cửa sổ theo bộ đếm của profile. Sống: không bao giờ cấp lại; giữ qua restart AIObox và qua session restore. Dùng: gọi cửa sổ trong mọi op (window=).
  • chatId, chat hiện tại (1a2b3c4d…9f0e). Là gì: chat đang mở. Notion ?t=, ChatGPT/Grok /c/, Claude /chat/, Gemini /app/. Sống: đổi khi mở chat mới, đổi workspace hoặc handoff. Dùng: from= (chính mình), expect= (chắc đúng chat).
  • targetId, tab Chrome (0D1E2F3A…). Là gì: tab CDP của lần chạy Chrome này. Sống: một lần chạy. Dùng: nội bộ, AkiMCP gửi CDP vào tab này.

Ví dụ: P2·W1 chuyển sang workspace mới để có thêm quota. chatId đổi, handle vẫn là P2·W1, nên mọi cửa sổ khác vẫn gọi đúng.

Profile, cửa sổ, handle, tab, chatProfile P2registry · số cố địnhid bền: chrome-profile-8Window W1bộ đếm của profilekhông cấp lạihandle P2·W1tên bền · gọi cửa sổwindow=P2·W1targetId0D1E2F3A…tab CDP · 1 lần chạy ChromeurlNotion ?t= · ChatGPT/Grok /c/Claude /chat/ · Gemini /app/chatId1a2b3c4d…9f0echat đang mở · from= / expect=chatId cũ 5e6f7a8b…workspace đã 87%hiện trỏtáchđổi workspace, handle giữ
Profile → Window → handle là tên bền. handle trỏ tới targetId hiện tại; chatId tách từ url của tab đó. Các id ở đây là ví dụ giả.

3. Một cửa sổ biết gì

Không đọc file, không đoán theo tiêu đề: cửa sổ hỏi AkiMCP.

  • Chính mình: op=whoami trả handle, chatId, workspace ({ id, label, status }) và usage (session, weekly %, readAt). Mọi cửa sổ: cùng các trường đó trong op=state.
  • Chưa biết: usage hoặc workspace là null, usageWhy nói lý do. Không bao giờ suy từ tiêu đề.
  • Đi đâu: op=state workspaces, theo profileId: label, status, session, weekly của từng workspace.
  • Quota: đọc usage trong op=whoami; từ 82% giữ state; tới 87% AIObox tự chuyển chat.
  • Chat cutoff, kiểu đọc, busy: op=state.
  • Lượt automation: aki__aiobox op=runs liệt kê các lượt automation của AIObox (đọc usage, connect-akimcp) kèm kết quả; lọc theo automation, since, last.
aki__aiobox op=whoami
  quote="20+ ký tự của tin người dùng"
→ you: {
    handle: "P2·W1", chatId: "1a2b3c4d…9f0e",
    workspace: { id, label: "Team", status: null },
    usage: { session: 41, weekly: 63, readAt }
  }

4. Một lệnh window=X đi qua những đâu

X có thể là handle, chatId hay targetId. Mỗi bước chỉ có một lối ra đúng; các lối khác dừng lại kèm mã lỗi.

Mọi nhánh của một lệnh gửiop=send window=Xfrom=myChatIdexpect=? · textX là gì?Tra windows.jsonhandle · chatId · targetIdThấy tab sống?stale_maptitle mang handle khácRefreshghi windows.refresh, chờanswered=id, generation↑hoặc epoch đổi · ≤5 sThấy sau refresh?no_windowkhông đoán từ bản cũTheo retired[]handle nghỉ → người kếkết quả có resolvedFromChuỗi tới cửa sổđang mở?retired_loopA→B→Ano_windowkèm chuỗiexpect khớp?wrong_windowđang mở chat khácfrom = đích?self_targetkhông gửi cho chính mìnhÔ soạn đích có nháp?không đụng nhápsend v2: queued: true,positionv1: draft → thử lại wait=sread mode của provider?wait=s rồi gửiGemini · thiếu wait: busyGửi ngaykể cả giữa lượtmidAnswer: true"still answering"= AkiMCP cũ: run_macroconnect-akimcp reconnect,rồi gửi lạiop=read last=2thấy tin thì mới báo"đã tới"handle | chatId | targetIdkhônglệch titlecókhôngcóđã nghỉcóvòngđã đónglệchkhớp / không có expectcókhôngcókhônglive · queuedblockedlỗi
Toàn bộ nhánh của một lệnh gửi. Mỗi ô đỏ là một mã lỗi có tên trong kết quả, không bao giờ âm thầm đoán.

lệnh vàođúng đườngchờ, xếp hàng hoặc reconnectdừng, báo lỗi

Từng bước, kèm lệnh thật

  1. Tra sổ. windows.json tìm X theo handle, chatId hoặc targetId. Tab có tiêu đề mang handle khác: stale_map.
  2. Chưa thấy thì xin refresh. Ghi windows.refresh, chờ answered/generation mới (≤5 s), tra lại. Không ai giữ X: no_window.
  3. Handle đã nghỉ thì theo người kế. Đi theo retired[] tới cửa sổ còn mở. Kết quả ghi resolvedFrom. Handle đang sống luôn thắng. Vòng A→B→A: retired_loop.
  4. Kiểm expect. Cửa sổ phải còn mở đúng chat mong đợi. Khác: wrong_window.
  5. Không gửi cho chính mình. from trùng đích: self_target.
  6. Không đụng nháp. Nháp trong ô soạn của đích không bao giờ bị đụng. Send v2 (capabilities.send 2) xếp hàng tin: queued: true kèm position. Send v1 từ chối mã draft; thử lại ngay trong lượt bằng wait=<s>.
  7. Gửi theo kiểu đọc của provider. live/queued: gửi ngay, kể cả giữa lượt. blocked: wait= rồi gửi; thiếu nó thì đích báo busy.
  8. Đọc lại mới tính là tới. op=read last=2 thấy tin thì mới báo “đã tới”.
aki__aiobox_write op=send
  window=P2·W2
  from=1a2b3c4d…9f0e
  text="[P2·W1 → P2·W2] …"
→ sent, delivered, midAnswer

aki__aiobox op=read
  window=P2·W2 last=2
→ thấy tin → “đã tới”

5. Mỗi provider nhận tin một kiểu

Không chặn gửi chung một luật. Bên gửi biết đích đọc tin ra sao.

ProviderKiểu đọcGửi lúc đang trả lờiAI đích thấy tin
Notion, ChatGPT, GrokliveĐượcNgay, giữa lượt
ClaudequeuedĐượcSau khi hết lượt
GeminiblockedKhôngKhi hết busy (bên gửi wait=)

Riêng Notion: một profile là một tài khoản có nhiều workspace, quota tính theo workspace. Từ 82% giữ state; tới 87% AIObox tự chuyển chat.

Chọn tài khoản và workspace Notion

Chọn tài khoản và workspace Notionaki__aiobox op=stateworkspacestheo profileId: label, status,session, weeklyBỏ quaworkspace có status (free, failed)và mọi chat cutofftrong op=stateĐo mức dùngworkspace = max(session,weekly)tài khoản = workspace thấp nhấtChọn tài khoản thấp nhấtdưới 87%, từ 82% xếp cuốihoà → ít cửa sổ hơnProfile có cửa sổ Notiontrong op=state?Lấy tài khoản kế tiếpop=new_window từ cửa sổ đóAI không bao giờlaunch profileWorkspace thấp nhất (0% trước)sai thì đổi bằng sidebartrước tin đầucókhông
Luật chọn chỗ cho cửa sổ hoặc chat mới: tài khoản còn nhiều quota nhất, rồi workspace thấp nhất của nó. Thứ đang trong chat cutoff không nhận chat mới, kể cả một tin.

Chat cutoff ("Resting")

Chat cutoff, trong AIObox hiện là "Resting", là trạng thái bình thường và tạm thời: một tài khoản hoặc workspace nghỉ ngắn, không nhận chat AI mới (hết quota, 2 lượt trả lời liền bị Interrupted, hoặc nhà cung cấp tạm dừng lượt dùng). Nó tự hết lúc until, hoặc khi lượt dùng có lại; trong lúc đó AIObox mở việc chat AI mới trên tài khoản khác. Vào workspace, reconnect AkiMCP, đọc usage và quản trị tài khoản vẫn làm bình thường. op=profiles cho thấy canTakeChat và chatPause.

Báo AIObox điều bạn thấy: aki__aiobox_write op=pause_chat reason=<chat hiện gì>, kèm:

  • Trả lời bị Interrupted trên ≥2 cửa sổ cùng tài khoản: account=<label> profile=<profileId> hours=4.
  • Hết quota hoặc thông báo tạm dừng trên 1 workspace: workspace=<label>. Tài khoản cũng nghỉ khi chuyện này lặp trên ≥2 workspace của nó.
  • op=resume_chat kết thúc một mục.

Rồi AIObox chuyển ngay mọi cửa sổ ở đó sang tài khoản khác.

6. Handoff không làm mất tên

Hai kiểu handoff. Cả hai đều kết thúc ở cùng một bất biến: ai gọi tên cũ vẫn tới đúng người đang giữ vai.

Hai kiểu handoffA. Sang cửa sổ khác(subtask / đổi profile)op=new_windowcùng profile + providerhoặc op=new_chat cửa sổ rảnhop=send tin đầuvai + handle của mìnhop=read <old> last=30Kế nhiệm: whoamiđổi tên chattrả lời handle + chatIdplace_likeP4·W10 like P8·W13lấy vị trí + kích thướcclose_window P8·W13successor=P4·W10từ chối: đang trả lời / nhápcửa sổ chết vẫn đóng đượcretired {P8·W13 → P4·W10}chỉ khi có successor=windows.jsonop=… window=P8·W13→ tới P4·W10resolvedFrom: P8·W13B. Cùng cửa sổ(workspace Notion ≥87%)Theo dõiusage trong op=whoamisession | weekly ≥ 87Đổi workspacesidebar Notion, trong tab mìnhkhông op=new_chatop=read thấychat trốngop=send window=<my targetId>không fromvai + chatId cũKế nhiệm xác nhận chatIdtrong op=state · handle giữkhông có gì để place/close
A chỉ xong khi đủ cả ba: place_like, kế nhiệm đọc hết, close_window có successor=. B không có gì để place hay close.

Sang cửa sổ khác: P8·W13 → P4·W10

  1. place_like window=P4·W10 like=P8·W13: cửa sổ mới vào đúng chỗ cửa sổ cũ.
  2. close_window window=P8·W13 successor=P4·W10: AIObox ghi retired {P8·W13 → P4·W10}. Không có successor thì không ghi gì. Từ chối khi cửa sổ đang trả lời hoặc có nháp; cửa sổ chết (không ô soạn, không đang trả lời) vẫn đóng được.
  3. Ai gọi P8·W13 sẽ tới P4·W10, kết quả có resolvedFrom.

Cùng cửa sổ: P2·W1, chatId 5e6f7a8b… → 1a2b3c4d…

  1. usage trong op=whoami tới 82%: giữ state; tới 87% AIObox tự chuyển chat.
  2. op=read thấy chat trống thì op=send window=<targetId của mình> không kèm from: vai và chatId cũ.
  3. Kế nhiệm xác nhận chatId trong op=state. Handle giữ nguyên; không có gì để place hay close.

7. Năm luật bất biến

  1. Gọi nhau bằng handle. chatId không bao giờ là khoá theo dõi.
  2. Handle đang sống luôn thắng cạnh retired cũ; cạnh tạo vòng bị từ chối.
  3. AI chỉ đi qua op của AkiMCP: không script, không CDP thô, không đóng tab bằng devtools.
  4. AkiMCP chỉ ghi hai thứ vào thư mục của AIObox: windows.refresh và các file yêu cầu trong requests/. flags.json chỉ được đọc.
  5. Chưa op=read thấy tin thì chưa coi là “đã tới”. Nháp của người khác không bao giờ bị đụng.