da0491307173ba1b28f1c057847444a0ec8b5bb2
6
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4e990c8e35 |
docs/android.md: what a turn does when nobody answers, and md_to_pdf
The SAF picker is not 'still to come' any more (v0.9.6 did it): the list keeps the back gesture and the icon glyphs. In its place, what v0.9.7 changed on the phone -- a consent question refused in the background and bounded in the foreground, the bounded GUI hop, the phase in the header, and the window's own md_to_pdf. |
||
|
|
7299f39245 |
v0.9.4: a turn keeps running when the app is not on screen
A phone freezes an app it cannot see, and the front end had nothing holding it: a turn that took minutes -- a long shell command, a provider that thinks, a build -- stopped quietly the moment the user pressed home, and the answer was only there if the window happened to come back. The engine's own /attach flow made this visible: an APK turn survived, a pocket did not. - app/android/src/xyz/whcself/cstagent/CstService.java: a foreground service started with an explicit component (nothing outside the app can reach it, no intent filter) for as long as a turn runs. Being a foreground service makes the process one Android neither freezes nor kills; a partial wake lock (30 min ceiling) keeps the CPU awake with the screen off. Its ongoing notification says what is running -- "CST-agent is working" plus the first line of the prompt -- on the same cst-agent channel the notification_send tool uses, and taps back into the app. A phone that refuses the foreground start (Android 12+ does, from the background) loses the protection, not the turn: the failure is caught and the service stops itself. - Manifest: FOREGROUND_SERVICE, FOREGROUND_SERVICE_DATA_SYNC (API 34+ wants the typed one) and WAKE_LOCK, plus the <service> with foregroundServiceType="dataSync". The first build of this failed with "Expected '>', but got ' '" -- a manifest comment cannot contain "--". - Front end: app::holdForeground(text) / app::releaseForeground() in ToolProvider.h, one call for every platform -- Android starts and stops the service through JNI, a desktop has nothing to hold (its window is the app). AgentCore::send() calls it when the turn starts (firstLine() of the prompt is what the notification says) and finishTurn() releases it, so the notification exists exactly as long as the work does. CST_APP_NO_SERVICE=1 keeps a test run from touching the phone. - On the NX789J, with the app sent home and the screen off: the service read back isForeground=true with its notification, the wake lock cst-agent:turn was taken at the start of the turn and dropped at its end, run_shell "sleep 60" finished with the screen dark, and the service was gone after. Desktop: build clean, --uitest all green, --selftest and cst-app-selftest OK (the hold is a no-op there, so nothing about the Qt side changed). Version 0.9.4. |
||
|
|
d4f65b17cf |
v0.9.3: notification_send posts on the phone, and Java helpers have a home
The tool was the last Android one that never worked on the NX789J: it would fail with "Notification.Builder.build() failed: no exception detail" and the shade stayed empty. The detail was the whole story -- Qt clears a Java exception before C++ gets to look at it, so a build() that throws and a build() that returns null look exactly the same from here, and the only way to catch the difference is to let Java catch it. - app/android/src/xyz/whcself/cstagent/Notifier.java: the builder, the PendingIntent, build() and notify() in one try/catch that returns "" or the throwable's class and message. androiddeployqt copies app/android/ with the rest of the package source and Qt's Gradle template puts its src/ on java.srcDirs, so a class dropped there needs no build-script change (the first one is this; the foreground service of the next step goes there too). - AndroidTools.cpp keeps what it can see from C++ (the context, the channel on API 26+, the small icon looked up by name, the launch intent) and calls Notifier.post(); the result line now carries the Java failure when there is one. The javaErrorText() helper of the previous attempt is gone: reading a pending exception from C++ is exactly what does not work here. - On the device: the tool answers "notification sent", the system holds the record (channel cst-agent, importance 3, the framework small icon, the tap intent back into the app). Earlier attempts in the same session are in the transcript next to it: four failures from the old path, then this one. Docs: docs/android.md gets the Java-helper mechanism (and why the tool has to be wrapped in Java at all) and no longer lists notification_send as missing; app/README.md's platform table and the Android note say where a Java class goes. Version 0.9.3. |
||
|
|
60cbce3c5c |
v0.9.2: files the agent delivers come back as cards, and the saved key is used
A turn could write a report and say "here it is" -- and there was nothing to
open. The engine had no way to hand a finished file to the person, and the
front end had no way to show one. This release adds the whole path: a tool to
deliver, a store that keeps the copy, a card in the transcript, and open /
share through the platform. It also fixes two bugs the phone exposed while it
was being tried there.
deliver_file (engine)
- cst_attachment_t.for_user marks a copy that belongs to the person, not to
the model; cst_attach_import_artifact() stores any bytes up to
CST_ARTIFACT_MAX_BYTES (64 MB) with the mime taken from the name's
extension. Attachments are unchanged (content decides the type, 16 MB).
- It is a read-policy engine tool: path resolves inside the workspace, a
directory is refused, title (optional) is the display name.
- The wire is what keeps it out of the conversation: parts_add_attachments()
skips for_user entries without even a "not sent" note, and a turn that only
delivered a file no longer invents an empty user message. tests/test_artifacts.c
pins that down end to end, including a session-file round trip.
The card (Qt front end)
- filesText() replaced imagesText(): one line per file -- kind, stored name,
name, origin, mime, bytes, pixels -- decoded by ImageStrip.qml. A for_user
entry is a file card (extension badge, name, type and size, Open, Share);
a picture stays a thumbnail and opens on tap. Both shapes are read, so
sessions written by an older version still draw.
- AgentCore::openArtifact() / shareArtifact() accept a name only when one of
the session's own messages carries it, and hand the display name to the
platform layer for the status line.
- Android opens through the app's FileProvider (content:// + ACTION_VIEW,
resolveActivity() checked, read permission granted) and shares through the
share sheet; a desktop uses QDesktopServices / the clipboard.
CST_APP_NO_OPEN=1 makes --uitest run without opening anything.
Two bugs the device found
- The session writer had its own copy of the attachment JSON and forgot
`for_user`: the file survived the file, but came back as an attachment --
no card, and put back in front of the model. It now uses the one writer
(cst_attachments_to_json()), with the round trip covered by a test.
- engineToolImages() looked the card up with findTool(), which only finds a
card that is still running -- and the engine reports the result *before*
the files, so the card was already complete and the files were dropped.
A live turn therefore showed no card while a replay of the same session
did. It now matches on the call id.
- A tap that could not work said nothing: ImageStrip.qml's errorText was
never drawn. The card now carries the reason ("no app on the phone opens
report.md").
Also
- The saved provider key reaches the engine on every start: AgentCore::open()
was handed an empty key and the engine never resolves credentials itself
(only the CLI did), so every start asked for it again. It resolves the same
order main.c uses (saved key, provider environment, CST_API_KEY) and wipes
the copy it made; --selftest checks it against a scratch credentials file.
- Android: notification_send reads the Java exception text into its result,
and keeps failing on the NX789J -- the build() returns null and Qt clears
the exception before C++ can read it, so the call has to be wrapped in Java
next (docs/android.md says so).
- --uitest grew the delivered-file case (card, Open, Share, a forged name
refused), the live order of result-then-files, and the startup row count.
Version 0.9.2.
|
||
|
|
c2320f946b |
v0.9.0: the Qt front end as an Android APK, images in the transcript
Two strands land together: the window draws pictures now, and the whole front end builds, installs and runs on the phone with the engine linked in-process. Images (the "make a picture actually show up" part) - Engine, additively: cst_agent_hooks_t.on_tool_images(call, list, n, ud) fires when a call left attachments behind, and cst_agent_pending_rel() / cst_agent_pending_is_image() read that list without the app touching the store. tests/test_attach.c::test_agent_pending pins the three cases (an image, a stored name that passes cst_attach_rel_ok, out-of-range NULL). - App: image://cstattach/<stored name> (app/src/ui/SessionImages.cpp) serves only the current session's attachment directory, checked with cst_attach_rel_ok, decodes down to the requested sourceSize and never upscales, and holds a mutex because requestImage() runs on the render thread while the directory can change underneath it. AgentCore gained attachmentDir(), pendingImages(), the toolImages()/promptImages() signals and hookToolImages(); history() gives replayed rows their images too. - QML: ImageStrip.qml (a thumb strip, <=150 px tall, shaped by the picture, tooltip with name/size/origin, the file's name when it went missing), wired into MessageCard and ToolCard. Qt 6's ListModel cannot hold a list-valued role (append() reads back undefined, setProperty drops it), so a row carries one image per line as <stored>|<name>|<origin>|<w>x<h> and ImageStrip decodes that. Android (the engine is already in the APK; this is the front end) - app/src/main.cpp: on Android the workspace, config, tmp and HOME go under the app's own data directory before the engine opens anything, because QDir::currentPath() is "/" there and /tmp is not writable. - app/android/: the manifest (QtApplication/QtActivity, INTERNET and POST_NOTIFICATIONS, adjustResize, no storage permission -- the picker will be the system one), strings, a vector icon; minSdk 24 and targetSdk 35 come from app/CMakeLists.txt through QT_ANDROID_MIN/TARGET_SDK_VERSION. - app/src/platform/android/AndroidTools.cpp: the Android file behind PlatformInit(), owner "app:android" (notification, clipboard, share sheet, file picker are stubs for now, so the tool list does not lie about them). - app/tools/selftest.c decides its scratch workspace with os_temp_dir(), so the C smoke test runs on the device too. - scripts/build_apk_android.sh: sources to installed APK in one command (environment checks, qt-cmake configure, --target apk, adb install), and the host-side plumbing the NixOS + qt.io combination needs -- the NDK sysroot include dirs for Qt's FindGLESv2/FindEGL (they only look under <sysroot>/include), -L$PREFIX/lib on the shared/module link lines as well as the executables', the Android debug keystore created on first use with QT_ANDROID_SIGN_APK + QT_ANDROID_KEYSTORE_* (androiddeployqt copies the unsigned gradle output and signs it itself), and the #!/bin/bash shebang of the build-tools wrappers, which NixOS does not have. - scripts/gradle-android-init.gradle (installed into ~/.gradle/init.d/): the release lint task is switched off -- lintVitalAnalyzeRelease resolves its own tool jars straight from dl.google.com, bypassing every repository the build configures, and fails the build after all the real work is done. - Icons: app/icons/*.svg (Breeze's 24@3x files, the 19 names the window asks for) are compiled in as :/cst-icons and ThemeIcons.cpp falls back to them when the platform has no icon theme. On the phone the Qt theme lookup found nothing and the QML glyph fallback was an empty box in the phone's fonts. - docs/android.md: "The APK (Qt front end)" -- the SDK assembled without sdkmanager, the host Qt's missing libraries on NixOS, the Gradle mirrors, lint, signing -- and a Status that says what runs where. app/README.md gains the images and APK lines, and stops claiming Android platform tools that are still stubs. Verified: engine `make check` green; desktop `cst-app-selftest: OK` and --uitest all checks pass; phone (NX789J, Android 16, arm64-v8a) -- the APK installs, starts on the workspace under /data/user/0/xyz.whcself.cstagent/ files/, a turn reaches a configured provider over HTTPS from inside the ART process and renders the reply, rotation keeps the session and docks the sidebar, and the toolbar/card icons draw. Version 0.9.0. mode = coding |
||
|
|
5ba215b36c |
v0.8.7: tool capability metadata, in-process engine, Qt app, Android engine
The engine can be embedded now instead of only driven from a terminal: the same objects link into a Qt front end (app/) that owns a cst_agent_t inside its own process -- no port, no token, no second process -- and that front end now runs on the desktop and, engine-side, on the phone. Capability metadata (include/cst_tools.h, src/tools/registry.c): - cst_tool_def_t gains perm_name, platforms and available (a probe that returns a reason), so a permission is addressable by name and a tool can say why it cannot run here. - cst_platform_name(), cst_tool_platform_ok(), cst_tool_available(), cst_tool_meta_reload(), cst_tool_perm_name() and CST_PLAT_DESKTOP[]. - cst_tool_register() may take over a built-in name that is not runnable on this platform, so a front end replaces it with its own implementation. - the registry entries carry their class (delete_file -> "delete"), the desktop clipboard tools carry a clipboard probe; find/active/run gate on it. - /api/settings.tools[] exposes perm_name, platforms, available, why and the declared parameters. Engine as a library (Makefile, CMakeLists.txt): - libcstcore.a from the CORE_OBJ set, and a CMake cstcore target shared by the CLI and by app/ (-DCST_BUILD_APP=ON): the engine is compiled once. - Android branch for the vendored sashidown: a per-ABI archive built with the NDK's aarch64-linux-android24-clang wrapper, because CMake's compiler is plain clang and keeps --target/--sysroot in its own variables. Qt front end (app/, new): - AgentCore wraps one cst_agent_t: turns run on a worker thread, every cst_agent_hooks_t callback becomes a Qt signal, and the two hooks that need an answer (on_permission, on_ask_user) block the worker until the GUI replies; mode, approval and plan mode go through the engine's config. - ToolProvider hands the front end's tools to the engine (owner "app:*"); the Linux one is notification_send with a probe for notify-send/D-Bus. - Main.qml: transcript, tool blocks, consent dialog (Deny/Once/Always -> CST_PERM_*), ask_user, mode switch, Esc to cancel; --selftest stays headless and runs the embedding checks without QML. Android (scripts/, docs/android.md, new): - scripts/build_third_party_android.sh builds static mbedTLS + libcurl for an ABI (CA path /system/etc/security/cacerts); build_android.sh builds cst-agent, libcstcore.a and the test binaries for the phone, can push them and run the whole suite there. - sandbox_exec.c keeps the plain exec path on Android: the seccomp supervisor compiled in and the probe reported "seccomp", but writes outside the workspace were not denied on the device -- claiming a sandbox that does not hold is worse than reporting none. - tests/test_diff.c compares layouts only against real GNU diffutils: toybox's diff prints the same hunk header with its own hunk/context rules. Tests: test_tool_meta (metadata, platform gate, probe reasons, takeover of a built-in name) and test_embed (in-process embedding contract) are new; the existing registry/llm_body assertions now follow availability. 38 test binaries pass on the host, 36/36 on arm64-v8a Android 16, cst-app-selftest passes in both the standalone app/ build and the repo build. Version 0.8.7. |