Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src-tauri/context-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@
"detail": "# brains — working with the tools\n\n**Use the cheapest useful read**: `list_pages` for recents, `search` for\nexact terms, `query` for conceptual questions, `get_page` only once a\nresult handed you a slug. Chain dependent reads; don't fan them out.\nCache `whoami` and `list_integrations` for the session.\n\n**Calendar is its own tool.** For schedules and agendas use\n`list_calendar_events start=… end=…` — never `list_pages` filtered by\ntype: a calendar page's update time is not the event's time.\n\n**Reads are free; writes are not.** A write through an integration needs\nthe user's approval of that specific action, every time.",
"detailPath": "src/engines/brains/prompts/brains-context.md"
},
{
"name": "docs",
"dir": "src/apps/docs",
"index": "Working with the user's open Google Doc: reading it, editing wording, drafting content, changing formatting — all through the brains Drive integration, never by typing in the page.",
"detail": "# docs\n\nThe document open beside this conversation is a **live Google Doc**, and it\nlives in the user's Google Drive — not in a brains board. When it is open you\ncan **read** it (the browser reports the page, and any text the user selected\ncomes through in the context block), but you **cannot type into it**: the page\nis a native browser view you have no write access to. Every change you make\ngoes through brains — the `gdrive-files` integration — and Google re-renders\nthe doc. There is no other way to edit it, and you never \"type\" into the page.\n\n**Which doc.** The target is the open Doc's `file_id` — the `/document/d/<id>/`\nsegment of its URL. If no Doc is open (no such URL), no document is open yet:\nsay so and ask the user to open one, rather than guessing. Find the\n`gdrive-files` install once via `list_my_integrations`, then call its actions\nthrough `act_on_integration`.\n\n**Reading.** `read_document` returns the body — `format: \"markdown\"` (default)\nfor the text, or `format: \"html\"` when a later edit must preserve exact\nformatting and colour. The body is capped at ~64 KB: an oversize markdown read\nis trimmed (`oversize: true` — a partial body, never write it back), and an\noversize html read is refused outright.\n\n**Editing.** Pick the smallest tool for the change:\n\n- `edit_doc_text` — surgical find → replace on the existing text, leaving\n surrounding formatting untouched. This is the default for fixing or\n rewording existing content. Give whole phrases, and set\n `expected_occurrences` when a find hits more than one place.\n- `write_document` with `mode: \"append\"` — adds plain text at the end,\n non-destructive (not markdown-rendered).\n- `write_document` with `mode: \"replace\"` — rewrites the **entire** body from\n `content_markdown` or `content_html`. This is a full-body flatten: any\n formatting not in your payload is lost (recoverable only from version\n history). Use it only when genuinely rewriting the whole document.\n- Colour and styling that markdown can't express only come through\n `content_html` on a replace. There is **no surgical colour primitive**: to\n recolour one heading you would have to read the full html and write it all\n back, which the 64 KB cap blocks on any real doc. When that's the request,\n say the integration can't do it surgically yet rather than flattening the doc.\n\n**Writes are approval-gated.** A write does not happen when you call it — it is\nheld for the user's approval and only then executed. **Never say the document\nwas changed until the approval has actually gone through.** Once it has,\nre-read the doc and verify the change landed before you report it as done.\n\n**Preserve what's there.** Keep every fact, number, claim, link, and the\nexisting formatting; make the smallest coherent edit to the one open document,\nand never rewrite a different doc or invent content the user didn't ask for.\nNothing may depend on a doc being open — when none is, say what you can see and\nask one question rather than acting.",
"detailPath": "src/apps/docs/context.md"
},
{
"name": "exo",
"dir": "src/apps/exo",
Expand Down
Binary file added src-tauri/recall-runtime/desktop-sdk.tar.gz
Binary file not shown.
7 changes: 7 additions & 0 deletions src-tauri/recall-runtime/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"version": "2.0.26",
"commit_sha": "482cad45d71ed45aa5f111f4a7b5a56ec0fc1362",
"sha256": "1d7cd142646dd7ea1f0ae23ece9778fb98f4694df3b79537a5579e55f970c8d8",
"platform": "darwin",
"arch": "arm64"
}
12 changes: 11 additions & 1 deletion src-tauri/src/browser_host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use tauri_plugin_shell::ShellExt;

use brains_browser::host::{Host, WindowPtr};

use crate::commands::browser::{BrowserReport, REPORT_EVENT};
use crate::commands::browser::{BrowserNavigated, BrowserReport, NAVIGATED_EVENT, REPORT_EVENT};

/// Start the bundled engine, or say why not. Called once, from `setup`.
///
Expand Down Expand Up @@ -81,6 +81,16 @@ impl Host for TauriHost {
);
}

fn emit_navigated(&self, key: &str, url: &str) {
let _ = self.app.emit(
NAVIGATED_EVENT,
BrowserNavigated {
key: key.to_string(),
url: url.to_string(),
},
);
}

