diff --git a/plugins/workiq-preview/skills/workiq-preview/SKILL.md b/plugins/workiq-preview/skills/workiq-preview/SKILL.md index 8ae8485..2c3a62b 100644 --- a/plugins/workiq-preview/skills/workiq-preview/SKILL.md +++ b/plugins/workiq-preview/skills/workiq-preview/SKILL.md @@ -50,9 +50,8 @@ See [Resolving tool names in your host](#resolving-tool-names-in-your-host) belo | 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` | | Listing files in a OneDrive/SharePoint folder | "List files in my OneDrive 'Specs' folder" | `fetch` | -| Listing tasks/plans/buckets in Planner | "List my Planner tasks due this week" | `fetch` | +| Listing tasks/plans/buckets in Planner | "List my Planner tasks due this week" | `fetch` β€” see `references/tasks-work-iq.md` avoid `ask` | | Listing / creating / completing Planner tasks | "Add a task to follow up with finance", "Mark my task done", "List my Planner tasks" | entity tools on `/planner/...` β€” see `references/tasks-work-iq.md` | -| List my To Do task lists | "Show me my to-do lists" | `fetch` (`/me/todo/lists`) β€” subject to server policy | | 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`) | @@ -68,7 +67,8 @@ See [Resolving tool names in your host](#resolving-tool-names-in-your-host) belo > local markdown file, insert into a local/SQL table, or use any other builtin > task tracker β€” that does not satisfy the request and the user cannot see it in Planner. > If a WorkIQ task call fails, report the failure; do not silently substitute local storage. -> See `references/tasks-work-iq.md`. +> See `references/tasks-work-iq.md`; for named Planner plan requests, read that +> reference before resolving the plan so group-backed plans are checked correctly. ### Required workflow order β€” don't stop after a preparatory lookup @@ -228,7 +228,6 @@ Entity tools provide **fast, direct access to specific M365 data** via Work IQ A | Planner | `/me/planner/plans`, `/planner/tasks` | list/create/update/complete/delete β€” see `references/tasks-work-iq.md` | | Teams | `/me/chats`, `/chats/{chatId}/messages`, `/me/joinedTeams`, `/teams/{teamId}/channels/{channelId}/messages`, `/me/presence` | chats vs channels are different surfaces β€” see `references/teams-work-iq.md` | | People | `/me`, `/users/{id}`, `/users/{id}/directReports`, `/me/manager`, `/me/contacts` | profile, org, contacts β€” see directory-vs-contacts warning below | -| To Do | `/me/todo/lists`, `/me/todo/lists/{listId}/tasks` | list/create/update/delete β€” **commonly policy-denied**, see note below | | Outlook categories | `/me/outlook/masterCategories` | list/get/create/update/delete β€” writes commonly policy-denied | | Files | `/me/drive`, `/drives/{id}`, `/sites/{id}` | list/get JSON metadata only β€” binary content (file bytes, attachment payloads) is not released yet, see the deny rule below | | Change tracking | `/me/mailFolders/inbox/messages/delta`, `/me/calendarView/delta?...`, `/me/contacts/delta` | "what's new/changed since" β€” via `call_function` only, never `fetch` | 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 f9e08cc..4476ad0 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 @@ -23,15 +23,43 @@ with…", "mark … done", or "list my tasks", that is M365 data: route it to Wo Planner task body fields: `planId`, `title`, `bucketId`, `assignments`, `dueDateTime`, `percentComplete` (`0` = not started, `50` = in progress, `100` = complete). +- **Find the plan before using `ask` (required for named-plan requests):** + 1. Fetch owned plans with `/me/planner/plans?$select=id,title,owner`. + 2. Search that full result locally for the requested title or keywords. Do not stop after the + first page if the response includes `@odata.nextLink`. + 3. If the plan is not in `/me/planner/plans`, resolve likely backing groups before using `ask`. + Fetch `/me/joinedTeams?$select=id,displayName,description` to get group IDs for Teams the + user has joined, guess likely team/group names, then fetch + `/groups/{group-id}/planner/plans?$select=id,title,owner` to get the plan ID. Do not pass + `$top` to `/me/joinedTeams`. + 4. If `/me/joinedTeams` misses, use known group IDs when provided or fetch the user's joined + groups and then fetch `/groups/{group-id}/planner/plans?$select=id,title,owner`. + 5. If you have an owner/group ID but not the group-plans path, use + `/planner/plans?$filter=owner eq '{Group or UserId}'&$select=id,title,owner`. + 6. Only use `ask` after the structured `/me/planner/plans`, assigned-task `planId`, group-backed + `/groups/{group-id}/planner/plans`, and owner-filtered `/planner/plans` lookup paths are + exhausted, unavailable, or policy-blocked. +- **Private tasks and "Assigned to me" tasks:** use `/me/planner/tasks`. +- **Enforce filtering on Planner collection GETs:** + - `GET /planner/plans` requires `$filter=owner eq '{Group or UserId}'`. + - `GET /planner/tasks` requires a `$filter` containing `planId`. +- **Forbidden create plans/tasks paths** Do not use `create_entity`, `update_entity`, `delete_entity` for the following paths + - /me/planner/plans + - /me/planner/tasks + - /users/{user-id}/planner/plans + - /users/{user-id}/planner/tasks + - /groups/{group-id}/planner/plans + - **Mark a Planner task done:** `update_entity` with `{"percentComplete":100}`. - **Planner gotcha:** `update_entity` / `delete_entity` on Planner resources require the current `@odata.etag` (an `If-Match` precondition). Fetch the task first to read its etag; if a Planner write returns a `412`/precondition error, re-fetch and retry. + ## Resolve-then-act (do not loop) -1. Resolve the target with **one** `fetch` (Planner task) β€” match by `title`. -2. If the first fetch does not find it, try **one** `ask` to locate it semantically. +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. 4. Once you have the id, call the mutation (`create_entity` / `update_entity` / `delete_entity`). diff --git a/plugins/workiq/skills/workiq/SKILL.md b/plugins/workiq/skills/workiq/SKILL.md index c912264..5b085f7 100644 --- a/plugins/workiq/skills/workiq/SKILL.md +++ b/plugins/workiq/skills/workiq/SKILL.md @@ -50,9 +50,8 @@ See [Resolving tool names in your host](#resolving-tool-names-in-your-host) belo | 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` | | Listing files in a OneDrive/SharePoint folder | "List files in my OneDrive 'Specs' folder" | `fetch` | -| Listing tasks/plans/buckets in Planner | "List my Planner tasks due this week" | `fetch` | +| Listing tasks/plans/buckets in Planner | "List my Planner tasks due this week" | `fetch` β€” see `references/tasks-work-iq.md` avoid `ask` | | Listing / creating / completing Planner tasks | "Add a task to follow up with finance", "Mark my task done", "List my Planner tasks" | entity tools on `/planner/...` β€” see `references/tasks-work-iq.md` | -| List my To Do task lists | "Show me my to-do lists" | `fetch` (`/me/todo/lists`) β€” subject to server policy | | 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`) | @@ -68,7 +67,8 @@ See [Resolving tool names in your host](#resolving-tool-names-in-your-host) belo > local markdown file, insert into a local/SQL table, or use any other builtin > task tracker β€” that does not satisfy the request and the user cannot see it in Planner. > If a WorkIQ task call fails, report the failure; do not silently substitute local storage. -> See `references/tasks-work-iq.md`. +> See `references/tasks-work-iq.md`; for named Planner plan requests, read that +> reference before resolving the plan so group-backed plans are checked correctly. ### Required workflow order β€” don't stop after a preparatory lookup @@ -228,7 +228,6 @@ Entity tools provide **fast, direct access to specific M365 data** via Work IQ A | Planner | `/me/planner/plans`, `/planner/tasks` | list/create/update/complete/delete β€” see `references/tasks-work-iq.md` | | Teams | `/me/chats`, `/chats/{chatId}/messages`, `/me/joinedTeams`, `/teams/{teamId}/channels/{channelId}/messages`, `/me/presence` | chats vs channels are different surfaces β€” see `references/teams-work-iq.md` | | People | `/me`, `/users/{id}`, `/users/{id}/directReports`, `/me/manager`, `/me/contacts` | profile, org, contacts β€” see directory-vs-contacts warning below | -| To Do | `/me/todo/lists`, `/me/todo/lists/{listId}/tasks` | list/create/update/delete β€” **commonly policy-denied**, see note below | | Outlook categories | `/me/outlook/masterCategories` | list/get/create/update/delete β€” writes commonly policy-denied | | Files | `/me/drive`, `/drives/{id}`, `/sites/{id}` | list/get JSON metadata only β€” binary content (file bytes, attachment payloads) is not released yet, see the deny rule below | | Change tracking | `/me/mailFolders/inbox/messages/delta`, `/me/calendarView/delta?...`, `/me/contacts/delta` | "what's new/changed since" β€” via `call_function` only, never `fetch` | diff --git a/plugins/workiq/skills/workiq/references/tasks-work-iq.md b/plugins/workiq/skills/workiq/references/tasks-work-iq.md index f9e08cc..4476ad0 100644 --- a/plugins/workiq/skills/workiq/references/tasks-work-iq.md +++ b/plugins/workiq/skills/workiq/references/tasks-work-iq.md @@ -23,15 +23,43 @@ with…", "mark … done", or "list my tasks", that is M365 data: route it to Wo Planner task body fields: `planId`, `title`, `bucketId`, `assignments`, `dueDateTime`, `percentComplete` (`0` = not started, `50` = in progress, `100` = complete). +- **Find the plan before using `ask` (required for named-plan requests):** + 1. Fetch owned plans with `/me/planner/plans?$select=id,title,owner`. + 2. Search that full result locally for the requested title or keywords. Do not stop after the + first page if the response includes `@odata.nextLink`. + 3. If the plan is not in `/me/planner/plans`, resolve likely backing groups before using `ask`. + Fetch `/me/joinedTeams?$select=id,displayName,description` to get group IDs for Teams the + user has joined, guess likely team/group names, then fetch + `/groups/{group-id}/planner/plans?$select=id,title,owner` to get the plan ID. Do not pass + `$top` to `/me/joinedTeams`. + 4. If `/me/joinedTeams` misses, use known group IDs when provided or fetch the user's joined + groups and then fetch `/groups/{group-id}/planner/plans?$select=id,title,owner`. + 5. If you have an owner/group ID but not the group-plans path, use + `/planner/plans?$filter=owner eq '{Group or UserId}'&$select=id,title,owner`. + 6. Only use `ask` after the structured `/me/planner/plans`, assigned-task `planId`, group-backed + `/groups/{group-id}/planner/plans`, and owner-filtered `/planner/plans` lookup paths are + exhausted, unavailable, or policy-blocked. +- **Private tasks and "Assigned to me" tasks:** use `/me/planner/tasks`. +- **Enforce filtering on Planner collection GETs:** + - `GET /planner/plans` requires `$filter=owner eq '{Group or UserId}'`. + - `GET /planner/tasks` requires a `$filter` containing `planId`. +- **Forbidden create plans/tasks paths** Do not use `create_entity`, `update_entity`, `delete_entity` for the following paths + - /me/planner/plans + - /me/planner/tasks + - /users/{user-id}/planner/plans + - /users/{user-id}/planner/tasks + - /groups/{group-id}/planner/plans + - **Mark a Planner task done:** `update_entity` with `{"percentComplete":100}`. - **Planner gotcha:** `update_entity` / `delete_entity` on Planner resources require the current `@odata.etag` (an `If-Match` precondition). Fetch the task first to read its etag; if a Planner write returns a `412`/precondition error, re-fetch and retry. + ## Resolve-then-act (do not loop) -1. Resolve the target with **one** `fetch` (Planner task) β€” match by `title`. -2. If the first fetch does not find it, try **one** `ask` to locate it semantically. +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. 4. Once you have the id, call the mutation (`create_entity` / `update_entity` / `delete_entity`).