v0.9.6: documents, downloads, PDFs and the phone's own files
Four tools and a document writer, plus the storage half of the phone: until now the picker's answer (a content:// URI) reached the engine as a path, and it said "cannot read" -- the app had no way to hand a phone's file over. - md_to_html (new, src/tools/web.c + cst_web_md_document()): markdown in, one self-contained HTML document out -- doctype, utf-8, viewport, escaped title, a small stylesheet; no script, no external request. It is written into the workspace and delivered as a file card (for_user, so it never enters a request), the way deliver_file does. The default name comes from the source file's stem or, for pasted markdown, from the title with what a path cannot carry turned into '-'. It has a preview, so an approval prompt says "render x -> y.html" before anything is written. - download (src/tools/web.c): an http(s) URL into a file in the workspace, streamed with a 128 MB ceiling; a redirect chain, the same CA handling and the same refusals as web_fetch, but the bytes stay on disk (a half file is removed on any failure, including HTTP >= 400). The name comes from the URL when the caller names only a directory. - read_pdf (src/tools/fs_tools.c): the text layer and page count of a PDF, which read_file refuses because the bytes are binary -- and a PDF is exactly what arrives from a phone. Same extractor an attachment uses, its own 4 MiB input ceiling, 256 KiB of text by default with a note when cut. - session_search (new, src/tools/session_tools.c): every stored session -- this one and all the ones before it -- searched for a substring, reporting the session's id, message count and title, then the matching lines with their number and role. Case-insensitive by default, capped at 20 matches. - device_info (same file, perm_name "device"): version, system/arch/CPU count, workspace, sandbox, model and permissions, config/temp/program directories, pid -- so the model stops guessing where it runs. - Tools 3 and 4 come with tests/test_tools_ext.c (also in both build files): the document is a whole page, a script tag and a javascript: link do not survive, a hostile title stays text and cannot write outside the workspace, the preview names the file, read_pdf/download refuse what they must (no network in a test), session_search finds a fixture session and says so when nothing matches, device_info names the workspace. README's tool table grew five rows. - Android storage: the picker (SAF) answers with a content:// URI, and the engine opens files by path, so the app now copies what it points at into its own directory first -- app/android/src/xyz/whcself/cstagent/ AttachFile.java, called through a new app::materializeAttachment() (ToolProvider.h: one API for every platform; a desktop strips file:// and refuses content:// with a reason). The copy needs no permission at all: the grant travels with the URI. - "All files access" (MANAGE_EXTERNAL_STORAGE) is the other half: it is what lets the engine read a path in shared storage that a task names rather than picks. Android has no runtime dialog for it, so main.cpp opens the one Settings screen once (AttachFile.java's requestAllFilesAccess, with the all-apps screen as a fallback) and never insists: without it everything works, one picked file at a time. On the NX789J the screen came up, the app-op reads back "allow", and a relaunch no longer asks. - Composer.qml hands a content:// URI through untouched (it is not a path, and decoding it would corrupt it); AgentCore::attachFile() turns whatever the picker gave into a path first. Desktop: build clean, make check 36/36 (the new test included, also under ASan), --uitest all green, --selftest and cst-app-selftest OK. The APK builds and installs (86 MB, release-signed) with the new Java class in its dex. Version 0.9.6.
This commit is contained in:
@@ -166,6 +166,53 @@ static const cst_tool_def_t TOOLS[] = {
|
||||
{{"url", S, 1, "http:// or https:// URL"},
|
||||
{"max_bytes", I, 0, "maximum bytes to return (default 65536)"}},
|
||||
CST_TP_NET, 0, cst_tool_web_fetch, NULL, NO_DYN, NO_META},
|
||||
{"download",
|
||||
"Fetch an http(s) URL into the workspace as a file (up to 128 MB) — a "
|
||||
"release, a dataset, an image, a PDF. The bytes stay on disk: this "
|
||||
"answer only says what was saved. Use web_fetch when you want to read "
|
||||
"the content yourself.",
|
||||
"Download an http(s) URL to a file in the workspace.",
|
||||
{{"url", S, 1, "http:// or https:// URL"},
|
||||
{"path", S, 0, "file path (default: the URL's own file name)"},
|
||||
{"max_bytes", I, 0, "give up above this size (default 128 MB)"}},
|
||||
CST_TP_NET, 0, cst_tool_download, NULL, NO_DYN, NO_META},
|
||||
{"read_pdf",
|
||||
"Read a PDF's text layer, page count and all — what read_file refuses "
|
||||
"because the bytes are binary. Layout, tables and images are not "
|
||||
"reproduced.",
|
||||
"Read the text of a PDF file in the workspace.",
|
||||
{{"path", S, 1, "PDF file path relative to the workspace"},
|
||||
{"max_bytes", I, 0, "cap on the returned text (default 256 KiB)"}},
|
||||
CST_TP_READ, 0, cst_tool_read_pdf, NULL, NO_DYN, NO_META},
|
||||
{"md_to_html",
|
||||
"Write a document: markdown in, one self-contained HTML file out "
|
||||
"(the engine's own renderer — no script, safe to open anywhere), saved "
|
||||
"in the workspace and handed to the person as a card they can open or "
|
||||
"share. Use it when the answer should be a document to keep.",
|
||||
"Render markdown into a standalone HTML document and hand it over.",
|
||||
{{"markdown", S, 0, "markdown text to render"},
|
||||
{"file", S, 0, "workspace .md file to render instead"},
|
||||
{"out", S, 0, "output path (default: the source's name + .html)"},
|
||||
{"title", S, 0, "document title (default: the file name)"}},
|
||||
CST_TP_WRITE, 0, cst_tool_md_to_html, cst_preview_md_to_html, NO_DYN,
|
||||
NO_META},
|
||||
{"session_search",
|
||||
"Search the workspace's stored sessions (this one and every earlier "
|
||||
"conversation) for a substring and get the matching lines with their "
|
||||
"session, message number and context. Answers \"what did we decide "
|
||||
"before\" without a shell.",
|
||||
"Search earlier and current sessions for text.",
|
||||
{{"query", S, 1, "text to look for"},
|
||||
{"max_matches", I, 0, "stop after this many matches (default 20)"},
|
||||
{"ignore_case", B, 0, "case-insensitive (default true)"}},
|
||||
CST_TP_READ, 0, cst_tool_session_search, NULL, NO_DYN, NO_META},
|
||||
{"device_info",
|
||||
"Where this agent is running: system and CPU count, workspace, sandbox, "
|
||||
"model and permissions, config/temp/program directories, pid. Call it "
|
||||
"instead of guessing the environment.",
|
||||
"Report the machine and session the agent runs on.",
|
||||
{{"section", S, 0, "unused; the whole report is always returned"}},
|
||||
CST_TP_READ, 0, cst_tool_device_info, NULL, NO_DYN, "device", NULL, NULL},
|
||||
{"ask_user",
|
||||
"Ask the user and wait for the answer. Only for decisions that are "
|
||||
"truly theirs (preferences, credentials, destructive choices).",
|
||||
|
||||
Reference in New Issue
Block a user