fn open_externally(&self, url: &str) {
// Deprecated in favour of tauri-plugin-opener, taken the same way
// `commands/brains.rs` and the OS-webview backend take it: the shell plugin is
Expand Down
12 changes: 12 additions & 0 deletions src-tauri/src/commands/browser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@ pub struct BrowserReport {
pub payload: String,
}

/// The event a view emits when its main frame navigates. The opening app maps
/// the URL to meaning (Docs: list vs an open document) — the engine only says
/// which view went where.
pub const NAVIGATED_EVENT: &str = "browser-navigated";

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BrowserNavigated {
pub key: String,
pub url: String,
}

/// The window-unique name for a tab's view.
///
/// Prefixed because a webview label is a namespace shared with the app's own
Expand Down
28 changes: 26 additions & 2 deletions src/apps/docs/DocsTab.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
import { workspaceTabs } from "$core/runtime/stores/workspace-tabs.svelte";
import { BrowserPanel } from "$panes/canvas";
import { browserAvailability } from "$panes/canvas/browser-driver";
import { browserUrl } from "$panes/canvas/browser-navigation.svelte";
import type { Availability } from "$engines/browser/client";
import { DOCS_PROFILE, DOCS_URL } from "./site";
import type { ContentPort, SkillContent } from "$panes/session/content-port";
import DocsMock from "./mock/DocsMock.svelte";
import DocsIntro from "./mock/DocsIntro.svelte";
import { ensureDocsState, getDocsState, setDocsTitle, setDocsContent } from "./state.svelte";
import { docsExtraSkills } from "./skills";

const tab = $derived(workspaceTabs.activeTab);
const tabId = $derived(tab?.id ?? "");
Expand Down Expand Up @@ -54,14 +56,35 @@
if (tabId) setDocsContent(tabId, newContent);
}

// SCOPE = which Actions set shows, and it is CONTEXT: the docs list/home is
// the MAIN screen ("docs-home", cross-doc skills like "Summarize recent
// work"); an open `/document/d/<id>/` is a specific document ("doc"). In live
// mode we read the view's current URL (pushed by the engine, see
// browser-navigation) and parse the file id from it; in mock mode content
// presence stands in for "a document is open".
const liveUrl = $derived(live ? browserUrl(tabId) : undefined);
const openDocId = $derived(liveUrl?.match(/\/document\/d\/([A-Za-z0-9_-]+)/)?.[1] ?? null);
const docScope = $derived(
live ? (openDocId ? "doc" : "docs-home") : content.trim() ? "doc" : "docs-home",
);

const scopeChips = $derived([{ label: title || "Untitled document", icon: "docs" as const }]);
const docScope = $derived(content.trim() ? "doc" : "blank");

const contentPort: ContentPort = {
async read(): Promise<SkillContent> {
if (live) {
// The skill acts on the real Doc through gdrive-files, not on any local
// text — so what it needs from here is the file id, carried as metadata.
return {
scope: openDocId ? "doc" : "docs-home",
text: "",
title: title || "Untitled document",
metadata: openDocId ? { file_id: openDocId } : undefined,
};
}
const s = tabId ? getDocsState(tabId) : null;
return {
scope: (s?.content ?? "").trim() ? "doc" : "blank",
scope: (s?.content ?? "").trim() ? "doc" : "docs-home",
text: s?.content ?? "",
title: s?.title ?? "Untitled document",
};
Expand Down Expand Up @@ -95,6 +118,7 @@
room: "workstation",
scope: docScope,
contentPort,
skills: docsExtraSkills(docScope),
}}
{scopeChips}
/>
5 changes: 5 additions & 0 deletions src/apps/docs/context.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "docs",
"index": "Working with the user's open Google Doc: reading it, editing wording, drafting content, changing formatting — all through the brains Drive integration, never by typing in the page.",
"detail": "context.md"
}
50 changes: 50 additions & 0 deletions src/apps/docs/context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# docs

The document open beside this conversation is a **live Google Doc**, and it
lives in the user's Google Drive — not in a brains board. When it is open you
can **read** it (the browser reports the page, and any text the user selected
comes through in the context block), but you **cannot type into it**: the page
is a native browser view you have no write access to. Every change you make
goes through brains — the `gdrive-files` integration — and Google re-renders
the doc. There is no other way to edit it, and you never "type" into the page.

**Which doc.** The target is the open Doc's `file_id` — the `/document/d/<id>/`
segment of its URL. If no Doc is open (no such URL), no document is open yet:
say so and ask the user to open one, rather than guessing. Find the
`gdrive-files` install once via `list_my_integrations`, then call its actions
through `act_on_integration`.

**Reading.** `read_document` returns the body — `format: "markdown"` (default)
for the text, or `format: "html"` when a later edit must preserve exact
formatting and colour. The body is capped at ~64 KB: an oversize markdown read
is trimmed (`oversize: true` — a partial body, never write it back), and an
oversize html read is refused outright.

**Editing.** Pick the smallest tool for the change:

- `edit_doc_text` — surgical find → replace on the existing text, leaving
surrounding formatting untouched. This is the default for fixing or
rewording existing content. Give whole phrases, and set
`expected_occurrences` when a find hits more than one place.
- `write_document` with `mode: "append"` — adds plain text at the end,
non-destructive (not markdown-rendered).
- `write_document` with `mode: "replace"` — rewrites the **entire** body from
`content_markdown` or `content_html`. This is a full-body flatten: any
formatting not in your payload is lost (recoverable only from version
history). Use it only when genuinely rewriting the whole document.
- Colour and styling that markdown can't express only come through
`content_html` on a replace. There is **no surgical colour primitive**: to
recolour one heading you would have to read the full html and write it all
back, which the 64 KB cap blocks on any real doc. When that's the request,
say the integration can't do it surgically yet rather than flattening the doc.

**Writes are approval-gated.** A write does not happen when you call it — it is
held for the user's approval and only then executed. **Never say the document
was changed until the approval has actually gone through.** Once it has,
re-read the doc and verify the change landed before you report it as done.

**Preserve what's there.** Keep every fact, number, claim, link, and the
existing formatting; make the smallest coherent edit to the one open document,
and never rewrite a different doc or invent content the user didn't ask for.
Nothing may depend on a doc being open — when none is, say what you can see and
ask one question rather than acting.
Loading
Loading