Omniscio documentation
Browse all documentation
  1. Getting Started13
  2. Sessions & Agents115
  3. Inbox & Notifications59
  4. Projects & Tasks95
  5. Automation & Scheduling75
  6. Knowledge & Memory26
  7. AI Features60
  8. Integrations100
  9. Plugins & Marketplace33
  10. Cloud & Teams56
  11. Settings & Customization58
  12. Account & Billing28
  13. Troubleshooting84
  14. CLI & API Reference22
  15. Legal & Policies4
  16. Uncategorised22

Tasks (markdown outliner, in development) (part 9)

An eighth agent session type, Define & enrich, is the fuller sibling of Clarify & enrich: it not only sharpens each task's wording, it also captures the context behind it. Like Clarify it is project-launch only — it never appears on a single-task launch (the picker filters it via LISTONLYSESSIONTYPES), because its whole job is to walk the list. Launch it from a project's "Launch agent…" menu (the launch popup in list mode) and pick Define & enrich.

What it is

This is part 9 of the Tasks (markdown outliner, in development) page. It carries the next stretch of the material on that page, moved here because a single page is capped at 40,000 characters.

Where to find it

Reach this part through Tasks (markdown outliner, in development) — it lists every part and explains where the feature lives in the product. Everything below is reached from the same place.

How it behaves

Everything below is the behaviour, detail and edge cases that belong to this stretch of the Tasks (markdown outliner, in development) page.

Define & enrich

An eighth agent session type, Define & enrich, is the fuller sibling of Clarify & enrich: it not only sharpens each task's wording, it also captures the context behind it. Like Clarify it is project-launch only — it never appears on a single-task launch (the picker filters it via LIST_ONLY_SESSION_TYPES), because its whole job is to walk the list.

Launch it from a project's "Launch agent…" menu (the launch popup in list mode) and pick Define & enrich. The spawned agent walks the project's open tasks one at a time and, for each task that needs it, prepares two deliverables — then, only after you approve that task, saves both: (1) a clearer rewrite of the task's own text in place (POST /tasks-v2/update), and (2) a durable context note capturing what a future AI would need — what the task really is, why it matters, what "done" looks like, and the specifics that live only in your head (POST /tasks-v2/context/create). It skips tasks that are already clear and well-understood, finishes one task before the next, and — where a task needs only one of the two — does just that one. Where it helps it also offers to break a task into subtasks (POST /tasks-v2/create). It handles an empty / already-clear list ("nothing to do") and a long, truncated list (works what it can see, offers to continue) gracefully.

Where Clarify & enrich treats the context note as optional (the clearer description is the point), Define & enrich makes the context note first-class — reach for it when you want every task both legible AND fully briefed for a future agent. Under the hood it reuses the same machinery as Clarify: one main-only preamble (DEFINE_PREAMBLE, never shipped to the renderer), the hidden/visible split, and the existing buildListInterviewPrompt + tasks-v2:launch-list-agent channels — no new IPC channel, Zod schema, DB table, or Labs toggle (it rides the existing Tasks flag). Like the other groomer, the project prompt's hidden background task list carries each task's id, and its session-type-aware footer names both write routes. Still behind the Tasks Labs flag; invariants (define-groomer, define-list-only) live in .claude/memory/contracts/tasks-v2-session-types-contract.md.

Context attachments

