2025-12-29 15:30:57 +05:30
|
|
|
import { z } from 'zod';
|
|
|
|
|
import { RelPath, Encoding, Stat, DirEntry, ReaddirOptions, ReadFileResult, WorkspaceChangeEvent, WriteFileOptions, WriteFileResult, RemoveOptions } from './workspace.js';
|
|
|
|
|
import { ListToolsResponse } from './mcp.js';
|
2026-01-16 09:48:11 +05:30
|
|
|
import { AskHumanResponsePayload, CreateRunOptions, Run, ListRunsResponse, ToolPermissionAuthorizePayload } from './runs.js';
|
2026-02-04 01:12:06 +05:30
|
|
|
import { LlmModelConfig } from './models.js';
|
2026-02-04 23:21:13 +05:30
|
|
|
import { AgentScheduleConfig, AgentScheduleEntry } from './agent-schedule.js';
|
|
|
|
|
import { AgentScheduleState } from './agent-schedule-state.js';
|
2026-02-05 16:05:10 +05:30
|
|
|
import { ServiceEvent } from './service-events.js';
|
feat: tracks — frontmatter directives, sidebar UI, multi-trigger
Recasts the old "track blocks" as "tracks" — directives stored in a
note's frontmatter rather than inline YAML fences and HTML-comment
target regions. The motivation is UX: the inline anatomy made notes
feel like config, leaked into the editing surface, and competed with
the writing flow. Frontmatter is invisible to the body editor, so
moving directives there reclaims the body as just markdown the user
wrote.
The runtime agent now edits the note body freely via standard
workspace tools rather than rewriting a constrained target region.
Each track's instruction names an H2 section to own; the agent
finds or creates that section, updates only its content, and
self-heals position on subsequent runs.
Triggers are now a unified array per track. cron / window / once /
event in any combination, including multi-trigger setups (the
flagship example: a priorities track that rebuilds at three
day-windows and reacts to incoming gmail / calendar events).
window is forgiving — fires once per day anywhere inside its
band — so users opening the app late in the morning still get the
morning run.
The chip-in-editor is gone. Tracks are managed from a right-side
sidebar opened by a Radio-icon button at the top-right of the
editor toolbar. Cmd+K is no longer a Copilot entry point — search-
only — pending a more intuitive invocation surface later.
Today.md ships as the flagship demo of what tracks can do, with a
versioned migration system so future template updates roll out
cleanly to existing users (existing body preserved, old version
backed up).
Copilot is tuned to listen for any signal that the user wants
something dynamic — not just the literal word "track". Strong
phrasings get acted on directly; one-off questions about decaying
information are answered first and then offered as a track. New or
edited tracks run once by default so the user immediately sees
content.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:00:20 +05:30
|
|
|
import { TrackEvent } from './track.js';
|
2026-02-26 21:41:17 +05:30
|
|
|
import { UserMessageContent } from './message.js';
|
2026-03-24 11:50:22 +05:30
|
|
|
import { RowboatApiConfig } from './rowboat-account.js';
|
Feature/composio tools library (#461)
* first version of composio
* Enhance error handling in Composio tool execution. Added try-catch block to log errors and return a structured error response when executing tools fails.
* Add tool search functionality to settings dialog
Implemented a debounced search feature for tools within the toolkit, allowing users to filter tools based on a search query. Added state management for search results and loading status. Updated the UI to accommodate the search input and results display. Additionally, modified the API call to use 'query' instead of 'search' for consistency.
* Enhance Composio OAuth flow management and improve tool handling
- Updated the activeFlows management to prevent concurrent OAuth flows for the same toolkit by using toolkitSlug as the key.
- Implemented cleanup logic for existing flows, ensuring proper resource management by aborting and closing servers as needed.
- Introduced a timeout mechanism for abandoned flows, enhancing reliability.
- Refactored the Composio tools repository to use an in-memory cache for improved performance and added methods for persisting changes to disk.
- Updated the detailed tools listing to use a consistent API call structure and improved input parameter handling.
- Made connectionData in the response optional for better flexibility in handling connected accounts.
* Improve error handling in Composio API calls
- Enhanced error reporting by extracting human-readable messages from the JSON response body when the API call fails.
- Added logic to parse the response and include specific error details in the thrown error message, improving debugging and user feedback.
* Refactor Composio integration handling and improve UI components
- Updated composio-handler.ts to invalidate the copilot instructions cache upon connection and disconnection.
- Removed unused functions related to tool management in composio-handler.ts.
- Enhanced IPC handlers in ipc.ts to streamline Composio connection processes.
- Introduced ComposioConnectCard in the renderer to display connection status and handle events.
- Refactored tool rendering in App.tsx and chat-sidebar.tsx to utilize new tabbed content for parameters and results.
- Improved Composio tools prompt generation in instructions.ts to clarify integration usage and discovery flow.
- Cleaned up unused code and improved overall structure for better maintainability.
* Refactor listToolkits function to remove cursor parameter and implement pagination
- Updated listToolkits in composio-handler.ts to paginate through API results, collecting all curated toolkits.
- Adjusted IPC handler in ipc.ts to call the modified listToolkits without cursor argument.
- Made properties in ToolkitInfo optional in settings-dialog.tsx for improved flexibility.
- Removed the unused enabled-tools-repo.ts file to clean up the codebase.
* Refactor Composio toolkit management for improved structure and maintainability
- Consolidated toolkit definitions and display names into a single source of truth in shared/composio.ts.
- Updated core composio/curated-toolkits.ts to re-export types and constants for backward compatibility.
- Enhanced the organization of toolkit data, ensuring clarity and ease of access for future development.
* Refactor Composio integration and improve component structure
- Updated imports in composio-handler.ts and various components to utilize shared/composio.js for consistency.
- Simplified ComposioConnectCard by removing unnecessary state management and improving event handling.
- Enhanced chat-conversation.ts to directly reference COMPOSIO_DISPLAY_NAMES from shared/composio.js.
- Cleaned up unused functions and types in client.ts and types.ts for better maintainability.
- Removed deprecated curated-toolkits.ts file to streamline the codebase.
* Refactor Composio connection handling and improve tool display logic
- Removed the connection bridge for Composio toolkit OAuth, simplifying the connection process.
- Updated ComposioConnectCard to display a more user-friendly connection message.
- Introduced a new utility function, getToolDisplayName, to provide human-friendly names for builtin tools.
- Refactored App and ChatSidebar components to utilize the new getToolDisplayName function for improved clarity in tool titles.
- Cleaned up imports and removed unused code for better maintainability.
* remove from diff
* Address PR review: consolidate types, refactor CopilotAgent, sort toolkits
- Move ZSearchResultTool and ZNormalizedToolResult into composio/types.ts
- Convert CopilotAgent from static const to async buildCopilotAgent()
- Simplify loadAgent to delegate to buildCopilotAgent()
- Sort CURATED_TOOLKITS alphabetically by slug
- Remove inline type annotations in composio-handler, use inferred types
- Bump search limit from 15→50 for unscoped queries
- Add docstrings explaining inferToolkitSlug fallback behavior
- Add IPC schema reference comment for composio channels
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Enhance Composio connection handling and improve rendering logic
- Added a 'hidden' property to ComposioConnectCardData to prevent rendering of duplicate connection cards.
- Updated App and ChatSidebar components to skip rendering if the card is marked as hidden.
- Refactored ComposioConnectCard to utilize a ref for callback firing, ensuring onConnected is only called once.
- Improved instructions for Composio integration to clarify usage and loading of the composio-integration skill.
This update streamlines the user experience by avoiding duplicate connection prompts and enhances the overall clarity of integration instructions.
* Address PR round 2: use query param, remove inferToolkitSlug, consolidate types
- Rename deprecated `search` param to `query` per Composio docs
- Remove inferToolkitSlug fallback; make toolkit required in ZSearchResultTool
- Replace inline Awaited<ReturnType<...>> with concrete Toolkit type in handler
- Move ZToolkitMeta/ZToolkitItem/ZListToolkitsResponse to shared/composio.ts
- Reference shared schemas in ipc.ts and core/types.ts (single source of truth)
- Remove unused ZTool import from client.ts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add Toolkit type inference to composio/types.ts
- Introduced a new type `Toolkit` inferred from `ZToolkit` to enhance type safety and clarity in type definitions.
- This addition supports better integration and usage of the toolkit within the Composio framework.
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 13:30:46 +05:30
|
|
|
import { ZListToolkitsResponse } from './composio.js';
|
2026-04-15 13:21:09 +05:30
|
|
|
import { BrowserStateSchema } from './browser-control.js';
|
2025-12-29 15:30:57 +05:30
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Runtime Validation Schemas (Single Source of Truth)
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
const ipcSchemas = {
|
|
|
|
|
'app:getVersions': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
|
|
|
|
chrome: z.string(),
|
|
|
|
|
node: z.string(),
|
|
|
|
|
electron: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-04-28 19:53:40 +05:30
|
|
|
'analytics:bootstrap': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
|
|
|
|
installationId: z.string(),
|
|
|
|
|
apiUrl: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2025-12-29 15:30:57 +05:30
|
|
|
'workspace:getRoot': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
|
|
|
|
root: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'workspace:exists': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
path: RelPath,
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
exists: z.boolean(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'workspace:stat': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
path: RelPath,
|
|
|
|
|
}),
|
|
|
|
|
res: Stat,
|
|
|
|
|
},
|
|
|
|
|
'workspace:readdir': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
path: z.string(), // Empty string allowed for root directory
|
|
|
|
|
opts: ReaddirOptions.optional(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.array(DirEntry),
|
|
|
|
|
},
|
|
|
|
|
'workspace:readFile': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
path: RelPath,
|
|
|
|
|
encoding: Encoding.optional(),
|
|
|
|
|
}),
|
|
|
|
|
res: ReadFileResult,
|
|
|
|
|
},
|
|
|
|
|
'workspace:writeFile': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
path: RelPath,
|
|
|
|
|
data: z.string(),
|
|
|
|
|
opts: WriteFileOptions.optional(),
|
|
|
|
|
}),
|
|
|
|
|
res: WriteFileResult,
|
|
|
|
|
},
|
|
|
|
|
'workspace:mkdir': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
path: RelPath,
|
|
|
|
|
recursive: z.boolean().optional(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
ok: z.literal(true),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'workspace:rename': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
from: RelPath,
|
|
|
|
|
to: RelPath,
|
|
|
|
|
overwrite: z.boolean().optional(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
ok: z.literal(true),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'workspace:copy': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
from: RelPath,
|
|
|
|
|
to: RelPath,
|
|
|
|
|
overwrite: z.boolean().optional(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
ok: z.literal(true),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'workspace:remove': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
path: RelPath,
|
|
|
|
|
opts: RemoveOptions.optional(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
ok: z.literal(true),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'workspace:didChange': {
|
|
|
|
|
req: WorkspaceChangeEvent,
|
|
|
|
|
res: z.null(),
|
|
|
|
|
},
|
|
|
|
|
'mcp:listTools': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
serverName: z.string(),
|
|
|
|
|
cursor: z.string().optional(),
|
|
|
|
|
}),
|
|
|
|
|
res: ListToolsResponse,
|
|
|
|
|
},
|
|
|
|
|
'mcp:executeTool': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
serverName: z.string(),
|
|
|
|
|
toolName: z.string(),
|
|
|
|
|
input: z.record(z.string(), z.unknown()),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
result: z.unknown(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'runs:create': {
|
|
|
|
|
req: CreateRunOptions,
|
|
|
|
|
res: Run,
|
|
|
|
|
},
|
|
|
|
|
'runs:createMessage': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
runId: z.string(),
|
2026-02-26 21:41:17 +05:30
|
|
|
message: UserMessageContent,
|
2026-03-13 10:26:08 +05:30
|
|
|
voiceInput: z.boolean().optional(),
|
|
|
|
|
voiceOutput: z.enum(['summary', 'full']).optional(),
|
2026-03-13 13:58:30 +05:30
|
|
|
searchEnabled: z.boolean().optional(),
|
2026-04-16 16:24:26 +05:30
|
|
|
middlePaneContext: z.discriminatedUnion('kind', [
|
|
|
|
|
z.object({
|
|
|
|
|
kind: z.literal('note'),
|
|
|
|
|
path: z.string(),
|
|
|
|
|
content: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
z.object({
|
|
|
|
|
kind: z.literal('browser'),
|
|
|
|
|
url: z.string(),
|
|
|
|
|
title: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
]).optional(),
|
2025-12-29 15:30:57 +05:30
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
messageId: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'runs:authorizePermission': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
runId: z.string(),
|
|
|
|
|
authorization: ToolPermissionAuthorizePayload,
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.literal(true),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'runs:provideHumanInput': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
runId: z.string(),
|
|
|
|
|
reply: AskHumanResponsePayload,
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.literal(true),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'runs:stop': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
runId: z.string(),
|
2026-01-30 06:53:50 +05:30
|
|
|
force: z.boolean().optional().default(false),
|
2025-12-29 15:30:57 +05:30
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.literal(true),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-01-16 09:48:11 +05:30
|
|
|
'runs:fetch': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
runId: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
res: Run,
|
|
|
|
|
},
|
|
|
|
|
'runs:list': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
cursor: z.string().optional(),
|
|
|
|
|
}),
|
|
|
|
|
res: ListRunsResponse,
|
|
|
|
|
},
|
2026-02-16 15:28:20 +05:30
|
|
|
'runs:delete': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
runId: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({ success: z.boolean() }),
|
|
|
|
|
},
|
2025-12-29 15:30:57 +05:30
|
|
|
'runs:events': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.null(),
|
2026-01-06 06:56:42 +05:30
|
|
|
},
|
2026-02-05 16:05:10 +05:30
|
|
|
'services:events': {
|
|
|
|
|
req: ServiceEvent,
|
|
|
|
|
res: z.null(),
|
|
|
|
|
},
|
Add tracks — auto-updating note blocks with scheduled and event-driven triggers
Track blocks are YAML-fenced sections embedded in markdown notes whose output
is rewritten by a background agent. Three trigger types: manual (Run button or
Copilot), scheduled (cron / window / once with a 2 min grace window), and
event-driven (Gmail/Calendar sync events routed via an LLM classifier with a
second-pass agent decision). Output lives between <!--track-target:ID-->
comment markers that render as editable content in the Tiptap editor so users
can read and extend AI-generated content inline.
Core:
- Schedule and event pipelines run as independent polling loops (15s / 5s),
both calling the same triggerTrackUpdate orchestrator. Events are FIFO via
monotonic IDs; a per-track Set guards against duplicate runs.
- Track-run agent builds three message variants (manual/timed/event) — the
event variant includes a Pass 2 directive to skip updates on false positives
flagged by the liberal Pass 1 router.
- IPC surface: track:run/get/update/replaceYaml/delete plus tracks:events
forward of the pub-sub bus to the renderer.
- Gmail emits per-thread events; Calendar bundles a digest per sync.
Copilot:
- New `tracks` skill (auto-generated canonical schema from Zod via
z.toJSONSchema) teaches block creation, editing, and proactive suggestion.
- `run-track-block` tool with optional `context` parameter for backfills
(e.g. seeding a new email-tracking block from existing synced emails).
Renderer:
- Tiptap chip (display-only) opens a rich modal with tabs, toggle, schedule
details, raw YAML editor, and confirm-to-delete. All mutations go through
IPC so the backend stays the single writer.
- Target regions use two atom marker nodes (open/close) around real editable
content — custom blocks render natively, users can add their own notes.
- "Edit with Copilot" seeds a chat session with the note attached.
Docs: apps/x/TRACKS.md covers product flows, technical pipeline, and a
catalog of every LLM prompt involved with file+line pointers.
2026-04-14 13:51:45 +05:30
|
|
|
'tracks:events': {
|
|
|
|
|
req: TrackEvent,
|
|
|
|
|
res: z.null(),
|
|
|
|
|
},
|
2026-02-04 01:12:06 +05:30
|
|
|
'models:list': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
|
|
|
|
providers: z.array(z.object({
|
|
|
|
|
id: z.string(),
|
|
|
|
|
name: z.string(),
|
|
|
|
|
models: z.array(z.object({
|
|
|
|
|
id: z.string(),
|
|
|
|
|
name: z.string().optional(),
|
|
|
|
|
release_date: z.string().optional(),
|
|
|
|
|
})),
|
|
|
|
|
})),
|
|
|
|
|
lastUpdated: z.string().optional(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'models:test': {
|
|
|
|
|
req: LlmModelConfig,
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.boolean(),
|
|
|
|
|
error: z.string().optional(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'models:saveConfig': {
|
|
|
|
|
req: LlmModelConfig,
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.literal(true),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-01-06 06:56:42 +05:30
|
|
|
'oauth:connect': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
provider: z.string(),
|
2026-02-03 23:01:23 +05:30
|
|
|
clientId: z.string().optional(),
|
2026-04-10 00:42:59 +05:30
|
|
|
clientSecret: z.string().optional(),
|
2026-01-06 06:56:42 +05:30
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.boolean(),
|
|
|
|
|
error: z.string().optional(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'oauth:disconnect': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
provider: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.boolean(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'oauth:list-providers': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
|
|
|
|
providers: z.array(z.string()),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-02-17 09:54:34 +05:30
|
|
|
'oauth:getState': {
|
2026-01-06 06:56:42 +05:30
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
2026-02-17 09:54:34 +05:30
|
|
|
config: z.record(z.string(), z.object({
|
|
|
|
|
connected: z.boolean(),
|
2026-02-21 22:58:38 +05:30
|
|
|
error: z.string().nullable().optional(),
|
2026-04-07 12:38:19 +05:30
|
|
|
userId: z.string().optional(),
|
2026-04-07 11:24:25 -07:00
|
|
|
clientId: z.string().nullable().optional(),
|
2026-02-17 09:54:34 +05:30
|
|
|
})),
|
2026-01-06 06:56:42 +05:30
|
|
|
}),
|
|
|
|
|
},
|
2026-03-24 11:50:22 +05:30
|
|
|
'account:getRowboat': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
|
|
|
|
signedIn: z.boolean(),
|
|
|
|
|
accessToken: z.string().nullable(),
|
|
|
|
|
config: RowboatApiConfig.nullable(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-01-20 15:18:47 +05:30
|
|
|
'oauth:didConnect': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
provider: z.string(),
|
|
|
|
|
success: z.boolean(),
|
|
|
|
|
error: z.string().optional(),
|
2026-04-07 12:38:19 +05:30
|
|
|
userId: z.string().optional(),
|
2026-01-20 15:18:47 +05:30
|
|
|
}),
|
|
|
|
|
res: z.null(),
|
|
|
|
|
},
|
2026-05-04 15:47:30 +05:30
|
|
|
'app:openUrl': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
url: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.null(),
|
|
|
|
|
},
|
|
|
|
|
'app:takeMeetingNotes': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
// Pass the raw calendar event JSON through; renderer adapts to its existing flow.
|
|
|
|
|
event: z.unknown(),
|
|
|
|
|
// When true, the renderer should also open the meeting URL (Zoom/Meet/etc.)
|
|
|
|
|
// in addition to triggering the take-notes flow.
|
|
|
|
|
openMeeting: z.boolean().optional(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.null(),
|
|
|
|
|
},
|
|
|
|
|
'app:consumePendingDeepLink': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
|
|
|
|
url: z.string().nullable(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-01-08 11:19:43 +05:30
|
|
|
'granola:getConfig': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
|
|
|
|
enabled: z.boolean(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'granola:setConfig': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
enabled: z.boolean(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.literal(true),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-03-10 16:26:51 +05:30
|
|
|
'slack:getConfig': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
|
|
|
|
enabled: z.boolean(),
|
|
|
|
|
workspaces: z.array(z.object({ url: z.string(), name: z.string() })),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'slack:setConfig': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
enabled: z.boolean(),
|
|
|
|
|
workspaces: z.array(z.object({ url: z.string(), name: z.string() })),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.literal(true),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'slack:listWorkspaces': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
|
|
|
|
workspaces: z.array(z.object({ url: z.string(), name: z.string() })),
|
|
|
|
|
error: z.string().optional(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-01-21 20:29:24 +05:30
|
|
|
'onboarding:getStatus': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
|
|
|
|
showOnboarding: z.boolean(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'onboarding:markComplete': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.literal(true),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-02-02 21:34:47 +05:30
|
|
|
// Composio integration channels
|
|
|
|
|
'composio:is-configured': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
|
|
|
|
configured: z.boolean(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'composio:set-api-key': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
apiKey: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.boolean(),
|
|
|
|
|
error: z.string().optional(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'composio:initiate-connection': {
|
2026-02-03 07:25:33 +05:30
|
|
|
req: z.object({
|
2026-02-02 21:34:47 +05:30
|
|
|
toolkitSlug: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.boolean(),
|
|
|
|
|
redirectUrl: z.string().optional(),
|
|
|
|
|
connectedAccountId: z.string().optional(),
|
|
|
|
|
error: z.string().optional(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'composio:get-connection-status': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
toolkitSlug: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
isConnected: z.boolean(),
|
|
|
|
|
status: z.string().optional(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'composio:sync-connection': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
toolkitSlug: z.string(),
|
|
|
|
|
connectedAccountId: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
status: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'composio:disconnect': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
toolkitSlug: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.boolean(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'composio:list-connected': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
|
|
|
|
toolkits: z.array(z.string()),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-05-05 14:28:46 +05:30
|
|
|
'migration:check-composio-google': {
|
2026-03-02 17:52:23 +05:30
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
2026-05-05 14:28:46 +05:30
|
|
|
shouldShow: z.boolean(),
|
2026-03-17 12:04:57 +05:30
|
|
|
}),
|
|
|
|
|
},
|
2026-02-02 21:34:47 +05:30
|
|
|
'composio:didConnect': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
toolkitSlug: z.string(),
|
|
|
|
|
success: z.boolean(),
|
|
|
|
|
error: z.string().optional(),
|
2026-02-03 07:25:33 +05:30
|
|
|
}),
|
|
|
|
|
res: z.null(),
|
|
|
|
|
},
|
Feature/composio tools library (#461)
* first version of composio
* Enhance error handling in Composio tool execution. Added try-catch block to log errors and return a structured error response when executing tools fails.
* Add tool search functionality to settings dialog
Implemented a debounced search feature for tools within the toolkit, allowing users to filter tools based on a search query. Added state management for search results and loading status. Updated the UI to accommodate the search input and results display. Additionally, modified the API call to use 'query' instead of 'search' for consistency.
* Enhance Composio OAuth flow management and improve tool handling
- Updated the activeFlows management to prevent concurrent OAuth flows for the same toolkit by using toolkitSlug as the key.
- Implemented cleanup logic for existing flows, ensuring proper resource management by aborting and closing servers as needed.
- Introduced a timeout mechanism for abandoned flows, enhancing reliability.
- Refactored the Composio tools repository to use an in-memory cache for improved performance and added methods for persisting changes to disk.
- Updated the detailed tools listing to use a consistent API call structure and improved input parameter handling.
- Made connectionData in the response optional for better flexibility in handling connected accounts.
* Improve error handling in Composio API calls
- Enhanced error reporting by extracting human-readable messages from the JSON response body when the API call fails.
- Added logic to parse the response and include specific error details in the thrown error message, improving debugging and user feedback.
* Refactor Composio integration handling and improve UI components
- Updated composio-handler.ts to invalidate the copilot instructions cache upon connection and disconnection.
- Removed unused functions related to tool management in composio-handler.ts.
- Enhanced IPC handlers in ipc.ts to streamline Composio connection processes.
- Introduced ComposioConnectCard in the renderer to display connection status and handle events.
- Refactored tool rendering in App.tsx and chat-sidebar.tsx to utilize new tabbed content for parameters and results.
- Improved Composio tools prompt generation in instructions.ts to clarify integration usage and discovery flow.
- Cleaned up unused code and improved overall structure for better maintainability.
* Refactor listToolkits function to remove cursor parameter and implement pagination
- Updated listToolkits in composio-handler.ts to paginate through API results, collecting all curated toolkits.
- Adjusted IPC handler in ipc.ts to call the modified listToolkits without cursor argument.
- Made properties in ToolkitInfo optional in settings-dialog.tsx for improved flexibility.
- Removed the unused enabled-tools-repo.ts file to clean up the codebase.
* Refactor Composio toolkit management for improved structure and maintainability
- Consolidated toolkit definitions and display names into a single source of truth in shared/composio.ts.
- Updated core composio/curated-toolkits.ts to re-export types and constants for backward compatibility.
- Enhanced the organization of toolkit data, ensuring clarity and ease of access for future development.
* Refactor Composio integration and improve component structure
- Updated imports in composio-handler.ts and various components to utilize shared/composio.js for consistency.
- Simplified ComposioConnectCard by removing unnecessary state management and improving event handling.
- Enhanced chat-conversation.ts to directly reference COMPOSIO_DISPLAY_NAMES from shared/composio.js.
- Cleaned up unused functions and types in client.ts and types.ts for better maintainability.
- Removed deprecated curated-toolkits.ts file to streamline the codebase.
* Refactor Composio connection handling and improve tool display logic
- Removed the connection bridge for Composio toolkit OAuth, simplifying the connection process.
- Updated ComposioConnectCard to display a more user-friendly connection message.
- Introduced a new utility function, getToolDisplayName, to provide human-friendly names for builtin tools.
- Refactored App and ChatSidebar components to utilize the new getToolDisplayName function for improved clarity in tool titles.
- Cleaned up imports and removed unused code for better maintainability.
* remove from diff
* Address PR review: consolidate types, refactor CopilotAgent, sort toolkits
- Move ZSearchResultTool and ZNormalizedToolResult into composio/types.ts
- Convert CopilotAgent from static const to async buildCopilotAgent()
- Simplify loadAgent to delegate to buildCopilotAgent()
- Sort CURATED_TOOLKITS alphabetically by slug
- Remove inline type annotations in composio-handler, use inferred types
- Bump search limit from 15→50 for unscoped queries
- Add docstrings explaining inferToolkitSlug fallback behavior
- Add IPC schema reference comment for composio channels
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Enhance Composio connection handling and improve rendering logic
- Added a 'hidden' property to ComposioConnectCardData to prevent rendering of duplicate connection cards.
- Updated App and ChatSidebar components to skip rendering if the card is marked as hidden.
- Refactored ComposioConnectCard to utilize a ref for callback firing, ensuring onConnected is only called once.
- Improved instructions for Composio integration to clarify usage and loading of the composio-integration skill.
This update streamlines the user experience by avoiding duplicate connection prompts and enhances the overall clarity of integration instructions.
* Address PR round 2: use query param, remove inferToolkitSlug, consolidate types
- Rename deprecated `search` param to `query` per Composio docs
- Remove inferToolkitSlug fallback; make toolkit required in ZSearchResultTool
- Replace inline Awaited<ReturnType<...>> with concrete Toolkit type in handler
- Move ZToolkitMeta/ZToolkitItem/ZListToolkitsResponse to shared/composio.ts
- Reference shared schemas in ipc.ts and core/types.ts (single source of truth)
- Remove unused ZTool import from client.ts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add Toolkit type inference to composio/types.ts
- Introduced a new type `Toolkit` inferred from `ZToolkit` to enhance type safety and clarity in type definitions.
- This addition supports better integration and usage of the toolkit within the Composio framework.
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 13:30:46 +05:30
|
|
|
// Composio Tools Library channels
|
|
|
|
|
'composio:list-toolkits': {
|
|
|
|
|
req: z.object({}),
|
|
|
|
|
res: ZListToolkitsResponse,
|
|
|
|
|
},
|
2026-02-04 23:21:13 +05:30
|
|
|
// Agent schedule channels
|
|
|
|
|
'agent-schedule:getConfig': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: AgentScheduleConfig,
|
|
|
|
|
},
|
|
|
|
|
'agent-schedule:getState': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: AgentScheduleState,
|
|
|
|
|
},
|
|
|
|
|
'agent-schedule:updateAgent': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
agentName: z.string(),
|
|
|
|
|
entry: AgentScheduleEntry,
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.literal(true),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'agent-schedule:deleteAgent': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
agentName: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.literal(true),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-02-07 12:53:43 +05:30
|
|
|
// Shell integration channels
|
|
|
|
|
'shell:openPath': {
|
|
|
|
|
req: z.object({ path: z.string() }),
|
|
|
|
|
res: z.object({ error: z.string().optional() }),
|
|
|
|
|
},
|
|
|
|
|
'shell:readFileBase64': {
|
|
|
|
|
req: z.object({ path: z.string() }),
|
|
|
|
|
res: z.object({ data: z.string(), mimeType: z.string(), size: z.number() }),
|
|
|
|
|
},
|
2026-05-06 23:14:00 +05:30
|
|
|
// Native dialog channels
|
|
|
|
|
'dialog:openDirectory': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
defaultPath: z.string().optional(),
|
|
|
|
|
title: z.string().optional(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
path: z.string().nullable(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-02-27 20:22:54 +05:30
|
|
|
// Knowledge version history channels
|
|
|
|
|
'knowledge:history': {
|
|
|
|
|
req: z.object({ path: RelPath }),
|
|
|
|
|
res: z.object({
|
|
|
|
|
commits: z.array(z.object({
|
|
|
|
|
oid: z.string(),
|
|
|
|
|
message: z.string(),
|
|
|
|
|
timestamp: z.number(),
|
|
|
|
|
author: z.string(),
|
|
|
|
|
})),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'knowledge:fileAtCommit': {
|
|
|
|
|
req: z.object({ path: RelPath, oid: z.string() }),
|
|
|
|
|
res: z.object({ content: z.string() }),
|
|
|
|
|
},
|
|
|
|
|
'knowledge:restore': {
|
|
|
|
|
req: z.object({ path: RelPath, oid: z.string() }),
|
|
|
|
|
res: z.object({ ok: z.literal(true) }),
|
|
|
|
|
},
|
|
|
|
|
'knowledge:didCommit': {
|
|
|
|
|
req: z.object({}),
|
|
|
|
|
res: z.null(),
|
|
|
|
|
},
|
2026-02-18 18:08:24 +05:30
|
|
|
// Search channels
|
|
|
|
|
'search:query': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
query: z.string(),
|
|
|
|
|
limit: z.number().optional(),
|
|
|
|
|
types: z.array(z.enum(['knowledge', 'chat'])).optional(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
results: z.array(z.object({
|
|
|
|
|
type: z.enum(['knowledge', 'chat']),
|
|
|
|
|
title: z.string(),
|
|
|
|
|
preview: z.string(),
|
|
|
|
|
path: z.string(),
|
|
|
|
|
})),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-03-13 10:26:08 +05:30
|
|
|
// Voice mode channels
|
|
|
|
|
'voice:getConfig': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
|
|
|
|
deepgram: z.object({ apiKey: z.string() }).nullable(),
|
|
|
|
|
elevenlabs: z.object({ apiKey: z.string(), voiceId: z.string().optional() }).nullable(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'voice:synthesize': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
text: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
audioBase64: z.string(),
|
|
|
|
|
mimeType: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-03-30 22:31:49 +05:30
|
|
|
'meeting:checkScreenPermission': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
|
|
|
|
granted: z.boolean(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'meeting:openScreenRecordingSettings': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({ success: z.boolean() }),
|
|
|
|
|
},
|
2026-03-19 22:12:42 +05:30
|
|
|
'meeting:summarize': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
transcript: z.string(),
|
|
|
|
|
meetingStartTime: z.string().optional(),
|
2026-03-20 15:03:34 +05:30
|
|
|
calendarEventJson: z.string().optional(),
|
2026-03-19 22:12:42 +05:30
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
notes: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-03-04 22:15:15 +05:30
|
|
|
// Inline task schedule classification
|
2026-03-14 21:29:53 +05:30
|
|
|
'export:note': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
markdown: z.string(),
|
|
|
|
|
format: z.enum(['md', 'pdf', 'docx']),
|
|
|
|
|
title: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.boolean(),
|
|
|
|
|
error: z.string().optional(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-03-04 22:15:15 +05:30
|
|
|
'inline-task:classifySchedule': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
instruction: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
schedule: z.union([
|
|
|
|
|
z.object({ type: z.literal('cron'), expression: z.string(), startDate: z.string(), endDate: z.string(), label: z.string() }),
|
|
|
|
|
z.object({ type: z.literal('window'), cron: z.string(), startTime: z.string(), endTime: z.string(), startDate: z.string(), endDate: z.string(), label: z.string() }),
|
|
|
|
|
z.object({ type: z.literal('once'), runAt: z.string(), label: z.string() }),
|
|
|
|
|
]).nullable(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-03-19 01:34:10 +05:30
|
|
|
'inline-task:process': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
instruction: z.string(),
|
|
|
|
|
noteContent: z.string(),
|
|
|
|
|
notePath: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
instruction: z.string(),
|
|
|
|
|
schedule: z.union([
|
|
|
|
|
z.object({ type: z.literal('cron'), expression: z.string(), startDate: z.string(), endDate: z.string() }),
|
|
|
|
|
z.object({ type: z.literal('window'), cron: z.string(), startTime: z.string(), endTime: z.string(), startDate: z.string(), endDate: z.string() }),
|
|
|
|
|
z.object({ type: z.literal('once'), runAt: z.string() }),
|
|
|
|
|
]).nullable(),
|
|
|
|
|
scheduleLabel: z.string().nullable(),
|
|
|
|
|
response: z.string().nullable(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
Add tracks — auto-updating note blocks with scheduled and event-driven triggers
Track blocks are YAML-fenced sections embedded in markdown notes whose output
is rewritten by a background agent. Three trigger types: manual (Run button or
Copilot), scheduled (cron / window / once with a 2 min grace window), and
event-driven (Gmail/Calendar sync events routed via an LLM classifier with a
second-pass agent decision). Output lives between <!--track-target:ID-->
comment markers that render as editable content in the Tiptap editor so users
can read and extend AI-generated content inline.
Core:
- Schedule and event pipelines run as independent polling loops (15s / 5s),
both calling the same triggerTrackUpdate orchestrator. Events are FIFO via
monotonic IDs; a per-track Set guards against duplicate runs.
- Track-run agent builds three message variants (manual/timed/event) — the
event variant includes a Pass 2 directive to skip updates on false positives
flagged by the liberal Pass 1 router.
- IPC surface: track:run/get/update/replaceYaml/delete plus tracks:events
forward of the pub-sub bus to the renderer.
- Gmail emits per-thread events; Calendar bundles a digest per sync.
Copilot:
- New `tracks` skill (auto-generated canonical schema from Zod via
z.toJSONSchema) teaches block creation, editing, and proactive suggestion.
- `run-track-block` tool with optional `context` parameter for backfills
(e.g. seeding a new email-tracking block from existing synced emails).
Renderer:
- Tiptap chip (display-only) opens a rich modal with tabs, toggle, schedule
details, raw YAML editor, and confirm-to-delete. All mutations go through
IPC so the backend stays the single writer.
- Target regions use two atom marker nodes (open/close) around real editable
content — custom blocks render natively, users can add their own notes.
- "Edit with Copilot" seeds a chat session with the note attached.
Docs: apps/x/TRACKS.md covers product flows, technical pipeline, and a
catalog of every LLM prompt involved with file+line pointers.
2026-04-14 13:51:45 +05:30
|
|
|
// Track channels
|
|
|
|
|
'track:run': {
|
|
|
|
|
req: z.object({
|
feat: tracks — frontmatter directives, sidebar UI, multi-trigger
Recasts the old "track blocks" as "tracks" — directives stored in a
note's frontmatter rather than inline YAML fences and HTML-comment
target regions. The motivation is UX: the inline anatomy made notes
feel like config, leaked into the editing surface, and competed with
the writing flow. Frontmatter is invisible to the body editor, so
moving directives there reclaims the body as just markdown the user
wrote.
The runtime agent now edits the note body freely via standard
workspace tools rather than rewriting a constrained target region.
Each track's instruction names an H2 section to own; the agent
finds or creates that section, updates only its content, and
self-heals position on subsequent runs.
Triggers are now a unified array per track. cron / window / once /
event in any combination, including multi-trigger setups (the
flagship example: a priorities track that rebuilds at three
day-windows and reacts to incoming gmail / calendar events).
window is forgiving — fires once per day anywhere inside its
band — so users opening the app late in the morning still get the
morning run.
The chip-in-editor is gone. Tracks are managed from a right-side
sidebar opened by a Radio-icon button at the top-right of the
editor toolbar. Cmd+K is no longer a Copilot entry point — search-
only — pending a more intuitive invocation surface later.
Today.md ships as the flagship demo of what tracks can do, with a
versioned migration system so future template updates roll out
cleanly to existing users (existing body preserved, old version
backed up).
Copilot is tuned to listen for any signal that the user wants
something dynamic — not just the literal word "track". Strong
phrasings get acted on directly; one-off questions about decaying
information are answered first and then offered as a track. New or
edited tracks run once by default so the user immediately sees
content.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:00:20 +05:30
|
|
|
id: z.string(),
|
Add tracks — auto-updating note blocks with scheduled and event-driven triggers
Track blocks are YAML-fenced sections embedded in markdown notes whose output
is rewritten by a background agent. Three trigger types: manual (Run button or
Copilot), scheduled (cron / window / once with a 2 min grace window), and
event-driven (Gmail/Calendar sync events routed via an LLM classifier with a
second-pass agent decision). Output lives between <!--track-target:ID-->
comment markers that render as editable content in the Tiptap editor so users
can read and extend AI-generated content inline.
Core:
- Schedule and event pipelines run as independent polling loops (15s / 5s),
both calling the same triggerTrackUpdate orchestrator. Events are FIFO via
monotonic IDs; a per-track Set guards against duplicate runs.
- Track-run agent builds three message variants (manual/timed/event) — the
event variant includes a Pass 2 directive to skip updates on false positives
flagged by the liberal Pass 1 router.
- IPC surface: track:run/get/update/replaceYaml/delete plus tracks:events
forward of the pub-sub bus to the renderer.
- Gmail emits per-thread events; Calendar bundles a digest per sync.
Copilot:
- New `tracks` skill (auto-generated canonical schema from Zod via
z.toJSONSchema) teaches block creation, editing, and proactive suggestion.
- `run-track-block` tool with optional `context` parameter for backfills
(e.g. seeding a new email-tracking block from existing synced emails).
Renderer:
- Tiptap chip (display-only) opens a rich modal with tabs, toggle, schedule
details, raw YAML editor, and confirm-to-delete. All mutations go through
IPC so the backend stays the single writer.
- Target regions use two atom marker nodes (open/close) around real editable
content — custom blocks render natively, users can add their own notes.
- "Edit with Copilot" seeds a chat session with the note attached.
Docs: apps/x/TRACKS.md covers product flows, technical pipeline, and a
catalog of every LLM prompt involved with file+line pointers.
2026-04-14 13:51:45 +05:30
|
|
|
filePath: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.boolean(),
|
|
|
|
|
summary: z.string().optional(),
|
|
|
|
|
error: z.string().optional(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'track:get': {
|
|
|
|
|
req: z.object({
|
feat: tracks — frontmatter directives, sidebar UI, multi-trigger
Recasts the old "track blocks" as "tracks" — directives stored in a
note's frontmatter rather than inline YAML fences and HTML-comment
target regions. The motivation is UX: the inline anatomy made notes
feel like config, leaked into the editing surface, and competed with
the writing flow. Frontmatter is invisible to the body editor, so
moving directives there reclaims the body as just markdown the user
wrote.
The runtime agent now edits the note body freely via standard
workspace tools rather than rewriting a constrained target region.
Each track's instruction names an H2 section to own; the agent
finds or creates that section, updates only its content, and
self-heals position on subsequent runs.
Triggers are now a unified array per track. cron / window / once /
event in any combination, including multi-trigger setups (the
flagship example: a priorities track that rebuilds at three
day-windows and reacts to incoming gmail / calendar events).
window is forgiving — fires once per day anywhere inside its
band — so users opening the app late in the morning still get the
morning run.
The chip-in-editor is gone. Tracks are managed from a right-side
sidebar opened by a Radio-icon button at the top-right of the
editor toolbar. Cmd+K is no longer a Copilot entry point — search-
only — pending a more intuitive invocation surface later.
Today.md ships as the flagship demo of what tracks can do, with a
versioned migration system so future template updates roll out
cleanly to existing users (existing body preserved, old version
backed up).
Copilot is tuned to listen for any signal that the user wants
something dynamic — not just the literal word "track". Strong
phrasings get acted on directly; one-off questions about decaying
information are answered first and then offered as a track. New or
edited tracks run once by default so the user immediately sees
content.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:00:20 +05:30
|
|
|
id: z.string(),
|
Add tracks — auto-updating note blocks with scheduled and event-driven triggers
Track blocks are YAML-fenced sections embedded in markdown notes whose output
is rewritten by a background agent. Three trigger types: manual (Run button or
Copilot), scheduled (cron / window / once with a 2 min grace window), and
event-driven (Gmail/Calendar sync events routed via an LLM classifier with a
second-pass agent decision). Output lives between <!--track-target:ID-->
comment markers that render as editable content in the Tiptap editor so users
can read and extend AI-generated content inline.
Core:
- Schedule and event pipelines run as independent polling loops (15s / 5s),
both calling the same triggerTrackUpdate orchestrator. Events are FIFO via
monotonic IDs; a per-track Set guards against duplicate runs.
- Track-run agent builds three message variants (manual/timed/event) — the
event variant includes a Pass 2 directive to skip updates on false positives
flagged by the liberal Pass 1 router.
- IPC surface: track:run/get/update/replaceYaml/delete plus tracks:events
forward of the pub-sub bus to the renderer.
- Gmail emits per-thread events; Calendar bundles a digest per sync.
Copilot:
- New `tracks` skill (auto-generated canonical schema from Zod via
z.toJSONSchema) teaches block creation, editing, and proactive suggestion.
- `run-track-block` tool with optional `context` parameter for backfills
(e.g. seeding a new email-tracking block from existing synced emails).
Renderer:
- Tiptap chip (display-only) opens a rich modal with tabs, toggle, schedule
details, raw YAML editor, and confirm-to-delete. All mutations go through
IPC so the backend stays the single writer.
- Target regions use two atom marker nodes (open/close) around real editable
content — custom blocks render natively, users can add their own notes.
- "Edit with Copilot" seeds a chat session with the note attached.
Docs: apps/x/TRACKS.md covers product flows, technical pipeline, and a
catalog of every LLM prompt involved with file+line pointers.
2026-04-14 13:51:45 +05:30
|
|
|
filePath: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.boolean(),
|
feat: tracks — frontmatter directives, sidebar UI, multi-trigger
Recasts the old "track blocks" as "tracks" — directives stored in a
note's frontmatter rather than inline YAML fences and HTML-comment
target regions. The motivation is UX: the inline anatomy made notes
feel like config, leaked into the editing surface, and competed with
the writing flow. Frontmatter is invisible to the body editor, so
moving directives there reclaims the body as just markdown the user
wrote.
The runtime agent now edits the note body freely via standard
workspace tools rather than rewriting a constrained target region.
Each track's instruction names an H2 section to own; the agent
finds or creates that section, updates only its content, and
self-heals position on subsequent runs.
Triggers are now a unified array per track. cron / window / once /
event in any combination, including multi-trigger setups (the
flagship example: a priorities track that rebuilds at three
day-windows and reacts to incoming gmail / calendar events).
window is forgiving — fires once per day anywhere inside its
band — so users opening the app late in the morning still get the
morning run.
The chip-in-editor is gone. Tracks are managed from a right-side
sidebar opened by a Radio-icon button at the top-right of the
editor toolbar. Cmd+K is no longer a Copilot entry point — search-
only — pending a more intuitive invocation surface later.
Today.md ships as the flagship demo of what tracks can do, with a
versioned migration system so future template updates roll out
cleanly to existing users (existing body preserved, old version
backed up).
Copilot is tuned to listen for any signal that the user wants
something dynamic — not just the literal word "track". Strong
phrasings get acted on directly; one-off questions about decaying
information are answered first and then offered as a track. New or
edited tracks run once by default so the user immediately sees
content.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:00:20 +05:30
|
|
|
// Fresh, authoritative YAML of the track from frontmatter.
|
|
|
|
|
// Renderer should use this for display/edit — never a stale cached copy.
|
Add tracks — auto-updating note blocks with scheduled and event-driven triggers
Track blocks are YAML-fenced sections embedded in markdown notes whose output
is rewritten by a background agent. Three trigger types: manual (Run button or
Copilot), scheduled (cron / window / once with a 2 min grace window), and
event-driven (Gmail/Calendar sync events routed via an LLM classifier with a
second-pass agent decision). Output lives between <!--track-target:ID-->
comment markers that render as editable content in the Tiptap editor so users
can read and extend AI-generated content inline.
Core:
- Schedule and event pipelines run as independent polling loops (15s / 5s),
both calling the same triggerTrackUpdate orchestrator. Events are FIFO via
monotonic IDs; a per-track Set guards against duplicate runs.
- Track-run agent builds three message variants (manual/timed/event) — the
event variant includes a Pass 2 directive to skip updates on false positives
flagged by the liberal Pass 1 router.
- IPC surface: track:run/get/update/replaceYaml/delete plus tracks:events
forward of the pub-sub bus to the renderer.
- Gmail emits per-thread events; Calendar bundles a digest per sync.
Copilot:
- New `tracks` skill (auto-generated canonical schema from Zod via
z.toJSONSchema) teaches block creation, editing, and proactive suggestion.
- `run-track-block` tool with optional `context` parameter for backfills
(e.g. seeding a new email-tracking block from existing synced emails).
Renderer:
- Tiptap chip (display-only) opens a rich modal with tabs, toggle, schedule
details, raw YAML editor, and confirm-to-delete. All mutations go through
IPC so the backend stays the single writer.
- Target regions use two atom marker nodes (open/close) around real editable
content — custom blocks render natively, users can add their own notes.
- "Edit with Copilot" seeds a chat session with the note attached.
Docs: apps/x/TRACKS.md covers product flows, technical pipeline, and a
catalog of every LLM prompt involved with file+line pointers.
2026-04-14 13:51:45 +05:30
|
|
|
yaml: z.string().optional(),
|
|
|
|
|
error: z.string().optional(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'track:update': {
|
|
|
|
|
req: z.object({
|
feat: tracks — frontmatter directives, sidebar UI, multi-trigger
Recasts the old "track blocks" as "tracks" — directives stored in a
note's frontmatter rather than inline YAML fences and HTML-comment
target regions. The motivation is UX: the inline anatomy made notes
feel like config, leaked into the editing surface, and competed with
the writing flow. Frontmatter is invisible to the body editor, so
moving directives there reclaims the body as just markdown the user
wrote.
The runtime agent now edits the note body freely via standard
workspace tools rather than rewriting a constrained target region.
Each track's instruction names an H2 section to own; the agent
finds or creates that section, updates only its content, and
self-heals position on subsequent runs.
Triggers are now a unified array per track. cron / window / once /
event in any combination, including multi-trigger setups (the
flagship example: a priorities track that rebuilds at three
day-windows and reacts to incoming gmail / calendar events).
window is forgiving — fires once per day anywhere inside its
band — so users opening the app late in the morning still get the
morning run.
The chip-in-editor is gone. Tracks are managed from a right-side
sidebar opened by a Radio-icon button at the top-right of the
editor toolbar. Cmd+K is no longer a Copilot entry point — search-
only — pending a more intuitive invocation surface later.
Today.md ships as the flagship demo of what tracks can do, with a
versioned migration system so future template updates roll out
cleanly to existing users (existing body preserved, old version
backed up).
Copilot is tuned to listen for any signal that the user wants
something dynamic — not just the literal word "track". Strong
phrasings get acted on directly; one-off questions about decaying
information are answered first and then offered as a track. New or
edited tracks run once by default so the user immediately sees
content.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:00:20 +05:30
|
|
|
id: z.string(),
|
Add tracks — auto-updating note blocks with scheduled and event-driven triggers
Track blocks are YAML-fenced sections embedded in markdown notes whose output
is rewritten by a background agent. Three trigger types: manual (Run button or
Copilot), scheduled (cron / window / once with a 2 min grace window), and
event-driven (Gmail/Calendar sync events routed via an LLM classifier with a
second-pass agent decision). Output lives between <!--track-target:ID-->
comment markers that render as editable content in the Tiptap editor so users
can read and extend AI-generated content inline.
Core:
- Schedule and event pipelines run as independent polling loops (15s / 5s),
both calling the same triggerTrackUpdate orchestrator. Events are FIFO via
monotonic IDs; a per-track Set guards against duplicate runs.
- Track-run agent builds three message variants (manual/timed/event) — the
event variant includes a Pass 2 directive to skip updates on false positives
flagged by the liberal Pass 1 router.
- IPC surface: track:run/get/update/replaceYaml/delete plus tracks:events
forward of the pub-sub bus to the renderer.
- Gmail emits per-thread events; Calendar bundles a digest per sync.
Copilot:
- New `tracks` skill (auto-generated canonical schema from Zod via
z.toJSONSchema) teaches block creation, editing, and proactive suggestion.
- `run-track-block` tool with optional `context` parameter for backfills
(e.g. seeding a new email-tracking block from existing synced emails).
Renderer:
- Tiptap chip (display-only) opens a rich modal with tabs, toggle, schedule
details, raw YAML editor, and confirm-to-delete. All mutations go through
IPC so the backend stays the single writer.
- Target regions use two atom marker nodes (open/close) around real editable
content — custom blocks render natively, users can add their own notes.
- "Edit with Copilot" seeds a chat session with the note attached.
Docs: apps/x/TRACKS.md covers product flows, technical pipeline, and a
catalog of every LLM prompt involved with file+line pointers.
2026-04-14 13:51:45 +05:30
|
|
|
filePath: z.string(),
|
feat: tracks — frontmatter directives, sidebar UI, multi-trigger
Recasts the old "track blocks" as "tracks" — directives stored in a
note's frontmatter rather than inline YAML fences and HTML-comment
target regions. The motivation is UX: the inline anatomy made notes
feel like config, leaked into the editing surface, and competed with
the writing flow. Frontmatter is invisible to the body editor, so
moving directives there reclaims the body as just markdown the user
wrote.
The runtime agent now edits the note body freely via standard
workspace tools rather than rewriting a constrained target region.
Each track's instruction names an H2 section to own; the agent
finds or creates that section, updates only its content, and
self-heals position on subsequent runs.
Triggers are now a unified array per track. cron / window / once /
event in any combination, including multi-trigger setups (the
flagship example: a priorities track that rebuilds at three
day-windows and reacts to incoming gmail / calendar events).
window is forgiving — fires once per day anywhere inside its
band — so users opening the app late in the morning still get the
morning run.
The chip-in-editor is gone. Tracks are managed from a right-side
sidebar opened by a Radio-icon button at the top-right of the
editor toolbar. Cmd+K is no longer a Copilot entry point — search-
only — pending a more intuitive invocation surface later.
Today.md ships as the flagship demo of what tracks can do, with a
versioned migration system so future template updates roll out
cleanly to existing users (existing body preserved, old version
backed up).
Copilot is tuned to listen for any signal that the user wants
something dynamic — not just the literal word "track". Strong
phrasings get acted on directly; one-off questions about decaying
information are answered first and then offered as a track. New or
edited tracks run once by default so the user immediately sees
content.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:00:20 +05:30
|
|
|
// Partial Track updates — merged into the entry on disk.
|
Add tracks — auto-updating note blocks with scheduled and event-driven triggers
Track blocks are YAML-fenced sections embedded in markdown notes whose output
is rewritten by a background agent. Three trigger types: manual (Run button or
Copilot), scheduled (cron / window / once with a 2 min grace window), and
event-driven (Gmail/Calendar sync events routed via an LLM classifier with a
second-pass agent decision). Output lives between <!--track-target:ID-->
comment markers that render as editable content in the Tiptap editor so users
can read and extend AI-generated content inline.
Core:
- Schedule and event pipelines run as independent polling loops (15s / 5s),
both calling the same triggerTrackUpdate orchestrator. Events are FIFO via
monotonic IDs; a per-track Set guards against duplicate runs.
- Track-run agent builds three message variants (manual/timed/event) — the
event variant includes a Pass 2 directive to skip updates on false positives
flagged by the liberal Pass 1 router.
- IPC surface: track:run/get/update/replaceYaml/delete plus tracks:events
forward of the pub-sub bus to the renderer.
- Gmail emits per-thread events; Calendar bundles a digest per sync.
Copilot:
- New `tracks` skill (auto-generated canonical schema from Zod via
z.toJSONSchema) teaches block creation, editing, and proactive suggestion.
- `run-track-block` tool with optional `context` parameter for backfills
(e.g. seeding a new email-tracking block from existing synced emails).
Renderer:
- Tiptap chip (display-only) opens a rich modal with tabs, toggle, schedule
details, raw YAML editor, and confirm-to-delete. All mutations go through
IPC so the backend stays the single writer.
- Target regions use two atom marker nodes (open/close) around real editable
content — custom blocks render natively, users can add their own notes.
- "Edit with Copilot" seeds a chat session with the note attached.
Docs: apps/x/TRACKS.md covers product flows, technical pipeline, and a
catalog of every LLM prompt involved with file+line pointers.
2026-04-14 13:51:45 +05:30
|
|
|
// Backend is the sole writer; avoids races with scheduler/runner writes.
|
|
|
|
|
updates: z.record(z.string(), z.unknown()),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.boolean(),
|
|
|
|
|
yaml: z.string().optional(),
|
|
|
|
|
error: z.string().optional(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'track:replaceYaml': {
|
|
|
|
|
req: z.object({
|
feat: tracks — frontmatter directives, sidebar UI, multi-trigger
Recasts the old "track blocks" as "tracks" — directives stored in a
note's frontmatter rather than inline YAML fences and HTML-comment
target regions. The motivation is UX: the inline anatomy made notes
feel like config, leaked into the editing surface, and competed with
the writing flow. Frontmatter is invisible to the body editor, so
moving directives there reclaims the body as just markdown the user
wrote.
The runtime agent now edits the note body freely via standard
workspace tools rather than rewriting a constrained target region.
Each track's instruction names an H2 section to own; the agent
finds or creates that section, updates only its content, and
self-heals position on subsequent runs.
Triggers are now a unified array per track. cron / window / once /
event in any combination, including multi-trigger setups (the
flagship example: a priorities track that rebuilds at three
day-windows and reacts to incoming gmail / calendar events).
window is forgiving — fires once per day anywhere inside its
band — so users opening the app late in the morning still get the
morning run.
The chip-in-editor is gone. Tracks are managed from a right-side
sidebar opened by a Radio-icon button at the top-right of the
editor toolbar. Cmd+K is no longer a Copilot entry point — search-
only — pending a more intuitive invocation surface later.
Today.md ships as the flagship demo of what tracks can do, with a
versioned migration system so future template updates roll out
cleanly to existing users (existing body preserved, old version
backed up).
Copilot is tuned to listen for any signal that the user wants
something dynamic — not just the literal word "track". Strong
phrasings get acted on directly; one-off questions about decaying
information are answered first and then offered as a track. New or
edited tracks run once by default so the user immediately sees
content.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:00:20 +05:30
|
|
|
id: z.string(),
|
Add tracks — auto-updating note blocks with scheduled and event-driven triggers
Track blocks are YAML-fenced sections embedded in markdown notes whose output
is rewritten by a background agent. Three trigger types: manual (Run button or
Copilot), scheduled (cron / window / once with a 2 min grace window), and
event-driven (Gmail/Calendar sync events routed via an LLM classifier with a
second-pass agent decision). Output lives between <!--track-target:ID-->
comment markers that render as editable content in the Tiptap editor so users
can read and extend AI-generated content inline.
Core:
- Schedule and event pipelines run as independent polling loops (15s / 5s),
both calling the same triggerTrackUpdate orchestrator. Events are FIFO via
monotonic IDs; a per-track Set guards against duplicate runs.
- Track-run agent builds three message variants (manual/timed/event) — the
event variant includes a Pass 2 directive to skip updates on false positives
flagged by the liberal Pass 1 router.
- IPC surface: track:run/get/update/replaceYaml/delete plus tracks:events
forward of the pub-sub bus to the renderer.
- Gmail emits per-thread events; Calendar bundles a digest per sync.
Copilot:
- New `tracks` skill (auto-generated canonical schema from Zod via
z.toJSONSchema) teaches block creation, editing, and proactive suggestion.
- `run-track-block` tool with optional `context` parameter for backfills
(e.g. seeding a new email-tracking block from existing synced emails).
Renderer:
- Tiptap chip (display-only) opens a rich modal with tabs, toggle, schedule
details, raw YAML editor, and confirm-to-delete. All mutations go through
IPC so the backend stays the single writer.
- Target regions use two atom marker nodes (open/close) around real editable
content — custom blocks render natively, users can add their own notes.
- "Edit with Copilot" seeds a chat session with the note attached.
Docs: apps/x/TRACKS.md covers product flows, technical pipeline, and a
catalog of every LLM prompt involved with file+line pointers.
2026-04-14 13:51:45 +05:30
|
|
|
filePath: z.string(),
|
|
|
|
|
yaml: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.boolean(),
|
|
|
|
|
yaml: z.string().optional(),
|
|
|
|
|
error: z.string().optional(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'track:delete': {
|
|
|
|
|
req: z.object({
|
feat: tracks — frontmatter directives, sidebar UI, multi-trigger
Recasts the old "track blocks" as "tracks" — directives stored in a
note's frontmatter rather than inline YAML fences and HTML-comment
target regions. The motivation is UX: the inline anatomy made notes
feel like config, leaked into the editing surface, and competed with
the writing flow. Frontmatter is invisible to the body editor, so
moving directives there reclaims the body as just markdown the user
wrote.
The runtime agent now edits the note body freely via standard
workspace tools rather than rewriting a constrained target region.
Each track's instruction names an H2 section to own; the agent
finds or creates that section, updates only its content, and
self-heals position on subsequent runs.
Triggers are now a unified array per track. cron / window / once /
event in any combination, including multi-trigger setups (the
flagship example: a priorities track that rebuilds at three
day-windows and reacts to incoming gmail / calendar events).
window is forgiving — fires once per day anywhere inside its
band — so users opening the app late in the morning still get the
morning run.
The chip-in-editor is gone. Tracks are managed from a right-side
sidebar opened by a Radio-icon button at the top-right of the
editor toolbar. Cmd+K is no longer a Copilot entry point — search-
only — pending a more intuitive invocation surface later.
Today.md ships as the flagship demo of what tracks can do, with a
versioned migration system so future template updates roll out
cleanly to existing users (existing body preserved, old version
backed up).
Copilot is tuned to listen for any signal that the user wants
something dynamic — not just the literal word "track". Strong
phrasings get acted on directly; one-off questions about decaying
information are answered first and then offered as a track. New or
edited tracks run once by default so the user immediately sees
content.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:00:20 +05:30
|
|
|
id: z.string(),
|
Add tracks — auto-updating note blocks with scheduled and event-driven triggers
Track blocks are YAML-fenced sections embedded in markdown notes whose output
is rewritten by a background agent. Three trigger types: manual (Run button or
Copilot), scheduled (cron / window / once with a 2 min grace window), and
event-driven (Gmail/Calendar sync events routed via an LLM classifier with a
second-pass agent decision). Output lives between <!--track-target:ID-->
comment markers that render as editable content in the Tiptap editor so users
can read and extend AI-generated content inline.
Core:
- Schedule and event pipelines run as independent polling loops (15s / 5s),
both calling the same triggerTrackUpdate orchestrator. Events are FIFO via
monotonic IDs; a per-track Set guards against duplicate runs.
- Track-run agent builds three message variants (manual/timed/event) — the
event variant includes a Pass 2 directive to skip updates on false positives
flagged by the liberal Pass 1 router.
- IPC surface: track:run/get/update/replaceYaml/delete plus tracks:events
forward of the pub-sub bus to the renderer.
- Gmail emits per-thread events; Calendar bundles a digest per sync.
Copilot:
- New `tracks` skill (auto-generated canonical schema from Zod via
z.toJSONSchema) teaches block creation, editing, and proactive suggestion.
- `run-track-block` tool with optional `context` parameter for backfills
(e.g. seeding a new email-tracking block from existing synced emails).
Renderer:
- Tiptap chip (display-only) opens a rich modal with tabs, toggle, schedule
details, raw YAML editor, and confirm-to-delete. All mutations go through
IPC so the backend stays the single writer.
- Target regions use two atom marker nodes (open/close) around real editable
content — custom blocks render natively, users can add their own notes.
- "Edit with Copilot" seeds a chat session with the note attached.
Docs: apps/x/TRACKS.md covers product flows, technical pipeline, and a
catalog of every LLM prompt involved with file+line pointers.
2026-04-14 13:51:45 +05:30
|
|
|
filePath: z.string(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.boolean(),
|
|
|
|
|
error: z.string().optional(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-05-06 11:59:37 +05:30
|
|
|
'track:setNoteActive': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
path: RelPath,
|
|
|
|
|
active: z.boolean(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
success: z.boolean(),
|
|
|
|
|
note: z.object({
|
|
|
|
|
path: RelPath,
|
|
|
|
|
trackCount: z.number().int().positive(),
|
|
|
|
|
createdAt: z.string().nullable(),
|
|
|
|
|
lastRunAt: z.string().nullable(),
|
|
|
|
|
isActive: z.boolean(),
|
|
|
|
|
}).optional(),
|
|
|
|
|
error: z.string().optional(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'track:listNotes': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
|
|
|
|
notes: z.array(z.object({
|
|
|
|
|
path: RelPath,
|
|
|
|
|
trackCount: z.number().int().positive(),
|
|
|
|
|
createdAt: z.string().nullable(),
|
|
|
|
|
lastRunAt: z.string().nullable(),
|
|
|
|
|
isActive: z.boolean(),
|
|
|
|
|
})),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2026-04-15 13:21:09 +05:30
|
|
|
// Embedded browser (WebContentsView) channels
|
|
|
|
|
'browser:setBounds': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
x: z.number().int(),
|
|
|
|
|
y: z.number().int(),
|
|
|
|
|
width: z.number().int().nonnegative(),
|
|
|
|
|
height: z.number().int().nonnegative(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({ ok: z.literal(true) }),
|
|
|
|
|
},
|
|
|
|
|
'browser:setVisible': {
|
|
|
|
|
req: z.object({ visible: z.boolean() }),
|
|
|
|
|
res: z.object({ ok: z.literal(true) }),
|
|
|
|
|
},
|
|
|
|
|
'browser:newTab': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
url: z.string().min(1).refine(
|
|
|
|
|
(u) => {
|
|
|
|
|
const lower = u.trim().toLowerCase();
|
|
|
|
|
if (lower.startsWith('javascript:')) return false;
|
|
|
|
|
if (lower.startsWith('file://')) return false;
|
|
|
|
|
if (lower.startsWith('chrome://')) return false;
|
|
|
|
|
if (lower.startsWith('chrome-extension://')) return false;
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
{ message: 'Unsafe URL scheme' },
|
|
|
|
|
).optional(),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
ok: z.boolean(),
|
|
|
|
|
tabId: z.string().optional(),
|
|
|
|
|
error: z.string().optional(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'browser:switchTab': {
|
|
|
|
|
req: z.object({ tabId: z.string().min(1) }),
|
|
|
|
|
res: z.object({ ok: z.boolean() }),
|
|
|
|
|
},
|
|
|
|
|
'browser:closeTab': {
|
|
|
|
|
req: z.object({ tabId: z.string().min(1) }),
|
|
|
|
|
res: z.object({ ok: z.boolean() }),
|
|
|
|
|
},
|
|
|
|
|
'browser:navigate': {
|
|
|
|
|
req: z.object({
|
|
|
|
|
url: z.string().min(1).refine(
|
|
|
|
|
(u) => {
|
|
|
|
|
const lower = u.trim().toLowerCase();
|
|
|
|
|
if (lower.startsWith('javascript:')) return false;
|
|
|
|
|
if (lower.startsWith('file://')) return false;
|
|
|
|
|
if (lower.startsWith('chrome://')) return false;
|
|
|
|
|
if (lower.startsWith('chrome-extension://')) return false;
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
{ message: 'Unsafe URL scheme' },
|
|
|
|
|
),
|
|
|
|
|
}),
|
|
|
|
|
res: z.object({
|
|
|
|
|
ok: z.boolean(),
|
|
|
|
|
error: z.string().optional(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
'browser:back': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({ ok: z.boolean() }),
|
|
|
|
|
},
|
|
|
|
|
'browser:forward': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({ ok: z.boolean() }),
|
|
|
|
|
},
|
|
|
|
|
'browser:reload': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({ ok: z.literal(true) }),
|
|
|
|
|
},
|
|
|
|
|
'browser:getState': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: BrowserStateSchema,
|
|
|
|
|
},
|
|
|
|
|
'browser:didUpdateState': {
|
|
|
|
|
req: BrowserStateSchema,
|
|
|
|
|
res: z.null(),
|
|
|
|
|
},
|
2026-03-17 12:04:57 +05:30
|
|
|
// Billing channels
|
|
|
|
|
'billing:getInfo': {
|
|
|
|
|
req: z.null(),
|
|
|
|
|
res: z.object({
|
2026-03-18 16:15:02 +05:30
|
|
|
userEmail: z.string().nullable(),
|
|
|
|
|
userId: z.string().nullable(),
|
2026-03-17 12:04:57 +05:30
|
|
|
subscriptionPlan: z.string().nullable(),
|
|
|
|
|
subscriptionStatus: z.string().nullable(),
|
2026-04-07 21:51:17 +05:30
|
|
|
trialExpiresAt: z.string().nullable(),
|
2026-03-17 12:04:57 +05:30
|
|
|
sanctionedCredits: z.number(),
|
|
|
|
|
availableCredits: z.number(),
|
|
|
|
|
}),
|
|
|
|
|
},
|
2025-12-29 15:30:57 +05:30
|
|
|
} as const;
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Type Helpers
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
export type IPCChannels = {
|
|
|
|
|
[K in keyof typeof ipcSchemas]: {
|
|
|
|
|
req: z.infer<typeof ipcSchemas[K]['req']>;
|
|
|
|
|
res: z.infer<typeof ipcSchemas[K]['res']>;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Channels that use invoke/handle (request/response pattern)
|
|
|
|
|
* These are channels with non-null responses
|
|
|
|
|
*/
|
|
|
|
|
export type InvokeChannels = {
|
|
|
|
|
[K in keyof IPCChannels]:
|
|
|
|
|
IPCChannels[K]['res'] extends null ? never : K
|
|
|
|
|
}[keyof IPCChannels];
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Channels that use send/on (fire-and-forget pattern)
|
|
|
|
|
* These are channels with null responses (no response expected)
|
|
|
|
|
*/
|
|
|
|
|
export type SendChannels = {
|
|
|
|
|
[K in keyof IPCChannels]:
|
|
|
|
|
IPCChannels[K]['res'] extends null ? K : never
|
|
|
|
|
}[keyof IPCChannels];
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Type Guards
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
export function validateRequest<K extends keyof IPCChannels>(
|
|
|
|
|
channel: K,
|
|
|
|
|
data: unknown
|
|
|
|
|
): IPCChannels[K]['req'] {
|
|
|
|
|
const schema = ipcSchemas[channel].req;
|
|
|
|
|
return schema.parse(data) as IPCChannels[K]['req'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function validateResponse<K extends keyof IPCChannels>(
|
|
|
|
|
channel: K,
|
|
|
|
|
data: unknown
|
|
|
|
|
): IPCChannels[K]['res'] {
|
|
|
|
|
const schema = ipcSchemas[channel].res;
|
|
|
|
|
return schema.parse(data) as IPCChannels[K]['res'];
|
2026-02-04 01:12:06 +05:30
|
|
|
}
|