diff --git a/plugins/workiq-preview/skills/workiq-preview/SKILL.md b/plugins/workiq-preview/skills/workiq-preview/SKILL.md index 2c3a62b..c5632fc 100644 --- a/plugins/workiq-preview/skills/workiq-preview/SKILL.md +++ b/plugins/workiq-preview/skills/workiq-preview/SKILL.md @@ -1,6 +1,6 @@ --- name: workiq-preview -description: WorkIQ - Microsoft 365 tool surface for agents. Use for any workplace question or write action where data lives in M365. Supports semantic `ask` plus structured tools (`fetch`, create/update/delete, actions, functions, path/schema discovery) for mail, meetings/calendar, documents/files, Teams chats/channels, OneDrive/SharePoint, and people. Read triggers, "what did [person] say", priorities/top of mind, meeting decisions/action items, summarize thread/chat, find emails/docs, list meetings/messages/files/channels, project status/updates, "what changed since". Write triggers, send/reply/forward email, create/update/accept/decline meetings, mark read, delete drafts/items, send/post/reply/react in Teams, set presence, upload/download via web URL. Discovery triggers, available endpoints/paths, fields, required/updatable properties, request body, operation parameters, schema/data model. When in doubt about workplace context, try WorkIQ first. Prefer `ask` for synthesis; use entity tools for exact reads/writes. +description: workiq-preview - Microsoft 365 tools for workplace questions and actions. Use `retrieve` for explicit semantic content search and ranked indexed evidence, including requests to summarize the matches, when intent cannot be expressed as a known resource path plus deterministic query. Use `fetch` / `call_function` for authoritative results when the resource and query can be expressed deterministically, including exact counts, ordering, filters, IDs, and fields. Use `ask` for synthesis or reasoning when the user did not explicitly request an evidence search. `ask` and `retrieve` are mutually exclusive within one user turn; never call both or alternate between them. Writes use a bounded deterministic lookup followed by the mutation tool. Retrieve and fetch may be chained only when each serves its distinct semantic-discovery or exact-read purpose. compatibility: > Uses the hosted WorkIQ MCP endpoint. No local package is required for MCP tool calls. @@ -8,7 +8,7 @@ compatibility: > # WorkIQ -WorkIQ connects AI agents to Microsoft 365 Copilot for workplace intelligence grounded in organizational data. This skill teaches the model how to use the full WorkIQ toolset: the agentic `ask` tool for semantic questions and the fast **entity tools** for direct structured access to M365 data (`fetch`, `create_entity`, `update_entity`, `delete_entity`, `do_action`, `call_function`, `search_paths`, `get_schema`). +WorkIQ connects AI agents to Microsoft 365 Copilot for workplace intelligence grounded in organizational data. This skill teaches the model how to use the full WorkIQ toolset: the `retrieve` tool for structured M365 resource retrieval (natural-language queries β†’ per-source hits + grounding markdown), the agentic `ask` tool for synthesized semantic answers, and the fast **entity tools** for direct structured access to M365 data (`fetch`, `create_entity`, `update_entity`, `delete_entity`, `do_action`, `call_function`, `search_paths`, `get_schema`). ## πŸ›‘ STOP β€” Read This Before Your First Tool Call @@ -28,7 +28,28 @@ See [Resolving tool names in your host](#resolving-tool-names-in-your-host) belo **USE WorkIQ for ANY workplace-related question.** If the answer might exist in Microsoft 365 data, try WorkIQ first. -**Choosing the right tool:** Use `ask` when the question requires **semantic understanding, synthesis, or reasoning** across M365 data ("what did someone say", "what's the status", "summarize"). Use `fetch` (or another entity tool) when the question is a **literal lookup of structured data** with a known shape ("list my meetings on Monday", "show me unread emails from X"). Entity tools return in under a second; `ask` typically takes 10–60 seconds per call and broad questions can run several minutes. +**Choosing the right tool β€” decide by query semantics, requested output, and data plane.** + +First ask: **Can the requested result be expressed as a known resource path plus deterministic query semantics?** If yes, use `fetch` / `call_function`; if the request instead depends on meaning or conceptual relevance, use `retrieve`. + +1. **Final human-facing answer or reasoning without an explicit evidence search β†’ `ask`.** Route directly to `ask` for summaries, explanations, comparisons, decisions, recommendations, expertise/ownership, or conversational answers when the user is asking for the conclusion rather than asking you to find semantically relevant source material. +2. **Explicit semantic content search or ranked M365 indexed evidence β†’ `retrieve`.** Use it when the user asks to find, search, identify, or return items or passages selected by meaning, including search within a named document or container by topic. An explicit semantic evidence request takes precedence when the user also asks for a summary or interpretation: call `retrieve`, then synthesize directly from its cited evidence without calling `ask`. Results are ranked and may not be exhaustive or perfectly precise. +3. **Authoritative deterministic query β†’ `fetch` / `call_function`.** Use entity reads when the agent can express the resource path and exact query, such as the chronological last five messages, a complete filtered collection, exact keyword search, IDs, or current entity fields. +4. **Writes β†’ bounded deterministic lookup, then the mutation tool.** Resolve the target once with `fetch` or the appropriate function, confirm ambiguity, then call the write tool. + +`retrieve` is not a prerequisite for `ask`, `fetch`, `call_function`, or mutations. The fact that a prompt is written in natural language does not by itself make it a retrieval request. + +**Hard mutual-exclusion rule:** Choose `ask` or `retrieve` once per user turn. After calling either tool, the other is off-limits for that turn. Do not switch between them as fallback, verification, broadening, or retry, and never alternate them in a loop. If the chosen tool returns empty, weak, or failed results, report that outcome instead of calling the other tool. The only retry exception is one failed `retrieve` call with `strategy: grounding` retried once with `strategy: copilot`. + +**Distinct-purpose chaining:** Use both only when each call serves a distinct purpose. For example, `retrieve` discovers relevant hits, then `fetch` reads exact fields from a selected hit; or `fetch` identifies a document or container, then `retrieve` searches its content semantically. Do not chain them merely to verify, broaden, or retry a sufficient result. + +**Grounding/synthesis stop rule:** If the user explicitly asks to find, search, identify, or return semantically relevant source material, use `retrieve`. Return its cited evidence directly or synthesize the requested summary from that evidence; do not call `ask` afterward. Use `ask` only when the user requests a synthesized answer without explicitly requesting an evidence search. A distinct exact read may follow `retrieve` when concrete fields are independently required. + +When the caller says the result will be consumed by another agent, prefer `retrieve` for grounding unless it also requires authoritative count, ordering, completeness, IDs, or fields. Exactness requirements override the downstream-agent preference. + +Suggested draft text belongs to `ask`; a persisted Outlook draft belongs to `create_entity`. + +Latency: entity tools < 1 s; `retrieve` typically 10–60 s; `ask` 10–60 s and broad questions can run several minutes. **ALWAYS use WorkIQ when the user asks about:** @@ -39,13 +60,18 @@ See [Resolving tool names in your host](#resolving-tool-names-in-your-host) belo | Meeting content/decisions/action items | "What was decided in yesterday's standup?" | `ask` | | Summarizing email threads or conversations | "Summarize the deadline thread with John" | `ask` | | Synthesizing Teams chat activity | "What's the team's take on the release?" | `ask` | -| Finding documents by topic | "Where is the design doc for Project X?" | `ask` | +| Finding documents by topic | "Where is the design doc for Project X?" | `retrieve` | +| Curating which sources cover a topic | "Which documents cover Project X?" | `retrieve` | | Colleague expertise or ownership | "Who owns the billing system?" | `ask` | | Organizational context / goals | "What are the team's Q1 goals?" | `ask` | | Project status or updates | "What's the status of Project X?" | `ask` | | Open-ended "any updates" / catch-up questions | "Any updates I should know about?" | `ask` | +| Grounding another agent with indexed evidence | "Find relevant emails with James about launch risk for another agent" | `retrieve` (`copilot`; omit `strategy`) | | Listing meetings on a known date/range | "What meetings do I have Monday?" | `fetch` (`/me/calendarView`) | | Listing emails with concrete filters | "Show my unread emails from Rob this week" | `fetch` (`/me/messages`) | +| Semantically relevant indexed evidence | "Return ranked email snippets from Rob that suggest launch risk" | `retrieve` (`copilot`; omit `strategy`) | +| Semantic search plus human-facing synthesis | "Find semantically relevant emails about launch risk and summarize the top matches" | `retrieve`, then synthesize from its cited evidence | +| Search inside a document or container by meaning | "Find where this folder's documents discuss launch risk" | `retrieve` (`copilot`; omit `strategy`) | | Listing Teams chats / channels / members | "List the channels in the DevX team" | `fetch` | | Sending/replying/reacting in Teams, setting presence | "Send a chat to Alex", "Post in the Daily channel", "React with πŸ‘", "Set me to Busy" | entity tools on `/chats/...` or `/teams/...` β€” see `references/teams-work-iq.md` | | Fetching a known entity by ID | "Get event `AAMk...` details" | `fetch` | @@ -55,8 +81,18 @@ See [Resolving tool names in your host](#resolving-tool-names-in-your-host) belo | Get a personal contact by name | "Get the contact card for Morgan Avery" | `fetch` (`/me/contacts?$filter=...`) β€” subject to server policy | | List or manage Outlook categories | "What Outlook categories do I have?" | `fetch` (`/me/outlook/masterCategories`); writes subject to server policy | | Org chart / direct reports / manager lookup | "Who are Rob's direct reports?" | `fetch` (`/users/{id}/directReports`) | +| Signed-in user's profile photo metadata | "Show my profile photo dimensions and content type" | `fetch` `/me?$select=id`, then `fetch` `/users/{id}/photo?$select=id,width,height`. Do not use the policy-denied `/me/photo` alias, request `/$value`, or put `@odata.mediaContentType` in `$select`; read the media content type annotation returned with the metadata. | +| Finding a 30-minute slot for the whole team | "Find a 30-min slot when the whole team is free this week" | Do not use `ask`. Resolve `/me`, `/me/manager`, and the manager's `/users/{managerId}/directReports` with at most two `fetch` calls, then call `do_action` `/me/calendar/getSchedule` exactly once with all schedulable addresses and `AvailabilityViewInterval: 30`. Compute the earliest common working-hours slot from that response; skip `search_paths`, `get_schema`, `findMeetingTimes`, and a second verification action. | | What's new/changed/removed since a point in time | "What's new in my Inbox since this morning?", "What's changed on my calendar since yesterday?", "What's been added to my contacts recently?" | `call_function` (delta β€” `/me/mailFolders/inbox/messages/delta`, `/me/calendarView/delta?...`, `/me/contacts/delta`). **Never call delta via `fetch`** β€” see `references/call-function-work-iq.md` | | Sending mail, accepting/declining meetings | "Send this draft", "Accept the 2pm meeting" | `do_action` | +| Tentatively accepting a meeting by title | "Mark the Office hours sync as tentative" | `fetch` the exact event ID, then `do_action` `/me/events/{id}/tentativelyAccept` with `{"sendResponse":false}`. Do not include an empty `comment`; do not call `get_schema` for this known contract. | +| Declining a meeting by title without a response message | "Decline the upcoming Daily standup invite" | `fetch` the exact event ID, then `do_action` `/me/events/{id}/decline` with `{"sendResponse":false}`. Omit `comment`; do not call `get_schema` or retry alternate payloads. | +| Cancelling an organizer-owned meeting by title | "Cancel the Friday staff meeting I organized" | `fetch` the exact event ID, then `do_action` `/me/events/{id}/cancel` with `{"Comment":""}`. This is a known contract: do not call `search_paths` or `get_schema`. A `202` response confirms acceptance; do not fetch again solely to verify. | +| Creating an upload session for an existing OneDrive file | "Create an upload session to replace my file; do not upload content" | `call_function` once with `/me/drive/root/search(q='{urlEncodedExactName}')?$select=id,name,parentReference,file&$top=10` to resolve the exact driveItem and retain `parentReference.driveId` plus item `id`, then `do_action` `/drives/{driveId}/items/{itemId}/createUploadSession` with `{}`. This is a validated deployed contract: skip `search_paths` and `get_schema`, do not add an `item` wrapper, and do not upload file content. | +| Copying a named OneDrive file to a named folder | "Copy Q3 plan.txt to Shared" | Use two `call_function` calls to `/me/drive/root/search(q='{urlEncodedExactName}')?$select=id,name,parentReference,file,folder&$top=10`, retain the source `parentReference.driveId`, then `do_action` `/drives/{driveId}/items/{sourceId}/copy` with `{"parentReference":{"driveId":"{driveId}","id":"{folderId}"}}`. Skip `search_paths`, `get_schema`, and verification fetches. | +| Renaming a OneDrive file | "Rename Draft.txt to Final.txt" | `call_function` once with `/me/drive/root/search(q='{urlEncodedExactName}')?$select=id,name,parentReference,file&$top=10` to resolve the exact driveItem and retain `parentReference.driveId` plus item `id`, then `update_entity` `/drives/{driveId}/items/{itemId}` with `{"name":"Final.txt"}`. Skip `search_paths` and `get_schema`; do not PATCH `/me/drive/items/{id}`. | +| Reading the first accessible SharePoint site's default drive or lists | "Show the first site's drive metadata", "List the first site's lists" | `fetch` `/sites?search=*&$select=id,displayName,name,webUrl&$top=1`, treat the first returned item as "first accessible", then `fetch` `/sites/{siteId}/drive` or `/sites/{siteId}/lists`. The parameter is `search=*`, **not** `$search=*`; do not use `ask`, guessed search terms, or an empty search. | +| Searching documents across SharePoint team sites | "List documents from SharePoint team sites" | `do_action` `/search/query` for `driveItem` documents, discard personal `-my.sharepoint.com` hits, then one batched `fetch` with `/sites?search={siteSlug}` to resolve site display names. Return exact file name, site display name, and `webUrl`; see `references/do-action-work-iq.md`. | | Creating a calendar event, draft, or task | "Create a calendar event Friday at 3pm" | `create_entity` | **DO NOT say "I don't have access to emails/meetings/messages"** - use WorkIQ instead! @@ -76,8 +112,9 @@ Follow the user's request through to completion. A discovery or read call **alon 1. **Path discovery** ("endpoint", "available operations", "what can I do with X") β†’ `search_paths` first. Continue to the read/write tool if the prompt also asks to act. 2. **Schema inspection** ("schema", "data model", "fields", "what does X take") β†’ `get_schema` first. Continue to the write/action tool if the prompt also asks to act. -3. **Exact entity read or mutation by title/name/channel/thread** β†’ `fetch` to resolve the target's ID, then `update_entity` / `delete_entity` / `do_action`. Do not use `ask` to resolve exact titled events, messages, drafts, folders, Teams chats/channels, or threads. -4. **Semantic summary/status/decisions** β†’ `ask`. If the prompt then asks to draft, send, create, update, delete, forward, or react, continue with the mutation tool β€” the `ask` answer alone is incomplete. +3. **Exact entity read by known ID or entity URL** β†’ `fetch`. For a mutation whose target is not yet identified, use one bounded mutation-target lookup with `fetch` against the relevant collection or filter, ask the user to choose if multiple entities match, then call `update_entity` / `delete_entity` / `do_action` with the confirmed ID. +4. **Summarize/explain/compare/draft/answer/decide/recommend without an explicit semantic evidence search** β†’ route directly to `ask`, then do not call `retrieve` in the same turn. If the prompt then asks to send, create, update, delete, forward, or react, continue with the mutation tool β€” the `ask` answer alone is incomplete. +5. **Find/search/identify semantically relevant M365 evidence** β†’ `retrieve` with the default `copilot` strategy (omit `strategy`), including when the caller also requests a summary of the matches. If the caller explicitly prioritizes speed or low latency, use `strategy: grounding`. Synthesize directly from the retrieved evidence; do not call `ask`, and never use `retrieve` to resolve a mutation target. ### Resolve-then-act β€” concrete examples @@ -88,7 +125,11 @@ When the user asks to delete, update, send, forward, copy, move, or react to som | "Mark email as read" | `fetch` to find the message | `update_entity` `/me/messages/{id}` with `{"isRead": true}` | | "Forward email to X" | `fetch` to find the message | `do_action` `/me/messages/{id}/forward` | | "Send email to X" | β€” | `do_action` `/me/sendMail` | -| "Copy file to folder" | `fetch` to find file and target folder | `do_action` `/me/drive/items/{id}/copy` | +| "Cancel the X meeting I organized" | `fetch` to find the event and verify `isOrganizer` | `do_action` `/me/events/{id}/cancel` with `{"Comment":""}`; accept `202` as success without a verification fetch | +| "Create an upload session to replace existing file X" | `call_function` once with `/me/drive/root/search(q='{urlEncodedExactName}')?$select=id,name,parentReference,file&$top=10` to resolve the exact driveItem and retain `parentReference.driveId` plus item `id` | `do_action` `/drives/{driveId}/items/{itemId}/createUploadSession` with `{}`; do not add `item`, inspect schema, or upload bytes | +| "Copy file to folder" | Two `call_function` calls to `/me/drive/root/search(q='{urlEncodedExactName}')?$select=id,name,parentReference,file,folder&$top=10`, one for the exact source and one for the exact folder | `do_action` `/drives/{driveId}/items/{sourceId}/copy` with `{"parentReference":{"driveId":"{driveId}","id":"{folderId}"}}`; skip `search_paths`, `get_schema`, and verification fetches | +| "Move file to folder" | Two `call_function` calls to `/me/drive/root/search(q='{urlEncodedExactName}')?$select=id,name,parentReference,file,folder&$top=10`, one for the exact source and one for the exact folder | `update_entity` `/drives/{driveId}/items/{sourceId}` with `{"parentReference":{"id":"{folderId}"}}`. This is an update, not a `/move` action; skip `search_paths`, `get_schema`, verification fetches, and `/move`. | +| "Rename file X to Y" | `call_function` once with `/me/drive/root/search(q='{urlEncodedExactName}')?$select=id,name,parentReference,file&$top=10` to resolve the exact driveItem and retain `parentReference.driveId` plus item `id` | `update_entity` `/drives/{driveId}/items/{itemId}` with `{"name":"Y"}`; skip `search_paths` and `get_schema`, and do not use `/me/drive/items/{id}` | | "Set presence to busy" | β€” | `do_action` `/me/presence/setUserPreferredPresence` β€” see `references/teams-work-iq.md` | | "React to Teams message" | `fetch` to find the message | `do_action` `/teams/{teamId}/channels/{channelId}/messages/{messageId}/setReaction` | | "Delete" any entity | `fetch` to find it | `delete_entity` on the entity URL | @@ -185,11 +226,13 @@ If you call the logical name verbatim and get a "tool does not exist" error, thi ## MCP Tools -### `ask` β€” Agentic natural language M365 queries +### `ask` β€” Synthesized M365 reasoning and answers -The primary tool. Ask any workplace question in plain English. This is an **agentic tool** β€” it orchestrates multi-step operations internally (searching emails, meetings, Teams chats, documents, people) to answer complex questions. Use it when you need intelligence, synthesis, or semantic understanding across M365 data. +Ask Microsoft 365 Copilot to summarize, explain, compare, draft, answer, decide, or recommend using workplace data when the user has not explicitly requested a semantic evidence search. This is an **agentic tool** β€” it orchestrates multi-step operations internally (searching emails, meetings, Teams chats, documents, people) and produces a synthesized conversational answer. Use `retrieve` when the prompt explicitly asks to find, search, identify, or return semantically relevant items or passages, even when it also asks for a summary. > **⏱️ High latency:** A call typically takes **10–60 seconds** as the agent performs multiple backend operations, and broad questions can run several minutes (the hard limit is ~300s). Avoid calling it in tight loops or for simple data retrieval β€” use the entity tools below for that instead. If a question is broad, split it into scoped sub-questions rather than one mega-question. +> +> **Terminal choice:** Once you call `ask`, do not call `retrieve` later in the same turn, including after an empty or failed response. Report the observed outcome instead. Use at most one `ask` call per user turn. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| @@ -206,20 +249,54 @@ For detailed usage and examples, read `references/ask-work-iq.md`. --- +### `retrieve` β€” M365 indexed grounding evidence + +Retrieve ranked evidence from the M365 index for user-facing semantic discovery or grounding another agent, model, or RAG workflow. It returns snippets, citations, source metadata, grounding markdown, and structured hits. It does not provide authoritative database-style completeness or ordering. When the user also requests a summary, synthesize it directly from the retrieved evidence. + +> **Strategy default:** Use `copilot` by default and normally omit `strategy`. Use `grounding` only when the user explicitly asks for faster, optimized-for-latency, or low-latency retrieval, or explicitly requests the grounding strategy. If "optimize" could mean quality or coverage rather than speed, stay on `copilot`. If connected or external sources may be required, keep `copilot` and explain that `grounding` would narrow coverage. +> +> **⏱️ Latency:** Typically 10–60 seconds. `grounding` can be faster because it limits retrieval to the M365 index, but do not choose it merely because the query looks M365-only; the user must explicitly prioritize speed or request grounding. Use `fetch` / `call_function` when the consumer needs exact records, ordering, counts, IDs, or fields. +> +> **πŸ“„ Consuming the response:** The `markdown` field is the **preferred** output β€” a model-friendly grounding summary with inline `[^id]` citations that map to `retrievalHits[*].id`. Emit it verbatim to the caller; do not paraphrase it or rebuild a summary from `retrievalHits`. Reach into the structured `retrievalHits` payload only for specific fields the markdown doesn't already expose (e.g., a raw `webUrl` or `sensitivityLabel` for one hit). +> +> **Retry policy:** If a `grounding` call fails, retry the same intent exactly once with `strategy: copilot`, then stop. If the first call uses `copilot`, do not retry it. After the final retrieval result, report the observed outcome and never fall back to `ask`. Use `fetch` or `search_paths` afterward only when the original request independently requires a concrete known-resource read or path discovery. +> +> **Terminal choice:** Once you call `retrieve`, do not call `ask` later in the same turn. If synthesis is requested, produce it directly from the retrieval response. Do not alternate `retrieve` and `ask`. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `query` | string[] | Yes | One or more natural-language search queries. At least one non-empty entry required. | +| `strategy` | string | No | `copilot` (default β€” M365 indexed content plus connected data sources, federated connectors, MCP tools) or `grounding` (M365 indexed content only). Any other value is rejected. | +| `includeDeveloperCard` | boolean | No | When `true`, includes orchestration diagnostics in the response. Defaults to `false`. | +| `agentId` | string | No | Only `bizchat-as-gpt-scenario` is accepted; any other value is rejected. | + +```json +{ "query": ["design doc for authentication", "auth spec Project X"] } +``` + +For detailed usage, response structure, strategy guidance, and faceted metadata, read `references/retrieve-work-iq.md`. + +--- + ## Entity Tools -Entity tools provide **fast, direct access to specific M365 data** via Work IQ APIs. They return structured results quickly but have **no intelligence** β€” they don't interpret, synthesize, or reason about the data. Use them when you know exactly what you want and where it lives. +Entity tools provide **fast, direct access to specific M365 data** via Work IQ APIs. They return structured results quickly but have **no intelligence** β€” they don't interpret, synthesize, or reason about the data. Use them for writes, path/schema operations, or literal reads after a concrete resource ID or entity URL is supplied. **When to use each:** | Scenario | Use | |----------|-----| -| Open-ended question, semantic search, synthesis | `ask` (slow but smart) | -| Fetch a known list, apply a filter, get structured data | entity tools (fast but literal) | +| Ground an agent with ranked enterprise references β€” "Find relevant emails about WorkIQ for another agent" | `retrieve` (`copilot`, omit `strategy`) | +| User explicitly prioritizes faster / low-latency M365-only retrieval | `retrieve` (`strategy: grounding`) | +| Human-facing summarize/explain/compare/draft/answer/decide/recommend without an explicit evidence search | `ask` (route directly; do not call `retrieve` first) | +| Explicit semantic search plus a summary of the matches | `retrieve`, then synthesize directly from its cited evidence | +| Read exact structured records, counts, ordered items, filtered collections, IDs, or fields | `fetch` / `call_function` | + +**Recommended workflow:** use `retrieve` with its default `copilot` strategy whenever the user explicitly asks to find or search by meaning, including requests to summarize the resulting matches. Use `ask` for synthesis without an explicit evidence search. Use `grounding` only for an explicit speed/low-latency optimization request. Use `fetch` / `call_function` for authoritative exactness even when the request is phrased naturally. Use `search_paths` β†’ `get_schema` β†’ an entity tool only when the user asks about paths/schema or a requested write needs an unknown path or body shape. -**Recommended workflow:** for **well-known paths, go direct** β€” call the read/write tool immediately (use the cheat sheet below). Only fall back to `search_paths` β†’ `get_schema` β†’ tool when the path is genuinely unknown or a write body shape is unfamiliar. Do **not** reflexively run `search_paths`/`get_schema` before every common operation. +### πŸ—ΊοΈ Known entity paths β€” skip discovery after entity-tool routing is established -### πŸ—ΊοΈ Known paths β€” go direct, skip discovery +This catalog helps execute exact entity reads and operations. Requests such as "get my next meeting" or "last five emails" require deterministic ordering and therefore use entity tools, while "find relevant recent meetings about launch risk for another agent" uses `retrieve`. | Resource | Path root | Common ops | |----------|-----------|-----------| @@ -284,6 +361,7 @@ above) and call the MCP tool. - ❌ Calling `get_schema` on paths you already know (contacts, messages, events, drive items). - ❌ Using `fetch` to "explore" when the path is already implied by context. - ❌ Falling back to dozens of `fetch` calls when `ask` fails β€” report the failure instead. +- ❌ Calling both `ask` and `retrieve` in one turn, or alternating them to broaden, verify, or retry results. **Do:** use the path patterns in this document to route directly to the correct tool in 1–2 calls. If you need the entity ID first, one `fetch` to resolve, then one write tool call. @@ -302,10 +380,10 @@ no subject named, an empty title, or a generic "the meeting"): To act on a named entity ("the X email", "my Y task", "the Z draft"): -1. Resolve it with **one** `fetch` (filter by subject/title/displayName). -2. If the first fetch misses, try **one** `ask` to locate it semantically. +1. Resolve it with **one bounded** `fetch` against the relevant collection, filtering by subject/title/displayName when supported. +2. If the first fetch misses, try **one** `retrieve` to locate it semantically. 3. If still not found, **stop and report "not found"** β€” do **not** fire 10+ more - `fetch`/`search_paths`/`ask` calls hunting for it. + `fetch`/`search_paths`/`ask`/ `retrieve` calls hunting for it. 4. Once you have the id, call the mutation (`update_entity` / `delete_entity` / `do_action`) **directly** β€” finding the target is not the goal; performing the requested action is. 5. If a mutation fails, fix the request (URL shape, `jsonBody` encoding, ID) and retry **at most @@ -345,16 +423,18 @@ Reference examples use `{id}`, `{listId}`, `{teamId}`, `{taskId}`, `{driveId}`, `do_action` (especially `/me/sendMail`, `/forward`, `/accept`, `/decline`, `/permanentDelete`) and write-side `create_entity` / `update_entity` / `delete_entity` calls take effect immediately and are visible to other people (recipients, meeting organizers) or unrecoverable. **Before invoking any write tool, summarize what you're about to do and get the user's confirmation.** This is especially important for sendMail, forward, decline, and permanentDelete. -### "Draft", "compose", "prepare reply" requires a persisted draft +### Suggested draft text versus a persisted Outlook draft -When the user says "draft an email", "compose a reply", "prepare a response", or any variant -asking the draft to *exist* (not just suggest wording), call `create_entity` to POST: +When the user asks for suggested wording, a draft response, or help composing text without +asking to save it in Outlook, use `ask`. When the user explicitly asks to create, save, or +persist an Outlook draft, call `create_entity` to POST: - `/me/messages` for a fresh draft - `/me/messages/{id}/createReply`, `/createReplyAll`, or `/createForward` for replies/forwards (these are `create_entity` POSTs, **not** `do_action`) -Generating draft text inline does NOT satisfy the request β€” the user can't open it in Outlook. +For a persisted-draft request, generating text inline does NOT satisfy the request β€” the user +can't open it in Outlook. A common failure: call `ask` for the summary half of a "summarize then draft" chain and stop; the `create_entity` step is required. @@ -383,6 +463,7 @@ Read the relevant reference file for full parameter details and examples: - `references/search-paths-work-iq.md` β€” if you need to discover what paths are available - `references/get-schema-work-iq.md` β€” if you need to understand an entity's fields before reading or writing +- `references/retrieve-work-iq.md` β€” if you need to retrieve structured M365 references (emails, files, meetings, Teams messages, people) for programmatic use, RAG, or agent-to-agent grounding - `references/fetch-work-iq.md` β€” if you need to fetch structured or filtered M365 data - `references/call-function-work-iq.md` β€” if the path uses OData function call syntax (e.g., `reminderView(...)`, `delta`) - `references/create-entity-work-iq.md` β€” if you need to create a new calendar event, email draft, task, etc. diff --git a/plugins/workiq-preview/skills/workiq-preview/references/fetch-work-iq.md b/plugins/workiq-preview/skills/workiq-preview/references/fetch-work-iq.md index b558498..addd32f 100644 --- a/plugins/workiq-preview/skills/workiq-preview/references/fetch-work-iq.md +++ b/plugins/workiq-preview/skills/workiq-preview/references/fetch-work-iq.md @@ -1,6 +1,8 @@ # fetch -Fetch one or more WorkIQ entities by path using HTTP GET. Use this for precise, structured retrieval of M365 data when `ask` isn't specific enough β€” for example, to get a list of items with specific fields, apply filters, or read a single entity by ID. +Read authoritative Microsoft 365 resources using a known path, ID, or deterministic collection/filter. Use `fetch` when exact records, counts, ordering, IDs, or fields matter, including one bounded mutation-target lookup. Do not use it for semantic relevance ranking, indexed grounding, reasoning, or answer synthesis. + +The primary test is whether the agent can express the requested result as a resource path plus deterministic query semantics. A natural-language request can still use `fetch` when it maps cleanly to a deterministic query such as an OData filter, ordering clause, or exact keyword `$search`. ## Parameters @@ -10,21 +12,34 @@ Fetch one or more WorkIQ entities by path using HTTP GET. Use this for precise, ## When to Use -- When you need a structured list of entities (messages, events, files, etc.) -- When you need to apply specific OData filters or select specific fields -- When you already have an entity ID and want its full details +- Read an entity whose ID or exact entity URL is already known +- Read a known collection path with explicit fields or deterministic OData filters +- Run an exact keyword search when the resource collection and search expression are known +- Resolve the exact target of a requested mutation with one bounded collection/filter read - For multi-fetch: pass multiple URLs to retrieve several entities in one call -Prefer `ask` for open-ended questions. Use `fetch` when you need precise, filtered, or structured data. +Do not use `fetch` for semantic discovery, relevance ranking, indexed grounding, or answer synthesis. + +- Ranked M365 indexed evidence, citations, and grounding for another agent/RAG workflow β†’ `retrieve` +- Summaries, explanations, comparisons, recommendations, and conversational answers β†’ `ask` + +Use `fetch` for exact recent/latest/last-N requests when count and chronological ordering are requirements. For example, "get my last five emails" is a deterministic collection read; "find the most relevant recent emails about launch risk for another agent" is indexed grounding and belongs to `retrieve`. + +`fetch` and `retrieve` may be combined only for distinct purposes. A semantic `retrieve` can identify a relevant hit before `fetch` reads exact authoritative fields from that selected resource. Do not add either tool merely to verify or broaden a sufficient result. -Use `fetch` (not `ask`) to resolve exact targets before mutations β€” find an event ID before deleting/updating, a draft before adding recipients or sending, a Teams chat/channel/message before editing/reacting/posting, a mail thread before reply/forward/move/mark-read. +Use `fetch` to resolve exact targets before mutations β€” find an event ID before deleting/updating, a draft before adding recipients or sending, a Teams chat/channel/message before editing/reacting/posting, or a mail thread before reply/forward/move/mark-read. Keep the lookup bounded; ask the user to choose when multiple entities match, and stop when none match. -For exact reads ("show/list/get latest messages", "list members", "show my chats", "retrieve the event titled…"), prefer filtered `fetch` or a known function path. Do not answer from general knowledge, local SQL, or `ask` unless the prompt asks for synthesis. +For literal structured reads such as "list members," "show my chats," or "get event AAMk...," use filtered `fetch` or a known function path. Do not answer from general knowledge or local SQL. > **⚠️ Not for delta queries.** Calling `/.../delta` or `/.../delta()` through `fetch` > fails β€” delta is an OData **function** and must go through `call_function`. See > `references/call-function-work-iq.md`. +> **Named OneDrive search is also a function.** For a file identified by exact +> name, call `/me/drive/root/search(q='...')` through `call_function` once and +> answer from that result. Do not follow it with `/me/drive/items/{id}` merely +> to retrieve the same metadata again. + ## Multi-fetch caveats - The batch result can report an error when **any one** URL fails, even if the other URLs @@ -47,8 +62,8 @@ Collection responses are **pages**, not the full result set. When a response con support it and the call fails. - If you stop before exhausting pages, **tell the user the list is partial** ("first 25 of more") β€” never present one page as the complete answer. -- **Cap your paging.** For "latest/recent" questions one page is usually enough; otherwise stop - after 2–3 pages unless the user explicitly asked for the complete set. Do not follow +- **Cap your paging.** For bounded structured collection reads, one page is usually enough; + otherwise stop after 2–3 pages unless the user explicitly asked for the complete set. Do not follow `@odata.nextLink` for dozens of pages to enumerate an entire mailbox or message history. ## URL Format @@ -103,11 +118,33 @@ Never fabricate base64 content, `@odata.mediaContentType`, or an `@microsoft.gra ## Examples +These are literal structured reads against known paths or deterministic filters. For semantic +search, relevance ranking, citations, or downstream-agent grounding, use `retrieve` instead. + ### Get the signed-in user's profile ```json { "entityUrls": ["/me"] } ``` +### Get the signed-in user's profile photo metadata + +Resolve the signed-in user's id, then read the photo through the exposed +user-id path. Do not call the policy-denied `/me/photo` alias, and do not call +`/$value`, which is binary content. + +```json +{ "entityUrls": ["/me?$select=id"] } +``` + +```json +{ "entityUrls": ["/users/{id}/photo?$select=id,width,height"] } +``` + +Do not put `@odata.mediaContentType` or `@odata.type` in `$select`; Graph rejects +those annotations in a select expression. Read the media content type annotation +from the metadata response when a profile photo exists. A `404 ImageNotFound` +means the selected user currently has no profile photo. + ### Get unread emails (top 10) ```json { "entityUrls": ["/me/messages?$top=10&$filter=isRead%20eq%20false&$select=subject,from,receivedDateTime"] } @@ -133,6 +170,39 @@ Never fabricate base64 content, `@odata.mediaContentType`, or an `@microsoft.gra { "entityUrls": ["/me/drive/root/children?$select=name,size,lastModifiedDateTime"] } ``` +### Get the first accessible SharePoint site's default drive or lists + +For prompts that say "the first SharePoint site I can access," use the first +item returned by the exact site search below. Microsoft Graph's site collection +uses the `search` parameter without a `$` prefix. Do not try `$search=*`, an +empty search, guessed terms, or `ask`. + +```json +{ + "entityUrls": [ + "/sites?search=*&$select=id,displayName,name,webUrl&$top=1" + ] +} +``` + +Then use the returned site `id` in exactly one of these reads: + +```json +{ + "entityUrls": [ + "/sites/{siteId}/drive?$select=id,name,driveType,owner,quota,webUrl,createdDateTime,lastModifiedDateTime,description,system" + ] +} +``` + +```json +{ + "entityUrls": [ + "/sites/{siteId}/lists?$select=id,displayName,name,webUrl&$top=200" + ] +} +``` + ### Get Teams channels for a group ```json { "entityUrls": ["/teams/{teamId}/channels"] } diff --git a/plugins/workiq-preview/skills/workiq-preview/references/mail-work-iq.md b/plugins/workiq-preview/skills/workiq-preview/references/mail-work-iq.md index 35725cd..a9bad33 100644 --- a/plugins/workiq-preview/skills/workiq-preview/references/mail-work-iq.md +++ b/plugins/workiq-preview/skills/workiq-preview/references/mail-work-iq.md @@ -1,23 +1,27 @@ # Mail (Outlook messages and folders) -Use the WorkIQ **entity tools** for mail requests β€” listing/searching messages, reading folders, -drafting/sending/replying/forwarding, marking read, copying/moving, and deleting. Use `ask` only -for synthesis questions ("summarize the deadline thread with John"), not for finding, -listing, or mutating individual messages. - -## Mail delta: prefer `/me/messages/delta` for full-mailbox sync - -For "sync my mail", "fetch the mail delta", or "give me mail changes" with **no folder named**, -route `call_function` to `/me/messages/delta` β€” full mailbox in one cursor. -`/me/mailFolders/{folderId}/delta` (e.g. `/me/mailFolders/inbox/delta`) is folder-scoped; use it -only when the user names a folder. +Use `retrieve` for relevance-ranked M365 indexed mail grounding, especially for another +agent or RAG workflow. Use `ask` for synthesis questions such as "summarize the deadline +thread with John" or semantic search plus a human-facing summary of the top matches. If +the user asks to find meaning-based matches **and** summarize or interpret them, route +directly to `ask`; use `retrieve` only when the ranked mail evidence itself is the requested +deliverable. Use the WorkIQ **entity tools** for authoritative records, exact counts, +chronological ordering, deterministic filters, and for drafting, sending, replying, +forwarding, marking read, copying, moving, and deleting messages. + +## Mail delta: use `/me/mailFolders/{id}/messages/delta` (folder-scoped) + +Message delta is **always folder-scoped** β€” there is **no** tenant-wide `/me/messages/delta` +endpoint. For "sync my mail", "fetch the mail delta", or "give me mail changes" with **no folder +named**, default to the inbox cursor `/me/mailFolders/inbox/messages/delta`. When the user names a +folder, target that folder's messages delta, e.g. `/me/mailFolders/{folderId}/messages/delta`. Paginate `@odata.nextLink` until you reach `@odata.deltaLink` (resume token for the next sync) β€” stopping at the first page is wrong. > **Always `call_function`, never `fetch`.** `delta` is an OData function. Calling -> `/me/messages/delta` through `fetch` returns an `InvalidRequest` or wrong shape; route through -> `call_function` with the function URL. +> `/me/mailFolders/inbox/messages/delta` through `fetch` returns an `InvalidRequest` or wrong +> shape; route through `call_function` with the function URL. ## Finding a message by subject β€” use `$search`, not `$filter=contains` @@ -58,19 +62,20 @@ folder names are exact-match by design. Use it for `rename` / `move` / `delete` | Permanently delete (bypasses Deleted Items) | `do_action` | `/me/messages/{id}/permanentDelete` | | List folders | `fetch` | `/me/mailFolders` | | Find a folder by name | `fetch` | `/me/mailFolders?$filter=displayName eq 'Specs'` | -| Mail delta (no folder) | `call_function` | `/me/messages/delta` | -| Mail delta (folder-scoped) | `call_function` | `/me/mailFolders/inbox/messages/delta` | +| Mail delta (default / no folder named) | `call_function` | `/me/mailFolders/inbox/messages/delta` | +| Mail delta (specific folder) | `call_function` | `/me/mailFolders/{folderId}/messages/delta` | ## "Draft" vs "send" β€” pick the right verb -When the user says **"draft an email"**, **"compose a reply"**, **"prepare a response"**, or any -variant asking the draft to **exist** (not just suggest wording), call `create_entity` to POST: +When the user asks for suggested wording without asking to save it in Outlook, use `ask`. +When the user explicitly asks to create, save, or persist an Outlook draft, call +`create_entity` to POST: - Fresh draft β†’ `/me/messages` - Reply / reply-all / forward β†’ `/me/messages/{id}/createReply`, `/createReplyAll`, `/createForward` -These create persisted drafts the user can open in Outlook. **Generating draft text inline -does NOT satisfy the request** β€” the user can't open it in Outlook. +These create persisted drafts the user can open in Outlook. For a persisted-draft request, +**generating draft text inline does NOT satisfy the request** β€” the user can't open it in Outlook. `do_action` `/reply`, `/replyAll`, `/forward`, `/sendMail` all send **immediately** β€” never use those when the user asked for a draft. @@ -79,7 +84,7 @@ those when the user asked for a draft. 1. Resolve the message with **one** `fetch` (filter by `$search` for subject, or by `id`). 2. If the first fetch misses, try **one** `ask` to locate it semantically. -3. If still not found, **stop and report "not found"** β€” do not fire 10+ more - `fetch`/`search_paths`/`ask` calls. +3. If the fetch returns no matches, **stop and report "not found"** β€” do not fire repeated + `fetch` or `search_paths` calls. 4. Once you have the id, call the mutation directly. Finding the message is not the goal; performing the requested action is. diff --git a/plugins/workiq-preview/skills/workiq-preview/references/retrieve-work-iq.md b/plugins/workiq-preview/skills/workiq-preview/references/retrieve-work-iq.md new file mode 100644 index 0000000..5634aaa --- /dev/null +++ b/plugins/workiq-preview/skills/workiq-preview/references/retrieve-work-iq.md @@ -0,0 +1,270 @@ +# retrieve + +Semantically search Microsoft 365 and connected tools for grounding data (emails, files, meetings, Teams messages, people) for the current user when the desired content cannot be expressed as a deterministic resource query. Returns ranked evidence, snippets, citations, source metadata, structured hits, and model-friendly grounding markdown. `retrieve` supports user-facing semantic discovery as well as grounding another agent, model, or RAG workflow; it does not synthesize a final conversational answer or guarantee authoritative count, ordering, or completeness. + +> **When to reach for `retrieve`:** use it when the requested deliverable is relevant grounding evidence selected by meaning or conceptual relevance rather than a resource path plus deterministic query. This includes finding emails, files, meetings, messages, people, or passages about a topic and searching within a known document or container by meaning. +> +> **Strategy default:** Use `copilot` by default. Omit `strategy` for the normal `copilot` path. Use `grounding` only when the user explicitly asks for faster, optimized-for-latency, or low-latency retrieval, or explicitly names the grounding strategy. Ambiguous requests to "optimize" stay on `copilot` unless the user ties optimization to speed or latency. If connected or external sources may be required, keep `copilot` and explain that `grounding` would narrow coverage even when speed is requested. +> +> **Output boundary:** route to `ask`, not `retrieve`, when the requested deliverable is a synthesized conclusion or narrative β€” for example, comparing sources or versions, identifying which sources cover a topic, summarizing the top semantic matches, identifying ownership, a point of contact, expertise, dependencies, blockers, implications, priorities, a career timeline, or whether something needs the user's attention. Citations do not change this boundary: supporting sources can accompany an `ask` answer without turning the request into a ranked-evidence deliverable. The fact that evidence must be found across multiple sources does not make the final-answer request a retrieval task. Use `fetch` / `call_function` when the result can be expressed as authoritative deterministic records, fields, counts, ordering, or filters. +> +> **Precision and recall:** Semantic results are ranked and truncated, so recall and precision are not guaranteed. Relevant items may be omitted, and weakly related items may be returned. Do not present retrieval hits as exhaustive or as a deterministic filter result. +> +> **Not a prerequisite:** Do not call `retrieve` before `ask`, an exact entity read, or a mutation. Natural-language phrasing alone does not make a request a retrieval task. +> +> **Mutually exclusive with `ask`:** Choose one of these tools for the current user turn. Once `retrieve` is called, do not call `ask` afterward for synthesis, fallback, verification, broadening, or retry. If synthesis is requested, produce it directly from the cited retrieval evidence. Never alternate the two tools in a loop. +> +> **Distinct-purpose chaining:** `retrieve` may precede `fetch` when semantic discovery identifies a hit whose exact fields are then required. It may follow `fetch` when a deterministic read identifies a document or container whose content must then be searched semantically. Do not chain the tools merely to verify, broaden, or retry a sufficient result. +> +> **Stop at the requested deliverable:** For a grounding request, return sufficient cited retrieval evidence and stop unless a distinct exact read is also required. For a synthesized answer, choose `ask` instead of `retrieve`, even when citations or source names are requested. Use `retrieve` and synthesize directly from its evidence only when the caller explicitly requests a separate ranked package of hits, snippets, citations, or grounding for downstream consumption; do not call `ask` afterward. +> +> **Mutation target resolution:** Do not use `retrieve` to identify a mutation target. Use one bounded `fetch` against the relevant entity collection or filter, ask the user to choose if multiple entities match, and perform the write only with the confirmed ID. +> +> **⏱️ Latency:** Typically 15–60 seconds depending on strategy. Lower than `ask` because it skips conversational synthesis. Prefer `fetch` when you already know the exact resource ID or entity URL. +> +> **Grounding:** Base your answer only on what the response actually contains. If `retrieve` reports no accessible results or weak evidence, say so β€” do not pad the answer with information the response does not support. + +## Parameters + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `query` | string[] | Yes | One or more natural-language search queries. Each string becomes a separate retrieval query. At least one non-empty, non-whitespace entry is required after stripping. | +| `strategy` | string | No | Retrieval strategy. `copilot` (default) or `grounding`. Any other value is rejected. Case-insensitive. | +| `includeDeveloperCard` | boolean | No | When `true`, requests orchestration diagnostics (agent metadata, tool invocations, retrieval summary) from Sydney. Defaults to `false`. | +| `agentId` | string | No | Only `bizchat-as-gpt-scenario` is accepted; any other value is rejected without reaching Sydney. Omit to use the default. | + +## Strategies + +The strategy controls **where data comes from**, not how results are ranked or how the response is shaped. Both strategies return the same `RetrievalResponse` envelope β€” the response parser does not need to change when switching. + +### `copilot` (default) + +Semantically discover relevant enterprise content and return structured evidence, snippets, citations, and metadata. It can use M365 indexed content plus federated connectors, external connected data sources, plugins, MCP tools, and other supported enterprise systems. Another agent should reason over the returned evidence; `retrieve` does not produce the final conversational answer. Omitting `strategy` selects `copilot`. + +Use `copilot` when: +- The request may need data beyond the M365 index (e.g., a Salesforce connector, ServiceNow, a connected enterprise system, or an MCP tool) +- Data location is unknown or requirements may expand beyond M365 indexed content +- You want the broadest possible coverage as a safe default + +### `grounding` + +Return ranked grounding snippets and citations for a focused query over M365 indexed content only β€” SharePoint, OneDrive, Teams chats and messages, Outlook mail, and other content in the Microsoft 365 semantic index. Use it for low-latency RAG context assembly. It does not synthesize an answer or perform full agent reasoning. No connected connectors, external sources, plugins, or tools are invoked. + +Use `grounding` when: +- The user explicitly asks for faster, optimized-for-latency, or low-latency retrieval +- The user explicitly requests `grounding` +- The request does not require connected or external sources + +> **Decision rule: default to `copilot`; opt into `grounding` only for an explicit speed/latency preference.** M365-only data location is not enough by itself to choose `grounding`. If connected or external sources are needed or possible, use `copilot`. + +## Response structure + +The tool returns two content forms: + +1. **Text block** (`content[0].text`) β€” the `markdown` field verbatim. Contains the grounding summary with inline citation markers like `[^h1]`. This is suitable for direct inclusion in a response without further processing. +2. **Structured content** (`structuredContent["application/vnd.ms-workiq.retrieval"]`) β€” the full typed payload for programmatic consumers. + +> **Choose the response form for the consumer.** Model and prompt consumers should usually receive the citation-bearing `markdown`. Programmatic clients that need fields, IDs, URLs, scores, or facets should consume `structuredContent` / `retrievalHits` directly. Do not force a programmatic caller to re-parse prose. + +### Envelope shape (`application/vnd.ms-workiq.retrieval`) + +```json +{ + "markdown": "## Found 3 relevant sources\n\n...[^h1]...[^h2]...", + "resultCount": 3, + "stoppedReason": "completed", + "retrievalHits": [ ... ] +} +``` + +| Field | Type | Description | +|-------|------|-------------| +| `markdown` | string | Model-friendly grounding summary with inline citation markers (`[^id]`) that reference hits by their `id`. | +| `resultCount` | integer | Number of surviving hits after Sydney's shim/filter/trim pass. | +| `stoppedReason` | string | Terminal state: `completed`, `cancelled`, or `error`. | +| `retrievalHits` | RetrievalHit[] | Per-source references. May be absent when Sydney returns no hits. | + +### RetrievalHit + +Each element in `retrievalHits` represents one M365 source: + +| Field | Type | Description | +|-------|------|-------------| +| `id` | string | Hit ID that cross-references the markdown citations (`[^h1]` β†’ `id: "h1"`). | +| `webUrl` | string? | URL the user can navigate to in order to view the source. | +| `resourceMetadata` | ResourceMetadata? | Per-resource metadata facets (see below). | +| `sensitivityLabel` | object? | MIP sensitivity label for the source: `id`, `displayName`, `isEncrypted`. Present only when Sydney supplies it. | +| `relevanceScore` | number? | Sydney relevance score. Present only when Sydney supplies it. | + +### ResourceMetadata facets + +Only the facet(s) matching the resource type are present on a given hit. Multiple facets may appear simultaneously. + +| Facet | Present for | Fields | +|-------|-------------|--------| +| `document` | Files, OneDrive/SharePoint documents | `title`, `authorName`, `occurrenceTime` (ISO-8601 last-modified) | +| `email` | Emails | `subject`, `senderName`, `sentTime` (ISO-8601) | +| `meeting` | Calendar meetings | `title`, `organizerName`, `startTime` (ISO-8601) | +| `video` | Videos | `title`, `durationSeconds` | +| `web` | Web pages, external links, connector content | `title`, `siteName` | +| `chat` | Teams chat/channel messages | `title` | +| `image` | Images | `title` | +| `general` | **Non-contractual fallback** for resource types without a dedicated facet | `title`, `authorName`, `occurrenceTime` | + + +### Runtime handling (large responses) + +Some MCP hosts (including the GitHub Copilot CLI) surface an MCP tool result as a **single concatenated blob** β€” the `markdown` text block followed by the JSON `structuredContent`. When the combined payload exceeds the host's inline output cap (typically ~20 KB), the entire blob is spilled to a temporary file and the agent only sees a short preview. In that case: + +- **Read the spilled payload once.** Do not repeatedly inspect the same payload with `view`, `rg`, or shell commands. +- **Do not** reparse the structured JSON to rebuild a summary β€” the `snippet` / discussion fields are long and noisy, and doing so drops the built-in `[^h1]` citations. +- **Do** slice the spilled file to isolate the leading `markdown` segment and use it verbatim. The markdown ends immediately before the JSON tail, which starts with a line like: + + ``` + {"application/vnd.ms-workiq.retrieval":{"markdown":"… + ``` + + Everything above that line is the model-friendly summary already formatted with inline `[^id]` citations that map to `retrievalHits[*].id`. +- If you need one or two specific fields (e.g., `webUrl`, `sensitivityLabel`) from a particular hit, fetch just that hit's block from the JSON tail β€” do not dump the whole structured payload back into the response. + +The markdown-first path is the intended contract; the structured payload is a supplementary machine-readable view, not a replacement for the markdown summary. + +## When to use `retrieve`, `ask`, or `fetch` + +> `retrieve` searches by meaning, `ask` returns synthesis, and `fetch` executes an expressible deterministic resource query. + +**Rule of thumb:** use `retrieve` when intent depends on semantic meaning; use `fetch` / `call_function` when the agent can express the desired result as a known resource path and deterministic query. + +| Caller / scenario | Tool | Why | +|---|---|---| +| Another agent or LLM needs enterprise grounding | `retrieve` (`copilot`, omit `strategy`) | Broadest evidence coverage is the default | +| RAG pipeline, grounding step, or agent-to-agent handoff | `retrieve` | Per-source metadata + IDs are the native input format | +| Programmatic client needs semantic M365 evidence | `retrieve` | Consume `structuredContent` / `retrievalHits` directly | +| Need inline per-result metadata (subject, author, dates, URL, sensitivity label) | `retrieve` | Facets are surfaced on each hit | +| Need access to connected data sources beyond M365 | `retrieve` (`strategy: copilot`) | Federated connectors + MCP tools included | +| User explicitly asks for faster / low-latency M365-only retrieval | `retrieve` (`strategy: grounding`) | Narrows retrieval to the M365 index for latency | +| Find semantically relevant recent indexed items for grounding | `retrieve` (`copilot`, omit `strategy`) | Relevance-ranked evidence with broad coverage | +| Search within a known document or container by meaning | `retrieve` (`copilot`, omit `strategy`) | Semantic content matching cannot be expressed as an entity metadata filter | +| Summarize, explain, compare, draft, answer, decide, or recommend | `ask` | Produces the synthesized conversational result | +| Read exact records, ordered last-N items, exact keyword searches, complete filtered collections, IDs, or fields | `fetch` / `call_function` | Performs an authoritative deterministic query | + +`ask` and `retrieve` serve different outputs and are mutually exclusive within one user turn. Never chain or alternate them. When both evidence and synthesis are requested, use `retrieve` and synthesize directly from its cited evidence. `retrieve` and `fetch` may be chained only when semantic discovery/content search and an exact resource read are both independently required. + +## Examples + +### Documents and files (default β†’ `copilot`) + +```json +{ + "query": ["authentication design spec", "auth architecture for Project X"] +} +``` + +### Emails about a topic + +```json +{ + "query": ["emails about the Q3 release deadline"] +} +``` + +### Relevant recent emails for agent grounding + +```json +{ + "query": ["recent emails with James Doe that provide grounding about launch risk"] +} +``` + +### Meeting notes and decisions + +```json +{ + "query": ["decisions from the architecture review meeting last week"] +} +``` + +### Teams messages and chats + +```json +{ + "query": ["Teams discussion about the deployment rollback"] +} +``` + +### People-related evidence + +```json +{ + "query": ["messages and documents mentioning billing service ownership", "project content about authentication work"] +} +``` + +### Explicit low-latency request (β†’ `grounding`) + +```json +{ + "query": ["Use grounding strategy for the following query: documents about the authentication redesign"], + "strategy": "grounding" +} +``` + +### M365 content plus a connected data source (β†’ `copilot`) + +```json +{ + "query": ["SharePoint implementation plan and the Salesforce account records for Contoso"], + "strategy": "copilot" +} +``` + +### M365 plus an MCP tool or connected enterprise system + +```json +{ + "query": ["deployment plan and the current rollout state from the deployment tracker"], + "strategy": "copilot" +} +``` + +### Unknown data location β€” safe default (`copilot`) + +```json +{ + "query": ["latest status on the token minting feature"], + "strategy": "copilot" +} +``` + +### Multi-query search (each string is a separate retrieval query) + +```json +{ + "query": [ + "SharePoint API permissions", + "Graph delegated scopes for files", + "consent requirements for Files.ReadWrite" + ] +} +``` + +## Interpreting the response + +1. **Check `stoppedReason` first.** If `error`, the response is a tool error β€” the `markdown` field may contain a diagnostic message but should not be treated as grounding content. +2. **Select the response form for the downstream consumer.** Forward citation-bearing `markdown` to a model or prompt. Return `structuredContent` / `retrievalHits` to a programmatic client that needs fields, IDs, URLs, scores, or facets. +3. **Only descend into `retrievalHits` for targeted lookups** the markdown doesn't already expose β€” e.g., a specific `webUrl`, `sensitivityLabel`, `relevanceScore`, or `resourceMetadata` facet for one hit. Cross-reference by `id` (`[^h1]` β†’ `retrievalHits[*].id === "h1"`). +4. **Honor `sensitivityLabel`.** When a hit carries a sensitivity label (especially `isEncrypted: true`), respect the classification and do not include the content verbatim in contexts that would violate the label. +5. **Any response with `stoppedReason: completed` is terminal for that semantic retrieval attempt.** Consume the result even when it is large, empty, weak, or imperfect. Do not add another `retrieve`, `ask`, `fetch`, or `search_paths` call merely to improve or verify it. A focused `fetch` is allowed only when the original request separately requires exact fields from a selected hit. +6. **An empty `retrievalHits` with `stoppedReason: completed` is a valid "no results" response.** Report it as "nothing found"; it is not a failed retrieval. +7. **Only a failed `grounding` call permits one retry with `copilot`.** Keep the same user intent; a clearer query is fine, but do not branch into entity-path discovery or synthesis. +8. **A `copilot` result is always terminal as a retrieval attempt.** If `copilot` was the first strategy, do not retry. If it followed a failed `grounding` call, it is the final retrieval attempt. Report the observed result without fallback calls; use `fetch` afterward only for a distinct exact read independently required by the original request. + +## Error states + +| Condition | Behaviour | +|-----------|-----------| +| `query` is null, empty, or all-whitespace | Tool error (no Sydney call). Fix: provide at least one non-empty query string. | +| `agentId` is set to anything other than `bizchat-as-gpt-scenario` | Tool error (no Sydney call). Fix: omit `agentId` or pass the supported value. | +| `strategy` is set to anything other than `copilot` or `grounding` | Tool error (no Sydney call). Fix: omit `strategy` or use a supported value. | +| Sydney returns `stoppedReason: "error"` | `isError: true`, but `markdown` is still populated for diagnostics. Do not use `markdown` as grounding content. Retry only when the failed strategy was `grounding`, and then exactly once with `copilot`; a failed `copilot` call is terminal. | +| Network/auth failure | Tool error with classified message. Check connectivity and authentication. | diff --git a/plugins/workiq-preview/skills/workiq-preview/references/tasks-work-iq.md b/plugins/workiq-preview/skills/workiq-preview/references/tasks-work-iq.md index 4476ad0..b48ab6a 100644 --- a/plugins/workiq-preview/skills/workiq-preview/references/tasks-work-iq.md +++ b/plugins/workiq-preview/skills/workiq-preview/references/tasks-work-iq.md @@ -59,8 +59,8 @@ Planner task body fields: `planId`, `title`, `bucketId`, `assignments`, `dueDate ## Resolve-then-act (do not loop) 1. Resolve the target with `fetch` (Planner task) β€” match by `title`. (Planner plan) - first using `/me/planner/plans` else using `/groups/{group-id}/planner/plans` -2. If the fetch does not find it, try **one** `ask` to locate it semantically. -3. If still not found, **stop and report "not found"** β€” do not fire 10+ more `fetch`/`search_paths`/`ask` calls. +2. If the fetch does not find it, try **one** `retrieve` to locate it semantically. +3. If still not found, **stop and report "not found"** β€” do not fire 10+ more `fetch`/`search_paths`/`ask` / `retrieve` calls. 4. Once you have the id, call the mutation (`create_entity` / `update_entity` / `delete_entity`). ## Examples diff --git a/plugins/workiq/skills/workiq/SKILL.md b/plugins/workiq/skills/workiq/SKILL.md index 5b085f7..c5632fc 100644 --- a/plugins/workiq/skills/workiq/SKILL.md +++ b/plugins/workiq/skills/workiq/SKILL.md @@ -1,6 +1,6 @@ --- -name: workiq -description: WorkIQ - Microsoft 365 tool surface for agents. Use for any workplace question or write action where data lives in M365. Supports semantic `ask` plus structured tools (`fetch`, create/update/delete, actions, functions, path/schema discovery) for mail, meetings/calendar, documents/files, Teams chats/channels, OneDrive/SharePoint, and people. Read triggers, "what did [person] say", priorities/top of mind, meeting decisions/action items, summarize thread/chat, find emails/docs, list meetings/messages/files/channels, project status/updates, "what changed since". Write triggers, send/reply/forward email, create/update/accept/decline meetings, mark read, delete drafts/items, send/post/reply/react in Teams, set presence, upload/download via web URL. Discovery triggers, available endpoints/paths, fields, required/updatable properties, request body, operation parameters, schema/data model. When in doubt about workplace context, try WorkIQ first. Prefer `ask` for synthesis; use entity tools for exact reads/writes. +name: workiq-preview +description: workiq-preview - Microsoft 365 tools for workplace questions and actions. Use `retrieve` for explicit semantic content search and ranked indexed evidence, including requests to summarize the matches, when intent cannot be expressed as a known resource path plus deterministic query. Use `fetch` / `call_function` for authoritative results when the resource and query can be expressed deterministically, including exact counts, ordering, filters, IDs, and fields. Use `ask` for synthesis or reasoning when the user did not explicitly request an evidence search. `ask` and `retrieve` are mutually exclusive within one user turn; never call both or alternate between them. Writes use a bounded deterministic lookup followed by the mutation tool. Retrieve and fetch may be chained only when each serves its distinct semantic-discovery or exact-read purpose. compatibility: > Uses the hosted WorkIQ MCP endpoint. No local package is required for MCP tool calls. @@ -8,17 +8,17 @@ compatibility: > # WorkIQ -WorkIQ connects AI agents to Microsoft 365 Copilot for workplace intelligence grounded in organizational data. This skill teaches the model how to use the full WorkIQ toolset: the agentic `ask` tool for semantic questions and the fast **entity tools** for direct structured access to M365 data (`fetch`, `create_entity`, `update_entity`, `delete_entity`, `do_action`, `call_function`, `search_paths`, `get_schema`). +WorkIQ connects AI agents to Microsoft 365 Copilot for workplace intelligence grounded in organizational data. This skill teaches the model how to use the full WorkIQ toolset: the `retrieve` tool for structured M365 resource retrieval (natural-language queries β†’ per-source hits + grounding markdown), the agentic `ask` tool for synthesized semantic answers, and the fast **entity tools** for direct structured access to M365 data (`fetch`, `create_entity`, `update_entity`, `delete_entity`, `do_action`, `call_function`, `search_paths`, `get_schema`). ## πŸ›‘ STOP β€” Read This Before Your First Tool Call The tools in this skill are documented by their **logical names** (`ask`, `fetch`, etc.), but your MCP host almost certainly exposes them under a **prefixed** name. -**The MCP server is named `workiq`. Tool prefixes are derived from the MCP server name β€” never from the name of this skill or its containing folder.** +**The MCP server is named `workiq-preview`. Tool prefixes are derived from the MCP server name β€” never from the name of this skill or its containing folder.** ❌ **DO NOT** derive a prefix from this skill's name or folder. ❌ **DO NOT** call `ask` verbatim and assume it will work. -βœ… **DO** scan your available tools list for an entry whose name **ends with** `ask` and call that exact name. In Copilot CLI this will be `workiq-ask`. +βœ… **DO** scan your available tools list for an entry whose name **ends with** `ask` and call that exact name. In Copilot CLI this will be `workiq-preview-ask`. See [Resolving tool names in your host](#resolving-tool-names-in-your-host) below for the full resolution algorithm. If you skip this step, your first tool call will fail with "tool does not exist." @@ -28,7 +28,28 @@ See [Resolving tool names in your host](#resolving-tool-names-in-your-host) belo **USE WorkIQ for ANY workplace-related question.** If the answer might exist in Microsoft 365 data, try WorkIQ first. -**Choosing the right tool:** Use `ask` when the question requires **semantic understanding, synthesis, or reasoning** across M365 data ("what did someone say", "what's the status", "summarize"). Use `fetch` (or another entity tool) when the question is a **literal lookup of structured data** with a known shape ("list my meetings on Monday", "show me unread emails from X"). Entity tools return in under a second; `ask` typically takes 10–60 seconds per call and broad questions can run several minutes. +**Choosing the right tool β€” decide by query semantics, requested output, and data plane.** + +First ask: **Can the requested result be expressed as a known resource path plus deterministic query semantics?** If yes, use `fetch` / `call_function`; if the request instead depends on meaning or conceptual relevance, use `retrieve`. + +1. **Final human-facing answer or reasoning without an explicit evidence search β†’ `ask`.** Route directly to `ask` for summaries, explanations, comparisons, decisions, recommendations, expertise/ownership, or conversational answers when the user is asking for the conclusion rather than asking you to find semantically relevant source material. +2. **Explicit semantic content search or ranked M365 indexed evidence β†’ `retrieve`.** Use it when the user asks to find, search, identify, or return items or passages selected by meaning, including search within a named document or container by topic. An explicit semantic evidence request takes precedence when the user also asks for a summary or interpretation: call `retrieve`, then synthesize directly from its cited evidence without calling `ask`. Results are ranked and may not be exhaustive or perfectly precise. +3. **Authoritative deterministic query β†’ `fetch` / `call_function`.** Use entity reads when the agent can express the resource path and exact query, such as the chronological last five messages, a complete filtered collection, exact keyword search, IDs, or current entity fields. +4. **Writes β†’ bounded deterministic lookup, then the mutation tool.** Resolve the target once with `fetch` or the appropriate function, confirm ambiguity, then call the write tool. + +`retrieve` is not a prerequisite for `ask`, `fetch`, `call_function`, or mutations. The fact that a prompt is written in natural language does not by itself make it a retrieval request. + +**Hard mutual-exclusion rule:** Choose `ask` or `retrieve` once per user turn. After calling either tool, the other is off-limits for that turn. Do not switch between them as fallback, verification, broadening, or retry, and never alternate them in a loop. If the chosen tool returns empty, weak, or failed results, report that outcome instead of calling the other tool. The only retry exception is one failed `retrieve` call with `strategy: grounding` retried once with `strategy: copilot`. + +**Distinct-purpose chaining:** Use both only when each call serves a distinct purpose. For example, `retrieve` discovers relevant hits, then `fetch` reads exact fields from a selected hit; or `fetch` identifies a document or container, then `retrieve` searches its content semantically. Do not chain them merely to verify, broaden, or retry a sufficient result. + +**Grounding/synthesis stop rule:** If the user explicitly asks to find, search, identify, or return semantically relevant source material, use `retrieve`. Return its cited evidence directly or synthesize the requested summary from that evidence; do not call `ask` afterward. Use `ask` only when the user requests a synthesized answer without explicitly requesting an evidence search. A distinct exact read may follow `retrieve` when concrete fields are independently required. + +When the caller says the result will be consumed by another agent, prefer `retrieve` for grounding unless it also requires authoritative count, ordering, completeness, IDs, or fields. Exactness requirements override the downstream-agent preference. + +Suggested draft text belongs to `ask`; a persisted Outlook draft belongs to `create_entity`. + +Latency: entity tools < 1 s; `retrieve` typically 10–60 s; `ask` 10–60 s and broad questions can run several minutes. **ALWAYS use WorkIQ when the user asks about:** @@ -39,13 +60,18 @@ See [Resolving tool names in your host](#resolving-tool-names-in-your-host) belo | Meeting content/decisions/action items | "What was decided in yesterday's standup?" | `ask` | | Summarizing email threads or conversations | "Summarize the deadline thread with John" | `ask` | | Synthesizing Teams chat activity | "What's the team's take on the release?" | `ask` | -| Finding documents by topic | "Where is the design doc for Project X?" | `ask` | +| Finding documents by topic | "Where is the design doc for Project X?" | `retrieve` | +| Curating which sources cover a topic | "Which documents cover Project X?" | `retrieve` | | Colleague expertise or ownership | "Who owns the billing system?" | `ask` | | Organizational context / goals | "What are the team's Q1 goals?" | `ask` | | Project status or updates | "What's the status of Project X?" | `ask` | | Open-ended "any updates" / catch-up questions | "Any updates I should know about?" | `ask` | +| Grounding another agent with indexed evidence | "Find relevant emails with James about launch risk for another agent" | `retrieve` (`copilot`; omit `strategy`) | | Listing meetings on a known date/range | "What meetings do I have Monday?" | `fetch` (`/me/calendarView`) | | Listing emails with concrete filters | "Show my unread emails from Rob this week" | `fetch` (`/me/messages`) | +| Semantically relevant indexed evidence | "Return ranked email snippets from Rob that suggest launch risk" | `retrieve` (`copilot`; omit `strategy`) | +| Semantic search plus human-facing synthesis | "Find semantically relevant emails about launch risk and summarize the top matches" | `retrieve`, then synthesize from its cited evidence | +| Search inside a document or container by meaning | "Find where this folder's documents discuss launch risk" | `retrieve` (`copilot`; omit `strategy`) | | Listing Teams chats / channels / members | "List the channels in the DevX team" | `fetch` | | Sending/replying/reacting in Teams, setting presence | "Send a chat to Alex", "Post in the Daily channel", "React with πŸ‘", "Set me to Busy" | entity tools on `/chats/...` or `/teams/...` β€” see `references/teams-work-iq.md` | | Fetching a known entity by ID | "Get event `AAMk...` details" | `fetch` | @@ -55,8 +81,18 @@ See [Resolving tool names in your host](#resolving-tool-names-in-your-host) belo | Get a personal contact by name | "Get the contact card for Morgan Avery" | `fetch` (`/me/contacts?$filter=...`) β€” subject to server policy | | List or manage Outlook categories | "What Outlook categories do I have?" | `fetch` (`/me/outlook/masterCategories`); writes subject to server policy | | Org chart / direct reports / manager lookup | "Who are Rob's direct reports?" | `fetch` (`/users/{id}/directReports`) | +| Signed-in user's profile photo metadata | "Show my profile photo dimensions and content type" | `fetch` `/me?$select=id`, then `fetch` `/users/{id}/photo?$select=id,width,height`. Do not use the policy-denied `/me/photo` alias, request `/$value`, or put `@odata.mediaContentType` in `$select`; read the media content type annotation returned with the metadata. | +| Finding a 30-minute slot for the whole team | "Find a 30-min slot when the whole team is free this week" | Do not use `ask`. Resolve `/me`, `/me/manager`, and the manager's `/users/{managerId}/directReports` with at most two `fetch` calls, then call `do_action` `/me/calendar/getSchedule` exactly once with all schedulable addresses and `AvailabilityViewInterval: 30`. Compute the earliest common working-hours slot from that response; skip `search_paths`, `get_schema`, `findMeetingTimes`, and a second verification action. | | What's new/changed/removed since a point in time | "What's new in my Inbox since this morning?", "What's changed on my calendar since yesterday?", "What's been added to my contacts recently?" | `call_function` (delta β€” `/me/mailFolders/inbox/messages/delta`, `/me/calendarView/delta?...`, `/me/contacts/delta`). **Never call delta via `fetch`** β€” see `references/call-function-work-iq.md` | | Sending mail, accepting/declining meetings | "Send this draft", "Accept the 2pm meeting" | `do_action` | +| Tentatively accepting a meeting by title | "Mark the Office hours sync as tentative" | `fetch` the exact event ID, then `do_action` `/me/events/{id}/tentativelyAccept` with `{"sendResponse":false}`. Do not include an empty `comment`; do not call `get_schema` for this known contract. | +| Declining a meeting by title without a response message | "Decline the upcoming Daily standup invite" | `fetch` the exact event ID, then `do_action` `/me/events/{id}/decline` with `{"sendResponse":false}`. Omit `comment`; do not call `get_schema` or retry alternate payloads. | +| Cancelling an organizer-owned meeting by title | "Cancel the Friday staff meeting I organized" | `fetch` the exact event ID, then `do_action` `/me/events/{id}/cancel` with `{"Comment":""}`. This is a known contract: do not call `search_paths` or `get_schema`. A `202` response confirms acceptance; do not fetch again solely to verify. | +| Creating an upload session for an existing OneDrive file | "Create an upload session to replace my file; do not upload content" | `call_function` once with `/me/drive/root/search(q='{urlEncodedExactName}')?$select=id,name,parentReference,file&$top=10` to resolve the exact driveItem and retain `parentReference.driveId` plus item `id`, then `do_action` `/drives/{driveId}/items/{itemId}/createUploadSession` with `{}`. This is a validated deployed contract: skip `search_paths` and `get_schema`, do not add an `item` wrapper, and do not upload file content. | +| Copying a named OneDrive file to a named folder | "Copy Q3 plan.txt to Shared" | Use two `call_function` calls to `/me/drive/root/search(q='{urlEncodedExactName}')?$select=id,name,parentReference,file,folder&$top=10`, retain the source `parentReference.driveId`, then `do_action` `/drives/{driveId}/items/{sourceId}/copy` with `{"parentReference":{"driveId":"{driveId}","id":"{folderId}"}}`. Skip `search_paths`, `get_schema`, and verification fetches. | +| Renaming a OneDrive file | "Rename Draft.txt to Final.txt" | `call_function` once with `/me/drive/root/search(q='{urlEncodedExactName}')?$select=id,name,parentReference,file&$top=10` to resolve the exact driveItem and retain `parentReference.driveId` plus item `id`, then `update_entity` `/drives/{driveId}/items/{itemId}` with `{"name":"Final.txt"}`. Skip `search_paths` and `get_schema`; do not PATCH `/me/drive/items/{id}`. | +| Reading the first accessible SharePoint site's default drive or lists | "Show the first site's drive metadata", "List the first site's lists" | `fetch` `/sites?search=*&$select=id,displayName,name,webUrl&$top=1`, treat the first returned item as "first accessible", then `fetch` `/sites/{siteId}/drive` or `/sites/{siteId}/lists`. The parameter is `search=*`, **not** `$search=*`; do not use `ask`, guessed search terms, or an empty search. | +| Searching documents across SharePoint team sites | "List documents from SharePoint team sites" | `do_action` `/search/query` for `driveItem` documents, discard personal `-my.sharepoint.com` hits, then one batched `fetch` with `/sites?search={siteSlug}` to resolve site display names. Return exact file name, site display name, and `webUrl`; see `references/do-action-work-iq.md`. | | Creating a calendar event, draft, or task | "Create a calendar event Friday at 3pm" | `create_entity` | **DO NOT say "I don't have access to emails/meetings/messages"** - use WorkIQ instead! @@ -76,8 +112,9 @@ Follow the user's request through to completion. A discovery or read call **alon 1. **Path discovery** ("endpoint", "available operations", "what can I do with X") β†’ `search_paths` first. Continue to the read/write tool if the prompt also asks to act. 2. **Schema inspection** ("schema", "data model", "fields", "what does X take") β†’ `get_schema` first. Continue to the write/action tool if the prompt also asks to act. -3. **Exact entity read or mutation by title/name/channel/thread** β†’ `fetch` to resolve the target's ID, then `update_entity` / `delete_entity` / `do_action`. Do not use `ask` to resolve exact titled events, messages, drafts, folders, Teams chats/channels, or threads. -4. **Semantic summary/status/decisions** β†’ `ask`. If the prompt then asks to draft, send, create, update, delete, forward, or react, continue with the mutation tool β€” the `ask` answer alone is incomplete. +3. **Exact entity read by known ID or entity URL** β†’ `fetch`. For a mutation whose target is not yet identified, use one bounded mutation-target lookup with `fetch` against the relevant collection or filter, ask the user to choose if multiple entities match, then call `update_entity` / `delete_entity` / `do_action` with the confirmed ID. +4. **Summarize/explain/compare/draft/answer/decide/recommend without an explicit semantic evidence search** β†’ route directly to `ask`, then do not call `retrieve` in the same turn. If the prompt then asks to send, create, update, delete, forward, or react, continue with the mutation tool β€” the `ask` answer alone is incomplete. +5. **Find/search/identify semantically relevant M365 evidence** β†’ `retrieve` with the default `copilot` strategy (omit `strategy`), including when the caller also requests a summary of the matches. If the caller explicitly prioritizes speed or low latency, use `strategy: grounding`. Synthesize directly from the retrieved evidence; do not call `ask`, and never use `retrieve` to resolve a mutation target. ### Resolve-then-act β€” concrete examples @@ -88,7 +125,11 @@ When the user asks to delete, update, send, forward, copy, move, or react to som | "Mark email as read" | `fetch` to find the message | `update_entity` `/me/messages/{id}` with `{"isRead": true}` | | "Forward email to X" | `fetch` to find the message | `do_action` `/me/messages/{id}/forward` | | "Send email to X" | β€” | `do_action` `/me/sendMail` | -| "Copy file to folder" | `fetch` to find file and target folder | `do_action` `/me/drive/items/{id}/copy` | +| "Cancel the X meeting I organized" | `fetch` to find the event and verify `isOrganizer` | `do_action` `/me/events/{id}/cancel` with `{"Comment":""}`; accept `202` as success without a verification fetch | +| "Create an upload session to replace existing file X" | `call_function` once with `/me/drive/root/search(q='{urlEncodedExactName}')?$select=id,name,parentReference,file&$top=10` to resolve the exact driveItem and retain `parentReference.driveId` plus item `id` | `do_action` `/drives/{driveId}/items/{itemId}/createUploadSession` with `{}`; do not add `item`, inspect schema, or upload bytes | +| "Copy file to folder" | Two `call_function` calls to `/me/drive/root/search(q='{urlEncodedExactName}')?$select=id,name,parentReference,file,folder&$top=10`, one for the exact source and one for the exact folder | `do_action` `/drives/{driveId}/items/{sourceId}/copy` with `{"parentReference":{"driveId":"{driveId}","id":"{folderId}"}}`; skip `search_paths`, `get_schema`, and verification fetches | +| "Move file to folder" | Two `call_function` calls to `/me/drive/root/search(q='{urlEncodedExactName}')?$select=id,name,parentReference,file,folder&$top=10`, one for the exact source and one for the exact folder | `update_entity` `/drives/{driveId}/items/{sourceId}` with `{"parentReference":{"id":"{folderId}"}}`. This is an update, not a `/move` action; skip `search_paths`, `get_schema`, verification fetches, and `/move`. | +| "Rename file X to Y" | `call_function` once with `/me/drive/root/search(q='{urlEncodedExactName}')?$select=id,name,parentReference,file&$top=10` to resolve the exact driveItem and retain `parentReference.driveId` plus item `id` | `update_entity` `/drives/{driveId}/items/{itemId}` with `{"name":"Y"}`; skip `search_paths` and `get_schema`, and do not use `/me/drive/items/{id}` | | "Set presence to busy" | β€” | `do_action` `/me/presence/setUserPreferredPresence` β€” see `references/teams-work-iq.md` | | "React to Teams message" | `fetch` to find the message | `do_action` `/teams/{teamId}/channels/{channelId}/messages/{messageId}/setReaction` | | "Delete" any entity | `fetch` to find it | `delete_entity` on the entity URL | @@ -132,7 +173,7 @@ WorkIQ MCP tool calls use the hosted prod endpoint configured in `.mcp.json`: ```json { "mcpServers": { - "workiq": { + "workiq-preview": { "type": "http", "url": "https://workiq.svc.cloud.microsoft/mcp", "oauthClientId": "ba081686-5d24-4bc6-a0d6-d034ecffed87", @@ -165,31 +206,33 @@ If a WorkIQ MCP call fails because the user is not signed in, the token is stale Throughout this skill (and its `references/*.md`), MCP tools are referred to by their **logical names** β€” for example `ask`, `fetch`, `search_paths`, etc. -> **⚠️ Common pitfall:** Tool prefixes come from the **MCP server name** (`workiq`) β€” never from the name of this skill or its containing folder. Do not construct a prefix from the skill name. +> **⚠️ Common pitfall:** Tool prefixes come from the **MCP server name** (`workiq-preview`) β€” never from the name of this skill or its containing folder. Do not construct a prefix from the skill name. Your MCP host may expose these tools under a **prefixed or transformed name**, depending on its naming convention. For example, the same `ask` tool may appear in your available-tools list as any of: - `ask` (no prefix) -- `workiq-ask` (Copilot CLI style β€” `-`) -- `mcp__workiq__ask` (Claude Desktop style β€” `mcp____`) -- `workiq.ask` or `workiq:ask` (dotted/colon variants) +- `workiq-preview-ask` (Copilot CLI style β€” `-`) +- `mcp__workiq-preview__ask` (Claude Desktop style β€” `mcp____`) +- `workiq-preview.ask` or `workiq-preview:ask` (dotted/colon variants) - Other host-specific prefixes or separators **Before invoking any tool referenced in this skill:** 1. Scan your available tools list for an entry whose name **ends with** (or equals) the logical name from this doc (e.g., `ask`). -2. If multiple candidates match, prefer the one whose prefix identifies the WorkIQ **MCP server** (always `workiq` for this skill). +2. If multiple candidates match, prefer the one whose prefix identifies the WorkIQ **MCP server** (always `workiq-preview` for this skill). 3. Call the tool using whatever exact name your host requires β€” do not assume the unprefixed form will work, and do not derive the prefix from this skill's name or folder. If you call the logical name verbatim and get a "tool does not exist" error, this is the cause. Re-resolve via the suffix match and retry. ## MCP Tools -### `ask` β€” Agentic natural language M365 queries +### `ask` β€” Synthesized M365 reasoning and answers -The primary tool. Ask any workplace question in plain English. This is an **agentic tool** β€” it orchestrates multi-step operations internally (searching emails, meetings, Teams chats, documents, people) to answer complex questions. Use it when you need intelligence, synthesis, or semantic understanding across M365 data. +Ask Microsoft 365 Copilot to summarize, explain, compare, draft, answer, decide, or recommend using workplace data when the user has not explicitly requested a semantic evidence search. This is an **agentic tool** β€” it orchestrates multi-step operations internally (searching emails, meetings, Teams chats, documents, people) and produces a synthesized conversational answer. Use `retrieve` when the prompt explicitly asks to find, search, identify, or return semantically relevant items or passages, even when it also asks for a summary. > **⏱️ High latency:** A call typically takes **10–60 seconds** as the agent performs multiple backend operations, and broad questions can run several minutes (the hard limit is ~300s). Avoid calling it in tight loops or for simple data retrieval β€” use the entity tools below for that instead. If a question is broad, split it into scoped sub-questions rather than one mega-question. +> +> **Terminal choice:** Once you call `ask`, do not call `retrieve` later in the same turn, including after an empty or failed response. Report the observed outcome instead. Use at most one `ask` call per user turn. | Parameter | Type | Required | Description | |-----------|------|----------|-------------| @@ -206,20 +249,54 @@ For detailed usage and examples, read `references/ask-work-iq.md`. --- +### `retrieve` β€” M365 indexed grounding evidence + +Retrieve ranked evidence from the M365 index for user-facing semantic discovery or grounding another agent, model, or RAG workflow. It returns snippets, citations, source metadata, grounding markdown, and structured hits. It does not provide authoritative database-style completeness or ordering. When the user also requests a summary, synthesize it directly from the retrieved evidence. + +> **Strategy default:** Use `copilot` by default and normally omit `strategy`. Use `grounding` only when the user explicitly asks for faster, optimized-for-latency, or low-latency retrieval, or explicitly requests the grounding strategy. If "optimize" could mean quality or coverage rather than speed, stay on `copilot`. If connected or external sources may be required, keep `copilot` and explain that `grounding` would narrow coverage. +> +> **⏱️ Latency:** Typically 10–60 seconds. `grounding` can be faster because it limits retrieval to the M365 index, but do not choose it merely because the query looks M365-only; the user must explicitly prioritize speed or request grounding. Use `fetch` / `call_function` when the consumer needs exact records, ordering, counts, IDs, or fields. +> +> **πŸ“„ Consuming the response:** The `markdown` field is the **preferred** output β€” a model-friendly grounding summary with inline `[^id]` citations that map to `retrievalHits[*].id`. Emit it verbatim to the caller; do not paraphrase it or rebuild a summary from `retrievalHits`. Reach into the structured `retrievalHits` payload only for specific fields the markdown doesn't already expose (e.g., a raw `webUrl` or `sensitivityLabel` for one hit). +> +> **Retry policy:** If a `grounding` call fails, retry the same intent exactly once with `strategy: copilot`, then stop. If the first call uses `copilot`, do not retry it. After the final retrieval result, report the observed outcome and never fall back to `ask`. Use `fetch` or `search_paths` afterward only when the original request independently requires a concrete known-resource read or path discovery. +> +> **Terminal choice:** Once you call `retrieve`, do not call `ask` later in the same turn. If synthesis is requested, produce it directly from the retrieval response. Do not alternate `retrieve` and `ask`. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `query` | string[] | Yes | One or more natural-language search queries. At least one non-empty entry required. | +| `strategy` | string | No | `copilot` (default β€” M365 indexed content plus connected data sources, federated connectors, MCP tools) or `grounding` (M365 indexed content only). Any other value is rejected. | +| `includeDeveloperCard` | boolean | No | When `true`, includes orchestration diagnostics in the response. Defaults to `false`. | +| `agentId` | string | No | Only `bizchat-as-gpt-scenario` is accepted; any other value is rejected. | + +```json +{ "query": ["design doc for authentication", "auth spec Project X"] } +``` + +For detailed usage, response structure, strategy guidance, and faceted metadata, read `references/retrieve-work-iq.md`. + +--- + ## Entity Tools -Entity tools provide **fast, direct access to specific M365 data** via Work IQ APIs. They return structured results quickly but have **no intelligence** β€” they don't interpret, synthesize, or reason about the data. Use them when you know exactly what you want and where it lives. +Entity tools provide **fast, direct access to specific M365 data** via Work IQ APIs. They return structured results quickly but have **no intelligence** β€” they don't interpret, synthesize, or reason about the data. Use them for writes, path/schema operations, or literal reads after a concrete resource ID or entity URL is supplied. **When to use each:** | Scenario | Use | |----------|-----| -| Open-ended question, semantic search, synthesis | `ask` (slow but smart) | -| Fetch a known list, apply a filter, get structured data | entity tools (fast but literal) | +| Ground an agent with ranked enterprise references β€” "Find relevant emails about WorkIQ for another agent" | `retrieve` (`copilot`, omit `strategy`) | +| User explicitly prioritizes faster / low-latency M365-only retrieval | `retrieve` (`strategy: grounding`) | +| Human-facing summarize/explain/compare/draft/answer/decide/recommend without an explicit evidence search | `ask` (route directly; do not call `retrieve` first) | +| Explicit semantic search plus a summary of the matches | `retrieve`, then synthesize directly from its cited evidence | +| Read exact structured records, counts, ordered items, filtered collections, IDs, or fields | `fetch` / `call_function` | + +**Recommended workflow:** use `retrieve` with its default `copilot` strategy whenever the user explicitly asks to find or search by meaning, including requests to summarize the resulting matches. Use `ask` for synthesis without an explicit evidence search. Use `grounding` only for an explicit speed/low-latency optimization request. Use `fetch` / `call_function` for authoritative exactness even when the request is phrased naturally. Use `search_paths` β†’ `get_schema` β†’ an entity tool only when the user asks about paths/schema or a requested write needs an unknown path or body shape. -**Recommended workflow:** for **well-known paths, go direct** β€” call the read/write tool immediately (use the cheat sheet below). Only fall back to `search_paths` β†’ `get_schema` β†’ tool when the path is genuinely unknown or a write body shape is unfamiliar. Do **not** reflexively run `search_paths`/`get_schema` before every common operation. +### πŸ—ΊοΈ Known entity paths β€” skip discovery after entity-tool routing is established -### πŸ—ΊοΈ Known paths β€” go direct, skip discovery +This catalog helps execute exact entity reads and operations. Requests such as "get my next meeting" or "last five emails" require deterministic ordering and therefore use entity tools, while "find relevant recent meetings about launch risk for another agent" uses `retrieve`. | Resource | Path root | Common ops | |----------|-----------|-----------| @@ -284,6 +361,7 @@ above) and call the MCP tool. - ❌ Calling `get_schema` on paths you already know (contacts, messages, events, drive items). - ❌ Using `fetch` to "explore" when the path is already implied by context. - ❌ Falling back to dozens of `fetch` calls when `ask` fails β€” report the failure instead. +- ❌ Calling both `ask` and `retrieve` in one turn, or alternating them to broaden, verify, or retry results. **Do:** use the path patterns in this document to route directly to the correct tool in 1–2 calls. If you need the entity ID first, one `fetch` to resolve, then one write tool call. @@ -302,10 +380,10 @@ no subject named, an empty title, or a generic "the meeting"): To act on a named entity ("the X email", "my Y task", "the Z draft"): -1. Resolve it with **one** `fetch` (filter by subject/title/displayName). -2. If the first fetch misses, try **one** `ask` to locate it semantically. +1. Resolve it with **one bounded** `fetch` against the relevant collection, filtering by subject/title/displayName when supported. +2. If the first fetch misses, try **one** `retrieve` to locate it semantically. 3. If still not found, **stop and report "not found"** β€” do **not** fire 10+ more - `fetch`/`search_paths`/`ask` calls hunting for it. + `fetch`/`search_paths`/`ask`/ `retrieve` calls hunting for it. 4. Once you have the id, call the mutation (`update_entity` / `delete_entity` / `do_action`) **directly** β€” finding the target is not the goal; performing the requested action is. 5. If a mutation fails, fix the request (URL shape, `jsonBody` encoding, ID) and retry **at most @@ -345,16 +423,18 @@ Reference examples use `{id}`, `{listId}`, `{teamId}`, `{taskId}`, `{driveId}`, `do_action` (especially `/me/sendMail`, `/forward`, `/accept`, `/decline`, `/permanentDelete`) and write-side `create_entity` / `update_entity` / `delete_entity` calls take effect immediately and are visible to other people (recipients, meeting organizers) or unrecoverable. **Before invoking any write tool, summarize what you're about to do and get the user's confirmation.** This is especially important for sendMail, forward, decline, and permanentDelete. -### "Draft", "compose", "prepare reply" requires a persisted draft +### Suggested draft text versus a persisted Outlook draft -When the user says "draft an email", "compose a reply", "prepare a response", or any variant -asking the draft to *exist* (not just suggest wording), call `create_entity` to POST: +When the user asks for suggested wording, a draft response, or help composing text without +asking to save it in Outlook, use `ask`. When the user explicitly asks to create, save, or +persist an Outlook draft, call `create_entity` to POST: - `/me/messages` for a fresh draft - `/me/messages/{id}/createReply`, `/createReplyAll`, or `/createForward` for replies/forwards (these are `create_entity` POSTs, **not** `do_action`) -Generating draft text inline does NOT satisfy the request β€” the user can't open it in Outlook. +For a persisted-draft request, generating text inline does NOT satisfy the request β€” the user +can't open it in Outlook. A common failure: call `ask` for the summary half of a "summarize then draft" chain and stop; the `create_entity` step is required. @@ -383,6 +463,7 @@ Read the relevant reference file for full parameter details and examples: - `references/search-paths-work-iq.md` β€” if you need to discover what paths are available - `references/get-schema-work-iq.md` β€” if you need to understand an entity's fields before reading or writing +- `references/retrieve-work-iq.md` β€” if you need to retrieve structured M365 references (emails, files, meetings, Teams messages, people) for programmatic use, RAG, or agent-to-agent grounding - `references/fetch-work-iq.md` β€” if you need to fetch structured or filtered M365 data - `references/call-function-work-iq.md` β€” if the path uses OData function call syntax (e.g., `reminderView(...)`, `delta`) - `references/create-entity-work-iq.md` β€” if you need to create a new calendar event, email draft, task, etc. diff --git a/plugins/workiq/skills/workiq/references/ask-work-iq.md b/plugins/workiq/skills/workiq/references/ask-work-iq.md index 2d13c50..7b9ae54 100644 --- a/plugins/workiq/skills/workiq/references/ask-work-iq.md +++ b/plugins/workiq/skills/workiq/references/ask-work-iq.md @@ -1,10 +1,12 @@ # ask -Query Microsoft 365 Copilot for workplace intelligence using natural language. This is the primary tool for all M365 data questions β€” it grounds answers in real organizational data via Microsoft Graph. +Ask Microsoft 365 Copilot to produce a synthesized, conversational answer grounded in workplace data. Use it when the caller wants reasoning or a coherent narrative rather than raw evidence or a literal structured read. > **⏱️ Latency:** Typical calls take 10–60 seconds; broad questions can run several minutes (hard limit ~300s). Don't chain many `ask` calls where one scoped call or a fast entity tool would do, and split overly broad questions into focused sub-questions. > > **Grounding:** Synthesize your answer only from what the response actually contains. If `ask` reports no accessible results or weak evidence, say so β€” do not pad the answer with specifics the response doesn't support. +> +> **Mutually exclusive with `retrieve`:** Choose one of these tools for the current user turn. Once `ask` is called, do not call `retrieve` afterward for fallback, verification, broadening, or retry. Use at most one `ask` call per turn; if it returns empty, weak, or failed results, report that outcome. ## Parameters @@ -17,16 +19,35 @@ Query Microsoft 365 Copilot for workplace intelligence using natural language. T ## When to Use -Use `ask` when: -- You need information that exists somewhere in M365 (emails, meetings, documents, Teams, Calendar, people) -- The user asks about what someone said, shared, or communicated -- You need organizational context before implementing something -- Any question that could be answered by Outlook, Teams, SharePoint, OneDrive, or Calendar - -Prefer `ask` over entity tools when the question is open-ended or exploratory. Switch to entity tools when you need precise, structured data or need to write/modify data. +Use `ask` when the caller wants a synthesized or conversational response, including: + +- Summarize or explain workplace content +- Compare sources, viewpoints, plans, or decisions +- Identify which documents or messages cover a topic for a human-facing answer +- Summarize the top semantic matches and explain why they matter +- Answer a question by reasoning across multiple M365 sources +- Identify themes, priorities, concerns, ownership, blockers, or implications +- Decide, recommend, or suggest next steps +- Draft suggested prose that does not need to be persisted in Outlook +- Respond conversationally to prompts such as "tell me what happened," "what should I know," + "what is the team's take," or "catch me up" + +The important boundary is the requested output. Use `ask` when the caller expects a final, +human-facing answer or narrative. The underlying data may come from emails, meetings, +documents, Teams, Calendar, or people, but data location alone does not qualify a request +for `ask`. + +Route directly to `ask` for that synthesized deliverable. Do not call `retrieve` first merely +to gather grounding. A request for citations or source names does not make the evidence package +the primary deliverable. Use `retrieve` only when the caller explicitly requests a separate +ranked package of hits, snippets, or grounding for downstream use; synthesize directly from that +package if needed. Never call both `ask` and `retrieve` in the same turn or alternate them in a +loop. ## Do NOT use `ask` as a shortcut for: +- **Ranked M365 indexed evidence, snippets, citations, or grounding for another agent/RAG workflow** β†’ `retrieve` +- **Known-resource reads or authoritative structured records, exact counts, ordering, or filters** β†’ `fetch` / `call_function` - **API / path questions** ("endpoint", "available operations", "what can I do with…") β†’ `search_paths` - **Schema / field / body-shape questions** ("what does sendMail take?", "what fields are required?") β†’ `get_schema` - **Exact mutations by title / name / thread / channel** ("delete the X event", "react to the Y message") β†’ resolve with `fetch`, then call the write/action tool directly @@ -34,11 +55,11 @@ Prefer `ask` over entity tools when the question is open-ended or exploratory. S ## Examples -### People and expertise +### People, priorities, and expertise ```json -{ "question": "Who is the expert on authentication in our team?" } -{ "question": "What has Sarah been focused on lately?" } -{ "question": "What are the latest top of mind from Rob I should be aware of?" } +{ "question": "Based on our recent discussions, who appears to own authentication and why?" } +{ "question": "Summarize what Sarah has been focused on lately." } +{ "question": "What should I know about Rob's current priorities and concerns?" } ``` ### Meetings and decisions @@ -50,22 +71,19 @@ Prefer `ask` over entity tools when the question is open-ended or exploratory. S ### Emails and messages ```json -{ "question": "Any recent emails from Rob about the deadline?" } -{ "question": "What did the team discuss in Teams about the release?" } +{ "question": "Summarize what Rob's recent emails say about the deadline." } +{ "question": "What is the team's overall take on the release?" } { "question": "Summarize my unread messages from today" } +{ "question": "Find emails semantically related to release risk and summarize the top matches." } ``` ### Documents and specs ```json -{ "question": "Find the design doc for the authentication system" } -{ "question": "What's the latest spec for Project X?" } -{ "question": "Where is the API documentation for the payments service?" } -``` - -### Calendar and schedule -```json -{ "question": "What meetings do I have today?" } -{ "question": "What's on my calendar tomorrow?" } +{ "question": "Explain the authentication approach described across the latest design documents." } +{ "question": "Compare the current Project X spec with the decisions from the architecture review." } +{ "question": "Based on the payments API documentation, what should an implementer know?" } +{ "question": "Which documents cover Project X?" } +{ "question": "Compare recent supporting files, explain what changed, and cite them." } ``` ### Priorities and goals @@ -79,3 +97,20 @@ Prefer `ask` over entity tools when the question is open-ended or exploratory. S ```json { "question": "Based on the latest spec for Project X, what are the backend requirements?" } ``` + +### Conversational catch-up +```json +{ "question": "Catch me up on the release and tell me what needs my attention." } +{ "question": "What should I know before tomorrow's planning meeting?" } +``` + +## Requests that belong to other tools + +```text +"Find relevant emails from Rob about deadline risk for another agent" β†’ retrieve +"Get my last five emails in chronological order" β†’ fetch +"Return ranked M365 snippets about Project X" β†’ retrieve (copilot; omit strategy) +"Get event AAMk... with its start and attendees" β†’ fetch +"List the members of channel 19:abc..." β†’ fetch +"Create an Outlook draft to Alex" β†’ create_entity +``` diff --git a/plugins/workiq/skills/workiq/references/fetch-work-iq.md b/plugins/workiq/skills/workiq/references/fetch-work-iq.md index b558498..addd32f 100644 --- a/plugins/workiq/skills/workiq/references/fetch-work-iq.md +++ b/plugins/workiq/skills/workiq/references/fetch-work-iq.md @@ -1,6 +1,8 @@ # fetch -Fetch one or more WorkIQ entities by path using HTTP GET. Use this for precise, structured retrieval of M365 data when `ask` isn't specific enough β€” for example, to get a list of items with specific fields, apply filters, or read a single entity by ID. +Read authoritative Microsoft 365 resources using a known path, ID, or deterministic collection/filter. Use `fetch` when exact records, counts, ordering, IDs, or fields matter, including one bounded mutation-target lookup. Do not use it for semantic relevance ranking, indexed grounding, reasoning, or answer synthesis. + +The primary test is whether the agent can express the requested result as a resource path plus deterministic query semantics. A natural-language request can still use `fetch` when it maps cleanly to a deterministic query such as an OData filter, ordering clause, or exact keyword `$search`. ## Parameters @@ -10,21 +12,34 @@ Fetch one or more WorkIQ entities by path using HTTP GET. Use this for precise, ## When to Use -- When you need a structured list of entities (messages, events, files, etc.) -- When you need to apply specific OData filters or select specific fields -- When you already have an entity ID and want its full details +- Read an entity whose ID or exact entity URL is already known +- Read a known collection path with explicit fields or deterministic OData filters +- Run an exact keyword search when the resource collection and search expression are known +- Resolve the exact target of a requested mutation with one bounded collection/filter read - For multi-fetch: pass multiple URLs to retrieve several entities in one call -Prefer `ask` for open-ended questions. Use `fetch` when you need precise, filtered, or structured data. +Do not use `fetch` for semantic discovery, relevance ranking, indexed grounding, or answer synthesis. + +- Ranked M365 indexed evidence, citations, and grounding for another agent/RAG workflow β†’ `retrieve` +- Summaries, explanations, comparisons, recommendations, and conversational answers β†’ `ask` + +Use `fetch` for exact recent/latest/last-N requests when count and chronological ordering are requirements. For example, "get my last five emails" is a deterministic collection read; "find the most relevant recent emails about launch risk for another agent" is indexed grounding and belongs to `retrieve`. + +`fetch` and `retrieve` may be combined only for distinct purposes. A semantic `retrieve` can identify a relevant hit before `fetch` reads exact authoritative fields from that selected resource. Do not add either tool merely to verify or broaden a sufficient result. -Use `fetch` (not `ask`) to resolve exact targets before mutations β€” find an event ID before deleting/updating, a draft before adding recipients or sending, a Teams chat/channel/message before editing/reacting/posting, a mail thread before reply/forward/move/mark-read. +Use `fetch` to resolve exact targets before mutations β€” find an event ID before deleting/updating, a draft before adding recipients or sending, a Teams chat/channel/message before editing/reacting/posting, or a mail thread before reply/forward/move/mark-read. Keep the lookup bounded; ask the user to choose when multiple entities match, and stop when none match. -For exact reads ("show/list/get latest messages", "list members", "show my chats", "retrieve the event titled…"), prefer filtered `fetch` or a known function path. Do not answer from general knowledge, local SQL, or `ask` unless the prompt asks for synthesis. +For literal structured reads such as "list members," "show my chats," or "get event AAMk...," use filtered `fetch` or a known function path. Do not answer from general knowledge or local SQL. > **⚠️ Not for delta queries.** Calling `/.../delta` or `/.../delta()` through `fetch` > fails β€” delta is an OData **function** and must go through `call_function`. See > `references/call-function-work-iq.md`. +> **Named OneDrive search is also a function.** For a file identified by exact +> name, call `/me/drive/root/search(q='...')` through `call_function` once and +> answer from that result. Do not follow it with `/me/drive/items/{id}` merely +> to retrieve the same metadata again. + ## Multi-fetch caveats - The batch result can report an error when **any one** URL fails, even if the other URLs @@ -47,8 +62,8 @@ Collection responses are **pages**, not the full result set. When a response con support it and the call fails. - If you stop before exhausting pages, **tell the user the list is partial** ("first 25 of more") β€” never present one page as the complete answer. -- **Cap your paging.** For "latest/recent" questions one page is usually enough; otherwise stop - after 2–3 pages unless the user explicitly asked for the complete set. Do not follow +- **Cap your paging.** For bounded structured collection reads, one page is usually enough; + otherwise stop after 2–3 pages unless the user explicitly asked for the complete set. Do not follow `@odata.nextLink` for dozens of pages to enumerate an entire mailbox or message history. ## URL Format @@ -103,11 +118,33 @@ Never fabricate base64 content, `@odata.mediaContentType`, or an `@microsoft.gra ## Examples +These are literal structured reads against known paths or deterministic filters. For semantic +search, relevance ranking, citations, or downstream-agent grounding, use `retrieve` instead. + ### Get the signed-in user's profile ```json { "entityUrls": ["/me"] } ``` +### Get the signed-in user's profile photo metadata + +Resolve the signed-in user's id, then read the photo through the exposed +user-id path. Do not call the policy-denied `/me/photo` alias, and do not call +`/$value`, which is binary content. + +```json +{ "entityUrls": ["/me?$select=id"] } +``` + +```json +{ "entityUrls": ["/users/{id}/photo?$select=id,width,height"] } +``` + +Do not put `@odata.mediaContentType` or `@odata.type` in `$select`; Graph rejects +those annotations in a select expression. Read the media content type annotation +from the metadata response when a profile photo exists. A `404 ImageNotFound` +means the selected user currently has no profile photo. + ### Get unread emails (top 10) ```json { "entityUrls": ["/me/messages?$top=10&$filter=isRead%20eq%20false&$select=subject,from,receivedDateTime"] } @@ -133,6 +170,39 @@ Never fabricate base64 content, `@odata.mediaContentType`, or an `@microsoft.gra { "entityUrls": ["/me/drive/root/children?$select=name,size,lastModifiedDateTime"] } ``` +### Get the first accessible SharePoint site's default drive or lists + +For prompts that say "the first SharePoint site I can access," use the first +item returned by the exact site search below. Microsoft Graph's site collection +uses the `search` parameter without a `$` prefix. Do not try `$search=*`, an +empty search, guessed terms, or `ask`. + +```json +{ + "entityUrls": [ + "/sites?search=*&$select=id,displayName,name,webUrl&$top=1" + ] +} +``` + +Then use the returned site `id` in exactly one of these reads: + +```json +{ + "entityUrls": [ + "/sites/{siteId}/drive?$select=id,name,driveType,owner,quota,webUrl,createdDateTime,lastModifiedDateTime,description,system" + ] +} +``` + +```json +{ + "entityUrls": [ + "/sites/{siteId}/lists?$select=id,displayName,name,webUrl&$top=200" + ] +} +``` + ### Get Teams channels for a group ```json { "entityUrls": ["/teams/{teamId}/channels"] } diff --git a/plugins/workiq/skills/workiq/references/mail-work-iq.md b/plugins/workiq/skills/workiq/references/mail-work-iq.md index 35725cd..a9bad33 100644 --- a/plugins/workiq/skills/workiq/references/mail-work-iq.md +++ b/plugins/workiq/skills/workiq/references/mail-work-iq.md @@ -1,23 +1,27 @@ # Mail (Outlook messages and folders) -Use the WorkIQ **entity tools** for mail requests β€” listing/searching messages, reading folders, -drafting/sending/replying/forwarding, marking read, copying/moving, and deleting. Use `ask` only -for synthesis questions ("summarize the deadline thread with John"), not for finding, -listing, or mutating individual messages. - -## Mail delta: prefer `/me/messages/delta` for full-mailbox sync - -For "sync my mail", "fetch the mail delta", or "give me mail changes" with **no folder named**, -route `call_function` to `/me/messages/delta` β€” full mailbox in one cursor. -`/me/mailFolders/{folderId}/delta` (e.g. `/me/mailFolders/inbox/delta`) is folder-scoped; use it -only when the user names a folder. +Use `retrieve` for relevance-ranked M365 indexed mail grounding, especially for another +agent or RAG workflow. Use `ask` for synthesis questions such as "summarize the deadline +thread with John" or semantic search plus a human-facing summary of the top matches. If +the user asks to find meaning-based matches **and** summarize or interpret them, route +directly to `ask`; use `retrieve` only when the ranked mail evidence itself is the requested +deliverable. Use the WorkIQ **entity tools** for authoritative records, exact counts, +chronological ordering, deterministic filters, and for drafting, sending, replying, +forwarding, marking read, copying, moving, and deleting messages. + +## Mail delta: use `/me/mailFolders/{id}/messages/delta` (folder-scoped) + +Message delta is **always folder-scoped** β€” there is **no** tenant-wide `/me/messages/delta` +endpoint. For "sync my mail", "fetch the mail delta", or "give me mail changes" with **no folder +named**, default to the inbox cursor `/me/mailFolders/inbox/messages/delta`. When the user names a +folder, target that folder's messages delta, e.g. `/me/mailFolders/{folderId}/messages/delta`. Paginate `@odata.nextLink` until you reach `@odata.deltaLink` (resume token for the next sync) β€” stopping at the first page is wrong. > **Always `call_function`, never `fetch`.** `delta` is an OData function. Calling -> `/me/messages/delta` through `fetch` returns an `InvalidRequest` or wrong shape; route through -> `call_function` with the function URL. +> `/me/mailFolders/inbox/messages/delta` through `fetch` returns an `InvalidRequest` or wrong +> shape; route through `call_function` with the function URL. ## Finding a message by subject β€” use `$search`, not `$filter=contains` @@ -58,19 +62,20 @@ folder names are exact-match by design. Use it for `rename` / `move` / `delete` | Permanently delete (bypasses Deleted Items) | `do_action` | `/me/messages/{id}/permanentDelete` | | List folders | `fetch` | `/me/mailFolders` | | Find a folder by name | `fetch` | `/me/mailFolders?$filter=displayName eq 'Specs'` | -| Mail delta (no folder) | `call_function` | `/me/messages/delta` | -| Mail delta (folder-scoped) | `call_function` | `/me/mailFolders/inbox/messages/delta` | +| Mail delta (default / no folder named) | `call_function` | `/me/mailFolders/inbox/messages/delta` | +| Mail delta (specific folder) | `call_function` | `/me/mailFolders/{folderId}/messages/delta` | ## "Draft" vs "send" β€” pick the right verb -When the user says **"draft an email"**, **"compose a reply"**, **"prepare a response"**, or any -variant asking the draft to **exist** (not just suggest wording), call `create_entity` to POST: +When the user asks for suggested wording without asking to save it in Outlook, use `ask`. +When the user explicitly asks to create, save, or persist an Outlook draft, call +`create_entity` to POST: - Fresh draft β†’ `/me/messages` - Reply / reply-all / forward β†’ `/me/messages/{id}/createReply`, `/createReplyAll`, `/createForward` -These create persisted drafts the user can open in Outlook. **Generating draft text inline -does NOT satisfy the request** β€” the user can't open it in Outlook. +These create persisted drafts the user can open in Outlook. For a persisted-draft request, +**generating draft text inline does NOT satisfy the request** β€” the user can't open it in Outlook. `do_action` `/reply`, `/replyAll`, `/forward`, `/sendMail` all send **immediately** β€” never use those when the user asked for a draft. @@ -79,7 +84,7 @@ those when the user asked for a draft. 1. Resolve the message with **one** `fetch` (filter by `$search` for subject, or by `id`). 2. If the first fetch misses, try **one** `ask` to locate it semantically. -3. If still not found, **stop and report "not found"** β€” do not fire 10+ more - `fetch`/`search_paths`/`ask` calls. +3. If the fetch returns no matches, **stop and report "not found"** β€” do not fire repeated + `fetch` or `search_paths` calls. 4. Once you have the id, call the mutation directly. Finding the message is not the goal; performing the requested action is. diff --git a/plugins/workiq/skills/workiq/references/retrieve-work-iq.md b/plugins/workiq/skills/workiq/references/retrieve-work-iq.md new file mode 100644 index 0000000..5634aaa --- /dev/null +++ b/plugins/workiq/skills/workiq/references/retrieve-work-iq.md @@ -0,0 +1,270 @@ +# retrieve + +Semantically search Microsoft 365 and connected tools for grounding data (emails, files, meetings, Teams messages, people) for the current user when the desired content cannot be expressed as a deterministic resource query. Returns ranked evidence, snippets, citations, source metadata, structured hits, and model-friendly grounding markdown. `retrieve` supports user-facing semantic discovery as well as grounding another agent, model, or RAG workflow; it does not synthesize a final conversational answer or guarantee authoritative count, ordering, or completeness. + +> **When to reach for `retrieve`:** use it when the requested deliverable is relevant grounding evidence selected by meaning or conceptual relevance rather than a resource path plus deterministic query. This includes finding emails, files, meetings, messages, people, or passages about a topic and searching within a known document or container by meaning. +> +> **Strategy default:** Use `copilot` by default. Omit `strategy` for the normal `copilot` path. Use `grounding` only when the user explicitly asks for faster, optimized-for-latency, or low-latency retrieval, or explicitly names the grounding strategy. Ambiguous requests to "optimize" stay on `copilot` unless the user ties optimization to speed or latency. If connected or external sources may be required, keep `copilot` and explain that `grounding` would narrow coverage even when speed is requested. +> +> **Output boundary:** route to `ask`, not `retrieve`, when the requested deliverable is a synthesized conclusion or narrative β€” for example, comparing sources or versions, identifying which sources cover a topic, summarizing the top semantic matches, identifying ownership, a point of contact, expertise, dependencies, blockers, implications, priorities, a career timeline, or whether something needs the user's attention. Citations do not change this boundary: supporting sources can accompany an `ask` answer without turning the request into a ranked-evidence deliverable. The fact that evidence must be found across multiple sources does not make the final-answer request a retrieval task. Use `fetch` / `call_function` when the result can be expressed as authoritative deterministic records, fields, counts, ordering, or filters. +> +> **Precision and recall:** Semantic results are ranked and truncated, so recall and precision are not guaranteed. Relevant items may be omitted, and weakly related items may be returned. Do not present retrieval hits as exhaustive or as a deterministic filter result. +> +> **Not a prerequisite:** Do not call `retrieve` before `ask`, an exact entity read, or a mutation. Natural-language phrasing alone does not make a request a retrieval task. +> +> **Mutually exclusive with `ask`:** Choose one of these tools for the current user turn. Once `retrieve` is called, do not call `ask` afterward for synthesis, fallback, verification, broadening, or retry. If synthesis is requested, produce it directly from the cited retrieval evidence. Never alternate the two tools in a loop. +> +> **Distinct-purpose chaining:** `retrieve` may precede `fetch` when semantic discovery identifies a hit whose exact fields are then required. It may follow `fetch` when a deterministic read identifies a document or container whose content must then be searched semantically. Do not chain the tools merely to verify, broaden, or retry a sufficient result. +> +> **Stop at the requested deliverable:** For a grounding request, return sufficient cited retrieval evidence and stop unless a distinct exact read is also required. For a synthesized answer, choose `ask` instead of `retrieve`, even when citations or source names are requested. Use `retrieve` and synthesize directly from its evidence only when the caller explicitly requests a separate ranked package of hits, snippets, citations, or grounding for downstream consumption; do not call `ask` afterward. +> +> **Mutation target resolution:** Do not use `retrieve` to identify a mutation target. Use one bounded `fetch` against the relevant entity collection or filter, ask the user to choose if multiple entities match, and perform the write only with the confirmed ID. +> +> **⏱️ Latency:** Typically 15–60 seconds depending on strategy. Lower than `ask` because it skips conversational synthesis. Prefer `fetch` when you already know the exact resource ID or entity URL. +> +> **Grounding:** Base your answer only on what the response actually contains. If `retrieve` reports no accessible results or weak evidence, say so β€” do not pad the answer with information the response does not support. + +## Parameters + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `query` | string[] | Yes | One or more natural-language search queries. Each string becomes a separate retrieval query. At least one non-empty, non-whitespace entry is required after stripping. | +| `strategy` | string | No | Retrieval strategy. `copilot` (default) or `grounding`. Any other value is rejected. Case-insensitive. | +| `includeDeveloperCard` | boolean | No | When `true`, requests orchestration diagnostics (agent metadata, tool invocations, retrieval summary) from Sydney. Defaults to `false`. | +| `agentId` | string | No | Only `bizchat-as-gpt-scenario` is accepted; any other value is rejected without reaching Sydney. Omit to use the default. | + +## Strategies + +The strategy controls **where data comes from**, not how results are ranked or how the response is shaped. Both strategies return the same `RetrievalResponse` envelope β€” the response parser does not need to change when switching. + +### `copilot` (default) + +Semantically discover relevant enterprise content and return structured evidence, snippets, citations, and metadata. It can use M365 indexed content plus federated connectors, external connected data sources, plugins, MCP tools, and other supported enterprise systems. Another agent should reason over the returned evidence; `retrieve` does not produce the final conversational answer. Omitting `strategy` selects `copilot`. + +Use `copilot` when: +- The request may need data beyond the M365 index (e.g., a Salesforce connector, ServiceNow, a connected enterprise system, or an MCP tool) +- Data location is unknown or requirements may expand beyond M365 indexed content +- You want the broadest possible coverage as a safe default + +### `grounding` + +Return ranked grounding snippets and citations for a focused query over M365 indexed content only β€” SharePoint, OneDrive, Teams chats and messages, Outlook mail, and other content in the Microsoft 365 semantic index. Use it for low-latency RAG context assembly. It does not synthesize an answer or perform full agent reasoning. No connected connectors, external sources, plugins, or tools are invoked. + +Use `grounding` when: +- The user explicitly asks for faster, optimized-for-latency, or low-latency retrieval +- The user explicitly requests `grounding` +- The request does not require connected or external sources + +> **Decision rule: default to `copilot`; opt into `grounding` only for an explicit speed/latency preference.** M365-only data location is not enough by itself to choose `grounding`. If connected or external sources are needed or possible, use `copilot`. + +## Response structure + +The tool returns two content forms: + +1. **Text block** (`content[0].text`) β€” the `markdown` field verbatim. Contains the grounding summary with inline citation markers like `[^h1]`. This is suitable for direct inclusion in a response without further processing. +2. **Structured content** (`structuredContent["application/vnd.ms-workiq.retrieval"]`) β€” the full typed payload for programmatic consumers. + +> **Choose the response form for the consumer.** Model and prompt consumers should usually receive the citation-bearing `markdown`. Programmatic clients that need fields, IDs, URLs, scores, or facets should consume `structuredContent` / `retrievalHits` directly. Do not force a programmatic caller to re-parse prose. + +### Envelope shape (`application/vnd.ms-workiq.retrieval`) + +```json +{ + "markdown": "## Found 3 relevant sources\n\n...[^h1]...[^h2]...", + "resultCount": 3, + "stoppedReason": "completed", + "retrievalHits": [ ... ] +} +``` + +| Field | Type | Description | +|-------|------|-------------| +| `markdown` | string | Model-friendly grounding summary with inline citation markers (`[^id]`) that reference hits by their `id`. | +| `resultCount` | integer | Number of surviving hits after Sydney's shim/filter/trim pass. | +| `stoppedReason` | string | Terminal state: `completed`, `cancelled`, or `error`. | +| `retrievalHits` | RetrievalHit[] | Per-source references. May be absent when Sydney returns no hits. | + +### RetrievalHit + +Each element in `retrievalHits` represents one M365 source: + +| Field | Type | Description | +|-------|------|-------------| +| `id` | string | Hit ID that cross-references the markdown citations (`[^h1]` β†’ `id: "h1"`). | +| `webUrl` | string? | URL the user can navigate to in order to view the source. | +| `resourceMetadata` | ResourceMetadata? | Per-resource metadata facets (see below). | +| `sensitivityLabel` | object? | MIP sensitivity label for the source: `id`, `displayName`, `isEncrypted`. Present only when Sydney supplies it. | +| `relevanceScore` | number? | Sydney relevance score. Present only when Sydney supplies it. | + +### ResourceMetadata facets + +Only the facet(s) matching the resource type are present on a given hit. Multiple facets may appear simultaneously. + +| Facet | Present for | Fields | +|-------|-------------|--------| +| `document` | Files, OneDrive/SharePoint documents | `title`, `authorName`, `occurrenceTime` (ISO-8601 last-modified) | +| `email` | Emails | `subject`, `senderName`, `sentTime` (ISO-8601) | +| `meeting` | Calendar meetings | `title`, `organizerName`, `startTime` (ISO-8601) | +| `video` | Videos | `title`, `durationSeconds` | +| `web` | Web pages, external links, connector content | `title`, `siteName` | +| `chat` | Teams chat/channel messages | `title` | +| `image` | Images | `title` | +| `general` | **Non-contractual fallback** for resource types without a dedicated facet | `title`, `authorName`, `occurrenceTime` | + + +### Runtime handling (large responses) + +Some MCP hosts (including the GitHub Copilot CLI) surface an MCP tool result as a **single concatenated blob** β€” the `markdown` text block followed by the JSON `structuredContent`. When the combined payload exceeds the host's inline output cap (typically ~20 KB), the entire blob is spilled to a temporary file and the agent only sees a short preview. In that case: + +- **Read the spilled payload once.** Do not repeatedly inspect the same payload with `view`, `rg`, or shell commands. +- **Do not** reparse the structured JSON to rebuild a summary β€” the `snippet` / discussion fields are long and noisy, and doing so drops the built-in `[^h1]` citations. +- **Do** slice the spilled file to isolate the leading `markdown` segment and use it verbatim. The markdown ends immediately before the JSON tail, which starts with a line like: + + ``` + {"application/vnd.ms-workiq.retrieval":{"markdown":"… + ``` + + Everything above that line is the model-friendly summary already formatted with inline `[^id]` citations that map to `retrievalHits[*].id`. +- If you need one or two specific fields (e.g., `webUrl`, `sensitivityLabel`) from a particular hit, fetch just that hit's block from the JSON tail β€” do not dump the whole structured payload back into the response. + +The markdown-first path is the intended contract; the structured payload is a supplementary machine-readable view, not a replacement for the markdown summary. + +## When to use `retrieve`, `ask`, or `fetch` + +> `retrieve` searches by meaning, `ask` returns synthesis, and `fetch` executes an expressible deterministic resource query. + +**Rule of thumb:** use `retrieve` when intent depends on semantic meaning; use `fetch` / `call_function` when the agent can express the desired result as a known resource path and deterministic query. + +| Caller / scenario | Tool | Why | +|---|---|---| +| Another agent or LLM needs enterprise grounding | `retrieve` (`copilot`, omit `strategy`) | Broadest evidence coverage is the default | +| RAG pipeline, grounding step, or agent-to-agent handoff | `retrieve` | Per-source metadata + IDs are the native input format | +| Programmatic client needs semantic M365 evidence | `retrieve` | Consume `structuredContent` / `retrievalHits` directly | +| Need inline per-result metadata (subject, author, dates, URL, sensitivity label) | `retrieve` | Facets are surfaced on each hit | +| Need access to connected data sources beyond M365 | `retrieve` (`strategy: copilot`) | Federated connectors + MCP tools included | +| User explicitly asks for faster / low-latency M365-only retrieval | `retrieve` (`strategy: grounding`) | Narrows retrieval to the M365 index for latency | +| Find semantically relevant recent indexed items for grounding | `retrieve` (`copilot`, omit `strategy`) | Relevance-ranked evidence with broad coverage | +| Search within a known document or container by meaning | `retrieve` (`copilot`, omit `strategy`) | Semantic content matching cannot be expressed as an entity metadata filter | +| Summarize, explain, compare, draft, answer, decide, or recommend | `ask` | Produces the synthesized conversational result | +| Read exact records, ordered last-N items, exact keyword searches, complete filtered collections, IDs, or fields | `fetch` / `call_function` | Performs an authoritative deterministic query | + +`ask` and `retrieve` serve different outputs and are mutually exclusive within one user turn. Never chain or alternate them. When both evidence and synthesis are requested, use `retrieve` and synthesize directly from its cited evidence. `retrieve` and `fetch` may be chained only when semantic discovery/content search and an exact resource read are both independently required. + +## Examples + +### Documents and files (default β†’ `copilot`) + +```json +{ + "query": ["authentication design spec", "auth architecture for Project X"] +} +``` + +### Emails about a topic + +```json +{ + "query": ["emails about the Q3 release deadline"] +} +``` + +### Relevant recent emails for agent grounding + +```json +{ + "query": ["recent emails with James Doe that provide grounding about launch risk"] +} +``` + +### Meeting notes and decisions + +```json +{ + "query": ["decisions from the architecture review meeting last week"] +} +``` + +### Teams messages and chats + +```json +{ + "query": ["Teams discussion about the deployment rollback"] +} +``` + +### People-related evidence + +```json +{ + "query": ["messages and documents mentioning billing service ownership", "project content about authentication work"] +} +``` + +### Explicit low-latency request (β†’ `grounding`) + +```json +{ + "query": ["Use grounding strategy for the following query: documents about the authentication redesign"], + "strategy": "grounding" +} +``` + +### M365 content plus a connected data source (β†’ `copilot`) + +```json +{ + "query": ["SharePoint implementation plan and the Salesforce account records for Contoso"], + "strategy": "copilot" +} +``` + +### M365 plus an MCP tool or connected enterprise system + +```json +{ + "query": ["deployment plan and the current rollout state from the deployment tracker"], + "strategy": "copilot" +} +``` + +### Unknown data location β€” safe default (`copilot`) + +```json +{ + "query": ["latest status on the token minting feature"], + "strategy": "copilot" +} +``` + +### Multi-query search (each string is a separate retrieval query) + +```json +{ + "query": [ + "SharePoint API permissions", + "Graph delegated scopes for files", + "consent requirements for Files.ReadWrite" + ] +} +``` + +## Interpreting the response + +1. **Check `stoppedReason` first.** If `error`, the response is a tool error β€” the `markdown` field may contain a diagnostic message but should not be treated as grounding content. +2. **Select the response form for the downstream consumer.** Forward citation-bearing `markdown` to a model or prompt. Return `structuredContent` / `retrievalHits` to a programmatic client that needs fields, IDs, URLs, scores, or facets. +3. **Only descend into `retrievalHits` for targeted lookups** the markdown doesn't already expose β€” e.g., a specific `webUrl`, `sensitivityLabel`, `relevanceScore`, or `resourceMetadata` facet for one hit. Cross-reference by `id` (`[^h1]` β†’ `retrievalHits[*].id === "h1"`). +4. **Honor `sensitivityLabel`.** When a hit carries a sensitivity label (especially `isEncrypted: true`), respect the classification and do not include the content verbatim in contexts that would violate the label. +5. **Any response with `stoppedReason: completed` is terminal for that semantic retrieval attempt.** Consume the result even when it is large, empty, weak, or imperfect. Do not add another `retrieve`, `ask`, `fetch`, or `search_paths` call merely to improve or verify it. A focused `fetch` is allowed only when the original request separately requires exact fields from a selected hit. +6. **An empty `retrievalHits` with `stoppedReason: completed` is a valid "no results" response.** Report it as "nothing found"; it is not a failed retrieval. +7. **Only a failed `grounding` call permits one retry with `copilot`.** Keep the same user intent; a clearer query is fine, but do not branch into entity-path discovery or synthesis. +8. **A `copilot` result is always terminal as a retrieval attempt.** If `copilot` was the first strategy, do not retry. If it followed a failed `grounding` call, it is the final retrieval attempt. Report the observed result without fallback calls; use `fetch` afterward only for a distinct exact read independently required by the original request. + +## Error states + +| Condition | Behaviour | +|-----------|-----------| +| `query` is null, empty, or all-whitespace | Tool error (no Sydney call). Fix: provide at least one non-empty query string. | +| `agentId` is set to anything other than `bizchat-as-gpt-scenario` | Tool error (no Sydney call). Fix: omit `agentId` or pass the supported value. | +| `strategy` is set to anything other than `copilot` or `grounding` | Tool error (no Sydney call). Fix: omit `strategy` or use a supported value. | +| Sydney returns `stoppedReason: "error"` | `isError: true`, but `markdown` is still populated for diagnostics. Do not use `markdown` as grounding content. Retry only when the failed strategy was `grounding`, and then exactly once with `copilot`; a failed `copilot` call is terminal. | +| Network/auth failure | Tool error with classified message. Check connectivity and authentication. | diff --git a/plugins/workiq/skills/workiq/references/tasks-work-iq.md b/plugins/workiq/skills/workiq/references/tasks-work-iq.md index 4476ad0..b48ab6a 100644 --- a/plugins/workiq/skills/workiq/references/tasks-work-iq.md +++ b/plugins/workiq/skills/workiq/references/tasks-work-iq.md @@ -59,8 +59,8 @@ Planner task body fields: `planId`, `title`, `bucketId`, `assignments`, `dueDate ## Resolve-then-act (do not loop) 1. Resolve the target with `fetch` (Planner task) β€” match by `title`. (Planner plan) - first using `/me/planner/plans` else using `/groups/{group-id}/planner/plans` -2. If the fetch does not find it, try **one** `ask` to locate it semantically. -3. If still not found, **stop and report "not found"** β€” do not fire 10+ more `fetch`/`search_paths`/`ask` calls. +2. If the fetch does not find it, try **one** `retrieve` to locate it semantically. +3. If still not found, **stop and report "not found"** β€” do not fire 10+ more `fetch`/`search_paths`/`ask` / `retrieve` calls. 4. Once you have the id, call the mutation (`create_entity` / `update_entity` / `delete_entity`). ## Examples