v0.8.6: general mode, clipboard text, PDF text extraction
ci / tcc-check (push) Successful in 59s
ci / linux (aarch64, 0) (push) Successful in 19m11s
ci / linux (x86, 1) (push) Successful in 11m31s
ci / linux (x86_64, 1) (push) Successful in 11m11s
ci / windows (push) Successful in 9m24s

Working mode: --mode coding|general (CST_MODE, settings.json, /mode,
WebUI run settings). general swaps the default framing for a general
assistant one -- workflow, safety notes, tool wording (run_shell, the
subagents) -- and /status shows it; minimal (the paper profile) stays
byte-frozen. The prompt state carries the mode so a switch leaves a
system note in history.

Clipboard: clipboard_read / clipboard_write tools (16 MiB cap read,
default 64 KiB window; writing is a WRITE permission with a preview) on
pbcopy / wl-copy / xclip / pbpaste / wl-paste / xclip and the Win32
clipboard. In the TUI, /attach paste falls back to clipboard text when
there is no image on it.

PDF: a dependency-free text extractor (src/pdf_text.c): inflate,
Flate/ASCIIHex/ASCII85, PNG/TIFF predictors, layout-independent object
scan, object streams, page tree walk with inherited resources, content
lexer (Tj/TJ/'/" and the Td/TD/T*/Tm spacing heuristics), per-font
ToUnicode CMaps and inline images skipped. Encrypted files and pages
with no text layer are refused with the reason.

Attachments: /attach (a path or files dropped on the terminal) now take
images, PDFs and text. A PDF is stored as its text layer with the mime
still saying application/pdf and its page count; anything else has to be
UTF-8 text. Documents travel inline as text parts, so CST_NO_IMAGES and
text-only models no longer drop them, while images keep their old path.

Tests: test_pdf_text (plain, bare and zlib-framed Flate, inline image,
CID + ToUnicode, object stream, error paths), documents in test_attach,
inlined documents in test_llm_body, mode in test_config.
This commit is contained in:
WHC
2026-10-11 11:09:39 +08:00
parent 50abcf1a91
commit e0b0eddf3a
28 changed files with 3291 additions and 111 deletions
+28
View File
@@ -47,6 +47,8 @@ int cst_settings_load(cst_settings_t *s) {
cJSON_GetObjectItemCaseSensitive(root, "provider"));
copy_str(s->effort, sizeof(s->effort),
cJSON_GetObjectItemCaseSensitive(root, "effort"));
copy_str(s->mode, sizeof(s->mode),
cJSON_GetObjectItemCaseSensitive(root, "mode"));
copy_str(s->approval, sizeof(s->approval),
cJSON_GetObjectItemCaseSensitive(root, "approval"));
s->show_thinking =
@@ -86,6 +88,8 @@ int cst_settings_save(const cst_settings_t *s) {
cJSON_AddStringToObject(root, "provider", s->provider);
if (s->effort[0])
cJSON_AddStringToObject(root, "effort", s->effort);
if (s->mode[0])
cJSON_AddStringToObject(root, "mode", s->mode);
if (s->approval[0])
cJSON_AddStringToObject(root, "approval", s->approval);
if (s->show_thinking)
@@ -135,6 +139,21 @@ int cst_approval_parse(const char *s, int *out) {
return -1;
}
static const char *const MODE_NAMES[] = {"coding", "general"};
int cst_mode_valid(const char *m) {
for (size_t i = 0; m && m[0] && i < sizeof(MODE_NAMES) / sizeof(*MODE_NAMES);
i++) {
if (strcmp(m, MODE_NAMES[i]) == 0)
return 1;
}
return 0;
}
const char *cst_mode_name(const char *m) {
return cst_mode_valid(m) ? m : CST_DEFAULT_MODE;
}
const char *cst_settings_model(const cst_settings_t *s, const char *provider) {
for (int i = 0; provider && i < s->nmodels; i++) {
if (strcmp(s->models[i].provider, provider) == 0 && s->models[i].model[0])
@@ -148,6 +167,8 @@ int cst_settings_remember(const cst_agent_config_t *cfg) {
cst_settings_load(&s);
snprintf(s.provider, sizeof(s.provider), "%s", cfg->provider);
snprintf(s.effort, sizeof(s.effort), "%s", cfg->effort);
if (cfg->mode[0])
snprintf(s.mode, sizeof(s.mode), "%s", cfg->mode);
int i = 0;
while (i < s.nmodels && strcmp(s.models[i].provider, cfg->provider) != 0)
i++;
@@ -176,6 +197,13 @@ int cst_settings_remember_thinking(int show_thinking) {
return cst_settings_save(&s);
}
int cst_settings_remember_mode(const char *mode) {
cst_settings_t s;
cst_settings_load(&s);
snprintf(s.mode, sizeof(s.mode), "%s", cst_mode_name(mode));
return cst_settings_save(&s);
}
static void real_ws(const char *ws, char *out, size_t n) {
if (!ws || os_realpath(ws, out, n) != 0)
snprintf(out, n, "%s", ws ? ws : "");