v0.9.5: a session owns a directory, and the store hands out uuids
A session's transcript lived beside its attachments, both named after an id
that was a timestamp. Two things fell out of that: on a workspace two
machines share, a timestamp is not an id (the same second on both is enough
to hand out the same name), and "delete this conversation" had to chase the
jsonl, the title and a directory in a shared attachments/ tree, guessing what
belonged to whom. A phone's app data has nobody to tidy it by hand.
Now one session is one directory:
<workspace>/.agent/sessions/
<uuid>/session.jsonl the transcript
<uuid>/title the window title, one line
<uuid>/attachments/ images and delivered files (img-…, art-…)
<uuid>/sub-1.jsonl an agent role's own transcript
CURRENT the id the next run opens
- src/agent_store.c: new_id() makes a uuid v4 (16 bytes from /dev/urandom,
clock+pid only as a fallback), dir_for_id/flat_for_id/nested_for_id/
shape_of_id decide which shape an id has, so every reader takes either.
An id is a directory name, so it is checked as one (no "/", no "..",
nothing starting with ".", not CURRENT or attachments). path_for_id() keeps
the shape a session already has and makes the directory for a new id, so
nothing had to change in the callers. store_list() lists the directories
that hold a transcript plus the flat files, newest first. delete() is one
rm_tree of the session's directory, plus the old shared attachments/<id>
and CURRENT when it pointed there.
- cst_agent_store_migrate() (--migrate-store, or plain cst-agent for a
session): moves a flat store in — <id>.jsonl to <id>/session.jsonl,
<id>.title in, the shared attachments/<id>/ to <id>/attachments/. rename
first, copy when the filesystem says no, and counts what it moved, so a
second run is a no-op that says "nothing to move". On the real workspace:
6 sessions moved, the shared directory gone, --list-sessions reads them
back unchanged, a second run moved 0.
- cst_agent_store_id_of_path(): the id of a session path. This is the bug the
whole change had been hiding: cst_agent_session_id() took the id from the
file name, and the new file name is session.jsonl -- every session would
have called itself "session" (the sidebar, CURRENT, undo, an agent role's
directory, and the metrics file, which now also goes through the helper).
- src/attach.c: a session that owns its directory keeps its payloads inside
it (<id>/attachments/); only the flat layout needs the shared directory and
the id stem. Attachments behave exactly as before otherwise.
- tests/test_store.c: uuid shape and uniqueness, a small buffer refused, a
new session's path and directory, write/list/info/title read back, the
payload landing in <id>/attachments/, delete taking the directory with it;
then an old-layout store: readable before the move, moved byte for byte,
its payload readable through the new path, listed once, a second migration
a no-op, deletion covering the migrated payload. Also in the Makefile and
CMakeLists test lists.
- app/src/ui/UiTest.cpp: the image directory check wanted "/attachments/",
which the new layout has no trailing slash for.
Desktop: build clean, make check 35/35, --uitest all green, --selftest and
cst-app-selftest OK, and --selftest opens a session of the migrated workspace.
Version 0.9.5.
This commit is contained in:
@@ -167,7 +167,14 @@ void cst_session_unlock(cst_session_t *s);
|
||||
/* --- workspace .agent/sessions store (OpenCode-like) --- */
|
||||
int cst_agent_store_ensure(const char *workspace);
|
||||
int cst_agent_store_dir(const char *workspace, char *out, size_t out_sz);
|
||||
/* A fresh session id: a uuid v4, so two machines sharing a workspace cannot
|
||||
* hand out the same one (a timestamp can). id_sz >= 37. */
|
||||
int cst_agent_store_new_id(char *id_out, size_t id_sz);
|
||||
/* The transcript of an id. A session owns a directory under the store
|
||||
* (<id>/session.jsonl, with its attachments and sub-agent transcripts inside
|
||||
* it); an id that still has the older flat file (<id>.jsonl) keeps it until
|
||||
* cst_agent_store_migrate() moves it. A new id gets its directory made here,
|
||||
* so the caller can open the file next. */
|
||||
int cst_agent_store_path_for_id(const char *workspace, const char *id,
|
||||
char *out, size_t out_sz);
|
||||
int cst_agent_store_set_current(const char *workspace, const char *id);
|
||||
@@ -177,6 +184,10 @@ int cst_agent_store_resolve_default(const char *workspace, char *path_out,
|
||||
size_t path_sz, char *id_out,
|
||||
size_t id_sz);
|
||||
int cst_agent_store_list(const char *workspace, char ids[][128], int max_ids);
|
||||
/* The id a session path belongs to: <dir>/<id>.jsonl (the flat layout) or
|
||||
* <dir>/<id>/session.jsonl (a session's own directory). 0 / -1. */
|
||||
int cst_agent_store_id_of_path(const char *session_path, char *out,
|
||||
size_t out_sz);
|
||||
typedef struct {
|
||||
char id[128];
|
||||
long long mtime;
|
||||
@@ -189,6 +200,13 @@ int cst_agent_store_info(const char *workspace, const char *id,
|
||||
int cst_agent_store_title_set(const char *workspace, const char *id,
|
||||
const char *title);
|
||||
int cst_agent_store_delete(const char *workspace, const char *id);
|
||||
/* Move every flat session of the store into the per-session layout: the
|
||||
* transcript to <id>/session.jsonl, the title and the shared attachment
|
||||
* directory (<id>.title, attachments/<id>/) along with it. Sessions that are
|
||||
* already directories are left alone, so running it twice is harmless.
|
||||
* *moved_out / *failed_out (optional) count what happened; 0 / -1. */
|
||||
int cst_agent_store_migrate(const char *workspace, int *moved_out,
|
||||
int *failed_out, char *err, size_t err_sz);
|
||||
|
||||
/* --- tools --- */
|
||||
typedef struct {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#define CST_VERSION_MAJOR 0
|
||||
#define CST_VERSION_MINOR 9
|
||||
#define CST_VERSION_PATCH 4
|
||||
#define CST_VERSION_PATCH 5
|
||||
|
||||
#define CST_VERSION_STR_(x) #x
|
||||
#define CST_VERSION_STR(x) CST_VERSION_STR_(x)
|
||||
|
||||
Reference in New Issue
Block a user