# Copilot ב-SDK

> client.copilot מנהל את השיחות של ה-Copilot ומזרים את הפניות שלו. הוא עובד רק בתוך אפליקציה של Nodaro, עם הסשן של המשתמש המחובר עצמו.

Source: https://nodaro.ai/he/docs/developers/sdk/copilot

**`client.copilot`** מאפשר לשלוט מקוד ב-Copilot, עוזר התהליכים של Nodaro. **שיחה** נפתחת על תהליך מסוים, ו**פנייה** היא הודעה אחת, יחד עם כל מה שהעוזר עושה כדי לענות עליה. המתודות קוראות ל-`/v1/copilot/*`. כדי לקרוא על היכולת עצמה, ראו את הדף [בניית תהליך עם ה-Copilot](https://nodaro.ai/docs/get-started/workflow-copilot).

**ה-Copilot עובד רק בתוך אפליקציה של Nodaro.** כל נתיב דוחה עם `403 in_app_only` כל קריאה שלא נשלחת עם הסשן של המשתמש המחובר עצמו. ה-SDK זורק את השגיאה הזו כ-`ForbiddenError`. השדה `code` שלה הוא `forbidden`, ולכן כדי לזהות אותה בדקו את השדה `message`. אסימוני API ואסימוני OAuth לא יכולים לשלוט ב-Copilot של אף משתמש. השתמשו בו מאפליקציית דפדפן עם [`supabaseAuth`](https://nodaro.ai/docs/developers/sdk/auth#supabaseauthsupabase). ה-Copilot רץ ב-Nodaro Cloud. בפריסה שבה היכולת כבויה, פתיחת שיחה או שליחת פנייה נדחות עם `503 feature_disabled`.

## מתודות
| מתודה | מה היא עושה |
| --- | --- |
| [`create(input)`](#createinput) | פתיחת שיחה על תהליך קיים או על תהליך חדש |
| [`list(params)`](#listparams) | קריאת השיחה הפעילה של תהליך |
| [`get(id, opts?)`](#getid-opts) | קריאת שיחה עם ההודעות שלה |
| [`archive(id)`](#archiveid) | העברת שיחה לארכיון |
| [`cancel(id)`](#cancelid) | עצירת הפנייה שרצה עכשיו |
| [`stream(threadId, opts)`](#streamthreadid-opts) | שליחת הודעה וקריאת האירועים של הפנייה בזמן שהם מגיעים |

המתודות האלה מחזירות את מעטפת ה-`{ data }` של ה-API כמו שהיא מגיעה, חוץ מ-`stream()`, שמחזירה אירועים בזה אחר זה.

## client.copilot
### create(input)‎
פותחת שיחה. אפשר לשלוח `workflowId` של תהליך קיים, או `prompt`, ואז השרת יוצר תהליך שמתחיל מהפרומפט הזה. פתיחת שיחה על תהליך שכבר יש לו שיחה פעילה מחזירה את השיחה הזו, ולא פותחת שיחה שנייה.

```ts
create(input: { workflowId?: string; prompt?: string; name?: string }): Promise<{
data: { thread: CopilotThread; workflow: CopilotThreadWorkflow }
}>
```

<TypeTable
type={{
workflowId: { type: 'string', description: "תהליך קיים. שלחו workflowId או prompt." },
prompt: { type: 'string', description: "פרומפט שממנו מתחיל תהליך חדש." },
name: { type: 'string', description: "שם התהליך החדש, יחד עם prompt." },
}}
/>

```ts
const { data } = await client.copilot.create({ workflowId })
const threadId = data.thread.id
```

לשיחה יש `id`, `workflowId`, `runMode` (`ask`, שמציע הרצות ומחכה, או `auto`, שמריץ בגבולות `autoRunLimitCredits`), `modelTier`, `allowPublishing`, `userTurnCount`, `lastMessageAt` ו-`createdAt`. השדה `surface` מציין לאיזה עוזר השיחה שייכת; כשהשדה חסר, הפריסה לא ציינה זאת.

### list(params)‎
מחזירה את השיחה הפעילה של תהליך, או `null` כשאין שיחה כזו.

```ts
list(params: { workflowId: string }): Promise<{ data: { thread: CopilotThread | null } }>
```

<TypeTable
type={{
workflowId: { type: 'string', required: true, description: "מזהה התהליך." },
}}
/>

```ts
const { data: { thread } } = await client.copilot.list({ workflowId })
```

### get(id, opts?)‎
מחזירה שיחה עם ההודעות שלה. השיחה כוללת גם `status` מחושב (`running` או `idle`) ואת `activeTurnId`.

```ts
get(id: string, opts?: { after?: number; limit?: number }): Promise<{
data: { thread: CopilotThread; messages: CopilotMessage[] }
}>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "מזהה השיחה." },
after: { type: 'number', description: "רק הודעות שאחרי המספר הסידורי הזה, כדי להשלים את מה שפספסתם." },
limit: { type: 'number', description: "גודל העמוד, עד המגבלה של השרת." },
}}
/>

```ts
const { data: { messages } } = await client.copilot.get(threadId, { after: lastSeq })
```

לכל הודעה יש `id`, `seq`, `turnId`, `role`, `createdAt` ו-`parts`: חלקי טקסט וחלקים של קריאות לכלים.

### archive(id)‎
מעבירה שיחה לארכיון. אפשר עדיין לקרוא את ההודעות, ושום דבר לא נמחק. אי אפשר להעביר לארכיון שיחה שיש בה פנייה שרצה.

```ts
archive(id: string): Promise<{ data: { archived: true } }>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "מזהה השיחה." },
}}
/>

```ts
await client.copilot.archive(threadId)
```

### cancel(id)‎
עוצרת את הפנייה שרצה בשיחה, ומחזירה את המזהה של הפנייה שהיא ביקשה לעצור. הזרם של הפנייה הזו מסתיים באירוע `done`.

```ts
cancel(id: string): Promise<{ data: { cancelling: true; turnId: string } }>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "מזהה השיחה." },
}}
/>

```ts
await client.copilot.cancel(threadId)
```

### stream(threadId, opts)‎
שולחת הודעה ומחזירה את האירועים של הפנייה ברגע שהם מגיעים, בזרם של אירועי שרת (SSE). כך אפשר להציג טקסט, פעילות של כלים והצעות בזמן אמת, במקום לחכות לתשובה המלאה. זו המתודה היחידה בדף הזה שמוציאה קרדיטים.

```ts
stream(threadId: string, opts: {
message: string
baseVersion?: number
tier?: "economy" | "standard" | "premium"
signal?: AbortSignal
}): AsyncGenerator<CopilotStreamFrame>
```

<TypeTable
type={{
threadId: { type: 'string', required: true, description: "מזהה השיחה." },
message: { type: 'string', required: true, description: "ההודעה של המשתמש." },
baseVersion: { type: 'number', description: "גרסת התהליך שהמשתמש רואה." },
tier: { type: '"economy" | "standard" | "premium"', description: "רמת המודל של הפנייה הזו." },
signal: { type: 'AbortSignal', description: "מסיים את הזרם של הפנייה." },
}}
/>

```ts
try {
for await (const frame of client.copilot.stream(threadId, { message: "Tidy the graph" })) {
if (frame.type === "token") appendText(frame.data.text)
if (frame.type === "run_proposed") await askTheUser(frame.data)
if (frame.type === "done") break
}
} catch (err) {
if ((err as Error).name !== "AbortError") throw err // a stopped turn is a normal ending
}
```

הטיפוס `CopilotStreamFrame` הוא union שמובחן לפי `type`:

| אירוע | מה יש בו |
| --- | --- |
| `metadata` | המזהים של הפנייה, המודל, `runMode`, `autoRunLimitCredits` וההגדרות |
| `token` | קטע מהטקסט של התשובה |
| `tool_call` | כלי שהעוזר משתמש בו: `started`, ואחר כך `finished` או `failed` |
| `workflow_updated` | הרכיבים שנוספו, השתנו והוסרו, והגרסה החדשה |
| `workflow_created` | תהליך שהעוזר יצר |
| `run_proposed` | מה שהפנייה רוצה להריץ, לאישור של אדם |
| `memory_saved` | משהו שהעוזר בחר לזכור |
| `usage` | כמות הטוקנים שהפנייה השתמשה בה, ו-`creditsCharged` |
| `done` | הסיום: `completed`, `capped` או `cancelled` |
| `error` | הפנייה נכשלה, עם `code` ועם `message` |

השדה `data` של אירוע מועבר כמו שהוא, ולכן גם שדות שה-SDK לא מגדיר מגיעים אליכם. ה-SDK מדלג על סוג אירוע שהוא לא מגדיר, במקום לזרוק שגיאה, כך ששרת חדש יותר לא יכול לשבור לקוח ישן יותר. הגרסה הזו לא מגדירה את `action_proposed`, כרטיס ההצעה של שיחות שה-`surface` שלהן הוא `studio`, ולכן ההצעות האלה לא מגיעות לקוד שקורא למתודה.

**משך החיים של הפנייה בידיכם.** ה-`timeoutMs` של הלקוח לא חל על פנייה, כי פנייה יכולה לרוץ כמה דקות. כדי לסיים את הבקשה, העבירו `signal` או הפסיקו את הלולאה. כשמבטלים, הלולאה נכשלת עם ה-`AbortError` של סביבת הריצה עצמה, ולא עם `NodaroError`, בין שהביטול קורה לפני האירוע הראשון ובין שהוא קורה בין שני אירועים. אם הבקשה שפותחת את הזרם נכשלת עם סטטוס של שגיאה, המתודה זורקת כרגיל [שגיאה עם טיפוס](https://nodaro.ai/docs/developers/sdk/errors), עוד לפני כל אירוע.

## שאלות נפוצות

### האם אפשר להשתמש ב-client.copilot עם אסימון API?

לא. כל נתיבי ה-Copilot דוחים אסימוני API ואסימוני OAuth עם 403 in_app_only. רק הסשן של המשתמש המחובר עצמו, שנשלח עם supabaseAuth מתוך אפליקציה של Nodaro, יכול לעבוד עם client.copilot.

### מה מחזירה המתודה client.copilot.stream?

המתודה client.copilot.stream מחזירה איטרטור אסינכרוני של אירועים עם טיפוסים: המטא־דאטה של הפנייה, קטעי טקסט, קריאות לכלים, עדכונים של התהליך, הצעות להרצה, נתוני שימוש ובסוף אירוע done או error. עברו עליו בלולאת for await.

### האם פסק הזמן של הלקוח עוצר פנייה ארוכה ל-Copilot?

לא. פסק הזמן לא חל על פנייה, כי פנייה יכולה לרוץ כמה דקות. כדי לסיים אותה, העבירו AbortSignal או הפסיקו את הלולאה.

### אילו אירועים לא מגיעים לקוד שקורא למתודה?

גרסת ה-SDK הזו מדלגת על סוגי אירועים שהיא לא מגדירה. ביניהם האירוע action_proposed, כרטיס ההצעה של שיחות שה-surface שלהן הוא studio, ולכן ההצעות האלה לא מגיעות לקוד שקורא למתודה.
