สะพานแชท
ผู้ให้บริการ
หัวข้อที่มีชื่อว่า “ผู้ให้บริการ”| ผู้ให้บริการ | ค่าไวร์ | ทรานสปอร์ต | ชนิดสเปซ |
|---|---|---|---|
| Slack | slack |
Socket Mode (WSS ขาออก) | slack |
Slack เป็นผู้ให้บริการรายเดียว ChatProvider เป็น enum ที่มีค่าเดียว และ cc_server ลงทะเบียนปลั๊กอินหนึ่งตัว สคีมา การค้นหาทุกครั้ง และดัชนีไม่ซ้ำทุกตัวใช้ provider เป็นคีย์ ID จึงไม่ข้ามผู้ให้บริการกัน และการเพิ่มผู้ให้บริการอีกรายเป็นการเปลี่ยนแปลงฝั่งเซิร์ฟเวอร์เท่านั้น
หนึ่งแอปฝั่งผู้ให้บริการต่อ เวิร์กสเปซ × ผู้ให้บริการ
คำสั่งแชท
หัวข้อที่มีชื่อว่า “คำสั่งแชท”ชื่อคำสั่งเป็นรายแอป (cc โดยค่าเริ่มต้น จึงเป็น /cc) ผู้ดูแลระบบเปลี่ยนชื่อได้ สะพานจะใช้ชื่อที่มันถูกเรียกครั้งล่าสุด
| คำสั่ง | ผลลัพธ์ |
|---|---|
/cc help (หรือ /cc โดยไม่มีคำกริยา) |
แสดงรายการสิ่งที่บอททำได้ |
/cc link <code> |
แลกรหัสใช้ครั้งเดียวและลิงก์ตัวตนแชทของผู้ส่ง |
/cc ticket <title> |
สร้างตั๋วงานภายในที่รายงานโดยสมาชิกที่ลิงก์ไว้ |
/cc ticket <title> | <description> |
เหมือนข้างบน โดยทุกอย่างหลัง | ตัวแรกเป็นคำอธิบาย |
| คำกริยาที่ไม่รู้จัก | รายงานคำกริยาที่ไม่รู้จักพร้อมข้อความช่วยเหลือ |
วิธีเข้าถึงเอเจนต์:
| ท่าทาง | พฤติกรรม |
|---|---|
@bot … ในแชนเนล |
เปิดเธรด Slack และสเปซ Control Center ที่เข้าคู่กัน; เอเจนต์ตอบในเธรด Slack นั้น |
| ตอบกลับในเธรด Slack ที่เชื่อมสะพานไว้ | ทำบทสนทนาเดิมในสเปซ Control Center เดียวกันต่อ — ไม่ต้องกล่าวถึง |
| ข้อความธรรมดาในแชนเนล | ถูกละเว้น (ไม่ได้ส่งถึงบอทและไม่ได้อยู่ในเธรด Slack ที่เชื่อมสะพานไว้) |
| ส่งข้อความส่วนตัวถึงบอท | หนึ่งสเปซ Control Center ต่อการสนทนาทางข้อความส่วนตัวหนึ่งครั้ง; ตั้งชื่อเธรด Slack และสถานะ “is thinking…” ขณะเอเจนต์ทำงาน |
ฟิลด์ข้อมูลรับรอง (Slack)
หัวข้อที่มีชื่อว่า “ฟิลด์ข้อมูลรับรอง (Slack)”ประกาศโดย descriptor ของผู้ให้บริการ ซึ่งเป็นสิ่งที่สร้างกล่องโต้ตอบการเชื่อมต่อและตรวจสอบข้อมูลที่ส่งมา
| ID ฟิลด์ | ป้ายกำกับ | จำเป็น | คำนำหน้า | อยู่ที่ไหน |
|---|---|---|---|---|
botToken |
โทเค็น OAuth ของผู้ใช้บอท | ใช่ | xoxb- |
OAuth & Permissions |
appToken |
โทเค็นระดับแอป | ใช่ | xapp- |
Basic Information → App-Level Tokens, สโคป connections:write |
configRefreshToken |
โทเค็นรีเฟรชการตั้งค่าแอป | ไม่ | xoxe |
App configuration tokens บน api.slack.com/apps |
configRefreshToken คือข้อมูลรับรองสำหรับจัดการแอป: หากไม่มี การเชื่อมต่อยังทำงานได้ แต่บอทจะสร้างหรือแก้ไขจาก Control Center ไม่ได้ มันหมุนเวียนใหม่ทุกครั้งที่ใช้ และค่าที่หมุนใหม่จะถูกจัดเก็บทันที
ค่าจะถูกตัดช่องว่างหัวท้าย คีย์ที่ไม่ได้ประกาศไว้จะถูกทิ้ง และคำนำหน้าที่ไม่ตรงจะถูกปฏิเสธด้วยข้อความเดียวกันทั้งฝั่งไคลเอนต์และเซิร์ฟเวอร์ ฟิลด์ที่จำเป็นต้องมีทุกครั้งที่เชื่อมต่อ ส่วนฟิลด์ที่ไม่บังคับซึ่งเว้นว่างไว้จะคงค่าที่จัดเก็บไว้เดิม
ข้อกำหนดของแอป Slack
หัวข้อที่มีชื่อว่า “ข้อกำหนดของแอป Slack”สโคปของโทเค็นบอท:
app_mentions:readassistant:writechannels:historychat:writecommandsgroups:historyim:historyusers:readusers:read.emailการสมัครรับเหตุการณ์ของบอท:
app_home_openedapp_mentionmessage.channelsmessage.groupsmessage.imการตั้งค่าเชิงโครงสร้างที่ Control Center กำหนดเมื่อสร้างหรือแก้ไขแอป: เปิด Socket Mode, ไม่มี request URL, เปิดแท็บ Messages และเขียนได้, บอท always_online, รวมสโคปและเหตุการณ์ที่จำเป็นเข้ากับสิ่งที่แอปมีอยู่แล้ว (ไม่เคยตัดออก) และคำสั่งสแลชหนึ่งคำสั่งที่ใช้คำอธิบาย File a ticket or link your Control Center account — เครื่องหมายที่ระบุคำสั่งของ Control Center เองในแมนิเฟสต์ที่นำออก
สถานะการเชื่อมต่อ
หัวข้อที่มีชื่อว่า “สถานะการเชื่อมต่อ”| สถานะ | ค่าไวร์ | ความหมาย |
|---|---|---|
| ยังไม่เชื่อมต่อ | disconnected |
ยังไม่มีข้อมูลรับรองที่จัดเก็บไว้สำหรับเวิร์กสเปซ × ผู้ให้บริการนี้ |
| กำลังเชื่อมต่อ… | connecting |
จัดเก็บข้อมูลรับรองแล้ว ทรานสปอร์ตกำลังเชื่อมต่อหรือเชื่อมต่อใหม่ |
| ออนไลน์ | connected |
ทรานสปอร์ตทำงานอยู่และกำลังรับเหตุการณ์ |
| ข้อผิดพลาดในการเชื่อมต่อ | error |
จัดเก็บไว้แต่ล้มเหลว; ข้อผิดพลาดของผู้ให้บริการเองจะแสดงควบคู่กัน |
การเชื่อมต่อใหม่ใช้การถอยกลับแบบเอกซ์โพเนนเชียลพร้อม jitter และจำกัดไว้ที่ 30 วินาที การหมุนซ็อกเก็ตที่ผู้ให้บริการเป็นผู้เริ่มจะเชื่อมต่อใหม่ทันทีโดยไม่เพิ่มการถอยกลับ ข้อมูลรับรองที่ถูกปฏิเสธ (invalid_auth, not_authed, token_revoked, account_inactive, token_expired, invalid_app_id) หรือสโคปที่หายไปถือเป็นจุดสิ้นสุด: สะพานจะหยุดลองใหม่จนกว่าจะมีข้อมูลรับรองใหม่มา
ความสามารถของผู้ให้บริการ (Slack)
หัวข้อที่มีชื่อว่า “ความสามารถของผู้ให้บริการ (Slack)”สะพานถูกเขียนขึ้นโดยอิงพื้นผิวที่สมบูรณ์ที่สุด และจะลดความสามารถลงทีละรายการ
| ความสามารถ | Slack | ผลเมื่อไม่มี |
|---|---|---|
streaming |
ใช่ | คำตอบทั้งหมดจะถูกโพสต์ครั้งเดียวตอนจบ |
streamingRequiresThread |
ใช่ | เป้าหมายการตอบที่ไม่มีจุดยึดของเธรดจะโพสต์คำตอบทั้งหมด |
ephemeralMessages |
ใช่ | คำตอบของคำสั่งสแลชที่ไม่มีตัวจับการตอบที่ใช้ได้จะถูกทิ้ง (การปฏิเสธจะโพสต์ในเธรดเสมอ) |
threadStatus |
ใช่ | ไม่มีบรรทัด “is thinking…” ชั่วคราว |
threadTitle |
ใช่ | เธรดแชทคงชื่อเริ่มต้นไว้ |
slashCommands |
ใช่ | ไม่มีพื้นผิว /cc |
taskCards |
ใช่ | ข้อความเท่านั้น — ไม่มีการ์ดเหนือคำตอบและไม่มีลิงก์ “View in Control Center” |
maxMessageLength |
39,000 | คำตอบที่ยาวกว่าจะถูกตัด พร้อมตัวชี้ไปยัง Control Center |
maxStreamChunkLength |
3,800 | การต่อท้ายจะถูกแบ่งที่ขอบเขตย่อหน้า บรรทัด หรือช่องว่าง |
การประกาศว่าสตรีมได้ไม่ใช่คำสัญญาว่าจะสตรีมได้: ของ Slack ต้องใช้ฟีเจอร์ Agents & AI Apps บนแผนแบบชำระเงิน การปฏิเสธ ณ เวลาเรียก (streaming_not_supported, feature_not_enabled, paid_only, …) จะถูกจดจำไว้ตลอดอายุการเชื่อมต่อ และถูกเปิดเผยเป็น streamingAvailable: false การ์ดงานของ Slack ไปกับคำเรียกเดียวกัน ดังนั้น Slack ที่ปฏิเสธโหมดแสดงผลของการ์ดจะยังคงสตรีมไว้และเสียเพียงการ์ดเท่านั้น ครั้งเดียว ตลอดอายุการเชื่อมต่อ
สตรีมยังต้องระบุผู้อ่านที่มันมีไว้ให้ด้วย Slack ต้องมีทั้งผู้ใช้ปลายทางและทีมของผู้นั้นจึงจะเปิดสตรีมใน แชนเนล ได้ (ข้อความส่วนตัวทำไม่ได้) และจะปฏิเสธด้วย missing_recipient_user_id / missing_recipient_team_id มิฉะนั้น — เป็นการปฏิเสธต่อการเรียกแต่ละครั้ง สะพานจึงลองต่อไป และระหว่างนั้นเทิร์นจะมาถึงในรูปคำตอบที่โพสต์ครั้งเดียว ผู้รับคือผู้ที่ถาม นำมาจากข้อความขาเข้า (ทีมของผู้นั้น ไม่ใช่ของแอป แชนเนล Slack Connect จึงแก้ไขได้อย่างถูกต้อง) สำหรับเทิร์นที่ไม่มีใครเริ่มจากแชท — งานที่เริ่มใน Control Center บนบทสนทนาที่เชื่อมสะพานไว้แล้ว — ผู้รับคือสมาชิกที่การลิงก์ของเขาสร้างสะพานนี้ขึ้น; หากต่อมาผู้นั้นเลิกลิงก์บัญชีแชทของตน เทิร์นนั้นจะโพสต์ทั้งหมดแทนการสตรีม
การ์ดงาน
หัวข้อที่มีชื่อว่า “การ์ดงาน”การ์ดแบบกลุ่มหนึ่งใบต่อเทิร์นของเอเจนต์ (task_display_mode: plan) โดยมีหนึ่งแถวต่อหนึ่งขั้นตอน แต่ละแถวคือ task_update ที่ประกอบด้วย id + title + status บวก details ที่ส่งครั้งเดียวเมื่อความคิดพร้อม details และ output ที่มี id เดียวกันจะถูกต่อกันโดยไม่มีตัวคั่น details จึงถูกส่งครั้งเดียว และ output จะไม่ถูกสตรีมเลย ชื่อเรื่องของแผนคือคำขอ; คำตอบคือการต่อท้ายแยกต่างหากในภายหลังที่พา chunk markdown_text มา
| ส่วน | เนื้อหา |
|---|---|
| ชื่อเรื่องของแผน | สิ่งที่ถูกถาม |
| แถวการเตรียมการ | Working on it…, แล้ว Cloning …, แล้ว Starting the agent… — หนึ่งแถว โดยชื่อถูกแทนที่ |
| แถวความคิด | Thinking…, เพิ่มเมื่อเอเจนต์เริ่มให้เหตุผล; ความคิดคือ details ของแถวนี้ ส่งครั้งเดียวเมื่อการให้เหตุผลเสร็จสิ้น |
| แถวเครื่องมือ | หนึ่งแถวต่อเครื่องมือหนึ่งรายการ (Bash which swift) ใช้ id ไม่ซ้ำเพื่อให้วางเคียงกัน |
| สถานะ | กำลังดำเนินการขณะแถวนั้นทำงาน; เสร็จสมบูรณ์เมื่อแถวถัดไปเริ่มหรือเทิร์นจบ; ผิดพลาดเมื่อล้มเหลว ถูกขัดจังหวะ หรือถึงขีดจำกัดเทิร์น |
| การเรียกให้ลงมือ | View in Control Center เป็นแถวท้ายเมื่อเทิร์นจบ — ไม่มีขณะงานยังทำงานอยู่ และไม่มีเมื่อเซิร์ฟเวอร์ไม่มี URL สาธารณะที่เข้าถึงได้ |
สิ่งที่การ์ดไม่เคยพามา: เอาต์พุตของเครื่องมือ, อาร์กิวเมนต์ที่อยู่นอกรายการที่อนุญาตนั้น, หรือสิ่งใดก็ตามที่อ่านจากฐานข้อมูล ข้อความบรรยายการให้เหตุผลมีเพียง details ของแถว Thinking… (จำกัด 256 ตัวอักษร) ไม่ใช่เนื้อความของข้อความ การ์ดที่จะไม่บอกอะไรใหม่จะไม่ถูกส่งซ้ำ การกล่าวถึงจะเปิดสตรีมทันทีเพื่อให้เธรด Slack ไม่เงียบ; เทิร์นที่ต่อมาไม่ผลิตข้อความใดเลยจะปิดการ์ดใบเดิมนั้นด้วยบันทึกสั้น ๆ แทนที่จะโพสต์ข้อความที่สอง เทิร์นที่ไม่มีข้อความบนสเปซที่ไม่เคยเปิดสตรีม (ไม่มีการ์ด หรือสตรีมถูกปฏิเสธไปแล้ว) จะยังโพสต์ข้อความหนึ่งฉบับที่พาการ์ดที่เสร็จแล้วและบันทึกนั้นมา
การ์ดที่สตรีมกับการ์ดที่โพสต์เป็นเพย์โหลดที่ต่างกัน:
| รูปแบบ | โครงสร้าง | ข้อความบรรยาย |
|---|---|---|
| สตรีม | plan_update บวก chunk task_update ที่ใช้ id เป็นคีย์ |
สตริงธรรมดา จำกัด 256 ตัวอักษรต่อ chunk |
| โพสต์ | บล็อก plan ที่ห่อ task_card ซึ่งใช้ task_id เป็นคีย์ |
ข้อความแบบ rich text |
การจัดการความล้มเหลว:
| สถานการณ์ | พฤติกรรม |
|---|---|
| ผู้ให้บริการปฏิเสธการ์ดในการต่อท้ายหนึ่งครั้ง | การต่อท้ายจะถูกลองใหม่ทันทีโดยไม่มีการ์ด; ข้อความบรรยายยังมาถึงแบบสด และการ์ดจะถูกทิ้งไปตลอดอายุการเชื่อมต่อ |
| ผู้ให้บริการปฏิเสธข้อความบรรยายด้วย | สตรีมถูกปิดและคำตอบถูกโพสต์ทั้งหมด |
| สตรีมขาดหลังจากคำตอบส่วนหนึ่งไปถึงแล้ว | สตรีมถูกปิดและไม่มีอะไรถูกโพสต์ซ้ำ |
ดู สะพานแชท ว่าทำไมเพย์โหลดจึงถูกแบ่งแบบนี้
การเตรียมการ ก่อนที่จะมีเทิร์น
หัวข้อที่มีชื่อว่า “การเตรียมการ ก่อนที่จะมีเทิร์น”การ์ดจะเปิดเมื่อการกล่าวถึงได้รับการยอมรับ ก่อนการโคลนและก่อนโทเค็นแรก; เทิร์นที่ตามมาจะใช้การ์ดและสตรีมเดียวกัน คำบรรยายหลังจากนั้นมาจาก SpaceProvisioningChanged บนบัสเหตุการณ์โดเมน — ความคืบหน้าเดียวกับที่แถบการเตรียมการของเดสก์ท็อปอ่านจากแถวสเปซ
| สถานะการเตรียมการ | คำบรรยาย |
|---|---|
| การกล่าวถึงได้รับการยอมรับ | Working on it… |
ขั้นตอน repo |
Cloning <repo>… |
ขั้นตอน prCheckout |
Checking out the pull request in <repo>… |
ขั้นตอน script |
Running the setup script for <repo>… |
ขั้นตอน agent |
Setting up <agent>… |
provisioning ยังไม่มีขั้นตอน |
Preparing the workspace… |
ready |
Starting the agent… |
| เอเจนต์กำลังให้เหตุผล | Thinking… |
failed |
Workspace setup failed — details are in Control Center. |
cancelled |
Workspace setup was stopped — details are in Control Center. |
คำบรรยายการเตรียมการต้องมีครบทั้งสี่เงื่อนไข:
| เงื่อนไข |
|---|
| ผู้ให้บริการแสดงการ์ด |
| สตรีมมิ่งพร้อมใช้บนการเชื่อมต่อนี้ |
| สเปซมีคำขอแชทที่กำลังดำเนินอยู่ |
| ยังไม่มีการ์ดใดรายงานเทิร์นอยู่ที่นั่น |
คำขอจะคงอยู่ระหว่างดำเนินการจนกว่าเทิร์นจะเริ่มหรือการเตรียมการล้มเหลว เหตุการณ์ ready ที่ยังไม่มีการ์ดเปิดอยู่หมายถึงสเปซที่อุ่นอยู่แล้ว จึงถูกปล่อยให้เงียบ และการเตรียมการที่ไม่มีใครในแชทรอ — บทสนทนาที่เปิดในแอป หรือบทสนทนาที่ค้างอยู่และกลับมาทำงานต่อตอนบูต — ไม่ผลิตการ์ดใดเลย
หน้ากระเด้ง /open
หัวข้อที่มีชื่อว่า “หน้ากระเด้ง /open”ผลิตภัณฑ์แชทยอมรับเฉพาะ http(s) ในลิงก์และไม่รับอย่างอื่น การเรียกให้ลงมือจึงระบุสคีมาของเดสก์ท็อปโดยตรงไม่ได้
| คุณสมบัติ | ค่า |
|---|---|
| เส้นทาง | GET /open/workspaces/<workspaceId>/(spaces|tickets)/<id> |
| ต้นทาง | ได้มาจาก URL สาธารณะของเซิร์ฟเวอร์ (ws → http, wss → https); ที่อยู่ลูปแบ็กยังคงไว้ |
| การตอบกลับ | หน้า HTML ขนาดเล็กที่ไม่แคช (no-store) ซึ่งนำทางไปยัง control-center://workspaces/<workspaceId>/(spaces|tickets)/<id> และแสดงปุ่มให้กดเอง |
| การตรวจสอบ | ID ต้องตรงกับ [A-Za-z0-9_.-]{1,128}; สิ่งอื่นใดถือเป็น 404 |
| การเข้าถึง | สาธารณะและไม่ทำอะไร — มันไม่อ่านฐานข้อมูลใด จึงไม่ใช่วงออราเคิลบอกการมีอยู่; แอปที่มันเปิดเป็นผู้ทำการอนุญาต |
ฟิลด์โปรไฟล์บอท
หัวข้อที่มีชื่อว่า “ฟิลด์โปรไฟล์บอท”| ฟิลด์ | ขีดจำกัด | หมายเหตุ |
|---|---|---|
appName |
1–35 ตัวอักษร | ค่าเริ่มต้นคือ Control Center · <workspace name> |
botDisplayName |
1–80 ตัวอักษร | ค่าเริ่มต้นคือ control-center |
description |
≤ 140 ตัวอักษร | |
agentDescription |
≤ 300 ตัวอักษร | แสดงเหนือข้อความส่วนตัวที่ส่งถึงบอท |
command |
≤ 32 ตัวอักษร | ทำให้เป็นมาตรฐาน: ตัดสแลชนำหน้าออก ทำเป็นตัวพิมพ์เล็ก ลบ [^a-z0-9_-] ออก และถ้าว่างจะกลับไปใช้ cc |
agentEnabled |
— | ว่าเปิดเผยประสบการณ์ข้อความส่วนตัว/เอเจนต์หรือไม่ |
กล่องว่างหมายถึง “อย่าไปแตะ” ไม่ใช่ “ล้างทิ้ง” ขีดจำกัดเป็นของ Slack ซึ่งเข้มงวดที่สุดในบรรดาผลิตภัณฑ์ที่จำลองไว้
รหัสลิงก์
หัวข้อที่มีชื่อว่า “รหัสลิงก์”| คุณสมบัติ | ค่า |
|---|---|
| ความยาว | 6 ตัวอักษร |
| ชุดอักขระ | 23456789ABCDEFGHJKLMNPQRSTVWXYZ (โดยไม่มี 0/O, 1/I, U) |
| อายุการใช้งาน | 15 นาที |
| จำนวนการใช้ | หนึ่งครั้ง |
| ที่จัดเก็บ | ในหน่วยความจำ — การรีสตาร์ตเซิร์ฟเวอร์ทำให้รหัสที่ค้างอยู่ใช้ไม่ได้ |
| ขอบเขต | หนึ่งเวิร์กสเปซและหนึ่งผู้ให้บริการ; การสร้างรหัสใหม่ทำให้รหัสก่อนหน้าของสมาชิกใช้ไม่ได้ |
การแลกรหัสจะจับคู่โดยไม่คำนึงถึงตัวพิมพ์เล็กใหญ่ รหัสที่ไม่รู้จัก หมดอายุ มาจากเวิร์กสเปซอื่น และผิดผู้ให้บริการ ล้วนล้มเหลวเหมือนกันโดยเจตนา
วิธีการลิงก์
หัวข้อที่มีชื่อว่า “วิธีการลิงก์”| วิธีการ | ค่าไวร์ | อย่างไร |
|---|---|---|
| รหัส | code |
สมาชิกแลกรหัส /cc link <code> |
| อีเมล | email |
อีเมลที่ผ่านการยืนยันของผู้ให้บริการตรงกับผู้ใช้ที่เป็นสมาชิกของเวิร์กสเปซอยู่แล้ว |
การดำเนินการ RPC
หัวข้อที่มีชื่อว่า “การดำเนินการ RPC”ทั้งหมดจำกัดขอบเขตที่เวิร์กสเปซ เซิร์ฟเวอร์ไม่มีสถานะ คำเรียกแต่ละครั้งจึงพา workspace_id ของตัวเองมาใน args; dispatcher จะปฏิเสธ op ที่จำกัดขอบเขตเวิร์กสเปซซึ่งละเว้นค่านั้น ก่อนที่ฐานข้อมูลใดจะถูกเปิด ข้อมูลรับรองเดินทางเข้าเท่านั้น — การอ่านสถานะไม่เคยส่งคืนมันกลับมา
| การดำเนินการ | ชนิด | บทบาทขั้นต่ำ | คลาสการกระทำ |
|---|---|---|---|
chat.providers |
read | guest |
— |
chat.status |
read | guest |
— |
chat.connect |
mutate | admin |
secretAccess, networkEgress |
chat.disconnect |
destructive | admin |
secretAccess |
chat.listUserLinks |
read | guest |
— |
chat.beginUserLink |
mutate | member |
— |
chat.unlinkUser |
mutate | member สำหรับตัวเอง, admin สำหรับคนอื่น |
— |
chat.createApp |
mutate | admin |
secretAccess, networkEgress |
chat.setupLink |
read | admin |
— |
chat.botProfile |
read | admin |
networkEgress |
chat.updateBotProfile |
mutate | admin |
networkEgress |
การดำเนินการอ่านสืบทอดระดับขั้นต่ำ guest จากชนิดของมัน; การดำเนินการแบบ mutate ที่ไม่มีระดับขั้นต่ำระบุชัดเจนต้องใช้ member
chat.setupLink ประกอบ URL สำหรับสร้างแอปของผู้ให้บริการขึ้นจากโปรไฟล์บอท มันไม่ประกาศคลาสการกระทำใดเพราะไม่อ่านความลับใดและไม่ต่อออกไปไหน — เบราว์เซอร์ของผู้ใช้เองเป็นผู้เรียกเพียงครั้งเดียว
มีคำสั่งสืบค้นเชิงตอบสนองหนึ่งรายการที่ทำให้พื้นผิวสมบูรณ์:
| คำสั่งสืบค้น | ชนิด | ส่งออก |
|---|---|---|
chat.watchUserLinks |
watch | รายชื่อ ซึ่งถูกส่งใหม่ทุกครั้งที่มีการสร้างหรือลบลิงก์ |
มันมีอยู่เพราะการลิงก์เกิดขึ้นที่อีกฝั่ง: สมาชิกพิมพ์รหัสใช้ครั้งเดียวลงในบอท คำตอบจากไคลเอนต์จึงพาคำตอบกลับมาไม่ได้ รายชื่อในหน้าตั้งค่าและกล่องโต้ตอบ “ลิงก์บัญชีของฉัน” ที่เปิดค้างอยู่ต่างติดตามมัน และกล่องโต้ตอบจะเปลี่ยนไปเป็นการยืนยันทันทีที่ลิงก์ของสมาชิกเองมาถึง
chat.beginUserLink สร้างรหัสสำหรับตัวตนของตัวผู้เรียกเองเสมอ chat.updateBotProfile จะคืนค่า remaining_step เฉพาะเมื่อยังมีขั้นตอนเหลืออยู่ (โดยทั่วไปคือการติดตั้งใหม่); การไม่มีค่านั้นหมายความว่าการแก้ไขมีผลแล้ว
chat.providers คืนหนึ่งรายการต่อผู้ให้บริการที่เสนอ — descriptor บวกสถานะการเชื่อมต่อ (ถ้ามี) ทุกอย่างที่ UI ตั้งค่าแสดงล้วนมาจากเพย์โหลดนั้น จึงเป็นเหตุผลว่าทำไมการเพิ่มผู้ให้บริการจึงเป็นการเปลี่ยนแปลงฝั่งเซิร์ฟเวอร์เท่านั้น
ที่จัดเก็บ
หัวข้อที่มีชื่อว่า “ที่จัดเก็บ”| อะไร | ที่ไหน |
|---|---|
| ข้อมูลรับรองการเชื่อมต่อ | <dataDir>/<workspaceId>/chat_credentials/<provider>.json |
| การตั้งค่าแบบมีคำแนะนำที่ทำค้างไว้ | <dataDir>/<workspaceId>/chat_credentials/<provider>_setup.json |
| ไฟล์ Slack แบบเก่า | <dataDir>/<workspaceId>/slack_credentials.json และ slack_app_setup.json, อ่านครั้งเดียวและโยกย้ายในตำแหน่งเดิม |
ข้อมูลรับรองอยู่ข้างฐานข้อมูลของเวิร์กสเปซ ไม่ได้อยู่ข้างใน: การลบเวิร์กสเปซจะพาโทเค็นแชทของมันไปด้วย และ workspace.export หรือการสำรองข้อมูลจะคัดลอก workspace.db — เวิร์กสเปซที่ส่งต่อให้คนอื่นจึงไม่มีโทเค็นบอทที่ใช้งานได้ติดไป การเขียนเป็นแบบอะตอมมิก (ไฟล์ชั่วคราวบวกการเปลี่ยนชื่อ) และถูกจำกัดสิทธิ์ให้เฉพาะเจ้าของ
ตารางฐานข้อมูล
หัวข้อที่มีชื่อว่า “ตารางฐานข้อมูล”ทั้งสองอยู่ในฐานข้อมูลของเวิร์กสเปซ ซึ่งมี baseline v1 ที่ถูกยุบรวมไว้ — การอ่านข้ามเวิร์กสเปซจึงไม่คอมไพล์
chat_space_links — หนึ่งบทสนทนาภายนอก ↔ หนึ่งสเปซ Control Center:
| คอลัมน์ | หมายเหตุ |
|---|---|
id |
UUID v4 |
workspace_id |
ขอบเขตเวิร์กสเปซ |
provider |
ChatProvider.wire |
external_team_id |
ทีม Slack T… ว่างสำหรับผู้ให้บริการที่ไม่มีแนวคิดนี้ |
external_channel_id |
แชนเนล Slack C… กลุ่ม G… ข้อความส่วนตัว D… |
external_thread_id |
จุดยึดเธรด Slack หรือ null เมื่อทั้งบทสนทนาเป็นจุดยึด (ข้อความส่วนตัวกับบอท) |
cc_space_id |
REFERENCES spaces(id) ON DELETE CASCADE |
created_by_user_id, created_at, last_activity_at |
ไม่ซ้ำบน (workspace_id, provider, external_channel_id, external_thread_id) โดยมีดัชนีบางส่วนบังคับเช่นเดียวกันสำหรับ id ของเธรดที่เป็น null (SQLite ถือว่า NULL ต่างกัน)
chat_user_links — หนึ่งสมาชิกภายนอก ↔ หนึ่งผู้ใช้ Control Center:
| คอลัมน์ | หมายเหตุ |
|---|---|
id |
UUID v4 |
workspace_id |
ขอบเขตเวิร์กสเปซ |
provider |
ChatProvider.wire |
external_team_id, external_user_id |
สมาชิก Slack U… |
user_id |
ผู้ใช้ Control Center (ทั่วโลก จึงไม่มี foreign key) |
method |
code หรือ email |
linked_at |
ไม่ซ้ำบน (workspace_id, provider, external_team_id, external_user_id) และบน (workspace_id, provider, user_id) — การระบุที่มาแบบค่าเดียวทั้งสองทิศทาง ขณะที่คนหนึ่งคนสามารถถูกลิงก์บนหลายผู้ให้บริการได้
การลิงก์คือการแก้ไขตัวตนให้ตรงกัน ไม่ใช่ การให้สิทธิ์เข้าถึง: สมาชิกภาพถูกตรวจซ้ำในทุกเหตุการณ์ขาเข้า ลิงก์ที่ชี้ไปยังสมาชิกที่ต่อมาถูกถอดออกจึงลงเอยเป็นการปฏิเสธ
ชนิดสเปซและเมทาดาทาของข้อความ
หัวข้อที่มีชื่อว่า “ชนิดสเปซและเมทาดาทาของข้อความ”สเปซที่เชื่อมสะพานจะถูกสร้างด้วย SpaceKind ของผู้ให้บริการ (slack) มันทำงานเหมือนสเปซทั่วไปที่หันหน้าสู่มนุษย์ — แจ้งเตือนและจัดอยู่ในหมวดเหมือน topic — และ isExternalChat คือการตรวจที่ทุกพฤติกรรม (การมิเรอร์ การติดป้าย การเก็บรักษา) ต้องผ่าน
ทุกข้อความที่สะพานนำเข้ามาจะมีตราประทับ metadata['chat']:
{ "chat": { "provider": "slack", "teamId": "T024BE7LD", "channelId": "C0G9QF9GW", "threadId": "1700000000.000100", "messageId": "1700000000.000200", "userId": "U024BE7LH", "userLabel": "sam" }}ตราประทับนี้ยังเป็นตัวกันเสียงสะท้อนด้วย: การมิเรอร์ขาออกจะข้ามข้อความใดที่มีตรานี้ ซึ่งเป็นสิ่งที่หยุดไม่ให้ข้อความแชทขาเข้าถูกโพสต์กลับไปทันที
พฤติกรรมการส่งต่อ
หัวข้อที่มีชื่อว่า “พฤติกรรมการส่งต่อ”| พฤติกรรม | รายละเอียด |
|---|---|
| สิ่งที่ถูกส่งต่อออกไป | ข้อความของเอเจนต์เท่านั้น บวกบรรทัดสถานะของการ์ดงาน ข้อความบรรยายการให้เหตุผลและเอาต์พุตของเครื่องมือจะอยู่ใน Control Center |
| จังหวะการสตรีม | ถูกจำกัดไว้ที่หนึ่งครั้งต่อวินาทีขณะเทิร์นทำงาน; การ์ดและข้อความที่มันอธิบายไปในคำเรียกเดียวกัน |
| ข้อความของมนุษย์ที่พิมพ์ใน Control Center | ถูกมิเรอร์ไปยังเธรดที่ลิงก์ไว้ โดยนำหน้าด้วยชื่อที่แสดงของผู้เขียน |
| เทิร์นว่าง | ถูกรายงานเป็นบันทึกสั้น ๆ (การรันล้มเหลว ถูกขัดจังหวะ หรือหยุดที่ขีดจำกัดเทิร์นของมัน) แทนความเงียบ |
| การขจัดข้อมูลซ้ำ | ใช้ id เหตุการณ์ของผู้ให้บริการเองเป็นคีย์; จำ 512 รายการล่าสุดไว้ |
| เอเจนต์ประตูหน้า | บทสนทนาที่เชื่อมสะพานใหม่จะปลุกเอเจนต์ CEO ของเวิร์กสเปซ มิฉะนั้นเอเจนต์ตัวแรก มิฉะนั้นไม่มีใคร |
| การตั้งชื่อสเปซ | #channel · <request> สำหรับเธรดในแชนเนล Slack, Slack DM · <request> สำหรับข้อความส่วนตัว, ชื่อจำกัดที่ 48 ตัวอักษร |
| การแยกความล้มเหลว | เหตุการณ์เสียหนึ่งครั้งไม่เคยทำให้สะพานล่ม; การเชื่อมต่อยังให้บริการบทสนทนาอื่นทุกอันต่อไป |
ดูเพิ่มเติม
หัวข้อที่มีชื่อว่า “ดูเพิ่มเติม”- ตั้งค่าการผสาน Slack
- เชื่อมบัญชี Slack ของคุณ
- ปรับแต่งแชทบอต
- สะพานแชท
- กฎคุม สำหรับคลาสการกระทำที่ op ประกาศไว้