commit - 8e964caf96f4c4f928cb51a7f4fd99288ec87914
commit + 3f64e350370dd21067666d57867a764e16479365
blob - e9067ff4fafbb22cbc7d3f953d6bddb9c041a41f
blob + 015c7389169c6956ee20f00cc12e128beb8a7b75
--- regress/conf/conf_test.c
+++ regress/conf/conf_test.c
CHECK(c->initial_provider_slot == 0);
CHECK(c->max_tokens == 4096);
CHECK(c->context_limit == 0);
+ CHECK(c->auto_compact == 1);
+ CHECK(c->compact_reserve_tokens == 16384);
+ CHECK(c->compact_keep_tokens == 20000);
CHECK(c->allow_write == 1);
CHECK(c->allow_subprocess_net == 0);
CHECK(c->web_search == 1);
"provider \"openai\"\n"
"max_tokens 8192\n"
"context_limit 100000\n"
+ "auto_compact no\n"
+ "compact_reserve_tokens 12345\n"
+ "compact_keep_tokens 23456\n"
"allow_write no\n"
"web_search no\n"
"notify no\n"
CHECK(strcmp(conf_api_port(c), "8443") == 0);
CHECK(c->max_tokens == 8192);
CHECK(c->context_limit == 100000);
+ CHECK(c->auto_compact == 0);
+ CHECK(c->compact_reserve_tokens == 12345);
+ CHECK(c->compact_keep_tokens == 23456);
CHECK(c->allow_write == 0);
CHECK(c->web_search == 0);
CHECK(c->notify == 0);
static void
test_numbers(void)
{
+ struct fugu_conf *c;
+
CHECK(parse_fails("max_tokens 0\n"));
CHECK(parse_fails("max_tokens -5\n"));
CHECK(parse_fails("max_tokens 999999999999999999999999\n"));
CHECK(parse_fails("context_limit 0\n"));
+ CHECK(parse_fails("compact_reserve_tokens 0\n"));
+ CHECK(parse_fails("compact_reserve_tokens -1\n"));
+ CHECK(parse_fails("compact_reserve_tokens 1000000001\n"));
+ CHECK(parse_fails("compact_keep_tokens 0\n"));
+ CHECK(parse_fails("compact_keep_tokens -1\n"));
+ CHECK(parse_fails("compact_keep_tokens 1000000001\n"));
+ CHECK(parse_ok(
+ "compact_reserve_tokens 1\n"
+ "compact_keep_tokens 1000000000\n", &alice, &c));
+ CHECK(c->compact_reserve_tokens == 1);
+ CHECK(c->compact_keep_tokens == 1000000000);
+ conf_free(c);
CHECK(parse_fails("max_subagents -1\n"));
CHECK(parse_fails("max_subagents 1000\n"));
CHECK(parse_fails("max_bg_jobs -1\n"));
}
static void
+test_dump_compaction(void)
+{
+ struct fugu_conf *c;
+ FILE *f;
+ char *buf = NULL;
+ size_t sz = 0;
+
+ CHECK(parse_ok(
+ "auto_compact no\n"
+ "compact_reserve_tokens 123\n"
+ "compact_keep_tokens 456\n", &alice, &c));
+ if ((f = open_memstream(&buf, &sz)) == NULL)
+ err(1, "open_memstream");
+ conf_dump(c, f);
+ fclose(f);
+ CHECK(strstr(buf, "auto_compact no\n") != NULL);
+ CHECK(strstr(buf, "compact_reserve_tokens 123\n") != NULL);
+ CHECK(strstr(buf, "compact_keep_tokens 456\n") != NULL);
+ free(buf);
+ conf_free(c);
+}
+
+static void
test_dump_redacts(void)
{
struct fugu_conf *c;
test_model_bounds();
test_hostile();
test_include();
+ test_dump_compaction();
test_dump_redacts();
test_wipe_delegated_secrets();
test_lifetimes();
blob - 6c0eb932abd0c346232a70ae4cdd3c73f791f51d
blob + 5e66360b3f782e643849f899753939f640399269
--- src/fugu/conf.c
+++ src/fugu/conf.c
c->model = xstrdup("claude-sonnet-4-6");
c->provider_type = PROVIDER_ANTHROPIC;
c->max_tokens = 4096;
+ c->auto_compact = 1;
+ c->compact_reserve_tokens = FUGU_COMPACT_RESERVE_TOKENS_DEFAULT;
+ c->compact_keep_tokens = FUGU_COMPACT_KEEP_TOKENS_DEFAULT;
c->allow_write = 1;
c->web_search = 1;
c->search_provider = xstrdup("kagi");
if (c->context_limit > 0)
fprintf(f, "context_limit %lld\n",
(long long)c->context_limit);
+ fprintf(f, "auto_compact %s\n", yesno(c->auto_compact));
+ fprintf(f, "compact_reserve_tokens %lld\n",
+ (long long)c->compact_reserve_tokens);
+ fprintf(f, "compact_keep_tokens %lld\n",
+ (long long)c->compact_keep_tokens);
fprintf(f, "allow_write %s\n", yesno(c->allow_write));
fprintf(f, "allow_subprocess_net %s\n",
yesno(c->allow_subprocess_net));
blob - 4f704363a3ed8dae87743361ec043cbd9f0813cd
blob + ad1a542cfbdd0add698e93840eafde82b407b89d
--- src/fugu/conf.h
+++ src/fugu/conf.h
#include <stdio.h>
#define FUGU_CONF_PATH "/etc/fugu.conf"
+#define FUGU_COMPACT_RESERVE_TOKENS_DEFAULT INT64_C(16384)
+#define FUGU_COMPACT_KEEP_TOKENS_DEFAULT INT64_C(20000)
/* Unambiguous subagent_provider spelling for the implicit provider. */
#define FUGU_IMPLICIT_PROVIDER_REF "@default"
char *system; /* NULL: built-in prompt */
int64_t max_tokens;
int64_t context_limit; /* 0: unset */
+ int auto_compact;
+ int64_t compact_reserve_tokens;
+ int64_t compact_keep_tokens;
int allow_write;
int allow_subprocess_net;
int web_search;
blob - 1b7a16b8769e9b408730b8ed5e4c7c729edf5313
blob + bbc3a96f14f004077b95fe4bca03b4954f3b0adf
--- src/fugu/fugu.conf.5
+++ src/fugu/fugu.conf.5
.Sq ? .
The default is
.Ic no .
+.It Cm auto_compact Ic yes | no
+Automatically Compact the Lead Projection before a Generation when its
+estimated size exceeds the Effective window less
+.Cm compact_reserve_tokens .
+When enabled, a recognized Provider context-overflow rejection may also
+Compact and retry the rejected Generation once.
+With
+.Ic no ,
+neither automatic path runs;
+.Cm /compact
+remains available.
+The default is
+.Ic yes .
+.It Cm compact_keep_tokens Ar tokens
+Set the target size of the recent Projection suffix retained by automatic and
+manual Compaction.
+The value must be between 1 and 1000000000.
+The default is 20000.
+.It Cm compact_reserve_tokens Ar tokens
+Reserve this much of the Effective window when deciding whether to Compact
+before a Lead Generation.
+The value must be between 1 and 1000000000.
+The default is 16384.
.It Cm context_limit Ar tokens
Set an advisory projection budget and the effective window displayed by
.Cm /context
and the curses status bar.
+When set, it is also the first-priority Effective-window override used by
+automatic Compaction.
The value must be between 1 and 1000000000.
By default it is unset, and provider metadata or a compiled model table is
used when available.
blob - 21ec4235f66e401447251a9e19e7c6e0509a3ee9
blob + dc06ddff4712f16bda5f125ac86d2bd40df46300
--- src/fugu/parse.y
+++ src/fugu/parse.y
%}
%token ALLOWSUBPROCESSNET ALLOWWRITE ANTHROPICKEY ANTHROPICKEYFILE APIHOST
-%token APIKEY APIKEYFILE APIPATH APIPORT ASCII CONTEXTLIMIT ERROR GROUP
+%token APIKEY APIKEYFILE APIPATH APIPORT ASCII AUTOCOMPACT
+%token COMPACTKEEPTOKENS COMPACTRESERVETOKENS CONTEXTLIMIT ERROR GROUP
%token HTTPALLOW HTTPBLOCK INCLUDE KAGITOKEN KAGITOKENFILE MATCH MAXBGJOBS
%token MAXSUBAGENTS MAXTOKENS MODEL NO NOTIFY OAUTHTOKEN
%token PROJECTCONTEXT PROTECT
}
set_number(&conf->context_limit, $2);
}
+ | AUTOCOMPACT yesno {
+ set_bool(&conf->auto_compact, $2);
+ }
+ | COMPACTRESERVETOKENS NUMBER {
+ if ($2 < 1 || $2 > 1000000000) {
+ yyerror("compact_reserve_tokens out of range");
+ YYERROR;
+ }
+ set_number(&conf->compact_reserve_tokens, $2);
+ }
+ | COMPACTKEEPTOKENS NUMBER {
+ if ($2 < 1 || $2 > 1000000000) {
+ yyerror("compact_keep_tokens out of range");
+ YYERROR;
+ }
+ set_number(&conf->compact_keep_tokens, $2);
+ }
| ALLOWWRITE yesno {
set_bool(&conf->allow_write, $2);
}
{ "api_path", APIPATH },
{ "api_port", APIPORT },
{ "ascii", ASCII },
+ { "auto_compact", AUTOCOMPACT },
+ { "compact_keep_tokens", COMPACTKEEPTOKENS },
+ { "compact_reserve_tokens", COMPACTRESERVETOKENS },
{ "context_limit", CONTEXTLIMIT },
{ "group", GROUP },
{ "http_allow", HTTPALLOW },