Any task can carry a small pile of context attachments — pointers to other sessions, projects, or tasks, plus plain URLs, free-form text/markdown, an attached file, and a linked ContextDock bundle/list — so the work and everything it refers to stay together. The 📎 count chip that appears on any row with attachments is clickable — tapping it pops open a small peek listing those attachments, and clicking any one opens it right there (a URL in your browser; a session, project, task, or file in the app; a note or ContextDock bundle in a read-only content viewer), with a Manage link at the bottom that opens the full pane for adding or removing. On a focused outliner row, the 📎 Context action (in the row's ⋯ overflow menu, or the i key) opens the full Context pane — and the SAME manager is now also a Context tab in the task's two-tab details pane (Cmd/Ctrl+;), so a task's fields and its context are editable in one window. (A task's old single free-form "Context" note was folded into this system as a text entry — nothing is lost, and the row now shows just the 📎 count chip instead of the note's raw @… text.) The pane / tab lets you:

  • Attach anything via + Add context → an iconed list of types:
    • Session / Project / Task — pick the entity from a searchable list (the task picker spans every list, each option labeled with its list, so you can link to a task in another list).
    • URL — http(s) only (it opens via the OS browser, so other schemes are rejected at the input).
    • Text / Markdown — a real multi-line editor (plain Enter is a newline; Ctrl/⌘ + Enter or the Add button saves it).
    • File — opens your OS file picker and attaches the chosen file by path; the launched agent reads it. (Desktop only — hidden on the mobile web app, where the native picker can't run. The link is to the file where it sits, so moving or deleting the file breaks it.)
    • ContextDock — a searchable list of your cached ContextDock bundles/lists (the same curated doc sets you add to projects). Empty / not set up shows a "set up ContextDock in Settings" hint.
  • Give it a label (optional). Every type has an optional Label field when you add it — leave it blank and it names itself (the entity's name, the URL, or the note's first line, exactly as before); fill it (usually the AI does) to give the attachment a clean title, so a task can carry several clearly-labeled contexts instead of one indistinct pile.
  • Click an attachment to navigate / open — a session opens that chat, a project switches to it, a linked task jumps to its list (even one in another list; a deleted target says so instead of dead-ending), a URL opens externally, a file opens in its default app (with a folder button to reveal it); and a note or ContextDock item opens a read-only content viewer (TaskV2ContextContentModal) showing its saved text / materialized markdown — there's no target to jump to, so the content itself is the thing you see.
  • Refresh a ContextDock attachment from its row to re-pull the latest docs (the staleness mirror of a project doc-row refresh).
  • Edit an attachment with the pencil on its row, or the Edit button in the read-only viewer — a text/markdown note edits its label AND its body (add more detail without deleting and re-adding); every other kind renames its label. Save with Ctrl/⌘ + Enter or the Save button; ContextDock stays rename-only (its content comes from the bundle).
  • Remove an attachment with the trash button. Removal is undoable: a toast appears with Undo that re-adds the item (the lightweight Milestone 3 undo — a persisted, cross-restart history is a later milestone).

The express path — "Attach a session". Linking a session is the common case, so the right-click menu carries a dedicated "Attach a session" item that opens this Context pane straight at the session picker, skipping the five-step ⋯ → Context → + Add → Session dig. (It used to also be an on-row icon beside Launch agent; that icon was retired to declutter the row — the express capability now lives on the right-click menu, while the longer ⋯ → Context → Session path still works too.) Picking a session creates the same session-kind attachment; an empty session list shows a friendly "No sessions yet". (A session you launched for a task via Launch agent / g is attached to that task automatically now, so you rarely need this for one — see Per-task agent chat.) Under the hood it calls openContextPane(id, { addKind: 'session' }), which seeds a contextInitialAddKind the pane consumes once (a later manual "+ Add context" returns to the normal type picker). Both editable views wire the right-click item via TaskV2RowContextMenu.tsx, so they can't diverge. (TaskV2RowContextMenu.tsx; the pane's optional initialKind lives in TaskV2ContextAddMenu.tsx. See the contract's context-attach-session-icon invariant.)

The reverse — "Attach to task…" from the session side. You can also start from the session: an "Attach to task…" item on a session's menus opens a small task picker, and picking a task creates the very same session-kind link. It lives in two places (both shown only when Tasks is on): right-click a session in the sidebar (single selection), and the open-session header ⋯ menu — that second one renders on mobile too, so it's the phone entry point. The picker is one searchable modal (a full-screen sheet on a phone) with a fixed "Attach to a task" title — the session rides in a chip below it, so a long session name never truncates the title — listing your active tasks grouped under their list header (so same-named tasks in different lists read distinctly). Three redesign touches: a project filter (a scope dropdown — a task has no project of its own, so its project = its list's defaultProjectId; shown only when a task actually belongs to a project, and defaults to the session's own project when that project has tasks, else "All projects"), readable rows (a task whose text is a bare or markdown URL shows a friendly label — "Loom recording", "Shared artifact", else the hostname — with the raw link demoted to a quiet second line, never a chopped URL), and a pinned "New task from this session" action that creates an unfiled task (→ Inbox) titled from the session and attaches it in one click (one Undo removes the whole thing). Keyboard navigation stays — arrow ↑/↓ to move the highlight over the tasks and Enter to attach, without the mouse (the New-task button sits outside that cycle); it dedups ("Already attached"), confirms with an Undo toast, and reuses the unchanged TASKS_V2_CONTEXT_CREATE (the New-task action also uses TASKS_V2_CREATE) — no new table, channel, or setting. This is separate from a session's taskId (the "launched-from-this-task" binding). See the contract's session-attach-to-task invariant (AttachSessionToTaskPicker.tsx / attach-session-to-task.ts / attach-picker-labels.ts / attach-picker-project-filter.ts).

A row with attachments shows a small 📎 count chip (a quiet, clickable trailing indicator — it opens the attachment peek, see above — on every row that carries context — the focused/selected one included — with the hover action icons revealing to its right rather than covering it; the ⋯ menu's Context action also shows the count as a label), so you can see at a glance which tasks carry context. When a task launches its agent, its attachments are rendered into the launch prompt: the pointer kinds (session / project / task / url / note / file — a file shows its path + a "read it with your Read tool" hint) become a compact ## Attached context list, and each ContextDock item's curated content is inlined in full under a separate ## Reference docs section (capped so one bundle can't balloon the brief) — so the agent boots already aware of what the task points at AND carrying the curated docs.

Attachments persist in their own task_context_items table (one row per attachment, soft-deleted on remove, fractionally ordered per task). It ships the session | project | task | url | note | file | contextdock kinds. A ContextDock attachment is materialized at add-time — because the agent has no way to fetch a bundle on its own, Omniscio pulls the content (a bundle → its assembled docs, budget-capped; a list → a titles index) and stores it on the attachment, exactly how a project materializes a linked bundle (Refresh re-pulls it). Deferred to later milestones: the inbox / image kinds (images fold into file), rendering attachments into the tasks-v2.md mirror (Milestone 6), and a persisted activity-log + cross-restart undo history (Milestone 5). Tasks accepts the full set of file + contextdock kinds alongside the original five. Like the rest of Tasks, the whole surface stays behind the tasks-v2 Labs gate.

Context has its own gated IPC channels (constants in src/shared/ipc-channels/productivity.ts, handlers in src/main/ipc/tasks-v2-context-handlers.ts):

Constant Channel string Purpose
TASKS_V2_CONTEXT_LIST tasks-v2-context:list a task's attachments, by sort_order
TASKS_V2_CONTEXT_COUNTS tasks-v2-context:counts per-task attachment counts (the row chips)
TASKS_V2_CONTEXT_CREATE tasks-v2-context:create attach an item; returns it
TASKS_V2_CONTEXT_UPDATE tasks-v2-context:update patch an attachment's label / payload / rel — driven by the pane's Edit pencil + the content viewer's Edit button (TaskV2ContextEditModal → the store's updateContextItem): a note edits its label + body, every other kind renames its label
TASKS_V2_CONTEXT_DELETE tasks-v2-context:delete soft-delete an attachment; {deletedCount}
TASKS_V2_CONTEXT_REORDER tasks-v2-context:reorder reposition within a task (fractional); IPC-only
TASKS_V2_CONTEXT_OPEN_FILE tasks-v2-context:open-file open / reveal a file item (unscoped + validated)
TASKS_V2_CONTEXT_REFRESH tasks-v2-context:refresh re-materialize a contextdock item from its bundle/list
TASKS_V2_CONTEXT_CHANGED tasks-v2-context:changed push — cache-invalidate, strict empty payload

Every mutation emits TASKS_V2_CONTEXT_CHANGED after the DB commit (delete only when it removed a row). Unlike the list handlers, context mutations do not schedule a markdown-mirror write — context isn't in tasks-v2.md until Milestone 6.

Three of these are also reachable over the CLI (in src/main/services/cli/cli-server-tasks-v2-routes.ts), each 403 + disabled: true when the feature is off:

Route Auth / limit Notes
GET /tasks-v2/context/list bearer + 60 reads/min/token ?taskId= required (400 if missing)
POST /tasks-v2/context/create bearer (central) + 10 mutations/min 201 + the created item; blank label → 400; file + contextdock rejected (app-only)
POST /tasks-v2/context/delete bearer + 10/min idempotent; {deletedCount}

Reorder and update are deliberately IPC-only (human-curated, not exposed over CLI — matching /tasks-v2/reorder).

Cascading context — list → subtree → task (M3)

Context is layered and inherited down the tree. Three tiers feed a task:

  1. the list's master context (the whole-list note),
  2. a subtree tier — context attached to an ancestor task and marked "also apply to sub-items" cascades down to every descendant, and
  3. the task's own attachments.

When you add context you can flip an "Also apply to sub-items (cascade)" toggle (it sets the item's inherit flag); cascading items show a "cascades" badge on their row. The context pane shows a read-only "Inherited" section listing exactly what the task inherits and where each piece came from ("From list …", "from <parent task>") — so inheritance is always visible, never a surprise.

It is bounded end to end so it can never balloon an AI prompt: the resolver (resolveTaskContext) walks ancestors via a cycle-safe, depth-capped recursive CTE and caps inherited items (12, with a truncated flag); the agent brief truncates each inherited item and caps the section; and the "Right now" prioritizer is fed only a single length-capped shared preamble (the eligible tasks' list master contexts), not per-task inherited lines. The TASKS_V2_CONTEXT_RESOLVE channel (read-only, IPC-only, gated) returns the resolved context to the pane, and the same resolver briefs a launched agent (a new ## Inherited context section). Editing a master context invalidates the prioritizer's cached pick — the cache key folds in a content fingerprint of the preamble. The inherit flag lives on task_context_items (migration 20260608052857-…); existing items default to task-only.

Attachments — paste, drop, or attach files to a task

Distinct from the context references above, a task also holds stored file attachments — real copies of images and any other files, shown in an Attachments strip inside the same Context pane. Three ways to add them: paste an image (Ctrl/Cmd+V while the pane is open), drag-and-drop files onto the strip, or the "+" picker (a plain file picker, so a phone offers its camera/gallery). Any file type is accepted, up to 50 MB each.

Because they're copies (not links), a pasted screenshot — which has no file on disk to link to — sticks, and an attached file survives you moving or deleting the original. Images render as thumbnails; click one to view it full-size in the in-app lightbox and arrow through the image set. Every other file renders as a name + size pill; clicking a document opens it in your OS default app, while an unknown or program-type file is revealed in its folder rather than launched — so a double-click can never run an attached executable. Remove asks you to confirm.

It works on mobile (the images load over the same token-gated /attachment/… route the chat uses, and the picker is the phone's file/camera picker) and in both light and dark themes. Under the hood the bytes are copied into <userData>/attachments/<taskId>/ — reusing the same hardened store the chat composer uses (the 50 MB cap + type-safety sniff live there) — and recorded in a task_attachments table (migration 20260724042407-…). They are deliberately not fed into a launched agent's brief; that stays the job of the context references above. Full invariants + the safe-open / web-bridge / soft-delete rules: tasks-v2-attachments-contract.md.

For agents

IPC channels (TASKS_V2_*)

Defined in src/shared/ipc-channels/productivity.ts; handlers in src/main/ipc/tasks-v2-handlers.ts (all behind requireTasksV2Enabled()); request/response schemas in src/shared/ipc-schemas/tasks-v2.ts.

Constant Channel string Purpose
TASKS_V2_LIST tasks-v2:list list children of a parent (parentId: null = roots, omitted = whole tree)
TASKS_V2_CREATE tasks-v2:create append/insert a row; returns the created Task
TASKS_V2_UPDATE tasks-v2:update patch {text, dueAt, snoozedUntil, dueNotifiedAt, metadata, importance, urgency, startAt}; returns the row
TASKS_V2_COMPLETE tasks-v2:complete flip done; cascades through undone descendants when set true
TASKS_V2_DELETE tasks-v2:delete soft-cascade delete; returns {deletedCount}
TASKS_V2_REORDER tasks-v2:reorder reparent + reposition; void; push-only, no mirror write
TASKS_V2_INDENT tasks-v2:indent the v2 Tab — indent a row alone, leaving its sub-items at depth (payload {id}; server computes the move); void; push-only, no mirror write
TASKS_V2_MOVE_TO_LIST tasks-v2:move-to-list re-file a row + its subtree into another list; emits + writes the mirror
TASKS_V2_GET_MARKDOWN tasks-v2:get-markdown return current tasks-v2.md text (empty string on ENOENT)
TASKS_V2_LAUNCH_AGENT tasks-v2:launch-agent spawn (or reuse) a Claude agent session bound to a task; returns {sessionId}. The IPC variant accepts an optional promptOverride (the popup's edited text, used verbatim); the CLI route omits it
TASKS_V2_BUILD_AGENT_PROMPT tasks-v2:build-agent-prompt preview the launch popup's visible message (prompt) AND the hidden appended context (context, steered by the optional sessionType) for the collapsed "what the agent will also receive" disclosure — free, read-only, no spawn; same shared buildTaskAgentPrompt as launch. Carries no token
TASKS_V2_GET_SESSION tasks-v2:get-session the task's linked session id (or null) — the free-reopen lookup
TASKS_V2_INTERVIEW_GET tasks-v2:interview-get the task's breakdown thread (or null) — free read
TASKS_V2_INTERVIEW_RUN tasks-v2:interview-run open/generate the breakdown interview for a task — PAID (bounded, capped); friendly error over-cap
TASKS_V2_INTERVIEW_SEND tasks-v2:interview-send send a user turn, get a bounded AI reply + refreshed proposal — PAID
TASKS_V2_INTERVIEW_ACCEPT tasks-v2:interview-accept write the accepted (edited) subtasks under the task (atomic + idempotent) — free; the ONLY subtask-write path; emits TASKS_V2_CHANGED
TASKS_V2_FETCH_VIDEO_TITLE tasks-v2:fetch-video-title resolve a clean YouTube/Vimeo URL to the video's title via public oEmbed (backs paste-a-link → title) — free, read-only, no key/AI spend; own UNgated-by-KMS channel reusing fetchVideoTitle; humanized failure → renderer keeps the URL (handler ipc/tasks-v2/video-title.ts)
TASKS_V2_CHANGED tasks-v2:changed push event — cache-invalidate fan-out, strict empty payload

Every mutation emits TASKS_V2_CHANGED exactly once after the DB write commits. The payload is an empty object (schema in src/shared/push-event-schemas/tasks-v2.ts); the renderer reacts by refetching the whole tree, never by splicing a delta. Exceptions to the fan-out tail: reorder emits the push but schedules no mirror write; indent likewise schedules no mirror write and emits only when it actually moved a row (movedCount > 0); delete emits the push + schedules the mirror only when deletedCount > 0; move-to-list writes the mirror (it changes which list-heading a task lives under).

Related

The overview, the other parts, and everything else worth reading next all sit on Tasks (markdown outliner, in development).

Last verified 2026-09-23