From 626308ad954de6d4105211297d676256a14389d7 Mon Sep 17 00:00:00 2001 From: ZanzyTHEbar Date: Wed, 18 Feb 2026 23:40:03 +0000 Subject: [PATCH] feat(memory/sqlc): expand schema + harden queries + add vet rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Schema: - Add agent runtime tables: conversations, messages, runs, run_states, state_transitions, checkpoints, tool_results (all BLOB PKs, CASCADE) - Add jobs queue table with dedupe_key unique partial index - Add conversation graph tables: forks, links, threads, thread_messages, mentions, message_revisions sqlc config: - emit_db_tags: true — all generated structs carry db:"col" tags - Fix stale comment: "TEXT storage" → "BLOB storage" for UUID overrides - Add column overrides for all new agent runtime + conversation graph tables - Add CEL vet rules: no-unbounded-delete, one-select-requires-limit-1 Query hardening: - Add LIMIT 1 to 5 :one queries (recall, archival, working_context, docs, kv) - Bound 12 unbounded :many queries with LIMIT sqlc.arg(lim) - Add new query files for all agent runtime + jobs + conversation graph tables Delegate: - Add agentID param to GetRecallItem, DeleteRecallItem, GetArchivalChunk, ListArchivalChunks, ListAllArchivalChunks, CountArchivalChunks - Pass Lim param to ListDocumentsByCategory, ListAllDocuments (default 1000) - Pass Lim:10000 to ListArchivalChunks (bounded but practically unlimited) CI / Makefile: - Add sqlc-vet Makefile target (sqlc vet -f sqlc.yaml) - Add sqlc-vet step to sqlc-check CI job - Integrate sqlc-vet into check meta-target Tests: - Fix integration_test: add SetAgentID(testAgent), fix ListArchivalChunks/ GetArchivalChunk calls to pass agentID - Add benchmark suite for delegate ops --- .github/workflows/pr.yml | 3 + Makefile | 10 +- pkg/memory/delegate/factory_test.go | 2 +- pkg/memory/delegate/sqlite.go | 30 +- pkg/memory/delegate/sqlite_bench_test.go | 104 ++++ pkg/memory/delegate/sqlite_test.go | 40 +- pkg/memory/integration_test.go | 8 +- pkg/memory/sqlc/agent_audit_log.sql.go | 42 +- .../sqlc/agent_conversation_forks.sql.go | 136 +++++ .../sqlc/agent_conversation_links.sql.go | 126 +++++ pkg/memory/sqlc/agent_conversations.sql.go | 134 +++++ pkg/memory/sqlc/agent_documents.sql.go | 36 +- pkg/memory/sqlc/agent_kv.sql.go | 22 +- pkg/memory/sqlc/agent_mentions.sql.go | 114 ++++ .../sqlc/agent_message_revisions.sql.go | 110 ++++ pkg/memory/sqlc/agent_messages.sql.go | 148 ++++++ pkg/memory/sqlc/agent_state.sql.go | 499 ++++++++++++++++++ pkg/memory/sqlc/agent_threads.sql.go | 233 ++++++++ pkg/memory/sqlc/agent_tool_results.sql.go | 269 ++++++++++ pkg/memory/sqlc/archival.sql.go | 242 +++++---- pkg/memory/sqlc/jobs.sql.go | 432 +++++++++++++++ pkg/memory/sqlc/models.go | 261 +++++++-- pkg/memory/sqlc/querier.go | 462 ++++++++++++++-- .../sqlc/queries/agent_conversation_forks.sql | 21 + .../sqlc/queries/agent_conversation_links.sql | 21 + .../sqlc/queries/agent_conversations.sql | 20 + pkg/memory/sqlc/queries/agent_documents.sql | 9 +- pkg/memory/sqlc/queries/agent_kv.sql | 3 +- pkg/memory/sqlc/queries/agent_mentions.sql | 18 + .../sqlc/queries/agent_message_revisions.sql | 17 + pkg/memory/sqlc/queries/agent_messages.sql | 21 + pkg/memory/sqlc/queries/agent_state.sql | 80 +++ pkg/memory/sqlc/queries/agent_threads.sql | 25 + .../sqlc/queries/agent_tool_results.sql | 39 ++ pkg/memory/sqlc/queries/archival.sql | 96 ++-- pkg/memory/sqlc/queries/jobs.sql | 96 ++++ pkg/memory/sqlc/queries/recall.sql | 13 +- pkg/memory/sqlc/queries/working_context.sql | 3 +- pkg/memory/sqlc/recall.sql.go | 102 ++-- pkg/memory/sqlc/schema.sql | 188 ++++++- pkg/memory/sqlc/sqlc.yaml | 164 +++++- pkg/memory/sqlc/summaries.sql.go | 18 +- pkg/memory/sqlc/working_context.sql.go | 12 +- 43 files changed, 4040 insertions(+), 389 deletions(-) create mode 100644 pkg/memory/delegate/sqlite_bench_test.go create mode 100644 pkg/memory/sqlc/agent_conversation_forks.sql.go create mode 100644 pkg/memory/sqlc/agent_conversation_links.sql.go create mode 100644 pkg/memory/sqlc/agent_conversations.sql.go create mode 100644 pkg/memory/sqlc/agent_mentions.sql.go create mode 100644 pkg/memory/sqlc/agent_message_revisions.sql.go create mode 100644 pkg/memory/sqlc/agent_messages.sql.go create mode 100644 pkg/memory/sqlc/agent_state.sql.go create mode 100644 pkg/memory/sqlc/agent_threads.sql.go create mode 100644 pkg/memory/sqlc/agent_tool_results.sql.go create mode 100644 pkg/memory/sqlc/jobs.sql.go create mode 100644 pkg/memory/sqlc/queries/agent_conversation_forks.sql create mode 100644 pkg/memory/sqlc/queries/agent_conversation_links.sql create mode 100644 pkg/memory/sqlc/queries/agent_conversations.sql create mode 100644 pkg/memory/sqlc/queries/agent_mentions.sql create mode 100644 pkg/memory/sqlc/queries/agent_message_revisions.sql create mode 100644 pkg/memory/sqlc/queries/agent_messages.sql create mode 100644 pkg/memory/sqlc/queries/agent_state.sql create mode 100644 pkg/memory/sqlc/queries/agent_threads.sql create mode 100644 pkg/memory/sqlc/queries/agent_tool_results.sql create mode 100644 pkg/memory/sqlc/queries/jobs.sql diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 24ccb2954..d8f35c24d 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -38,6 +38,9 @@ jobs: - name: Check sqlc generated code run: make sqlc-check + - name: Run sqlc vet rules + run: make sqlc-vet + vet: runs-on: ubuntu-latest needs: fmt-check diff --git a/Makefile b/Makefile index c9f493873..a853de0f0 100644 --- a/Makefile +++ b/Makefile @@ -152,6 +152,12 @@ sqlc-check: @git diff --exit-code -- pkg/memory/sqlc/ || (echo "::error::sqlc generated code is stale. Run 'sqlc generate -f pkg/memory/sqlc/sqlc.yaml' and commit." && exit 1) @echo "sqlc OK" +## sqlc-vet: Run sqlc vet rules (no-unbounded-delete, one-select-requires-limit-1) +sqlc-vet: + @echo "Running sqlc vet..." + @sqlc vet -f pkg/memory/sqlc/sqlc.yaml + @echo "sqlc vet OK" + # --------------------------------------------------------------------------- # Fantasy SDK vendor management # Usage: make fantasy-diff FANTASY_VERSION=v0.9.0 @@ -181,8 +187,8 @@ test-integration: @$(GO) test -tags integration -count=1 -timeout 120s -v ./pkg/memory/... @echo "Integration tests OK" -## check: Run vet, fmt, and verify dependencies -check: deps fmt vet test +## check: Run vet, fmt, sqlc vet, and verify dependencies +check: deps fmt vet sqlc-vet test ## run: Build and run picoclaw run: build diff --git a/pkg/memory/delegate/factory_test.go b/pkg/memory/delegate/factory_test.go index 0736f092c..69b5a0fd8 100644 --- a/pkg/memory/delegate/factory_test.go +++ b/pkg/memory/delegate/factory_test.go @@ -178,7 +178,7 @@ func TestNewFromConfig_LocalFullRoundTrip(t *testing.T) { t.Fatalf("InsertRecallItem: %v", err) } - got, err := d.GetRecallItem(ctx, item.ID) + got, err := d.GetRecallItem(ctx, "a1", item.ID) if err != nil { t.Fatalf("GetRecallItem: %v", err) } diff --git a/pkg/memory/delegate/sqlite.go b/pkg/memory/delegate/sqlite.go index 800c198a8..5e18be400 100644 --- a/pkg/memory/delegate/sqlite.go +++ b/pkg/memory/delegate/sqlite.go @@ -240,8 +240,8 @@ func (d *LibSQLDelegate) InsertRecallItem(ctx context.Context, item *memory.Reca }) } -func (d *LibSQLDelegate) GetRecallItem(ctx context.Context, id ids.UUID) (*memory.RecallItem, error) { - row, err := d.queries.GetRecallItem(ctx, memsqlc.GetRecallItemParams{ID: id}) +func (d *LibSQLDelegate) GetRecallItem(ctx context.Context, agentID string, id ids.UUID) (*memory.RecallItem, error) { + row, err := d.queries.GetRecallItem(ctx, memsqlc.GetRecallItemParams{ID: id, AgentID: agentID}) if err == sql.ErrNoRows { return nil, nil } @@ -254,6 +254,7 @@ func (d *LibSQLDelegate) GetRecallItem(ctx context.Context, id ids.UUID) (*memor func (d *LibSQLDelegate) UpdateRecallItem(ctx context.Context, item *memory.RecallItem) error { return d.queries.UpdateRecallItem(ctx, memsqlc.UpdateRecallItemParams{ ID: item.ID, + AgentID: item.AgentID, Role: item.Role, Sector: item.Sector, Importance: item.Importance, @@ -264,8 +265,8 @@ func (d *LibSQLDelegate) UpdateRecallItem(ctx context.Context, item *memory.Reca }) } -func (d *LibSQLDelegate) DeleteRecallItem(ctx context.Context, id ids.UUID) error { - return d.queries.DeleteRecallItem(ctx, memsqlc.DeleteRecallItemParams{ID: id}) +func (d *LibSQLDelegate) DeleteRecallItem(ctx context.Context, agentID string, id ids.UUID) error { + return d.queries.DeleteRecallItem(ctx, memsqlc.DeleteRecallItemParams{ID: id, AgentID: agentID}) } func (d *LibSQLDelegate) ListRecallItems(ctx context.Context, agentID, sessionKey string, limit, offset int) ([]*memory.RecallItem, error) { @@ -317,8 +318,8 @@ func (d *LibSQLDelegate) InsertArchivalChunk(ctx context.Context, chunk *memory. }) } -func (d *LibSQLDelegate) GetArchivalChunk(ctx context.Context, id ids.UUID) (*memory.ArchivalChunk, error) { - row, err := d.queries.GetArchivalChunk(ctx, memsqlc.GetArchivalChunkParams{ID: id}) +func (d *LibSQLDelegate) GetArchivalChunk(ctx context.Context, agentID string, id ids.UUID) (*memory.ArchivalChunk, error) { + row, err := d.queries.GetArchivalChunk(ctx, memsqlc.GetArchivalChunkParams{ID: id, AgentID: agentID}) if err == sql.ErrNoRows { return nil, nil } @@ -328,8 +329,8 @@ func (d *LibSQLDelegate) GetArchivalChunk(ctx context.Context, id ids.UUID) (*me return sqlcChunkToMemory(row), nil } -func (d *LibSQLDelegate) ListArchivalChunks(ctx context.Context, recallID ids.UUID) ([]*memory.ArchivalChunk, error) { - rows, err := d.queries.ListArchivalChunks(ctx, memsqlc.ListArchivalChunksParams{RecallID: recallID}) +func (d *LibSQLDelegate) ListArchivalChunks(ctx context.Context, agentID string, recallID ids.UUID) ([]*memory.ArchivalChunk, error) { + rows, err := d.queries.ListArchivalChunks(ctx, memsqlc.ListArchivalChunksParams{RecallID: recallID, AgentID: agentID, Lim: 10000}) if err != nil { return nil, err } @@ -340,10 +341,11 @@ func (d *LibSQLDelegate) ListArchivalChunks(ctx context.Context, recallID ids.UU return chunks, nil } -func (d *LibSQLDelegate) ListAllArchivalChunks(ctx context.Context, limit, offset int) ([]*memory.ArchivalChunk, error) { +func (d *LibSQLDelegate) ListAllArchivalChunks(ctx context.Context, agentID string, limit, offset int) ([]*memory.ArchivalChunk, error) { rows, err := d.queries.ListAllArchivalChunks(ctx, memsqlc.ListAllArchivalChunksParams{ - Lim: int64(limit), - Off: int64(offset), + AgentID: agentID, + Lim: int64(limit), + Off: int64(offset), }) if err != nil { return nil, err @@ -406,8 +408,8 @@ func (d *LibSQLDelegate) CountRecallItems(ctx context.Context, agentID, sessionK return int(count), err } -func (d *LibSQLDelegate) CountArchivalChunks(ctx context.Context) (int, error) { - count, err := d.queries.CountArchivalChunks(ctx) +func (d *LibSQLDelegate) CountArchivalChunks(ctx context.Context, agentID string) (int, error) { + count, err := d.queries.CountArchivalChunks(ctx, memsqlc.CountArchivalChunksParams{AgentID: agentID}) return int(count), err } @@ -495,6 +497,7 @@ func (d *LibSQLDelegate) ListDocumentsByCategory(ctx context.Context, agentID, c rows, err := d.queries.ListDocumentsByCategory(ctx, memsqlc.ListDocumentsByCategoryParams{ AgentID: agentID, Category: category, + Lim: 1000, }) if err != nil { return nil, err @@ -509,6 +512,7 @@ func (d *LibSQLDelegate) ListDocumentsByCategory(ctx context.Context, agentID, c func (d *LibSQLDelegate) ListAllDocuments(ctx context.Context, agentID string) ([]*memory.AgentDocument, error) { rows, err := d.queries.ListAllDocuments(ctx, memsqlc.ListAllDocumentsParams{ AgentID: agentID, + Lim: 1000, }) if err != nil { return nil, err diff --git a/pkg/memory/delegate/sqlite_bench_test.go b/pkg/memory/delegate/sqlite_bench_test.go new file mode 100644 index 000000000..4b44abc7f --- /dev/null +++ b/pkg/memory/delegate/sqlite_bench_test.go @@ -0,0 +1,104 @@ +package delegate + +import ( + "context" + "testing" + + "github.com/sipeed/picoclaw/pkg/ids" + "github.com/sipeed/picoclaw/pkg/memory" +) + +func BenchmarkListRecallItems(b *testing.B) { + d := newBenchDelegate(b) + ctx := context.Background() + agent := "bench-agent" + session := "bench-sess" + + for i := 0; i < 50; i++ { + _ = d.InsertRecallItem(ctx, &memory.RecallItem{ + ID: ids.New(), + AgentID: agent, + SessionKey: session, + Role: "user", + Sector: memory.SectorEpisodic, + Importance: 0.5, + Content: "Benchmark recall content for testing delegate read performance.", + Tags: "bench", + }) + } + + b.ReportAllocs() + b.ResetTimer() + for b.Loop() { + _, _ = d.ListRecallItems(ctx, agent, session, 20, 0) + } +} + +func BenchmarkGetWorkingContext(b *testing.B) { + d := newBenchDelegate(b) + ctx := context.Background() + agent := "bench-agent" + session := "bench-sess" + + _ = d.UpsertWorkingContext(ctx, agent, session, "Working context content for benchmarking.") + + b.ReportAllocs() + b.ResetTimer() + for b.Loop() { + _, _ = d.GetWorkingContext(ctx, agent, session) + } +} + +func BenchmarkUpsertKV(b *testing.B) { + d := newBenchDelegate(b) + ctx := context.Background() + + b.ReportAllocs() + b.ResetTimer() + for b.Loop() { + _ = d.UpsertKV(ctx, "bench-agent", "bench-key", "bench-value") + } +} + +func BenchmarkGetKV(b *testing.B) { + d := newBenchDelegate(b) + ctx := context.Background() + _ = d.UpsertKV(ctx, "bench-agent", "bench-key", "bench-value") + + b.ReportAllocs() + b.ResetTimer() + for b.Loop() { + _, _ = d.GetKV(ctx, "bench-agent", "bench-key") + } +} + +func BenchmarkInsertAuditEntry(b *testing.B) { + d := newBenchDelegate(b) + ctx := context.Background() + + b.ReportAllocs() + b.ResetTimer() + for b.Loop() { + _ = d.InsertAuditEntry(ctx, &memory.AuditEntry{ + ID: ids.New(), + AgentID: "bench-agent", + SessionKey: "bench-sess", + Action: "tool_call", + Target: "read_file", + Input: `{"path": "/tmp/test"}`, + }) + } +} + +func newBenchDelegate(b *testing.B) *LibSQLDelegate { + b.Helper() + d, err := NewLibSQLInMemory() + if err != nil { + b.Fatal(err) + } + if err := d.Init(context.Background()); err != nil { + b.Fatal(err) + } + b.Cleanup(func() { d.Close() }) + return d +} diff --git a/pkg/memory/delegate/sqlite_test.go b/pkg/memory/delegate/sqlite_test.go index 712ea60ff..d3131743d 100644 --- a/pkg/memory/delegate/sqlite_test.go +++ b/pkg/memory/delegate/sqlite_test.go @@ -85,7 +85,7 @@ func TestLibSQLDelegate_RecallItemCRUD(t *testing.T) { } // Get - got, err := d.GetRecallItem(ctx, item.ID) + got, err := d.GetRecallItem(ctx, "agent-1", item.ID) if err != nil { t.Fatalf("GetRecallItem: %v", err) } @@ -109,7 +109,7 @@ func TestLibSQLDelegate_RecallItemCRUD(t *testing.T) { t.Fatalf("UpdateRecallItem: %v", err) } - got, err = d.GetRecallItem(ctx, item.ID) + got, err = d.GetRecallItem(ctx, "agent-1", item.ID) if err != nil { t.Fatalf("GetRecallItem after update: %v", err) } @@ -130,11 +130,11 @@ func TestLibSQLDelegate_RecallItemCRUD(t *testing.T) { } // Delete - if err := d.DeleteRecallItem(ctx, item.ID); err != nil { + if err := d.DeleteRecallItem(ctx, "agent-1", item.ID); err != nil { t.Fatalf("DeleteRecallItem: %v", err) } - got, err = d.GetRecallItem(ctx, item.ID) + got, err = d.GetRecallItem(ctx, "agent-1", item.ID) if err != nil { t.Fatalf("GetRecallItem after delete: %v", err) } @@ -159,11 +159,19 @@ func TestLibSQLDelegate_ArchivalChunkCRUD(t *testing.T) { d := newTestDelegate(t) ctx := context.Background() + parentRecall := &memory.RecallItem{ + ID: ids.New(), AgentID: "agent-1", SessionKey: "sess-1", + Role: "system", Sector: memory.SectorSemantic, Content: "parent for archival", + } + if err := d.InsertRecallItem(ctx, parentRecall); err != nil { + t.Fatalf("InsertRecallItem (parent): %v", err) + } + embedding := testEmbedding768(0.1, 0.2, 0.3, -0.4, 0.5) chunk := &memory.ArchivalChunk{ ID: ids.New(), - RecallID: ids.New(), + RecallID: parentRecall.ID, ChunkIndex: 0, Content: "This is chunk content for archival", Embedding: embedding, @@ -171,13 +179,11 @@ func TestLibSQLDelegate_ArchivalChunkCRUD(t *testing.T) { Hash: "abc123", } - // Insert if err := d.InsertArchivalChunk(ctx, chunk); err != nil { t.Fatalf("InsertArchivalChunk: %v", err) } - // Get - got, err := d.GetArchivalChunk(ctx, chunk.ID) + got, err := d.GetArchivalChunk(ctx, "agent-1", chunk.ID) if err != nil { t.Fatalf("GetArchivalChunk: %v", err) } @@ -191,7 +197,6 @@ func TestLibSQLDelegate_ArchivalChunkCRUD(t *testing.T) { t.Fatalf("source mismatch: %q", got.Source) } - // Verify embedding round-trip (check first 5 seed values) if len(got.Embedding) != 768 { t.Fatalf("embedding length mismatch: %d vs 768", len(got.Embedding)) } @@ -202,8 +207,16 @@ func TestLibSQLDelegate_ArchivalChunkCRUD(t *testing.T) { } } - // List by recall ID - chunks, err := d.ListArchivalChunks(ctx, chunk.RecallID) + // Verify cross-agent isolation: wrong agentID should not find chunk + wrongAgent, err := d.GetArchivalChunk(ctx, "agent-WRONG", chunk.ID) + if err != nil { + t.Fatalf("GetArchivalChunk wrong agent: %v", err) + } + if wrongAgent != nil { + t.Fatal("expected nil for wrong agentID") + } + + chunks, err := d.ListArchivalChunks(ctx, "agent-1", chunk.RecallID) if err != nil { t.Fatalf("ListArchivalChunks: %v", err) } @@ -211,11 +224,10 @@ func TestLibSQLDelegate_ArchivalChunkCRUD(t *testing.T) { t.Fatalf("expected 1 chunk, got %d", len(chunks)) } - // Delete if err := d.DeleteArchivalChunks(ctx, chunk.RecallID); err != nil { t.Fatalf("DeleteArchivalChunks: %v", err) } - chunks, err = d.ListArchivalChunks(ctx, chunk.RecallID) + chunks, err = d.ListArchivalChunks(ctx, "agent-1", chunk.RecallID) if err != nil { t.Fatalf("ListArchivalChunks after delete: %v", err) } @@ -291,7 +303,7 @@ func TestLibSQLDelegate_Counts(t *testing.T) { t.Fatalf("expected 0 recall items, got %d", rc) } - ac, err := d.CountArchivalChunks(ctx) + ac, err := d.CountArchivalChunks(ctx, "agent-1") if err != nil { t.Fatalf("CountArchivalChunks: %v", err) } diff --git a/pkg/memory/integration_test.go b/pkg/memory/integration_test.go index 74b091a5b..d08a7c6a3 100644 --- a/pkg/memory/integration_test.go +++ b/pkg/memory/integration_test.go @@ -28,6 +28,7 @@ func setupFullStack(t *testing.T) (*memstore.MemoryStore, *delegate.LibSQLDelega chunker := memstore.NewMarkdownChunker(memstore.DefaultMarkdownChunkerConfig()) store := memstore.New(del, chunker, nil, memstore.DefaultConfig()) + store.SetAgentID(testAgent) return store, del } @@ -59,6 +60,7 @@ func TestIntegration_GooseMigration_DownUpRoundTrip(t *testing.T) { } chunker := memstore.NewMarkdownChunker(memstore.DefaultMarkdownChunkerConfig()) store := memstore.New(del, chunker, nil, memstore.DefaultConfig()) + store.SetAgentID(testAgent) require.NoError(t, store.StoreRecall(ctx, item)) require.NoError(t, del.MigrateDown(ctx), "down migration should succeed") @@ -118,7 +120,7 @@ func TestIntegration_ArchivalChunking_EndToEnd(t *testing.T) { require.NoError(t, err) assert.False(t, recallID.IsZero(), "StoreArchival should return a valid recall ID") - chunks, err := del.ListArchivalChunks(ctx, recallID) + chunks, err := del.ListArchivalChunks(ctx, testAgent, recallID) require.NoError(t, err) assert.NotEmpty(t, chunks, "should create at least one archival chunk") @@ -179,14 +181,14 @@ func TestIntegration_CascadeDelete(t *testing.T) { }) require.NoError(t, err) - chunks, err := del.ListArchivalChunks(ctx, recallID) + chunks, err := del.ListArchivalChunks(ctx, testAgent, recallID) require.NoError(t, err) assert.NotEmpty(t, chunks) require.NoError(t, store.DeleteRecall(ctx, recallID)) for _, chunk := range chunks { - fetched, err := del.GetArchivalChunk(ctx, chunk.ID) + fetched, err := del.GetArchivalChunk(ctx, testAgent, chunk.ID) require.NoError(t, err) assert.Nil(t, fetched, "archival chunks should be cascade-deleted with parent recall item") } diff --git a/pkg/memory/sqlc/agent_audit_log.sql.go b/pkg/memory/sqlc/agent_audit_log.sql.go index ae8fccc0b..bc3e00a97 100644 --- a/pkg/memory/sqlc/agent_audit_log.sql.go +++ b/pkg/memory/sqlc/agent_audit_log.sql.go @@ -19,7 +19,7 @@ WHERE agent_id = ?1 ` type CountAuditEntriesParams struct { - AgentID string `json:"agent_id"` + AgentID string `db:"agent_id" json:"agent_id"` } // CountAuditEntries @@ -42,8 +42,8 @@ WHERE agent_id = ?1 ` type CountAuditEntriesByActionParams struct { - AgentID string `json:"agent_id"` - Action string `json:"action"` + AgentID string `db:"agent_id" json:"agent_id"` + Action string `db:"action" json:"action"` } // CountAuditEntriesByAction @@ -85,14 +85,14 @@ VALUES ( ` type InsertAuditEntryParams struct { - ID ids.UUID `json:"id"` - AgentID string `json:"agent_id"` - SessionKey string `json:"session_key"` - Action string `json:"action"` - Target string `json:"target"` - Input *string `json:"input"` - Output *string `json:"output"` - DurationMs *int64 `json:"duration_ms"` + ID ids.UUID `db:"id" json:"id"` + AgentID string `db:"agent_id" json:"agent_id"` + SessionKey string `db:"session_key" json:"session_key"` + Action string `db:"action" json:"action"` + Target string `db:"target" json:"target"` + Input *string `db:"input" json:"input"` + Output *string `db:"output" json:"output"` + DurationMs *int64 `db:"duration_ms" json:"duration_ms"` } // Agent Audit Log queries @@ -150,8 +150,8 @@ LIMIT ?2 ` type ListAuditEntriesParams struct { - AgentID string `json:"agent_id"` - Lim int64 `json:"lim"` + AgentID string `db:"agent_id" json:"agent_id"` + Lim int64 `db:"lim" json:"lim"` } // ListAuditEntries @@ -220,9 +220,9 @@ LIMIT ?3 ` type ListAuditEntriesByActionParams struct { - AgentID string `json:"agent_id"` - Action string `json:"action"` - Lim int64 `json:"lim"` + AgentID string `db:"agent_id" json:"agent_id"` + Action string `db:"action" json:"action"` + Lim int64 `db:"lim" json:"lim"` } // ListAuditEntriesByAction @@ -292,9 +292,9 @@ LIMIT ?3 ` type ListAuditEntriesBySessionParams struct { - AgentID string `json:"agent_id"` - SessionKey string `json:"session_key"` - Lim int64 `json:"lim"` + AgentID string `db:"agent_id" json:"agent_id"` + SessionKey string `db:"session_key" json:"session_key"` + Lim int64 `db:"lim" json:"lim"` } // ListAuditEntriesBySession @@ -353,8 +353,8 @@ WHERE agent_id = ?1 ` type PruneOldAuditEntriesParams struct { - AgentID string `json:"agent_id"` - Before time.Time `json:"before"` + AgentID string `db:"agent_id" json:"agent_id"` + Before time.Time `db:"before" json:"before"` } // PruneOldAuditEntries diff --git a/pkg/memory/sqlc/agent_conversation_forks.sql.go b/pkg/memory/sqlc/agent_conversation_forks.sql.go new file mode 100644 index 000000000..affa6f08b --- /dev/null +++ b/pkg/memory/sqlc/agent_conversation_forks.sql.go @@ -0,0 +1,136 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: agent_conversation_forks.sql + +package sqlc + +import ( + "context" + "encoding/json" + + "github.com/sipeed/picoclaw/pkg/ids" +) + +const CreateAgentConversationFork = `-- name: CreateAgentConversationFork :one +INSERT INTO agent_conversation_forks ( + id, parent_conversation_id, child_conversation_id, checkpoint_id, metadata_json +) +VALUES (?, ?, ?, ?, ?) +RETURNING id, parent_conversation_id, child_conversation_id, checkpoint_id, metadata_json, created_at, updated_at +` + +type CreateAgentConversationForkParams struct { + ID ids.UUID `db:"id" json:"id"` + ParentConversationID ids.UUID `db:"parent_conversation_id" json:"parent_conversation_id"` + ChildConversationID ids.UUID `db:"child_conversation_id" json:"child_conversation_id"` + CheckpointID ids.UUID `db:"checkpoint_id" json:"checkpoint_id"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` +} + +// CreateAgentConversationFork +// +// INSERT INTO agent_conversation_forks ( +// id, parent_conversation_id, child_conversation_id, checkpoint_id, metadata_json +// ) +// VALUES (?, ?, ?, ?, ?) +// RETURNING id, parent_conversation_id, child_conversation_id, checkpoint_id, metadata_json, created_at, updated_at +func (q *Queries) CreateAgentConversationFork(ctx context.Context, arg CreateAgentConversationForkParams) (AgentConversationFork, error) { + row := q.db.QueryRowContext(ctx, CreateAgentConversationFork, + arg.ID, + arg.ParentConversationID, + arg.ChildConversationID, + arg.CheckpointID, + arg.MetadataJson, + ) + var i AgentConversationFork + err := row.Scan( + &i.ID, + &i.ParentConversationID, + &i.ChildConversationID, + &i.CheckpointID, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const GetAgentConversationForkByChildConversationID = `-- name: GetAgentConversationForkByChildConversationID :one +SELECT id, parent_conversation_id, child_conversation_id, checkpoint_id, metadata_json, created_at, updated_at FROM agent_conversation_forks +WHERE child_conversation_id = ? +LIMIT 1 +` + +type GetAgentConversationForkByChildConversationIDParams struct { + ChildConversationID ids.UUID `db:"child_conversation_id" json:"child_conversation_id"` +} + +// GetAgentConversationForkByChildConversationID +// +// SELECT id, parent_conversation_id, child_conversation_id, checkpoint_id, metadata_json, created_at, updated_at FROM agent_conversation_forks +// WHERE child_conversation_id = ? +// LIMIT 1 +func (q *Queries) GetAgentConversationForkByChildConversationID(ctx context.Context, arg GetAgentConversationForkByChildConversationIDParams) (AgentConversationFork, error) { + row := q.db.QueryRowContext(ctx, GetAgentConversationForkByChildConversationID, arg.ChildConversationID) + var i AgentConversationFork + err := row.Scan( + &i.ID, + &i.ParentConversationID, + &i.ChildConversationID, + &i.CheckpointID, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const ListAgentConversationForksByParentConversationID = `-- name: ListAgentConversationForksByParentConversationID :many +SELECT id, parent_conversation_id, child_conversation_id, checkpoint_id, metadata_json, created_at, updated_at FROM agent_conversation_forks +WHERE parent_conversation_id = ? +ORDER BY created_at DESC +LIMIT ?2 +` + +type ListAgentConversationForksByParentConversationIDParams struct { + ParentConversationID ids.UUID `db:"parent_conversation_id" json:"parent_conversation_id"` + Lim int64 `db:"lim" json:"lim"` +} + +// ListAgentConversationForksByParentConversationID +// +// SELECT id, parent_conversation_id, child_conversation_id, checkpoint_id, metadata_json, created_at, updated_at FROM agent_conversation_forks +// WHERE parent_conversation_id = ? +// ORDER BY created_at DESC +// LIMIT ?2 +func (q *Queries) ListAgentConversationForksByParentConversationID(ctx context.Context, arg ListAgentConversationForksByParentConversationIDParams) ([]AgentConversationFork, error) { + rows, err := q.db.QueryContext(ctx, ListAgentConversationForksByParentConversationID, arg.ParentConversationID, arg.Lim) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AgentConversationFork{} + for rows.Next() { + var i AgentConversationFork + if err := rows.Scan( + &i.ID, + &i.ParentConversationID, + &i.ChildConversationID, + &i.CheckpointID, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/pkg/memory/sqlc/agent_conversation_links.sql.go b/pkg/memory/sqlc/agent_conversation_links.sql.go new file mode 100644 index 000000000..fcadf1403 --- /dev/null +++ b/pkg/memory/sqlc/agent_conversation_links.sql.go @@ -0,0 +1,126 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: agent_conversation_links.sql + +package sqlc + +import ( + "context" + "encoding/json" + + "github.com/sipeed/picoclaw/pkg/ids" +) + +const CreateAgentConversationLink = `-- name: CreateAgentConversationLink :one +INSERT INTO agent_conversation_links ( + id, conversation_id, linked_conversation_id, kind, metadata_json +) +VALUES (?, ?, ?, ?, ?) +RETURNING id, conversation_id, linked_conversation_id, kind, metadata_json, created_at, updated_at +` + +type CreateAgentConversationLinkParams struct { + ID ids.UUID `db:"id" json:"id"` + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + LinkedConversationID ids.UUID `db:"linked_conversation_id" json:"linked_conversation_id"` + Kind string `db:"kind" json:"kind"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` +} + +// CreateAgentConversationLink +// +// INSERT INTO agent_conversation_links ( +// id, conversation_id, linked_conversation_id, kind, metadata_json +// ) +// VALUES (?, ?, ?, ?, ?) +// RETURNING id, conversation_id, linked_conversation_id, kind, metadata_json, created_at, updated_at +func (q *Queries) CreateAgentConversationLink(ctx context.Context, arg CreateAgentConversationLinkParams) (AgentConversationLink, error) { + row := q.db.QueryRowContext(ctx, CreateAgentConversationLink, + arg.ID, + arg.ConversationID, + arg.LinkedConversationID, + arg.Kind, + arg.MetadataJson, + ) + var i AgentConversationLink + err := row.Scan( + &i.ID, + &i.ConversationID, + &i.LinkedConversationID, + &i.Kind, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const DeleteAgentConversationLink = `-- name: DeleteAgentConversationLink :exec +DELETE FROM agent_conversation_links +WHERE conversation_id = ? AND linked_conversation_id = ? AND kind = ? +` + +type DeleteAgentConversationLinkParams struct { + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + LinkedConversationID ids.UUID `db:"linked_conversation_id" json:"linked_conversation_id"` + Kind string `db:"kind" json:"kind"` +} + +// DeleteAgentConversationLink +// +// DELETE FROM agent_conversation_links +// WHERE conversation_id = ? AND linked_conversation_id = ? AND kind = ? +func (q *Queries) DeleteAgentConversationLink(ctx context.Context, arg DeleteAgentConversationLinkParams) error { + _, err := q.db.ExecContext(ctx, DeleteAgentConversationLink, arg.ConversationID, arg.LinkedConversationID, arg.Kind) + return err +} + +const ListAgentConversationLinksByConversationID = `-- name: ListAgentConversationLinksByConversationID :many +SELECT id, conversation_id, linked_conversation_id, kind, metadata_json, created_at, updated_at FROM agent_conversation_links +WHERE conversation_id = ? +ORDER BY created_at DESC +LIMIT ?2 +` + +type ListAgentConversationLinksByConversationIDParams struct { + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + Lim int64 `db:"lim" json:"lim"` +} + +// ListAgentConversationLinksByConversationID +// +// SELECT id, conversation_id, linked_conversation_id, kind, metadata_json, created_at, updated_at FROM agent_conversation_links +// WHERE conversation_id = ? +// ORDER BY created_at DESC +// LIMIT ?2 +func (q *Queries) ListAgentConversationLinksByConversationID(ctx context.Context, arg ListAgentConversationLinksByConversationIDParams) ([]AgentConversationLink, error) { + rows, err := q.db.QueryContext(ctx, ListAgentConversationLinksByConversationID, arg.ConversationID, arg.Lim) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AgentConversationLink{} + for rows.Next() { + var i AgentConversationLink + if err := rows.Scan( + &i.ID, + &i.ConversationID, + &i.LinkedConversationID, + &i.Kind, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/pkg/memory/sqlc/agent_conversations.sql.go b/pkg/memory/sqlc/agent_conversations.sql.go new file mode 100644 index 000000000..61992c428 --- /dev/null +++ b/pkg/memory/sqlc/agent_conversations.sql.go @@ -0,0 +1,134 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: agent_conversations.sql + +package sqlc + +import ( + "context" + + "github.com/sipeed/picoclaw/pkg/ids" +) + +const CreateAgentConversation = `-- name: CreateAgentConversation :one +INSERT INTO agent_conversations (id, title) +VALUES (?, ?) +RETURNING id, title, created_at, updated_at +` + +type CreateAgentConversationParams struct { + ID ids.UUID `db:"id" json:"id"` + Title *string `db:"title" json:"title"` +} + +// CreateAgentConversation +// +// INSERT INTO agent_conversations (id, title) +// VALUES (?, ?) +// RETURNING id, title, created_at, updated_at +func (q *Queries) CreateAgentConversation(ctx context.Context, arg CreateAgentConversationParams) (AgentConversation, error) { + row := q.db.QueryRowContext(ctx, CreateAgentConversation, arg.ID, arg.Title) + var i AgentConversation + err := row.Scan( + &i.ID, + &i.Title, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const GetAgentConversation = `-- name: GetAgentConversation :one +SELECT id, title, created_at, updated_at FROM agent_conversations WHERE id = ? LIMIT 1 +` + +type GetAgentConversationParams struct { + ID ids.UUID `db:"id" json:"id"` +} + +// GetAgentConversation +// +// SELECT id, title, created_at, updated_at FROM agent_conversations WHERE id = ? LIMIT 1 +func (q *Queries) GetAgentConversation(ctx context.Context, arg GetAgentConversationParams) (AgentConversation, error) { + row := q.db.QueryRowContext(ctx, GetAgentConversation, arg.ID) + var i AgentConversation + err := row.Scan( + &i.ID, + &i.Title, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const ListAgentConversations = `-- name: ListAgentConversations :many +SELECT id, title, created_at, updated_at FROM agent_conversations ORDER BY created_at DESC LIMIT ? +` + +type ListAgentConversationsParams struct { + Limit int64 `db:"limit" json:"limit"` +} + +// ListAgentConversations +// +// SELECT id, title, created_at, updated_at FROM agent_conversations ORDER BY created_at DESC LIMIT ? +func (q *Queries) ListAgentConversations(ctx context.Context, arg ListAgentConversationsParams) ([]AgentConversation, error) { + rows, err := q.db.QueryContext(ctx, ListAgentConversations, arg.Limit) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AgentConversation{} + for rows.Next() { + var i AgentConversation + if err := rows.Scan( + &i.ID, + &i.Title, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const UpdateAgentConversationTitle = `-- name: UpdateAgentConversationTitle :one +UPDATE agent_conversations +SET title = ?, + updated_at = (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) +WHERE id = ? +RETURNING id, title, created_at, updated_at +` + +type UpdateAgentConversationTitleParams struct { + Title *string `db:"title" json:"title"` + ID ids.UUID `db:"id" json:"id"` +} + +// UpdateAgentConversationTitle +// +// UPDATE agent_conversations +// SET title = ?, +// updated_at = (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) +// WHERE id = ? +// RETURNING id, title, created_at, updated_at +func (q *Queries) UpdateAgentConversationTitle(ctx context.Context, arg UpdateAgentConversationTitleParams) (AgentConversation, error) { + row := q.db.QueryRowContext(ctx, UpdateAgentConversationTitle, arg.Title, arg.ID) + var i AgentConversation + err := row.Scan( + &i.ID, + &i.Title, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} diff --git a/pkg/memory/sqlc/agent_documents.sql.go b/pkg/memory/sqlc/agent_documents.sql.go index 43530f112..9fb30f9aa 100644 --- a/pkg/memory/sqlc/agent_documents.sql.go +++ b/pkg/memory/sqlc/agent_documents.sql.go @@ -18,8 +18,8 @@ WHERE agent_id = ?1 ` type DeleteDocumentParams struct { - AgentID string `json:"agent_id"` - Name string `json:"name"` + AgentID string `db:"agent_id" json:"agent_id"` + Name string `db:"name" json:"name"` } // DeleteDocument @@ -45,11 +45,12 @@ SELECT id, FROM agent_documents WHERE agent_id = ?1 AND name = ?2 +LIMIT 1 ` type GetDocumentParams struct { - AgentID string `json:"agent_id"` - Name string `json:"name"` + AgentID string `db:"agent_id" json:"agent_id"` + Name string `db:"name" json:"name"` } // Agent Documents queries @@ -66,6 +67,7 @@ type GetDocumentParams struct { // FROM agent_documents // WHERE agent_id = ?1 // AND name = ?2 +// LIMIT 1 func (q *Queries) GetDocument(ctx context.Context, arg GetDocumentParams) (AgentDocument, error) { row := q.db.QueryRowContext(ctx, GetDocument, arg.AgentID, arg.Name) var i AgentDocument @@ -98,10 +100,12 @@ WHERE agent_id = ?1 AND is_active = 1 ORDER BY category, name +LIMIT ?2 ` type ListAllDocumentsParams struct { - AgentID string `json:"agent_id"` + AgentID string `db:"agent_id" json:"agent_id"` + Lim int64 `db:"lim" json:"lim"` } // ListAllDocuments @@ -120,8 +124,9 @@ type ListAllDocumentsParams struct { // AND is_active = 1 // ORDER BY category, // name +// LIMIT ?2 func (q *Queries) ListAllDocuments(ctx context.Context, arg ListAllDocumentsParams) ([]AgentDocument, error) { - rows, err := q.db.QueryContext(ctx, ListAllDocuments, arg.AgentID) + rows, err := q.db.QueryContext(ctx, ListAllDocuments, arg.AgentID, arg.Lim) if err != nil { return nil, err } @@ -168,11 +173,13 @@ WHERE agent_id = ?1 AND category = ?2 AND is_active = 1 ORDER BY name +LIMIT ?3 ` type ListDocumentsByCategoryParams struct { - AgentID string `json:"agent_id"` - Category string `json:"category"` + AgentID string `db:"agent_id" json:"agent_id"` + Category string `db:"category" json:"category"` + Lim int64 `db:"lim" json:"lim"` } // ListDocumentsByCategory @@ -191,8 +198,9 @@ type ListDocumentsByCategoryParams struct { // AND category = ?2 // AND is_active = 1 // ORDER BY name +// LIMIT ?3 func (q *Queries) ListDocumentsByCategory(ctx context.Context, arg ListDocumentsByCategoryParams) ([]AgentDocument, error) { - rows, err := q.db.QueryContext(ctx, ListDocumentsByCategory, arg.AgentID, arg.Category) + rows, err := q.db.QueryContext(ctx, ListDocumentsByCategory, arg.AgentID, arg.Category, arg.Lim) if err != nil { return nil, err } @@ -256,11 +264,11 @@ SET content = excluded.content, ` type UpsertDocumentParams struct { - ID ids.UUID `json:"id"` - AgentID string `json:"agent_id"` - Name string `json:"name"` - Category string `json:"category"` - Content string `json:"content"` + ID ids.UUID `db:"id" json:"id"` + AgentID string `db:"agent_id" json:"agent_id"` + Name string `db:"name" json:"name"` + Category string `db:"category" json:"category"` + Content string `db:"content" json:"content"` } // UpsertDocument diff --git a/pkg/memory/sqlc/agent_kv.sql.go b/pkg/memory/sqlc/agent_kv.sql.go index 4edb3521c..e402ff843 100644 --- a/pkg/memory/sqlc/agent_kv.sql.go +++ b/pkg/memory/sqlc/agent_kv.sql.go @@ -16,8 +16,8 @@ WHERE agent_id = ?1 ` type DeleteKVParams struct { - AgentID string `json:"agent_id"` - Key string `json:"key"` + AgentID string `db:"agent_id" json:"agent_id"` + Key string `db:"key" json:"key"` } // DeleteKV @@ -38,11 +38,12 @@ SELECT agent_id, FROM agent_kv WHERE agent_id = ?1 AND key = ?2 +LIMIT 1 ` type GetKVParams struct { - AgentID string `json:"agent_id"` - Key string `json:"key"` + AgentID string `db:"agent_id" json:"agent_id"` + Key string `db:"key" json:"key"` } // Agent KV Store queries @@ -54,6 +55,7 @@ type GetKVParams struct { // FROM agent_kv // WHERE agent_id = ?1 // AND key = ?2 +// LIMIT 1 func (q *Queries) GetKV(ctx context.Context, arg GetKVParams) (AgentKv, error) { row := q.db.QueryRowContext(ctx, GetKV, arg.AgentID, arg.Key) var i AgentKv @@ -79,9 +81,9 @@ LIMIT ?3 ` type ListKVByPrefixParams struct { - AgentID string `json:"agent_id"` - Prefix *string `json:"prefix"` - Lim int64 `json:"lim"` + AgentID string `db:"agent_id" json:"agent_id"` + Prefix *string `db:"prefix" json:"prefix"` + Lim int64 `db:"lim" json:"lim"` } // ListKVByPrefix @@ -137,9 +139,9 @@ SET value = excluded.value, ` type UpsertKVParams struct { - AgentID string `json:"agent_id"` - Key string `json:"key"` - Value string `json:"value"` + AgentID string `db:"agent_id" json:"agent_id"` + Key string `db:"key" json:"key"` + Value string `db:"value" json:"value"` } // UpsertKV diff --git a/pkg/memory/sqlc/agent_mentions.sql.go b/pkg/memory/sqlc/agent_mentions.sql.go new file mode 100644 index 000000000..217723fef --- /dev/null +++ b/pkg/memory/sqlc/agent_mentions.sql.go @@ -0,0 +1,114 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: agent_mentions.sql + +package sqlc + +import ( + "context" + "encoding/json" + + "github.com/sipeed/picoclaw/pkg/ids" +) + +const AddAgentMention = `-- name: AddAgentMention :one +INSERT INTO agent_mentions ( + id, conversation_id, message_id, kind, target_id, raw, metadata_json +) +VALUES (?, ?, ?, ?, ?, ?, ?) +RETURNING id, conversation_id, message_id, kind, target_id, raw, metadata_json, created_at, updated_at +` + +type AddAgentMentionParams struct { + ID ids.UUID `db:"id" json:"id"` + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + MessageID ids.UUID `db:"message_id" json:"message_id"` + Kind string `db:"kind" json:"kind"` + TargetID ids.UUID `db:"target_id" json:"target_id"` + Raw string `db:"raw" json:"raw"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` +} + +// AddAgentMention +// +// INSERT INTO agent_mentions ( +// id, conversation_id, message_id, kind, target_id, raw, metadata_json +// ) +// VALUES (?, ?, ?, ?, ?, ?, ?) +// RETURNING id, conversation_id, message_id, kind, target_id, raw, metadata_json, created_at, updated_at +func (q *Queries) AddAgentMention(ctx context.Context, arg AddAgentMentionParams) (AgentMention, error) { + row := q.db.QueryRowContext(ctx, AddAgentMention, + arg.ID, + arg.ConversationID, + arg.MessageID, + arg.Kind, + arg.TargetID, + arg.Raw, + arg.MetadataJson, + ) + var i AgentMention + err := row.Scan( + &i.ID, + &i.ConversationID, + &i.MessageID, + &i.Kind, + &i.TargetID, + &i.Raw, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const ListAgentMentionsByConversationID = `-- name: ListAgentMentionsByConversationID :many +SELECT id, conversation_id, message_id, kind, target_id, raw, metadata_json, created_at, updated_at FROM agent_mentions +WHERE conversation_id = ? +ORDER BY created_at DESC +LIMIT ?2 +` + +type ListAgentMentionsByConversationIDParams struct { + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + Lim int64 `db:"lim" json:"lim"` +} + +// ListAgentMentionsByConversationID +// +// SELECT id, conversation_id, message_id, kind, target_id, raw, metadata_json, created_at, updated_at FROM agent_mentions +// WHERE conversation_id = ? +// ORDER BY created_at DESC +// LIMIT ?2 +func (q *Queries) ListAgentMentionsByConversationID(ctx context.Context, arg ListAgentMentionsByConversationIDParams) ([]AgentMention, error) { + rows, err := q.db.QueryContext(ctx, ListAgentMentionsByConversationID, arg.ConversationID, arg.Lim) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AgentMention{} + for rows.Next() { + var i AgentMention + if err := rows.Scan( + &i.ID, + &i.ConversationID, + &i.MessageID, + &i.Kind, + &i.TargetID, + &i.Raw, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/pkg/memory/sqlc/agent_message_revisions.sql.go b/pkg/memory/sqlc/agent_message_revisions.sql.go new file mode 100644 index 000000000..f18cb70cc --- /dev/null +++ b/pkg/memory/sqlc/agent_message_revisions.sql.go @@ -0,0 +1,110 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: agent_message_revisions.sql + +package sqlc + +import ( + "context" + "encoding/json" + + "github.com/sipeed/picoclaw/pkg/ids" +) + +const AddAgentMessageRevision = `-- name: AddAgentMessageRevision :one +INSERT INTO agent_message_revisions ( + id, message_id, editor, old_content, new_content, metadata_json +) +VALUES (?, ?, ?, ?, ?, ?) +RETURNING id, message_id, editor, old_content, new_content, metadata_json, created_at, updated_at +` + +type AddAgentMessageRevisionParams struct { + ID ids.UUID `db:"id" json:"id"` + MessageID ids.UUID `db:"message_id" json:"message_id"` + Editor string `db:"editor" json:"editor"` + OldContent string `db:"old_content" json:"old_content"` + NewContent string `db:"new_content" json:"new_content"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` +} + +// AddAgentMessageRevision +// +// INSERT INTO agent_message_revisions ( +// id, message_id, editor, old_content, new_content, metadata_json +// ) +// VALUES (?, ?, ?, ?, ?, ?) +// RETURNING id, message_id, editor, old_content, new_content, metadata_json, created_at, updated_at +func (q *Queries) AddAgentMessageRevision(ctx context.Context, arg AddAgentMessageRevisionParams) (AgentMessageRevision, error) { + row := q.db.QueryRowContext(ctx, AddAgentMessageRevision, + arg.ID, + arg.MessageID, + arg.Editor, + arg.OldContent, + arg.NewContent, + arg.MetadataJson, + ) + var i AgentMessageRevision + err := row.Scan( + &i.ID, + &i.MessageID, + &i.Editor, + &i.OldContent, + &i.NewContent, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const ListAgentMessageRevisionsByMessageID = `-- name: ListAgentMessageRevisionsByMessageID :many +SELECT id, message_id, editor, old_content, new_content, metadata_json, created_at, updated_at FROM agent_message_revisions +WHERE message_id = ? +ORDER BY created_at DESC +LIMIT ?2 +` + +type ListAgentMessageRevisionsByMessageIDParams struct { + MessageID ids.UUID `db:"message_id" json:"message_id"` + Lim int64 `db:"lim" json:"lim"` +} + +// ListAgentMessageRevisionsByMessageID +// +// SELECT id, message_id, editor, old_content, new_content, metadata_json, created_at, updated_at FROM agent_message_revisions +// WHERE message_id = ? +// ORDER BY created_at DESC +// LIMIT ?2 +func (q *Queries) ListAgentMessageRevisionsByMessageID(ctx context.Context, arg ListAgentMessageRevisionsByMessageIDParams) ([]AgentMessageRevision, error) { + rows, err := q.db.QueryContext(ctx, ListAgentMessageRevisionsByMessageID, arg.MessageID, arg.Lim) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AgentMessageRevision{} + for rows.Next() { + var i AgentMessageRevision + if err := rows.Scan( + &i.ID, + &i.MessageID, + &i.Editor, + &i.OldContent, + &i.NewContent, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/pkg/memory/sqlc/agent_messages.sql.go b/pkg/memory/sqlc/agent_messages.sql.go new file mode 100644 index 000000000..2ad45d310 --- /dev/null +++ b/pkg/memory/sqlc/agent_messages.sql.go @@ -0,0 +1,148 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: agent_messages.sql + +package sqlc + +import ( + "context" + "encoding/json" + + "github.com/sipeed/picoclaw/pkg/ids" +) + +const AddAgentMessage = `-- name: AddAgentMessage :one +INSERT INTO agent_messages (id, conversation_id, role, content, metadata_json) +VALUES (?, ?, ?, ?, ?) +RETURNING id, conversation_id, role, content, metadata_json, created_at, updated_at +` + +type AddAgentMessageParams struct { + ID ids.UUID `db:"id" json:"id"` + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + Role string `db:"role" json:"role"` + Content string `db:"content" json:"content"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` +} + +// AddAgentMessage +// +// INSERT INTO agent_messages (id, conversation_id, role, content, metadata_json) +// VALUES (?, ?, ?, ?, ?) +// RETURNING id, conversation_id, role, content, metadata_json, created_at, updated_at +func (q *Queries) AddAgentMessage(ctx context.Context, arg AddAgentMessageParams) (AgentMessage, error) { + row := q.db.QueryRowContext(ctx, AddAgentMessage, + arg.ID, + arg.ConversationID, + arg.Role, + arg.Content, + arg.MetadataJson, + ) + var i AgentMessage + err := row.Scan( + &i.ID, + &i.ConversationID, + &i.Role, + &i.Content, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const ListAgentMessagesByConversationID = `-- name: ListAgentMessagesByConversationID :many +SELECT id, conversation_id, role, content, metadata_json, created_at, updated_at FROM agent_messages +WHERE conversation_id = ? +ORDER BY created_at ASC +` + +type ListAgentMessagesByConversationIDParams struct { + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` +} + +// ListAgentMessagesByConversationID +// +// SELECT id, conversation_id, role, content, metadata_json, created_at, updated_at FROM agent_messages +// WHERE conversation_id = ? +// ORDER BY created_at ASC +func (q *Queries) ListAgentMessagesByConversationID(ctx context.Context, arg ListAgentMessagesByConversationIDParams) ([]AgentMessage, error) { + rows, err := q.db.QueryContext(ctx, ListAgentMessagesByConversationID, arg.ConversationID) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AgentMessage{} + for rows.Next() { + var i AgentMessage + if err := rows.Scan( + &i.ID, + &i.ConversationID, + &i.Role, + &i.Content, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const ListAgentMessagesByConversationIDLimit = `-- name: ListAgentMessagesByConversationIDLimit :many +SELECT id, conversation_id, role, content, metadata_json, created_at, updated_at FROM agent_messages +WHERE conversation_id = ? +ORDER BY created_at ASC +LIMIT ? +` + +type ListAgentMessagesByConversationIDLimitParams struct { + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + Limit int64 `db:"limit" json:"limit"` +} + +// ListAgentMessagesByConversationIDLimit +// +// SELECT id, conversation_id, role, content, metadata_json, created_at, updated_at FROM agent_messages +// WHERE conversation_id = ? +// ORDER BY created_at ASC +// LIMIT ? +func (q *Queries) ListAgentMessagesByConversationIDLimit(ctx context.Context, arg ListAgentMessagesByConversationIDLimitParams) ([]AgentMessage, error) { + rows, err := q.db.QueryContext(ctx, ListAgentMessagesByConversationIDLimit, arg.ConversationID, arg.Limit) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AgentMessage{} + for rows.Next() { + var i AgentMessage + if err := rows.Scan( + &i.ID, + &i.ConversationID, + &i.Role, + &i.Content, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/pkg/memory/sqlc/agent_state.sql.go b/pkg/memory/sqlc/agent_state.sql.go new file mode 100644 index 000000000..d247a8f52 --- /dev/null +++ b/pkg/memory/sqlc/agent_state.sql.go @@ -0,0 +1,499 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: agent_state.sql + +package sqlc + +import ( + "context" + "encoding/json" + "time" + + "github.com/sipeed/picoclaw/pkg/ids" +) + +const AddAgentRunState = `-- name: AddAgentRunState :one +INSERT INTO agent_run_states (id, run_id, step_index, state, snapshot_json) +VALUES (?, ?, ?, ?, ?) +RETURNING id, run_id, step_index, state, snapshot_json, created_at, updated_at +` + +type AddAgentRunStateParams struct { + ID ids.UUID `db:"id" json:"id"` + RunID ids.UUID `db:"run_id" json:"run_id"` + StepIndex int64 `db:"step_index" json:"step_index"` + State string `db:"state" json:"state"` + SnapshotJson json.RawMessage `db:"snapshot_json" json:"snapshot_json"` +} + +// AddAgentRunState +// +// INSERT INTO agent_run_states (id, run_id, step_index, state, snapshot_json) +// VALUES (?, ?, ?, ?, ?) +// RETURNING id, run_id, step_index, state, snapshot_json, created_at, updated_at +func (q *Queries) AddAgentRunState(ctx context.Context, arg AddAgentRunStateParams) (AgentRunState, error) { + row := q.db.QueryRowContext(ctx, AddAgentRunState, + arg.ID, + arg.RunID, + arg.StepIndex, + arg.State, + arg.SnapshotJson, + ) + var i AgentRunState + err := row.Scan( + &i.ID, + &i.RunID, + &i.StepIndex, + &i.State, + &i.SnapshotJson, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const AddAgentStateTransition = `-- name: AddAgentStateTransition :one +INSERT INTO agent_state_transitions ( + id, run_id, step_index, from_state, to_state, trigger, at, meta_json, error +) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) +RETURNING id, run_id, step_index, from_state, to_state, "trigger", at, meta_json, error, created_at, updated_at +` + +type AddAgentStateTransitionParams struct { + ID ids.UUID `db:"id" json:"id"` + RunID ids.UUID `db:"run_id" json:"run_id"` + StepIndex int64 `db:"step_index" json:"step_index"` + FromState string `db:"from_state" json:"from_state"` + ToState string `db:"to_state" json:"to_state"` + Trigger string `db:"trigger" json:"trigger"` + At time.Time `db:"at" json:"at"` + MetaJson json.RawMessage `db:"meta_json" json:"meta_json"` + Error *string `db:"error" json:"error"` +} + +// AddAgentStateTransition +// +// INSERT INTO agent_state_transitions ( +// id, run_id, step_index, from_state, to_state, trigger, at, meta_json, error +// ) +// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) +// RETURNING id, run_id, step_index, from_state, to_state, "trigger", at, meta_json, error, created_at, updated_at +func (q *Queries) AddAgentStateTransition(ctx context.Context, arg AddAgentStateTransitionParams) (AgentStateTransition, error) { + row := q.db.QueryRowContext(ctx, AddAgentStateTransition, + arg.ID, + arg.RunID, + arg.StepIndex, + arg.FromState, + arg.ToState, + arg.Trigger, + arg.At, + arg.MetaJson, + arg.Error, + ) + var i AgentStateTransition + err := row.Scan( + &i.ID, + &i.RunID, + &i.StepIndex, + &i.FromState, + &i.ToState, + &i.Trigger, + &i.At, + &i.MetaJson, + &i.Error, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const CreateAgentCheckpoint = `-- name: CreateAgentCheckpoint :one +INSERT INTO agent_checkpoints (id, conversation_id, name, run_state_id, metadata_json) +VALUES (?, ?, ?, ?, ?) +RETURNING id, conversation_id, name, run_state_id, metadata_json, created_at, updated_at +` + +type CreateAgentCheckpointParams struct { + ID ids.UUID `db:"id" json:"id"` + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + Name string `db:"name" json:"name"` + RunStateID ids.UUID `db:"run_state_id" json:"run_state_id"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` +} + +// CreateAgentCheckpoint +// +// INSERT INTO agent_checkpoints (id, conversation_id, name, run_state_id, metadata_json) +// VALUES (?, ?, ?, ?, ?) +// RETURNING id, conversation_id, name, run_state_id, metadata_json, created_at, updated_at +func (q *Queries) CreateAgentCheckpoint(ctx context.Context, arg CreateAgentCheckpointParams) (AgentCheckpoint, error) { + row := q.db.QueryRowContext(ctx, CreateAgentCheckpoint, + arg.ID, + arg.ConversationID, + arg.Name, + arg.RunStateID, + arg.MetadataJson, + ) + var i AgentCheckpoint + err := row.Scan( + &i.ID, + &i.ConversationID, + &i.Name, + &i.RunStateID, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const CreateAgentRun = `-- name: CreateAgentRun :one +INSERT INTO agent_runs (id, conversation_id, status, metadata_json) +VALUES (?, ?, ?, ?) +RETURNING id, conversation_id, status, metadata_json, created_at, updated_at +` + +type CreateAgentRunParams struct { + ID ids.UUID `db:"id" json:"id"` + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + Status string `db:"status" json:"status"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` +} + +// CreateAgentRun +// +// INSERT INTO agent_runs (id, conversation_id, status, metadata_json) +// VALUES (?, ?, ?, ?) +// RETURNING id, conversation_id, status, metadata_json, created_at, updated_at +func (q *Queries) CreateAgentRun(ctx context.Context, arg CreateAgentRunParams) (AgentRun, error) { + row := q.db.QueryRowContext(ctx, CreateAgentRun, + arg.ID, + arg.ConversationID, + arg.Status, + arg.MetadataJson, + ) + var i AgentRun + err := row.Scan( + &i.ID, + &i.ConversationID, + &i.Status, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const GetAgentCheckpointByConversationIDAndName = `-- name: GetAgentCheckpointByConversationIDAndName :one +SELECT id, conversation_id, name, run_state_id, metadata_json, created_at, updated_at FROM agent_checkpoints +WHERE conversation_id = ? AND name = ? +LIMIT 1 +` + +type GetAgentCheckpointByConversationIDAndNameParams struct { + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + Name string `db:"name" json:"name"` +} + +// GetAgentCheckpointByConversationIDAndName +// +// SELECT id, conversation_id, name, run_state_id, metadata_json, created_at, updated_at FROM agent_checkpoints +// WHERE conversation_id = ? AND name = ? +// LIMIT 1 +func (q *Queries) GetAgentCheckpointByConversationIDAndName(ctx context.Context, arg GetAgentCheckpointByConversationIDAndNameParams) (AgentCheckpoint, error) { + row := q.db.QueryRowContext(ctx, GetAgentCheckpointByConversationIDAndName, arg.ConversationID, arg.Name) + var i AgentCheckpoint + err := row.Scan( + &i.ID, + &i.ConversationID, + &i.Name, + &i.RunStateID, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const GetAgentRunStateByID = `-- name: GetAgentRunStateByID :one +SELECT id, run_id, step_index, state, snapshot_json, created_at, updated_at FROM agent_run_states +WHERE id = ? +LIMIT 1 +` + +type GetAgentRunStateByIDParams struct { + ID ids.UUID `db:"id" json:"id"` +} + +// GetAgentRunStateByID +// +// SELECT id, run_id, step_index, state, snapshot_json, created_at, updated_at FROM agent_run_states +// WHERE id = ? +// LIMIT 1 +func (q *Queries) GetAgentRunStateByID(ctx context.Context, arg GetAgentRunStateByIDParams) (AgentRunState, error) { + row := q.db.QueryRowContext(ctx, GetAgentRunStateByID, arg.ID) + var i AgentRunState + err := row.Scan( + &i.ID, + &i.RunID, + &i.StepIndex, + &i.State, + &i.SnapshotJson, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const GetLatestAgentRunByConversationID = `-- name: GetLatestAgentRunByConversationID :one +SELECT id, conversation_id, status, metadata_json, created_at, updated_at FROM agent_runs +WHERE conversation_id = ? +ORDER BY created_at DESC +LIMIT 1 +` + +type GetLatestAgentRunByConversationIDParams struct { + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` +} + +// GetLatestAgentRunByConversationID +// +// SELECT id, conversation_id, status, metadata_json, created_at, updated_at FROM agent_runs +// WHERE conversation_id = ? +// ORDER BY created_at DESC +// LIMIT 1 +func (q *Queries) GetLatestAgentRunByConversationID(ctx context.Context, arg GetLatestAgentRunByConversationIDParams) (AgentRun, error) { + row := q.db.QueryRowContext(ctx, GetLatestAgentRunByConversationID, arg.ConversationID) + var i AgentRun + err := row.Scan( + &i.ID, + &i.ConversationID, + &i.Status, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const GetLatestAgentRunStateByRunID = `-- name: GetLatestAgentRunStateByRunID :one +SELECT id, run_id, step_index, state, snapshot_json, created_at, updated_at FROM agent_run_states +WHERE run_id = ? +ORDER BY step_index DESC +LIMIT 1 +` + +type GetLatestAgentRunStateByRunIDParams struct { + RunID ids.UUID `db:"run_id" json:"run_id"` +} + +// GetLatestAgentRunStateByRunID +// +// SELECT id, run_id, step_index, state, snapshot_json, created_at, updated_at FROM agent_run_states +// WHERE run_id = ? +// ORDER BY step_index DESC +// LIMIT 1 +func (q *Queries) GetLatestAgentRunStateByRunID(ctx context.Context, arg GetLatestAgentRunStateByRunIDParams) (AgentRunState, error) { + row := q.db.QueryRowContext(ctx, GetLatestAgentRunStateByRunID, arg.RunID) + var i AgentRunState + err := row.Scan( + &i.ID, + &i.RunID, + &i.StepIndex, + &i.State, + &i.SnapshotJson, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const ListAgentCheckpointsByConversationID = `-- name: ListAgentCheckpointsByConversationID :many +SELECT id, conversation_id, name, run_state_id, metadata_json, created_at, updated_at FROM agent_checkpoints +WHERE conversation_id = ? +ORDER BY created_at DESC +LIMIT ?2 +` + +type ListAgentCheckpointsByConversationIDParams struct { + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + Lim int64 `db:"lim" json:"lim"` +} + +// ListAgentCheckpointsByConversationID +// +// SELECT id, conversation_id, name, run_state_id, metadata_json, created_at, updated_at FROM agent_checkpoints +// WHERE conversation_id = ? +// ORDER BY created_at DESC +// LIMIT ?2 +func (q *Queries) ListAgentCheckpointsByConversationID(ctx context.Context, arg ListAgentCheckpointsByConversationIDParams) ([]AgentCheckpoint, error) { + rows, err := q.db.QueryContext(ctx, ListAgentCheckpointsByConversationID, arg.ConversationID, arg.Lim) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AgentCheckpoint{} + for rows.Next() { + var i AgentCheckpoint + if err := rows.Scan( + &i.ID, + &i.ConversationID, + &i.Name, + &i.RunStateID, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const ListAgentRunStatesByRunID = `-- name: ListAgentRunStatesByRunID :many +SELECT id, run_id, step_index, state, snapshot_json, created_at, updated_at FROM agent_run_states +WHERE run_id = ? +ORDER BY step_index ASC +LIMIT ?2 +` + +type ListAgentRunStatesByRunIDParams struct { + RunID ids.UUID `db:"run_id" json:"run_id"` + Lim int64 `db:"lim" json:"lim"` +} + +// ListAgentRunStatesByRunID +// +// SELECT id, run_id, step_index, state, snapshot_json, created_at, updated_at FROM agent_run_states +// WHERE run_id = ? +// ORDER BY step_index ASC +// LIMIT ?2 +func (q *Queries) ListAgentRunStatesByRunID(ctx context.Context, arg ListAgentRunStatesByRunIDParams) ([]AgentRunState, error) { + rows, err := q.db.QueryContext(ctx, ListAgentRunStatesByRunID, arg.RunID, arg.Lim) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AgentRunState{} + for rows.Next() { + var i AgentRunState + if err := rows.Scan( + &i.ID, + &i.RunID, + &i.StepIndex, + &i.State, + &i.SnapshotJson, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const ListAgentStateTransitionsByRunID = `-- name: ListAgentStateTransitionsByRunID :many +SELECT id, run_id, step_index, from_state, to_state, "trigger", at, meta_json, error, created_at, updated_at FROM agent_state_transitions +WHERE run_id = ? +ORDER BY at ASC +LIMIT ?2 +` + +type ListAgentStateTransitionsByRunIDParams struct { + RunID ids.UUID `db:"run_id" json:"run_id"` + Lim int64 `db:"lim" json:"lim"` +} + +// ListAgentStateTransitionsByRunID +// +// SELECT id, run_id, step_index, from_state, to_state, "trigger", at, meta_json, error, created_at, updated_at FROM agent_state_transitions +// WHERE run_id = ? +// ORDER BY at ASC +// LIMIT ?2 +func (q *Queries) ListAgentStateTransitionsByRunID(ctx context.Context, arg ListAgentStateTransitionsByRunIDParams) ([]AgentStateTransition, error) { + rows, err := q.db.QueryContext(ctx, ListAgentStateTransitionsByRunID, arg.RunID, arg.Lim) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AgentStateTransition{} + for rows.Next() { + var i AgentStateTransition + if err := rows.Scan( + &i.ID, + &i.RunID, + &i.StepIndex, + &i.FromState, + &i.ToState, + &i.Trigger, + &i.At, + &i.MetaJson, + &i.Error, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const UpdateAgentRunStatus = `-- name: UpdateAgentRunStatus :one +UPDATE agent_runs +SET status = ?, + metadata_json = ?, + updated_at = (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) +WHERE id = ? +RETURNING id, conversation_id, status, metadata_json, created_at, updated_at +` + +type UpdateAgentRunStatusParams struct { + Status string `db:"status" json:"status"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` + ID ids.UUID `db:"id" json:"id"` +} + +// UpdateAgentRunStatus +// +// UPDATE agent_runs +// SET status = ?, +// metadata_json = ?, +// updated_at = (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) +// WHERE id = ? +// RETURNING id, conversation_id, status, metadata_json, created_at, updated_at +func (q *Queries) UpdateAgentRunStatus(ctx context.Context, arg UpdateAgentRunStatusParams) (AgentRun, error) { + row := q.db.QueryRowContext(ctx, UpdateAgentRunStatus, arg.Status, arg.MetadataJson, arg.ID) + var i AgentRun + err := row.Scan( + &i.ID, + &i.ConversationID, + &i.Status, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} diff --git a/pkg/memory/sqlc/agent_threads.sql.go b/pkg/memory/sqlc/agent_threads.sql.go new file mode 100644 index 000000000..c1a32923b --- /dev/null +++ b/pkg/memory/sqlc/agent_threads.sql.go @@ -0,0 +1,233 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: agent_threads.sql + +package sqlc + +import ( + "context" + "encoding/json" + + "github.com/sipeed/picoclaw/pkg/ids" +) + +const AddAgentThreadMessage = `-- name: AddAgentThreadMessage :one +INSERT INTO agent_thread_messages (id, thread_id, role, content, metadata_json) +VALUES (?, ?, ?, ?, ?) +RETURNING id, thread_id, role, content, metadata_json, created_at, updated_at +` + +type AddAgentThreadMessageParams struct { + ID ids.UUID `db:"id" json:"id"` + ThreadID ids.UUID `db:"thread_id" json:"thread_id"` + Role string `db:"role" json:"role"` + Content string `db:"content" json:"content"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` +} + +// AddAgentThreadMessage +// +// INSERT INTO agent_thread_messages (id, thread_id, role, content, metadata_json) +// VALUES (?, ?, ?, ?, ?) +// RETURNING id, thread_id, role, content, metadata_json, created_at, updated_at +func (q *Queries) AddAgentThreadMessage(ctx context.Context, arg AddAgentThreadMessageParams) (AgentThreadMessage, error) { + row := q.db.QueryRowContext(ctx, AddAgentThreadMessage, + arg.ID, + arg.ThreadID, + arg.Role, + arg.Content, + arg.MetadataJson, + ) + var i AgentThreadMessage + err := row.Scan( + &i.ID, + &i.ThreadID, + &i.Role, + &i.Content, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const CreateAgentThread = `-- name: CreateAgentThread :one +INSERT INTO agent_threads (id, conversation_id, title, metadata_json) +VALUES (?, ?, ?, ?) +RETURNING id, conversation_id, title, metadata_json, created_at, updated_at +` + +type CreateAgentThreadParams struct { + ID ids.UUID `db:"id" json:"id"` + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + Title *string `db:"title" json:"title"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` +} + +// CreateAgentThread +// +// INSERT INTO agent_threads (id, conversation_id, title, metadata_json) +// VALUES (?, ?, ?, ?) +// RETURNING id, conversation_id, title, metadata_json, created_at, updated_at +func (q *Queries) CreateAgentThread(ctx context.Context, arg CreateAgentThreadParams) (AgentThread, error) { + row := q.db.QueryRowContext(ctx, CreateAgentThread, + arg.ID, + arg.ConversationID, + arg.Title, + arg.MetadataJson, + ) + var i AgentThread + err := row.Scan( + &i.ID, + &i.ConversationID, + &i.Title, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const ListAgentThreadMessagesByThreadID = `-- name: ListAgentThreadMessagesByThreadID :many +SELECT id, thread_id, role, content, metadata_json, created_at, updated_at FROM agent_thread_messages +WHERE thread_id = ? +ORDER BY created_at ASC +` + +type ListAgentThreadMessagesByThreadIDParams struct { + ThreadID ids.UUID `db:"thread_id" json:"thread_id"` +} + +// ListAgentThreadMessagesByThreadID +// +// SELECT id, thread_id, role, content, metadata_json, created_at, updated_at FROM agent_thread_messages +// WHERE thread_id = ? +// ORDER BY created_at ASC +func (q *Queries) ListAgentThreadMessagesByThreadID(ctx context.Context, arg ListAgentThreadMessagesByThreadIDParams) ([]AgentThreadMessage, error) { + rows, err := q.db.QueryContext(ctx, ListAgentThreadMessagesByThreadID, arg.ThreadID) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AgentThreadMessage{} + for rows.Next() { + var i AgentThreadMessage + if err := rows.Scan( + &i.ID, + &i.ThreadID, + &i.Role, + &i.Content, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const ListAgentThreadMessagesByThreadIDDescLimit = `-- name: ListAgentThreadMessagesByThreadIDDescLimit :many +SELECT id, thread_id, role, content, metadata_json, created_at, updated_at FROM agent_thread_messages +WHERE thread_id = ? +ORDER BY created_at DESC +LIMIT ? +` + +type ListAgentThreadMessagesByThreadIDDescLimitParams struct { + ThreadID ids.UUID `db:"thread_id" json:"thread_id"` + Limit int64 `db:"limit" json:"limit"` +} + +// ListAgentThreadMessagesByThreadIDDescLimit +// +// SELECT id, thread_id, role, content, metadata_json, created_at, updated_at FROM agent_thread_messages +// WHERE thread_id = ? +// ORDER BY created_at DESC +// LIMIT ? +func (q *Queries) ListAgentThreadMessagesByThreadIDDescLimit(ctx context.Context, arg ListAgentThreadMessagesByThreadIDDescLimitParams) ([]AgentThreadMessage, error) { + rows, err := q.db.QueryContext(ctx, ListAgentThreadMessagesByThreadIDDescLimit, arg.ThreadID, arg.Limit) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AgentThreadMessage{} + for rows.Next() { + var i AgentThreadMessage + if err := rows.Scan( + &i.ID, + &i.ThreadID, + &i.Role, + &i.Content, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const ListAgentThreadsByConversationID = `-- name: ListAgentThreadsByConversationID :many +SELECT id, conversation_id, title, metadata_json, created_at, updated_at FROM agent_threads +WHERE conversation_id = ? +ORDER BY created_at DESC +LIMIT ?2 +` + +type ListAgentThreadsByConversationIDParams struct { + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + Lim int64 `db:"lim" json:"lim"` +} + +// ListAgentThreadsByConversationID +// +// SELECT id, conversation_id, title, metadata_json, created_at, updated_at FROM agent_threads +// WHERE conversation_id = ? +// ORDER BY created_at DESC +// LIMIT ?2 +func (q *Queries) ListAgentThreadsByConversationID(ctx context.Context, arg ListAgentThreadsByConversationIDParams) ([]AgentThread, error) { + rows, err := q.db.QueryContext(ctx, ListAgentThreadsByConversationID, arg.ConversationID, arg.Lim) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AgentThread{} + for rows.Next() { + var i AgentThread + if err := rows.Scan( + &i.ID, + &i.ConversationID, + &i.Title, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/pkg/memory/sqlc/agent_tool_results.sql.go b/pkg/memory/sqlc/agent_tool_results.sql.go new file mode 100644 index 000000000..d75932f4b --- /dev/null +++ b/pkg/memory/sqlc/agent_tool_results.sql.go @@ -0,0 +1,269 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: agent_tool_results.sql + +package sqlc + +import ( + "context" + "encoding/json" + + "github.com/sipeed/picoclaw/pkg/ids" +) + +const AddAgentToolResult = `-- name: AddAgentToolResult :one +INSERT INTO agent_tool_results ( + id, conversation_id, run_id, step_index, + tool_call_id, tool_name, full_key, preview, chunk_count, metadata_json +) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +RETURNING id, conversation_id, run_id, step_index, tool_call_id, tool_name, full_key, preview, chunk_count, metadata_json, created_at, updated_at +` + +type AddAgentToolResultParams struct { + ID ids.UUID `db:"id" json:"id"` + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + RunID ids.UUID `db:"run_id" json:"run_id"` + StepIndex int64 `db:"step_index" json:"step_index"` + ToolCallID string `db:"tool_call_id" json:"tool_call_id"` + ToolName string `db:"tool_name" json:"tool_name"` + FullKey string `db:"full_key" json:"full_key"` + Preview *string `db:"preview" json:"preview"` + ChunkCount int64 `db:"chunk_count" json:"chunk_count"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` +} + +// AddAgentToolResult +// +// INSERT INTO agent_tool_results ( +// id, conversation_id, run_id, step_index, +// tool_call_id, tool_name, full_key, preview, chunk_count, metadata_json +// ) +// VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +// RETURNING id, conversation_id, run_id, step_index, tool_call_id, tool_name, full_key, preview, chunk_count, metadata_json, created_at, updated_at +func (q *Queries) AddAgentToolResult(ctx context.Context, arg AddAgentToolResultParams) (AgentToolResult, error) { + row := q.db.QueryRowContext(ctx, AddAgentToolResult, + arg.ID, + arg.ConversationID, + arg.RunID, + arg.StepIndex, + arg.ToolCallID, + arg.ToolName, + arg.FullKey, + arg.Preview, + arg.ChunkCount, + arg.MetadataJson, + ) + var i AgentToolResult + err := row.Scan( + &i.ID, + &i.ConversationID, + &i.RunID, + &i.StepIndex, + &i.ToolCallID, + &i.ToolName, + &i.FullKey, + &i.Preview, + &i.ChunkCount, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const GetAgentToolResultByRunIDAndToolCallID = `-- name: GetAgentToolResultByRunIDAndToolCallID :one +SELECT id, conversation_id, run_id, step_index, tool_call_id, tool_name, full_key, preview, chunk_count, metadata_json, created_at, updated_at FROM agent_tool_results +WHERE run_id = ? AND tool_call_id = ? +LIMIT 1 +` + +type GetAgentToolResultByRunIDAndToolCallIDParams struct { + RunID ids.UUID `db:"run_id" json:"run_id"` + ToolCallID string `db:"tool_call_id" json:"tool_call_id"` +} + +// GetAgentToolResultByRunIDAndToolCallID +// +// SELECT id, conversation_id, run_id, step_index, tool_call_id, tool_name, full_key, preview, chunk_count, metadata_json, created_at, updated_at FROM agent_tool_results +// WHERE run_id = ? AND tool_call_id = ? +// LIMIT 1 +func (q *Queries) GetAgentToolResultByRunIDAndToolCallID(ctx context.Context, arg GetAgentToolResultByRunIDAndToolCallIDParams) (AgentToolResult, error) { + row := q.db.QueryRowContext(ctx, GetAgentToolResultByRunIDAndToolCallID, arg.RunID, arg.ToolCallID) + var i AgentToolResult + err := row.Scan( + &i.ID, + &i.ConversationID, + &i.RunID, + &i.StepIndex, + &i.ToolCallID, + &i.ToolName, + &i.FullKey, + &i.Preview, + &i.ChunkCount, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const ListAgentToolResultsByConversationID = `-- name: ListAgentToolResultsByConversationID :many +SELECT id, conversation_id, run_id, step_index, tool_call_id, tool_name, full_key, preview, chunk_count, metadata_json, created_at, updated_at FROM agent_tool_results +WHERE conversation_id = ? +ORDER BY created_at DESC +` + +type ListAgentToolResultsByConversationIDParams struct { + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` +} + +// ListAgentToolResultsByConversationID +// +// SELECT id, conversation_id, run_id, step_index, tool_call_id, tool_name, full_key, preview, chunk_count, metadata_json, created_at, updated_at FROM agent_tool_results +// WHERE conversation_id = ? +// ORDER BY created_at DESC +func (q *Queries) ListAgentToolResultsByConversationID(ctx context.Context, arg ListAgentToolResultsByConversationIDParams) ([]AgentToolResult, error) { + rows, err := q.db.QueryContext(ctx, ListAgentToolResultsByConversationID, arg.ConversationID) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AgentToolResult{} + for rows.Next() { + var i AgentToolResult + if err := rows.Scan( + &i.ID, + &i.ConversationID, + &i.RunID, + &i.StepIndex, + &i.ToolCallID, + &i.ToolName, + &i.FullKey, + &i.Preview, + &i.ChunkCount, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const ListAgentToolResultsByConversationIDLimit = `-- name: ListAgentToolResultsByConversationIDLimit :many +SELECT id, conversation_id, run_id, step_index, tool_call_id, tool_name, full_key, preview, chunk_count, metadata_json, created_at, updated_at FROM agent_tool_results +WHERE conversation_id = ? +ORDER BY created_at DESC +LIMIT ? +` + +type ListAgentToolResultsByConversationIDLimitParams struct { + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + Limit int64 `db:"limit" json:"limit"` +} + +// ListAgentToolResultsByConversationIDLimit +// +// SELECT id, conversation_id, run_id, step_index, tool_call_id, tool_name, full_key, preview, chunk_count, metadata_json, created_at, updated_at FROM agent_tool_results +// WHERE conversation_id = ? +// ORDER BY created_at DESC +// LIMIT ? +func (q *Queries) ListAgentToolResultsByConversationIDLimit(ctx context.Context, arg ListAgentToolResultsByConversationIDLimitParams) ([]AgentToolResult, error) { + rows, err := q.db.QueryContext(ctx, ListAgentToolResultsByConversationIDLimit, arg.ConversationID, arg.Limit) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AgentToolResult{} + for rows.Next() { + var i AgentToolResult + if err := rows.Scan( + &i.ID, + &i.ConversationID, + &i.RunID, + &i.StepIndex, + &i.ToolCallID, + &i.ToolName, + &i.FullKey, + &i.Preview, + &i.ChunkCount, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const ListAgentToolResultsByRunID = `-- name: ListAgentToolResultsByRunID :many +SELECT id, conversation_id, run_id, step_index, tool_call_id, tool_name, full_key, preview, chunk_count, metadata_json, created_at, updated_at FROM agent_tool_results +WHERE run_id = ? +ORDER BY step_index ASC, created_at ASC +LIMIT ?2 +` + +type ListAgentToolResultsByRunIDParams struct { + RunID ids.UUID `db:"run_id" json:"run_id"` + Lim int64 `db:"lim" json:"lim"` +} + +// ListAgentToolResultsByRunID +// +// SELECT id, conversation_id, run_id, step_index, tool_call_id, tool_name, full_key, preview, chunk_count, metadata_json, created_at, updated_at FROM agent_tool_results +// WHERE run_id = ? +// ORDER BY step_index ASC, created_at ASC +// LIMIT ?2 +func (q *Queries) ListAgentToolResultsByRunID(ctx context.Context, arg ListAgentToolResultsByRunIDParams) ([]AgentToolResult, error) { + rows, err := q.db.QueryContext(ctx, ListAgentToolResultsByRunID, arg.RunID, arg.Lim) + if err != nil { + return nil, err + } + defer rows.Close() + items := []AgentToolResult{} + for rows.Next() { + var i AgentToolResult + if err := rows.Scan( + &i.ID, + &i.ConversationID, + &i.RunID, + &i.StepIndex, + &i.ToolCallID, + &i.ToolName, + &i.FullKey, + &i.Preview, + &i.ChunkCount, + &i.MetadataJson, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/pkg/memory/sqlc/archival.sql.go b/pkg/memory/sqlc/archival.sql.go index 4ed9982a3..fa1f9a5fc 100644 --- a/pkg/memory/sqlc/archival.sql.go +++ b/pkg/memory/sqlc/archival.sql.go @@ -15,15 +15,23 @@ import ( const CountArchivalChunks = `-- name: CountArchivalChunks :one SELECT COUNT(*) -FROM archival_chunks +FROM archival_chunks ac + JOIN recall_items ri ON ac.recall_id = ri.id +WHERE ri.agent_id = ?1 ` +type CountArchivalChunksParams struct { + AgentID string `db:"agent_id" json:"agent_id"` +} + // CountArchivalChunks // // SELECT COUNT(*) -// FROM archival_chunks -func (q *Queries) CountArchivalChunks(ctx context.Context) (int64, error) { - row := q.db.QueryRowContext(ctx, CountArchivalChunks) +// FROM archival_chunks ac +// JOIN recall_items ri ON ac.recall_id = ri.id +// WHERE ri.agent_id = ?1 +func (q *Queries) CountArchivalChunks(ctx context.Context, arg CountArchivalChunksParams) (int64, error) { + row := q.db.QueryRowContext(ctx, CountArchivalChunks, arg.AgentID) var count int64 err := row.Scan(&count) return count, err @@ -35,7 +43,7 @@ WHERE recall_id = ?1 ` type DeleteArchivalChunksByRecallParams struct { - RecallID ids.UUID `json:"recall_id"` + RecallID ids.UUID `db:"recall_id" json:"recall_id"` } // DeleteArchivalChunksByRecall @@ -48,36 +56,43 @@ func (q *Queries) DeleteArchivalChunksByRecall(ctx context.Context, arg DeleteAr } const GetArchivalChunk = `-- name: GetArchivalChunk :one -SELECT id, - recall_id, - chunk_index, - content, - embedding, - source, - hash, - created_at -FROM archival_chunks -WHERE id = ?1 +SELECT ac.id, + ac.recall_id, + ac.chunk_index, + ac.content, + ac.embedding, + ac.source, + ac.hash, + ac.created_at +FROM archival_chunks ac + JOIN recall_items ri ON ac.recall_id = ri.id +WHERE ac.id = ?1 + AND ri.agent_id = ?2 +LIMIT 1 ` type GetArchivalChunkParams struct { - ID ids.UUID `json:"id"` + ID ids.UUID `db:"id" json:"id"` + AgentID string `db:"agent_id" json:"agent_id"` } // GetArchivalChunk // -// SELECT id, -// recall_id, -// chunk_index, -// content, -// embedding, -// source, -// hash, -// created_at -// FROM archival_chunks -// WHERE id = ?1 +// SELECT ac.id, +// ac.recall_id, +// ac.chunk_index, +// ac.content, +// ac.embedding, +// ac.source, +// ac.hash, +// ac.created_at +// FROM archival_chunks ac +// JOIN recall_items ri ON ac.recall_id = ri.id +// WHERE ac.id = ?1 +// AND ri.agent_id = ?2 +// LIMIT 1 func (q *Queries) GetArchivalChunk(ctx context.Context, arg GetArchivalChunkParams) (ArchivalChunk, error) { - row := q.db.QueryRowContext(ctx, GetArchivalChunk, arg.ID) + row := q.db.QueryRowContext(ctx, GetArchivalChunk, arg.ID, arg.AgentID) var i ArchivalChunk err := row.Scan( &i.ID, @@ -93,34 +108,39 @@ func (q *Queries) GetArchivalChunk(ctx context.Context, arg GetArchivalChunkPara } const GetArchivalChunksByIDs = `-- name: GetArchivalChunksByIDs :many -SELECT id, - recall_id, - chunk_index, - content, - embedding, - source, - hash, - created_at -FROM archival_chunks -WHERE id IN (/*SLICE:ids*/?) +SELECT ac.id, + ac.recall_id, + ac.chunk_index, + ac.content, + ac.embedding, + ac.source, + ac.hash, + ac.created_at +FROM archival_chunks ac + JOIN recall_items ri ON ac.recall_id = ri.id +WHERE ac.id IN (/*SLICE:ids*/?) + AND ri.agent_id = ?2 ` type GetArchivalChunksByIDsParams struct { - Ids []ids.UUID `json:"ids"` + Ids []ids.UUID `db:"ids" json:"ids"` + AgentID string `db:"agent_id" json:"agent_id"` } // GetArchivalChunksByIDs // -// SELECT id, -// recall_id, -// chunk_index, -// content, -// embedding, -// source, -// hash, -// created_at -// FROM archival_chunks -// WHERE id IN (/*SLICE:ids*/?) +// SELECT ac.id, +// ac.recall_id, +// ac.chunk_index, +// ac.content, +// ac.embedding, +// ac.source, +// ac.hash, +// ac.created_at +// FROM archival_chunks ac +// JOIN recall_items ri ON ac.recall_id = ri.id +// WHERE ac.id IN (/*SLICE:ids*/?) +// AND ri.agent_id = ?2 func (q *Queries) GetArchivalChunksByIDs(ctx context.Context, arg GetArchivalChunksByIDsParams) ([]ArchivalChunk, error) { query := GetArchivalChunksByIDs var queryParams []interface{} @@ -132,6 +152,7 @@ func (q *Queries) GetArchivalChunksByIDs(ctx context.Context, arg GetArchivalChu } else { query = strings.Replace(query, "/*SLICE:ids*/?", "NULL", 1) } + queryParams = append(queryParams, arg.AgentID) rows, err := q.db.QueryContext(ctx, query, queryParams...) if err != nil { return nil, err @@ -187,13 +208,13 @@ VALUES ( ` type InsertArchivalChunkParams struct { - ID ids.UUID `json:"id"` - RecallID ids.UUID `json:"recall_id"` - ChunkIndex int64 `json:"chunk_index"` - Content string `json:"content"` - Embedding memory.Embedding `json:"embedding"` - Source string `json:"source"` - Hash string `json:"hash"` + ID ids.UUID `db:"id" json:"id"` + RecallID ids.UUID `db:"recall_id" json:"recall_id"` + ChunkIndex int64 `db:"chunk_index" json:"chunk_index"` + Content string `db:"content" json:"content"` + Embedding memory.Embedding `db:"embedding" json:"embedding"` + Source string `db:"source" json:"source"` + Hash string `db:"hash" json:"hash"` } // Archival Chunk queries @@ -232,39 +253,44 @@ func (q *Queries) InsertArchivalChunk(ctx context.Context, arg InsertArchivalChu } const ListAllArchivalChunks = `-- name: ListAllArchivalChunks :many -SELECT id, - recall_id, - chunk_index, - content, - embedding, - source, - hash, - created_at -FROM archival_chunks -ORDER BY created_at DESC -LIMIT ?2 OFFSET ?1 +SELECT ac.id, + ac.recall_id, + ac.chunk_index, + ac.content, + ac.embedding, + ac.source, + ac.hash, + ac.created_at +FROM archival_chunks ac + JOIN recall_items ri ON ac.recall_id = ri.id +WHERE ri.agent_id = ?1 +ORDER BY ac.created_at DESC +LIMIT ?3 OFFSET ?2 ` type ListAllArchivalChunksParams struct { - Off int64 `json:"off"` - Lim int64 `json:"lim"` + AgentID string `db:"agent_id" json:"agent_id"` + Off int64 `db:"off" json:"off"` + Lim int64 `db:"lim" json:"lim"` } // ListAllArchivalChunks // -// SELECT id, -// recall_id, -// chunk_index, -// content, -// embedding, -// source, -// hash, -// created_at -// FROM archival_chunks -// ORDER BY created_at DESC -// LIMIT ?2 OFFSET ?1 +// SELECT ac.id, +// ac.recall_id, +// ac.chunk_index, +// ac.content, +// ac.embedding, +// ac.source, +// ac.hash, +// ac.created_at +// FROM archival_chunks ac +// JOIN recall_items ri ON ac.recall_id = ri.id +// WHERE ri.agent_id = ?1 +// ORDER BY ac.created_at DESC +// LIMIT ?3 OFFSET ?2 func (q *Queries) ListAllArchivalChunks(ctx context.Context, arg ListAllArchivalChunksParams) ([]ArchivalChunk, error) { - rows, err := q.db.QueryContext(ctx, ListAllArchivalChunks, arg.Off, arg.Lim) + rows, err := q.db.QueryContext(ctx, ListAllArchivalChunks, arg.AgentID, arg.Off, arg.Lim) if err != nil { return nil, err } @@ -296,38 +322,46 @@ func (q *Queries) ListAllArchivalChunks(ctx context.Context, arg ListAllArchival } const ListArchivalChunks = `-- name: ListArchivalChunks :many -SELECT id, - recall_id, - chunk_index, - content, - embedding, - source, - hash, - created_at -FROM archival_chunks -WHERE recall_id = ?1 -ORDER BY chunk_index +SELECT ac.id, + ac.recall_id, + ac.chunk_index, + ac.content, + ac.embedding, + ac.source, + ac.hash, + ac.created_at +FROM archival_chunks ac + JOIN recall_items ri ON ac.recall_id = ri.id +WHERE ac.recall_id = ?1 + AND ri.agent_id = ?2 +ORDER BY ac.chunk_index +LIMIT ?3 ` type ListArchivalChunksParams struct { - RecallID ids.UUID `json:"recall_id"` + RecallID ids.UUID `db:"recall_id" json:"recall_id"` + AgentID string `db:"agent_id" json:"agent_id"` + Lim int64 `db:"lim" json:"lim"` } // ListArchivalChunks // -// SELECT id, -// recall_id, -// chunk_index, -// content, -// embedding, -// source, -// hash, -// created_at -// FROM archival_chunks -// WHERE recall_id = ?1 -// ORDER BY chunk_index +// SELECT ac.id, +// ac.recall_id, +// ac.chunk_index, +// ac.content, +// ac.embedding, +// ac.source, +// ac.hash, +// ac.created_at +// FROM archival_chunks ac +// JOIN recall_items ri ON ac.recall_id = ri.id +// WHERE ac.recall_id = ?1 +// AND ri.agent_id = ?2 +// ORDER BY ac.chunk_index +// LIMIT ?3 func (q *Queries) ListArchivalChunks(ctx context.Context, arg ListArchivalChunksParams) ([]ArchivalChunk, error) { - rows, err := q.db.QueryContext(ctx, ListArchivalChunks, arg.RecallID) + rows, err := q.db.QueryContext(ctx, ListArchivalChunks, arg.RecallID, arg.AgentID, arg.Lim) if err != nil { return nil, err } diff --git a/pkg/memory/sqlc/jobs.sql.go b/pkg/memory/sqlc/jobs.sql.go new file mode 100644 index 000000000..4ddd1b858 --- /dev/null +++ b/pkg/memory/sqlc/jobs.sql.go @@ -0,0 +1,432 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: jobs.sql + +package sqlc + +import ( + "context" + "time" + + "github.com/sipeed/picoclaw/pkg/ids" +) + +const ClaimJobByID = `-- name: ClaimJobByID :one +UPDATE jobs +SET status = 'running', + attempts = attempts + 1, + locked_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + locked_by = ?1, + updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + last_error = NULL +WHERE id = ?2 AND status = 'queued' +RETURNING id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at +` + +type ClaimJobByIDParams struct { + LockedBy *string `db:"locked_by" json:"locked_by"` + ID ids.UUID `db:"id" json:"id"` +} + +// ClaimJobByID +// +// UPDATE jobs +// SET status = 'running', +// attempts = attempts + 1, +// locked_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), +// locked_by = ?1, +// updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), +// last_error = NULL +// WHERE id = ?2 AND status = 'queued' +// RETURNING id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at +func (q *Queries) ClaimJobByID(ctx context.Context, arg ClaimJobByIDParams) (Job, error) { + row := q.db.QueryRowContext(ctx, ClaimJobByID, arg.LockedBy, arg.ID) + var i Job + err := row.Scan( + &i.ID, + &i.Kind, + &i.Status, + &i.RunAt, + &i.Attempts, + &i.MaxAttempts, + &i.LockedAt, + &i.LockedBy, + &i.PayloadJson, + &i.DedupeKey, + &i.LastError, + &i.CreatedAt, + &i.UpdatedAt, + &i.CompletedAt, + ) + return i, err +} + +const CountJobsByStatus = `-- name: CountJobsByStatus :many +SELECT status, count(*) AS count FROM jobs GROUP BY status +` + +type CountJobsByStatusRow struct { + Status string `db:"status" json:"status"` + Count int64 `db:"count" json:"count"` +} + +// CountJobsByStatus +// +// SELECT status, count(*) AS count FROM jobs GROUP BY status +func (q *Queries) CountJobsByStatus(ctx context.Context) ([]CountJobsByStatusRow, error) { + rows, err := q.db.QueryContext(ctx, CountJobsByStatus) + if err != nil { + return nil, err + } + defer rows.Close() + items := []CountJobsByStatusRow{} + for rows.Next() { + var i CountJobsByStatusRow + if err := rows.Scan(&i.Status, &i.Count); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const EnqueueJob = `-- name: EnqueueJob :one +INSERT INTO jobs ( + id, kind, status, run_at, max_attempts, payload_json, dedupe_key +) +VALUES ( + ?1, + ?2, + 'queued', + coalesce(?3, strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + coalesce(?4, 3), + coalesce(?5, '{}'), + ?6 +) ON CONFLICT(kind, dedupe_key) +WHERE dedupe_key IS NOT NULL DO UPDATE +SET status = 'queued', + run_at = excluded.run_at, + max_attempts = excluded.max_attempts, + payload_json = excluded.payload_json, + attempts = 0, + last_error = NULL, + updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') +RETURNING id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at +` + +type EnqueueJobParams struct { + ID ids.UUID `db:"id" json:"id"` + Kind string `db:"kind" json:"kind"` + RunAt interface{} `db:"run_at" json:"run_at"` + MaxAttempts interface{} `db:"max_attempts" json:"max_attempts"` + PayloadJson interface{} `db:"payload_json" json:"payload_json"` + DedupeKey *string `db:"dedupe_key" json:"dedupe_key"` +} + +// EnqueueJob +// +// INSERT INTO jobs ( +// id, kind, status, run_at, max_attempts, payload_json, dedupe_key +// ) +// VALUES ( +// ?1, +// ?2, +// 'queued', +// coalesce(?3, strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), +// coalesce(?4, 3), +// coalesce(?5, '{}'), +// ?6 +// ) ON CONFLICT(kind, dedupe_key) +// WHERE dedupe_key IS NOT NULL DO UPDATE +// SET status = 'queued', +// run_at = excluded.run_at, +// max_attempts = excluded.max_attempts, +// payload_json = excluded.payload_json, +// attempts = 0, +// last_error = NULL, +// updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') +// RETURNING id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at +func (q *Queries) EnqueueJob(ctx context.Context, arg EnqueueJobParams) (Job, error) { + row := q.db.QueryRowContext(ctx, EnqueueJob, + arg.ID, + arg.Kind, + arg.RunAt, + arg.MaxAttempts, + arg.PayloadJson, + arg.DedupeKey, + ) + var i Job + err := row.Scan( + &i.ID, + &i.Kind, + &i.Status, + &i.RunAt, + &i.Attempts, + &i.MaxAttempts, + &i.LockedAt, + &i.LockedBy, + &i.PayloadJson, + &i.DedupeKey, + &i.LastError, + &i.CreatedAt, + &i.UpdatedAt, + &i.CompletedAt, + ) + return i, err +} + +const FindNextRunnableJob = `-- name: FindNextRunnableJob :one +SELECT id FROM jobs +WHERE status = 'queued' + AND run_at <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now') +ORDER BY run_at ASC, created_at ASC +LIMIT 1 +` + +// FindNextRunnableJob +// +// SELECT id FROM jobs +// WHERE status = 'queued' +// AND run_at <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now') +// ORDER BY run_at ASC, created_at ASC +// LIMIT 1 +func (q *Queries) FindNextRunnableJob(ctx context.Context) (ids.UUID, error) { + row := q.db.QueryRowContext(ctx, FindNextRunnableJob) + var id ids.UUID + err := row.Scan(&id) + return id, err +} + +const GetJob = `-- name: GetJob :one +SELECT id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at FROM jobs WHERE id = ?1 LIMIT 1 +` + +type GetJobParams struct { + ID ids.UUID `db:"id" json:"id"` +} + +// GetJob +// +// SELECT id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at FROM jobs WHERE id = ?1 LIMIT 1 +func (q *Queries) GetJob(ctx context.Context, arg GetJobParams) (Job, error) { + row := q.db.QueryRowContext(ctx, GetJob, arg.ID) + var i Job + err := row.Scan( + &i.ID, + &i.Kind, + &i.Status, + &i.RunAt, + &i.Attempts, + &i.MaxAttempts, + &i.LockedAt, + &i.LockedBy, + &i.PayloadJson, + &i.DedupeKey, + &i.LastError, + &i.CreatedAt, + &i.UpdatedAt, + &i.CompletedAt, + ) + return i, err +} + +const ListJobs = `-- name: ListJobs :many +SELECT id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at FROM jobs ORDER BY created_at DESC LIMIT ?2 OFFSET ?1 +` + +type ListJobsParams struct { + Off int64 `db:"off" json:"off"` + Lim int64 `db:"lim" json:"lim"` +} + +// ListJobs +// +// SELECT id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at FROM jobs ORDER BY created_at DESC LIMIT ?2 OFFSET ?1 +func (q *Queries) ListJobs(ctx context.Context, arg ListJobsParams) ([]Job, error) { + rows, err := q.db.QueryContext(ctx, ListJobs, arg.Off, arg.Lim) + if err != nil { + return nil, err + } + defer rows.Close() + items := []Job{} + for rows.Next() { + var i Job + if err := rows.Scan( + &i.ID, + &i.Kind, + &i.Status, + &i.RunAt, + &i.Attempts, + &i.MaxAttempts, + &i.LockedAt, + &i.LockedBy, + &i.PayloadJson, + &i.DedupeKey, + &i.LastError, + &i.CreatedAt, + &i.UpdatedAt, + &i.CompletedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const MarkJobFailed = `-- name: MarkJobFailed :one +UPDATE jobs +SET status = 'failed', + completed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + last_error = ?1 +WHERE id = ?2 +RETURNING id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at +` + +type MarkJobFailedParams struct { + LastError *string `db:"last_error" json:"last_error"` + ID ids.UUID `db:"id" json:"id"` +} + +// MarkJobFailed +// +// UPDATE jobs +// SET status = 'failed', +// completed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), +// updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), +// last_error = ?1 +// WHERE id = ?2 +// RETURNING id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at +func (q *Queries) MarkJobFailed(ctx context.Context, arg MarkJobFailedParams) (Job, error) { + row := q.db.QueryRowContext(ctx, MarkJobFailed, arg.LastError, arg.ID) + var i Job + err := row.Scan( + &i.ID, + &i.Kind, + &i.Status, + &i.RunAt, + &i.Attempts, + &i.MaxAttempts, + &i.LockedAt, + &i.LockedBy, + &i.PayloadJson, + &i.DedupeKey, + &i.LastError, + &i.CreatedAt, + &i.UpdatedAt, + &i.CompletedAt, + ) + return i, err +} + +const MarkJobSucceeded = `-- name: MarkJobSucceeded :one +UPDATE jobs +SET status = 'succeeded', + completed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + last_error = NULL +WHERE id = ?1 +RETURNING id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at +` + +type MarkJobSucceededParams struct { + ID ids.UUID `db:"id" json:"id"` +} + +// MarkJobSucceeded +// +// UPDATE jobs +// SET status = 'succeeded', +// completed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), +// updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), +// last_error = NULL +// WHERE id = ?1 +// RETURNING id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at +func (q *Queries) MarkJobSucceeded(ctx context.Context, arg MarkJobSucceededParams) (Job, error) { + row := q.db.QueryRowContext(ctx, MarkJobSucceeded, arg.ID) + var i Job + err := row.Scan( + &i.ID, + &i.Kind, + &i.Status, + &i.RunAt, + &i.Attempts, + &i.MaxAttempts, + &i.LockedAt, + &i.LockedBy, + &i.PayloadJson, + &i.DedupeKey, + &i.LastError, + &i.CreatedAt, + &i.UpdatedAt, + &i.CompletedAt, + ) + return i, err +} + +const RequeueJob = `-- name: RequeueJob :one +UPDATE jobs +SET status = 'queued', + run_at = coalesce(?1, strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + last_error = ?2, + locked_at = NULL, + locked_by = NULL, + completed_at = NULL +WHERE id = ?3 +RETURNING id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at +` + +type RequeueJobParams struct { + RunAt time.Time `db:"run_at" json:"run_at"` + LastError *string `db:"last_error" json:"last_error"` + ID ids.UUID `db:"id" json:"id"` +} + +// RequeueJob +// +// UPDATE jobs +// SET status = 'queued', +// run_at = coalesce(?1, strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), +// updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), +// last_error = ?2, +// locked_at = NULL, +// locked_by = NULL, +// completed_at = NULL +// WHERE id = ?3 +// RETURNING id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at +func (q *Queries) RequeueJob(ctx context.Context, arg RequeueJobParams) (Job, error) { + row := q.db.QueryRowContext(ctx, RequeueJob, arg.RunAt, arg.LastError, arg.ID) + var i Job + err := row.Scan( + &i.ID, + &i.Kind, + &i.Status, + &i.RunAt, + &i.Attempts, + &i.MaxAttempts, + &i.LockedAt, + &i.LockedBy, + &i.PayloadJson, + &i.DedupeKey, + &i.LastError, + &i.CreatedAt, + &i.UpdatedAt, + &i.CompletedAt, + ) + return i, err +} diff --git a/pkg/memory/sqlc/models.go b/pkg/memory/sqlc/models.go index bd9198046..bbee18e52 100644 --- a/pkg/memory/sqlc/models.go +++ b/pkg/memory/sqlc/models.go @@ -5,6 +5,7 @@ package sqlc import ( + "encoding/json" "time" "github.com/sipeed/picoclaw/pkg/ids" @@ -12,75 +13,229 @@ import ( ) type AgentAuditLog struct { - ID ids.UUID `json:"id"` - AgentID string `json:"agent_id"` - SessionKey string `json:"session_key"` - Action string `json:"action"` - Target string `json:"target"` - Input *string `json:"input"` - Output *string `json:"output"` - DurationMs *int64 `json:"duration_ms"` - CreatedAt time.Time `json:"created_at"` + ID ids.UUID `db:"id" json:"id"` + AgentID string `db:"agent_id" json:"agent_id"` + SessionKey string `db:"session_key" json:"session_key"` + Action string `db:"action" json:"action"` + Target string `db:"target" json:"target"` + Input *string `db:"input" json:"input"` + Output *string `db:"output" json:"output"` + DurationMs *int64 `db:"duration_ms" json:"duration_ms"` + CreatedAt time.Time `db:"created_at" json:"created_at"` +} + +type AgentCheckpoint struct { + ID ids.UUID `db:"id" json:"id"` + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + Name string `db:"name" json:"name"` + RunStateID ids.UUID `db:"run_state_id" json:"run_state_id"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +} + +type AgentConversation struct { + ID ids.UUID `db:"id" json:"id"` + Title *string `db:"title" json:"title"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +} + +type AgentConversationFork struct { + ID ids.UUID `db:"id" json:"id"` + ParentConversationID ids.UUID `db:"parent_conversation_id" json:"parent_conversation_id"` + ChildConversationID ids.UUID `db:"child_conversation_id" json:"child_conversation_id"` + CheckpointID ids.UUID `db:"checkpoint_id" json:"checkpoint_id"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +} + +type AgentConversationLink struct { + ID ids.UUID `db:"id" json:"id"` + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + LinkedConversationID ids.UUID `db:"linked_conversation_id" json:"linked_conversation_id"` + Kind string `db:"kind" json:"kind"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` } type AgentDocument struct { - ID ids.UUID `json:"id"` - AgentID string `json:"agent_id"` - Name string `json:"name"` - Category string `json:"category"` - Content string `json:"content"` - Version int64 `json:"version"` - IsActive bool `json:"is_active"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + ID ids.UUID `db:"id" json:"id"` + AgentID string `db:"agent_id" json:"agent_id"` + Name string `db:"name" json:"name"` + Category string `db:"category" json:"category"` + Content string `db:"content" json:"content"` + Version int64 `db:"version" json:"version"` + IsActive bool `db:"is_active" json:"is_active"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` } type AgentKv struct { - AgentID string `json:"agent_id"` - Key string `json:"key"` - Value string `json:"value"` - UpdatedAt time.Time `json:"updated_at"` + AgentID string `db:"agent_id" json:"agent_id"` + Key string `db:"key" json:"key"` + Value string `db:"value" json:"value"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +} + +type AgentMention struct { + ID ids.UUID `db:"id" json:"id"` + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + MessageID ids.UUID `db:"message_id" json:"message_id"` + Kind string `db:"kind" json:"kind"` + TargetID ids.UUID `db:"target_id" json:"target_id"` + Raw string `db:"raw" json:"raw"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +} + +type AgentMessage struct { + ID ids.UUID `db:"id" json:"id"` + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + Role string `db:"role" json:"role"` + Content string `db:"content" json:"content"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +} + +type AgentMessageRevision struct { + ID ids.UUID `db:"id" json:"id"` + MessageID ids.UUID `db:"message_id" json:"message_id"` + Editor string `db:"editor" json:"editor"` + OldContent string `db:"old_content" json:"old_content"` + NewContent string `db:"new_content" json:"new_content"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +} + +type AgentRun struct { + ID ids.UUID `db:"id" json:"id"` + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + Status string `db:"status" json:"status"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +} + +type AgentRunState struct { + ID ids.UUID `db:"id" json:"id"` + RunID ids.UUID `db:"run_id" json:"run_id"` + StepIndex int64 `db:"step_index" json:"step_index"` + State string `db:"state" json:"state"` + SnapshotJson json.RawMessage `db:"snapshot_json" json:"snapshot_json"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +} + +type AgentStateTransition struct { + ID ids.UUID `db:"id" json:"id"` + RunID ids.UUID `db:"run_id" json:"run_id"` + StepIndex int64 `db:"step_index" json:"step_index"` + FromState string `db:"from_state" json:"from_state"` + ToState string `db:"to_state" json:"to_state"` + Trigger string `db:"trigger" json:"trigger"` + At time.Time `db:"at" json:"at"` + MetaJson json.RawMessage `db:"meta_json" json:"meta_json"` + Error *string `db:"error" json:"error"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +} + +type AgentThread struct { + ID ids.UUID `db:"id" json:"id"` + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + Title *string `db:"title" json:"title"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +} + +type AgentThreadMessage struct { + ID ids.UUID `db:"id" json:"id"` + ThreadID ids.UUID `db:"thread_id" json:"thread_id"` + Role string `db:"role" json:"role"` + Content string `db:"content" json:"content"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +} + +type AgentToolResult struct { + ID ids.UUID `db:"id" json:"id"` + ConversationID ids.UUID `db:"conversation_id" json:"conversation_id"` + RunID ids.UUID `db:"run_id" json:"run_id"` + StepIndex int64 `db:"step_index" json:"step_index"` + ToolCallID string `db:"tool_call_id" json:"tool_call_id"` + ToolName string `db:"tool_name" json:"tool_name"` + FullKey string `db:"full_key" json:"full_key"` + Preview *string `db:"preview" json:"preview"` + ChunkCount int64 `db:"chunk_count" json:"chunk_count"` + MetadataJson json.RawMessage `db:"metadata_json" json:"metadata_json"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` } type ArchivalChunk struct { - ID ids.UUID `json:"id"` - RecallID ids.UUID `json:"recall_id"` - ChunkIndex int64 `json:"chunk_index"` - Content string `json:"content"` - Embedding memory.Embedding `json:"embedding"` - Source string `json:"source"` - Hash string `json:"hash"` - CreatedAt time.Time `json:"created_at"` + ID ids.UUID `db:"id" json:"id"` + RecallID ids.UUID `db:"recall_id" json:"recall_id"` + ChunkIndex int64 `db:"chunk_index" json:"chunk_index"` + Content string `db:"content" json:"content"` + Embedding memory.Embedding `db:"embedding" json:"embedding"` + Source string `db:"source" json:"source"` + Hash string `db:"hash" json:"hash"` + CreatedAt time.Time `db:"created_at" json:"created_at"` +} + +type Job struct { + ID ids.UUID `db:"id" json:"id"` + Kind string `db:"kind" json:"kind"` + Status string `db:"status" json:"status"` + RunAt time.Time `db:"run_at" json:"run_at"` + Attempts int64 `db:"attempts" json:"attempts"` + MaxAttempts int64 `db:"max_attempts" json:"max_attempts"` + LockedAt *time.Time `db:"locked_at" json:"locked_at"` + LockedBy *string `db:"locked_by" json:"locked_by"` + PayloadJson json.RawMessage `db:"payload_json" json:"payload_json"` + DedupeKey *string `db:"dedupe_key" json:"dedupe_key"` + LastError *string `db:"last_error" json:"last_error"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + CompletedAt *time.Time `db:"completed_at" json:"completed_at"` } type MemorySummary struct { - ID ids.UUID `json:"id"` - AgentID string `json:"agent_id"` - SessionKey string `json:"session_key"` - Content string `json:"content"` - FromMsgIdx int64 `json:"from_msg_idx"` - ToMsgIdx int64 `json:"to_msg_idx"` - CreatedAt time.Time `json:"created_at"` + ID ids.UUID `db:"id" json:"id"` + AgentID string `db:"agent_id" json:"agent_id"` + SessionKey string `db:"session_key" json:"session_key"` + Content string `db:"content" json:"content"` + FromMsgIdx int64 `db:"from_msg_idx" json:"from_msg_idx"` + ToMsgIdx int64 `db:"to_msg_idx" json:"to_msg_idx"` + CreatedAt time.Time `db:"created_at" json:"created_at"` } type RecallItem struct { - ID ids.UUID `json:"id"` - AgentID string `json:"agent_id"` - SessionKey string `json:"session_key"` - Role string `json:"role"` - Sector memory.Sector `json:"sector"` - Importance float64 `json:"importance"` - Salience float64 `json:"salience"` - DecayRate float64 `json:"decay_rate"` - Content string `json:"content"` - Tags string `json:"tags"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + ID ids.UUID `db:"id" json:"id"` + AgentID string `db:"agent_id" json:"agent_id"` + SessionKey string `db:"session_key" json:"session_key"` + Role string `db:"role" json:"role"` + Sector memory.Sector `db:"sector" json:"sector"` + Importance float64 `db:"importance" json:"importance"` + Salience float64 `db:"salience" json:"salience"` + DecayRate float64 `db:"decay_rate" json:"decay_rate"` + Content string `db:"content" json:"content"` + Tags string `db:"tags" json:"tags"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` } type WorkingContext struct { - AgentID string `json:"agent_id"` - SessionKey string `json:"session_key"` - Content string `json:"content"` - UpdatedAt time.Time `json:"updated_at"` + AgentID string `db:"agent_id" json:"agent_id"` + SessionKey string `db:"session_key" json:"session_key"` + Content string `db:"content" json:"content"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` } diff --git a/pkg/memory/sqlc/querier.go b/pkg/memory/sqlc/querier.go index a8ac5293c..33da311fd 100644 --- a/pkg/memory/sqlc/querier.go +++ b/pkg/memory/sqlc/querier.go @@ -6,14 +6,81 @@ package sqlc import ( "context" + + "github.com/sipeed/picoclaw/pkg/ids" ) type Querier interface { + //AddAgentMention + // + // INSERT INTO agent_mentions ( + // id, conversation_id, message_id, kind, target_id, raw, metadata_json + // ) + // VALUES (?, ?, ?, ?, ?, ?, ?) + // RETURNING id, conversation_id, message_id, kind, target_id, raw, metadata_json, created_at, updated_at + AddAgentMention(ctx context.Context, arg AddAgentMentionParams) (AgentMention, error) + //AddAgentMessage + // + // INSERT INTO agent_messages (id, conversation_id, role, content, metadata_json) + // VALUES (?, ?, ?, ?, ?) + // RETURNING id, conversation_id, role, content, metadata_json, created_at, updated_at + AddAgentMessage(ctx context.Context, arg AddAgentMessageParams) (AgentMessage, error) + //AddAgentMessageRevision + // + // INSERT INTO agent_message_revisions ( + // id, message_id, editor, old_content, new_content, metadata_json + // ) + // VALUES (?, ?, ?, ?, ?, ?) + // RETURNING id, message_id, editor, old_content, new_content, metadata_json, created_at, updated_at + AddAgentMessageRevision(ctx context.Context, arg AddAgentMessageRevisionParams) (AgentMessageRevision, error) + //AddAgentRunState + // + // INSERT INTO agent_run_states (id, run_id, step_index, state, snapshot_json) + // VALUES (?, ?, ?, ?, ?) + // RETURNING id, run_id, step_index, state, snapshot_json, created_at, updated_at + AddAgentRunState(ctx context.Context, arg AddAgentRunStateParams) (AgentRunState, error) + //AddAgentStateTransition + // + // INSERT INTO agent_state_transitions ( + // id, run_id, step_index, from_state, to_state, trigger, at, meta_json, error + // ) + // VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + // RETURNING id, run_id, step_index, from_state, to_state, "trigger", at, meta_json, error, created_at, updated_at + AddAgentStateTransition(ctx context.Context, arg AddAgentStateTransitionParams) (AgentStateTransition, error) + //AddAgentThreadMessage + // + // INSERT INTO agent_thread_messages (id, thread_id, role, content, metadata_json) + // VALUES (?, ?, ?, ?, ?) + // RETURNING id, thread_id, role, content, metadata_json, created_at, updated_at + AddAgentThreadMessage(ctx context.Context, arg AddAgentThreadMessageParams) (AgentThreadMessage, error) + //AddAgentToolResult + // + // INSERT INTO agent_tool_results ( + // id, conversation_id, run_id, step_index, + // tool_call_id, tool_name, full_key, preview, chunk_count, metadata_json + // ) + // VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + // RETURNING id, conversation_id, run_id, step_index, tool_call_id, tool_name, full_key, preview, chunk_count, metadata_json, created_at, updated_at + AddAgentToolResult(ctx context.Context, arg AddAgentToolResultParams) (AgentToolResult, error) + //ClaimJobByID + // + // UPDATE jobs + // SET status = 'running', + // attempts = attempts + 1, + // locked_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + // locked_by = ?1, + // updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + // last_error = NULL + // WHERE id = ?2 AND status = 'queued' + // RETURNING id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at + ClaimJobByID(ctx context.Context, arg ClaimJobByIDParams) (Job, error) //CountArchivalChunks // // SELECT COUNT(*) - // FROM archival_chunks - CountArchivalChunks(ctx context.Context) (int64, error) + // FROM archival_chunks ac + // JOIN recall_items ri ON ac.recall_id = ri.id + // WHERE ri.agent_id = ?1 + CountArchivalChunks(ctx context.Context, arg CountArchivalChunksParams) (int64, error) //CountAuditEntries // // SELECT COUNT(*) @@ -27,6 +94,10 @@ type Querier interface { // WHERE agent_id = ?1 // AND action = ?2 CountAuditEntriesByAction(ctx context.Context, arg CountAuditEntriesByActionParams) (int64, error) + //CountJobsByStatus + // + // SELECT status, count(*) AS count FROM jobs GROUP BY status + CountJobsByStatus(ctx context.Context) ([]CountJobsByStatusRow, error) //CountRecallItems // // SELECT COUNT(*) @@ -45,6 +116,51 @@ type Querier interface { // AND session_key = ?2 // AND tags = 'session-message' CountSessionMessages(ctx context.Context, arg CountSessionMessagesParams) (int64, error) + //CreateAgentCheckpoint + // + // INSERT INTO agent_checkpoints (id, conversation_id, name, run_state_id, metadata_json) + // VALUES (?, ?, ?, ?, ?) + // RETURNING id, conversation_id, name, run_state_id, metadata_json, created_at, updated_at + CreateAgentCheckpoint(ctx context.Context, arg CreateAgentCheckpointParams) (AgentCheckpoint, error) + //CreateAgentConversation + // + // INSERT INTO agent_conversations (id, title) + // VALUES (?, ?) + // RETURNING id, title, created_at, updated_at + CreateAgentConversation(ctx context.Context, arg CreateAgentConversationParams) (AgentConversation, error) + //CreateAgentConversationFork + // + // INSERT INTO agent_conversation_forks ( + // id, parent_conversation_id, child_conversation_id, checkpoint_id, metadata_json + // ) + // VALUES (?, ?, ?, ?, ?) + // RETURNING id, parent_conversation_id, child_conversation_id, checkpoint_id, metadata_json, created_at, updated_at + CreateAgentConversationFork(ctx context.Context, arg CreateAgentConversationForkParams) (AgentConversationFork, error) + //CreateAgentConversationLink + // + // INSERT INTO agent_conversation_links ( + // id, conversation_id, linked_conversation_id, kind, metadata_json + // ) + // VALUES (?, ?, ?, ?, ?) + // RETURNING id, conversation_id, linked_conversation_id, kind, metadata_json, created_at, updated_at + CreateAgentConversationLink(ctx context.Context, arg CreateAgentConversationLinkParams) (AgentConversationLink, error) + //CreateAgentRun + // + // INSERT INTO agent_runs (id, conversation_id, status, metadata_json) + // VALUES (?, ?, ?, ?) + // RETURNING id, conversation_id, status, metadata_json, created_at, updated_at + CreateAgentRun(ctx context.Context, arg CreateAgentRunParams) (AgentRun, error) + //CreateAgentThread + // + // INSERT INTO agent_threads (id, conversation_id, title, metadata_json) + // VALUES (?, ?, ?, ?) + // RETURNING id, conversation_id, title, metadata_json, created_at, updated_at + CreateAgentThread(ctx context.Context, arg CreateAgentThreadParams) (AgentThread, error) + //DeleteAgentConversationLink + // + // DELETE FROM agent_conversation_links + // WHERE conversation_id = ? AND linked_conversation_id = ? AND kind = ? + DeleteAgentConversationLink(ctx context.Context, arg DeleteAgentConversationLinkParams) error //DeleteArchivalChunksByRecall // // DELETE FROM archival_chunks @@ -66,32 +182,98 @@ type Querier interface { // // DELETE FROM recall_items // WHERE id = ?1 + // AND agent_id = ?2 DeleteRecallItem(ctx context.Context, arg DeleteRecallItemParams) error + //EnqueueJob + // + // INSERT INTO jobs ( + // id, kind, status, run_at, max_attempts, payload_json, dedupe_key + // ) + // VALUES ( + // ?1, + // ?2, + // 'queued', + // coalesce(?3, strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + // coalesce(?4, 3), + // coalesce(?5, '{}'), + // ?6 + // ) ON CONFLICT(kind, dedupe_key) + // WHERE dedupe_key IS NOT NULL DO UPDATE + // SET status = 'queued', + // run_at = excluded.run_at, + // max_attempts = excluded.max_attempts, + // payload_json = excluded.payload_json, + // attempts = 0, + // last_error = NULL, + // updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') + // RETURNING id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at + EnqueueJob(ctx context.Context, arg EnqueueJobParams) (Job, error) + //FindNextRunnableJob + // + // SELECT id FROM jobs + // WHERE status = 'queued' + // AND run_at <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now') + // ORDER BY run_at ASC, created_at ASC + // LIMIT 1 + FindNextRunnableJob(ctx context.Context) (ids.UUID, error) + //GetAgentCheckpointByConversationIDAndName + // + // SELECT id, conversation_id, name, run_state_id, metadata_json, created_at, updated_at FROM agent_checkpoints + // WHERE conversation_id = ? AND name = ? + // LIMIT 1 + GetAgentCheckpointByConversationIDAndName(ctx context.Context, arg GetAgentCheckpointByConversationIDAndNameParams) (AgentCheckpoint, error) + //GetAgentConversation + // + // SELECT id, title, created_at, updated_at FROM agent_conversations WHERE id = ? LIMIT 1 + GetAgentConversation(ctx context.Context, arg GetAgentConversationParams) (AgentConversation, error) + //GetAgentConversationForkByChildConversationID + // + // SELECT id, parent_conversation_id, child_conversation_id, checkpoint_id, metadata_json, created_at, updated_at FROM agent_conversation_forks + // WHERE child_conversation_id = ? + // LIMIT 1 + GetAgentConversationForkByChildConversationID(ctx context.Context, arg GetAgentConversationForkByChildConversationIDParams) (AgentConversationFork, error) + //GetAgentRunStateByID + // + // SELECT id, run_id, step_index, state, snapshot_json, created_at, updated_at FROM agent_run_states + // WHERE id = ? + // LIMIT 1 + GetAgentRunStateByID(ctx context.Context, arg GetAgentRunStateByIDParams) (AgentRunState, error) + //GetAgentToolResultByRunIDAndToolCallID + // + // SELECT id, conversation_id, run_id, step_index, tool_call_id, tool_name, full_key, preview, chunk_count, metadata_json, created_at, updated_at FROM agent_tool_results + // WHERE run_id = ? AND tool_call_id = ? + // LIMIT 1 + GetAgentToolResultByRunIDAndToolCallID(ctx context.Context, arg GetAgentToolResultByRunIDAndToolCallIDParams) (AgentToolResult, error) //GetArchivalChunk // - // SELECT id, - // recall_id, - // chunk_index, - // content, - // embedding, - // source, - // hash, - // created_at - // FROM archival_chunks - // WHERE id = ?1 + // SELECT ac.id, + // ac.recall_id, + // ac.chunk_index, + // ac.content, + // ac.embedding, + // ac.source, + // ac.hash, + // ac.created_at + // FROM archival_chunks ac + // JOIN recall_items ri ON ac.recall_id = ri.id + // WHERE ac.id = ?1 + // AND ri.agent_id = ?2 + // LIMIT 1 GetArchivalChunk(ctx context.Context, arg GetArchivalChunkParams) (ArchivalChunk, error) //GetArchivalChunksByIDs // - // SELECT id, - // recall_id, - // chunk_index, - // content, - // embedding, - // source, - // hash, - // created_at - // FROM archival_chunks - // WHERE id IN (/*SLICE:ids*/?) + // SELECT ac.id, + // ac.recall_id, + // ac.chunk_index, + // ac.content, + // ac.embedding, + // ac.source, + // ac.hash, + // ac.created_at + // FROM archival_chunks ac + // JOIN recall_items ri ON ac.recall_id = ri.id + // WHERE ac.id IN (/*SLICE:ids*/?) + // AND ri.agent_id = ?2 GetArchivalChunksByIDs(ctx context.Context, arg GetArchivalChunksByIDsParams) ([]ArchivalChunk, error) // Agent Documents queries // @@ -107,7 +289,12 @@ type Querier interface { // FROM agent_documents // WHERE agent_id = ?1 // AND name = ?2 + // LIMIT 1 GetDocument(ctx context.Context, arg GetDocumentParams) (AgentDocument, error) + //GetJob + // + // SELECT id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at FROM jobs WHERE id = ?1 LIMIT 1 + GetJob(ctx context.Context, arg GetJobParams) (Job, error) // Agent KV Store queries // // SELECT agent_id, @@ -117,7 +304,22 @@ type Querier interface { // FROM agent_kv // WHERE agent_id = ?1 // AND key = ?2 + // LIMIT 1 GetKV(ctx context.Context, arg GetKVParams) (AgentKv, error) + //GetLatestAgentRunByConversationID + // + // SELECT id, conversation_id, status, metadata_json, created_at, updated_at FROM agent_runs + // WHERE conversation_id = ? + // ORDER BY created_at DESC + // LIMIT 1 + GetLatestAgentRunByConversationID(ctx context.Context, arg GetLatestAgentRunByConversationIDParams) (AgentRun, error) + //GetLatestAgentRunStateByRunID + // + // SELECT id, run_id, step_index, state, snapshot_json, created_at, updated_at FROM agent_run_states + // WHERE run_id = ? + // ORDER BY step_index DESC + // LIMIT 1 + GetLatestAgentRunStateByRunID(ctx context.Context, arg GetLatestAgentRunStateByRunIDParams) (AgentRunState, error) //GetRecallItem // // SELECT id, @@ -134,6 +336,8 @@ type Querier interface { // updated_at // FROM recall_items // WHERE id = ?1 + // AND agent_id = ?2 + // LIMIT 1 GetRecallItem(ctx context.Context, arg GetRecallItemParams) (RecallItem, error) //GetRecallItemsByIDs // @@ -151,6 +355,7 @@ type Querier interface { // updated_at // FROM recall_items // WHERE id IN (/*SLICE:ids*/?) + // AND agent_id = ?2 GetRecallItemsByIDs(ctx context.Context, arg GetRecallItemsByIDsParams) ([]RecallItem, error) // Working Context queries // @@ -161,6 +366,7 @@ type Querier interface { // FROM working_context // WHERE agent_id = ?1 // AND session_key = ?2 + // LIMIT 1 GetWorkingContext(ctx context.Context, arg GetWorkingContextParams) (WorkingContext, error) // Archival Chunk queries // @@ -293,19 +499,127 @@ type Querier interface { // datetime('now') // ) InsertSummary(ctx context.Context, arg InsertSummaryParams) error + //ListAgentCheckpointsByConversationID + // + // SELECT id, conversation_id, name, run_state_id, metadata_json, created_at, updated_at FROM agent_checkpoints + // WHERE conversation_id = ? + // ORDER BY created_at DESC + // LIMIT ?2 + ListAgentCheckpointsByConversationID(ctx context.Context, arg ListAgentCheckpointsByConversationIDParams) ([]AgentCheckpoint, error) + //ListAgentConversationForksByParentConversationID + // + // SELECT id, parent_conversation_id, child_conversation_id, checkpoint_id, metadata_json, created_at, updated_at FROM agent_conversation_forks + // WHERE parent_conversation_id = ? + // ORDER BY created_at DESC + // LIMIT ?2 + ListAgentConversationForksByParentConversationID(ctx context.Context, arg ListAgentConversationForksByParentConversationIDParams) ([]AgentConversationFork, error) + //ListAgentConversationLinksByConversationID + // + // SELECT id, conversation_id, linked_conversation_id, kind, metadata_json, created_at, updated_at FROM agent_conversation_links + // WHERE conversation_id = ? + // ORDER BY created_at DESC + // LIMIT ?2 + ListAgentConversationLinksByConversationID(ctx context.Context, arg ListAgentConversationLinksByConversationIDParams) ([]AgentConversationLink, error) + //ListAgentConversations + // + // SELECT id, title, created_at, updated_at FROM agent_conversations ORDER BY created_at DESC LIMIT ? + ListAgentConversations(ctx context.Context, arg ListAgentConversationsParams) ([]AgentConversation, error) + //ListAgentMentionsByConversationID + // + // SELECT id, conversation_id, message_id, kind, target_id, raw, metadata_json, created_at, updated_at FROM agent_mentions + // WHERE conversation_id = ? + // ORDER BY created_at DESC + // LIMIT ?2 + ListAgentMentionsByConversationID(ctx context.Context, arg ListAgentMentionsByConversationIDParams) ([]AgentMention, error) + //ListAgentMessageRevisionsByMessageID + // + // SELECT id, message_id, editor, old_content, new_content, metadata_json, created_at, updated_at FROM agent_message_revisions + // WHERE message_id = ? + // ORDER BY created_at DESC + // LIMIT ?2 + ListAgentMessageRevisionsByMessageID(ctx context.Context, arg ListAgentMessageRevisionsByMessageIDParams) ([]AgentMessageRevision, error) + //ListAgentMessagesByConversationID + // + // SELECT id, conversation_id, role, content, metadata_json, created_at, updated_at FROM agent_messages + // WHERE conversation_id = ? + // ORDER BY created_at ASC + ListAgentMessagesByConversationID(ctx context.Context, arg ListAgentMessagesByConversationIDParams) ([]AgentMessage, error) + //ListAgentMessagesByConversationIDLimit + // + // SELECT id, conversation_id, role, content, metadata_json, created_at, updated_at FROM agent_messages + // WHERE conversation_id = ? + // ORDER BY created_at ASC + // LIMIT ? + ListAgentMessagesByConversationIDLimit(ctx context.Context, arg ListAgentMessagesByConversationIDLimitParams) ([]AgentMessage, error) + //ListAgentRunStatesByRunID + // + // SELECT id, run_id, step_index, state, snapshot_json, created_at, updated_at FROM agent_run_states + // WHERE run_id = ? + // ORDER BY step_index ASC + // LIMIT ?2 + ListAgentRunStatesByRunID(ctx context.Context, arg ListAgentRunStatesByRunIDParams) ([]AgentRunState, error) + //ListAgentStateTransitionsByRunID + // + // SELECT id, run_id, step_index, from_state, to_state, "trigger", at, meta_json, error, created_at, updated_at FROM agent_state_transitions + // WHERE run_id = ? + // ORDER BY at ASC + // LIMIT ?2 + ListAgentStateTransitionsByRunID(ctx context.Context, arg ListAgentStateTransitionsByRunIDParams) ([]AgentStateTransition, error) + //ListAgentThreadMessagesByThreadID + // + // SELECT id, thread_id, role, content, metadata_json, created_at, updated_at FROM agent_thread_messages + // WHERE thread_id = ? + // ORDER BY created_at ASC + ListAgentThreadMessagesByThreadID(ctx context.Context, arg ListAgentThreadMessagesByThreadIDParams) ([]AgentThreadMessage, error) + //ListAgentThreadMessagesByThreadIDDescLimit + // + // SELECT id, thread_id, role, content, metadata_json, created_at, updated_at FROM agent_thread_messages + // WHERE thread_id = ? + // ORDER BY created_at DESC + // LIMIT ? + ListAgentThreadMessagesByThreadIDDescLimit(ctx context.Context, arg ListAgentThreadMessagesByThreadIDDescLimitParams) ([]AgentThreadMessage, error) + //ListAgentThreadsByConversationID + // + // SELECT id, conversation_id, title, metadata_json, created_at, updated_at FROM agent_threads + // WHERE conversation_id = ? + // ORDER BY created_at DESC + // LIMIT ?2 + ListAgentThreadsByConversationID(ctx context.Context, arg ListAgentThreadsByConversationIDParams) ([]AgentThread, error) + //ListAgentToolResultsByConversationID + // + // SELECT id, conversation_id, run_id, step_index, tool_call_id, tool_name, full_key, preview, chunk_count, metadata_json, created_at, updated_at FROM agent_tool_results + // WHERE conversation_id = ? + // ORDER BY created_at DESC + ListAgentToolResultsByConversationID(ctx context.Context, arg ListAgentToolResultsByConversationIDParams) ([]AgentToolResult, error) + //ListAgentToolResultsByConversationIDLimit + // + // SELECT id, conversation_id, run_id, step_index, tool_call_id, tool_name, full_key, preview, chunk_count, metadata_json, created_at, updated_at FROM agent_tool_results + // WHERE conversation_id = ? + // ORDER BY created_at DESC + // LIMIT ? + ListAgentToolResultsByConversationIDLimit(ctx context.Context, arg ListAgentToolResultsByConversationIDLimitParams) ([]AgentToolResult, error) + //ListAgentToolResultsByRunID + // + // SELECT id, conversation_id, run_id, step_index, tool_call_id, tool_name, full_key, preview, chunk_count, metadata_json, created_at, updated_at FROM agent_tool_results + // WHERE run_id = ? + // ORDER BY step_index ASC, created_at ASC + // LIMIT ?2 + ListAgentToolResultsByRunID(ctx context.Context, arg ListAgentToolResultsByRunIDParams) ([]AgentToolResult, error) //ListAllArchivalChunks // - // SELECT id, - // recall_id, - // chunk_index, - // content, - // embedding, - // source, - // hash, - // created_at - // FROM archival_chunks - // ORDER BY created_at DESC - // LIMIT ?2 OFFSET ?1 + // SELECT ac.id, + // ac.recall_id, + // ac.chunk_index, + // ac.content, + // ac.embedding, + // ac.source, + // ac.hash, + // ac.created_at + // FROM archival_chunks ac + // JOIN recall_items ri ON ac.recall_id = ri.id + // WHERE ri.agent_id = ?1 + // ORDER BY ac.created_at DESC + // LIMIT ?3 OFFSET ?2 ListAllArchivalChunks(ctx context.Context, arg ListAllArchivalChunksParams) ([]ArchivalChunk, error) //ListAllDocuments // @@ -323,20 +637,24 @@ type Querier interface { // AND is_active = 1 // ORDER BY category, // name + // LIMIT ?2 ListAllDocuments(ctx context.Context, arg ListAllDocumentsParams) ([]AgentDocument, error) //ListArchivalChunks // - // SELECT id, - // recall_id, - // chunk_index, - // content, - // embedding, - // source, - // hash, - // created_at - // FROM archival_chunks - // WHERE recall_id = ?1 - // ORDER BY chunk_index + // SELECT ac.id, + // ac.recall_id, + // ac.chunk_index, + // ac.content, + // ac.embedding, + // ac.source, + // ac.hash, + // ac.created_at + // FROM archival_chunks ac + // JOIN recall_items ri ON ac.recall_id = ri.id + // WHERE ac.recall_id = ?1 + // AND ri.agent_id = ?2 + // ORDER BY ac.chunk_index + // LIMIT ?3 ListArchivalChunks(ctx context.Context, arg ListArchivalChunksParams) ([]ArchivalChunk, error) //ListAuditEntries // @@ -404,7 +722,12 @@ type Querier interface { // AND category = ?2 // AND is_active = 1 // ORDER BY name + // LIMIT ?3 ListDocumentsByCategory(ctx context.Context, arg ListDocumentsByCategoryParams) ([]AgentDocument, error) + //ListJobs + // + // SELECT id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at FROM jobs ORDER BY created_at DESC LIMIT ?2 OFFSET ?1 + ListJobs(ctx context.Context, arg ListJobsParams) ([]Job, error) //ListKVByPrefix // // SELECT agent_id, @@ -483,12 +806,45 @@ type Querier interface { // ORDER BY created_at DESC // LIMIT ?3 ListSummaries(ctx context.Context, arg ListSummariesParams) ([]MemorySummary, error) + //MarkJobFailed + // + // UPDATE jobs + // SET status = 'failed', + // completed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + // updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + // last_error = ?1 + // WHERE id = ?2 + // RETURNING id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at + MarkJobFailed(ctx context.Context, arg MarkJobFailedParams) (Job, error) + //MarkJobSucceeded + // + // UPDATE jobs + // SET status = 'succeeded', + // completed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + // updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + // last_error = NULL + // WHERE id = ?1 + // RETURNING id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at + MarkJobSucceeded(ctx context.Context, arg MarkJobSucceededParams) (Job, error) //PruneOldAuditEntries // // DELETE FROM agent_audit_log // WHERE agent_id = ?1 // AND created_at < ?2 PruneOldAuditEntries(ctx context.Context, arg PruneOldAuditEntriesParams) error + //RequeueJob + // + // UPDATE jobs + // SET status = 'queued', + // run_at = coalesce(?1, strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + // updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + // last_error = ?2, + // locked_at = NULL, + // locked_by = NULL, + // completed_at = NULL + // WHERE id = ?3 + // RETURNING id, kind, status, run_at, attempts, max_attempts, locked_at, locked_by, payload_json, dedupe_key, last_error, created_at, updated_at, completed_at + RequeueJob(ctx context.Context, arg RequeueJobParams) (Job, error) //SearchRecallByKeyword // // SELECT ri.id, @@ -509,6 +865,23 @@ type Querier interface { // ORDER BY ri.importance DESC // LIMIT ?3 SearchRecallByKeyword(ctx context.Context, arg SearchRecallByKeywordParams) ([]RecallItem, error) + //UpdateAgentConversationTitle + // + // UPDATE agent_conversations + // SET title = ?, + // updated_at = (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) + // WHERE id = ? + // RETURNING id, title, created_at, updated_at + UpdateAgentConversationTitle(ctx context.Context, arg UpdateAgentConversationTitleParams) (AgentConversation, error) + //UpdateAgentRunStatus + // + // UPDATE agent_runs + // SET status = ?, + // metadata_json = ?, + // updated_at = (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) + // WHERE id = ? + // RETURNING id, conversation_id, status, metadata_json, created_at, updated_at + UpdateAgentRunStatus(ctx context.Context, arg UpdateAgentRunStatusParams) (AgentRun, error) //UpdateRecallItem // // UPDATE recall_items @@ -521,6 +894,7 @@ type Querier interface { // tags = ?7, // updated_at = datetime('now') // WHERE id = ?8 + // AND agent_id = ?9 UpdateRecallItem(ctx context.Context, arg UpdateRecallItemParams) error //UpsertDocument // diff --git a/pkg/memory/sqlc/queries/agent_conversation_forks.sql b/pkg/memory/sqlc/queries/agent_conversation_forks.sql new file mode 100644 index 000000000..560eb0cf3 --- /dev/null +++ b/pkg/memory/sqlc/queries/agent_conversation_forks.sql @@ -0,0 +1,21 @@ +-- name: CreateAgentConversationFork :one +INSERT INTO agent_conversation_forks ( + id, + parent_conversation_id, + child_conversation_id, + checkpoint_id, + metadata_json + ) +VALUES (?, ?, ?, ?, ?) +RETURNING *; +-- name: ListAgentConversationForksByParentConversationID :many +SELECT * +FROM agent_conversation_forks +WHERE parent_conversation_id = ? +ORDER BY created_at DESC +LIMIT sqlc.arg(lim); +-- name: GetAgentConversationForkByChildConversationID :one +SELECT * +FROM agent_conversation_forks +WHERE child_conversation_id = ? +LIMIT 1; \ No newline at end of file diff --git a/pkg/memory/sqlc/queries/agent_conversation_links.sql b/pkg/memory/sqlc/queries/agent_conversation_links.sql new file mode 100644 index 000000000..2e859fddb --- /dev/null +++ b/pkg/memory/sqlc/queries/agent_conversation_links.sql @@ -0,0 +1,21 @@ +-- name: CreateAgentConversationLink :one +INSERT INTO agent_conversation_links ( + id, + conversation_id, + linked_conversation_id, + kind, + metadata_json + ) +VALUES (?, ?, ?, ?, ?) +RETURNING *; +-- name: ListAgentConversationLinksByConversationID :many +SELECT * +FROM agent_conversation_links +WHERE conversation_id = ? +ORDER BY created_at DESC +LIMIT sqlc.arg(lim); +-- name: DeleteAgentConversationLink :exec +DELETE FROM agent_conversation_links +WHERE conversation_id = ? + AND linked_conversation_id = ? + AND kind = ?; \ No newline at end of file diff --git a/pkg/memory/sqlc/queries/agent_conversations.sql b/pkg/memory/sqlc/queries/agent_conversations.sql new file mode 100644 index 000000000..2c9c532bc --- /dev/null +++ b/pkg/memory/sqlc/queries/agent_conversations.sql @@ -0,0 +1,20 @@ +-- name: CreateAgentConversation :one +INSERT INTO agent_conversations (id, title) +VALUES (?, ?) +RETURNING *; +-- name: GetAgentConversation :one +SELECT * +FROM agent_conversations +WHERE id = ? +LIMIT 1; +-- name: ListAgentConversations :many +SELECT * +FROM agent_conversations +ORDER BY created_at DESC +LIMIT ?; +-- name: UpdateAgentConversationTitle :one +UPDATE agent_conversations +SET title = ?, + updated_at = (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) +WHERE id = ? +RETURNING *; \ No newline at end of file diff --git a/pkg/memory/sqlc/queries/agent_documents.sql b/pkg/memory/sqlc/queries/agent_documents.sql index 7ba1540c4..fff556bd8 100644 --- a/pkg/memory/sqlc/queries/agent_documents.sql +++ b/pkg/memory/sqlc/queries/agent_documents.sql @@ -11,7 +11,8 @@ SELECT id, updated_at FROM agent_documents WHERE agent_id = sqlc.arg(agent_id) - AND name = sqlc.arg(name); + AND name = sqlc.arg(name) +LIMIT 1; -- name: UpsertDocument :exec INSERT INTO agent_documents ( id, @@ -55,7 +56,8 @@ FROM agent_documents WHERE agent_id = sqlc.arg(agent_id) AND category = sqlc.arg(category) AND is_active = 1 -ORDER BY name; +ORDER BY name +LIMIT sqlc.arg(lim); -- name: DeleteDocument :exec DELETE FROM agent_documents WHERE agent_id = sqlc.arg(agent_id) @@ -74,4 +76,5 @@ FROM agent_documents WHERE agent_id = sqlc.arg(agent_id) AND is_active = 1 ORDER BY category, - name; \ No newline at end of file + name +LIMIT sqlc.arg(lim); \ No newline at end of file diff --git a/pkg/memory/sqlc/queries/agent_kv.sql b/pkg/memory/sqlc/queries/agent_kv.sql index 5b3bdaee2..572eb12d8 100644 --- a/pkg/memory/sqlc/queries/agent_kv.sql +++ b/pkg/memory/sqlc/queries/agent_kv.sql @@ -6,7 +6,8 @@ SELECT agent_id, updated_at FROM agent_kv WHERE agent_id = sqlc.arg(agent_id) - AND key = sqlc.arg(key); + AND key = sqlc.arg(key) +LIMIT 1; -- name: UpsertKV :exec INSERT INTO agent_kv (agent_id, key, value, updated_at) VALUES ( diff --git a/pkg/memory/sqlc/queries/agent_mentions.sql b/pkg/memory/sqlc/queries/agent_mentions.sql new file mode 100644 index 000000000..11067b11a --- /dev/null +++ b/pkg/memory/sqlc/queries/agent_mentions.sql @@ -0,0 +1,18 @@ +-- name: AddAgentMention :one +INSERT INTO agent_mentions ( + id, + conversation_id, + message_id, + kind, + target_id, + raw, + metadata_json + ) +VALUES (?, ?, ?, ?, ?, ?, ?) +RETURNING *; +-- name: ListAgentMentionsByConversationID :many +SELECT * +FROM agent_mentions +WHERE conversation_id = ? +ORDER BY created_at DESC +LIMIT sqlc.arg(lim); \ No newline at end of file diff --git a/pkg/memory/sqlc/queries/agent_message_revisions.sql b/pkg/memory/sqlc/queries/agent_message_revisions.sql new file mode 100644 index 000000000..a9990d348 --- /dev/null +++ b/pkg/memory/sqlc/queries/agent_message_revisions.sql @@ -0,0 +1,17 @@ +-- name: AddAgentMessageRevision :one +INSERT INTO agent_message_revisions ( + id, + message_id, + editor, + old_content, + new_content, + metadata_json + ) +VALUES (?, ?, ?, ?, ?, ?) +RETURNING *; +-- name: ListAgentMessageRevisionsByMessageID :many +SELECT * +FROM agent_message_revisions +WHERE message_id = ? +ORDER BY created_at DESC +LIMIT sqlc.arg(lim); \ No newline at end of file diff --git a/pkg/memory/sqlc/queries/agent_messages.sql b/pkg/memory/sqlc/queries/agent_messages.sql new file mode 100644 index 000000000..c29f591c4 --- /dev/null +++ b/pkg/memory/sqlc/queries/agent_messages.sql @@ -0,0 +1,21 @@ +-- name: AddAgentMessage :one +INSERT INTO agent_messages ( + id, + conversation_id, + role, + content, + metadata_json + ) +VALUES (?, ?, ?, ?, ?) +RETURNING *; +-- name: ListAgentMessagesByConversationID :many +SELECT * +FROM agent_messages +WHERE conversation_id = ? +ORDER BY created_at ASC; +-- name: ListAgentMessagesByConversationIDLimit :many +SELECT * +FROM agent_messages +WHERE conversation_id = ? +ORDER BY created_at ASC +LIMIT ?; \ No newline at end of file diff --git a/pkg/memory/sqlc/queries/agent_state.sql b/pkg/memory/sqlc/queries/agent_state.sql new file mode 100644 index 000000000..70b50552b --- /dev/null +++ b/pkg/memory/sqlc/queries/agent_state.sql @@ -0,0 +1,80 @@ +-- name: CreateAgentRun :one +INSERT INTO agent_runs (id, conversation_id, status, metadata_json) +VALUES (?, ?, ?, ?) +RETURNING *; +-- name: UpdateAgentRunStatus :one +UPDATE agent_runs +SET status = ?, + metadata_json = ?, + updated_at = (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) +WHERE id = ? +RETURNING *; +-- name: GetLatestAgentRunByConversationID :one +SELECT * +FROM agent_runs +WHERE conversation_id = ? +ORDER BY created_at DESC +LIMIT 1; +-- name: AddAgentRunState :one +INSERT INTO agent_run_states (id, run_id, step_index, state, snapshot_json) +VALUES (?, ?, ?, ?, ?) +RETURNING *; +-- name: ListAgentRunStatesByRunID :many +SELECT * +FROM agent_run_states +WHERE run_id = ? +ORDER BY step_index ASC +LIMIT sqlc.arg(lim); +-- name: GetLatestAgentRunStateByRunID :one +SELECT * +FROM agent_run_states +WHERE run_id = ? +ORDER BY step_index DESC +LIMIT 1; +-- name: GetAgentRunStateByID :one +SELECT * +FROM agent_run_states +WHERE id = ? +LIMIT 1; +-- name: AddAgentStateTransition :one +INSERT INTO agent_state_transitions ( + id, + run_id, + step_index, + from_state, + to_state, + trigger, + at, + meta_json, + error + ) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) +RETURNING *; +-- name: ListAgentStateTransitionsByRunID :many +SELECT * +FROM agent_state_transitions +WHERE run_id = ? +ORDER BY at ASC +LIMIT sqlc.arg(lim); +-- name: CreateAgentCheckpoint :one +INSERT INTO agent_checkpoints ( + id, + conversation_id, + name, + run_state_id, + metadata_json + ) +VALUES (?, ?, ?, ?, ?) +RETURNING *; +-- name: ListAgentCheckpointsByConversationID :many +SELECT * +FROM agent_checkpoints +WHERE conversation_id = ? +ORDER BY created_at DESC +LIMIT sqlc.arg(lim); +-- name: GetAgentCheckpointByConversationIDAndName :one +SELECT * +FROM agent_checkpoints +WHERE conversation_id = ? + AND name = ? +LIMIT 1; \ No newline at end of file diff --git a/pkg/memory/sqlc/queries/agent_threads.sql b/pkg/memory/sqlc/queries/agent_threads.sql new file mode 100644 index 000000000..f76de5a84 --- /dev/null +++ b/pkg/memory/sqlc/queries/agent_threads.sql @@ -0,0 +1,25 @@ +-- name: CreateAgentThread :one +INSERT INTO agent_threads (id, conversation_id, title, metadata_json) +VALUES (?, ?, ?, ?) +RETURNING *; +-- name: ListAgentThreadsByConversationID :many +SELECT * +FROM agent_threads +WHERE conversation_id = ? +ORDER BY created_at DESC +LIMIT sqlc.arg(lim); +-- name: AddAgentThreadMessage :one +INSERT INTO agent_thread_messages (id, thread_id, role, content, metadata_json) +VALUES (?, ?, ?, ?, ?) +RETURNING *; +-- name: ListAgentThreadMessagesByThreadID :many +SELECT * +FROM agent_thread_messages +WHERE thread_id = ? +ORDER BY created_at ASC; +-- name: ListAgentThreadMessagesByThreadIDDescLimit :many +SELECT * +FROM agent_thread_messages +WHERE thread_id = ? +ORDER BY created_at DESC +LIMIT ?; \ No newline at end of file diff --git a/pkg/memory/sqlc/queries/agent_tool_results.sql b/pkg/memory/sqlc/queries/agent_tool_results.sql new file mode 100644 index 000000000..230b2e8c8 --- /dev/null +++ b/pkg/memory/sqlc/queries/agent_tool_results.sql @@ -0,0 +1,39 @@ +-- name: AddAgentToolResult :one +INSERT INTO agent_tool_results ( + id, + conversation_id, + run_id, + step_index, + tool_call_id, + tool_name, + full_key, + preview, + chunk_count, + metadata_json + ) +VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) +RETURNING *; +-- name: GetAgentToolResultByRunIDAndToolCallID :one +SELECT * +FROM agent_tool_results +WHERE run_id = ? + AND tool_call_id = ? +LIMIT 1; +-- name: ListAgentToolResultsByConversationID :many +SELECT * +FROM agent_tool_results +WHERE conversation_id = ? +ORDER BY created_at DESC; +-- name: ListAgentToolResultsByConversationIDLimit :many +SELECT * +FROM agent_tool_results +WHERE conversation_id = ? +ORDER BY created_at DESC +LIMIT ?; +-- name: ListAgentToolResultsByRunID :many +SELECT * +FROM agent_tool_results +WHERE run_id = ? +ORDER BY step_index ASC, + created_at ASC +LIMIT sqlc.arg(lim); \ No newline at end of file diff --git a/pkg/memory/sqlc/queries/archival.sql b/pkg/memory/sqlc/queries/archival.sql index 3f0cbd726..c50d30401 100644 --- a/pkg/memory/sqlc/queries/archival.sql +++ b/pkg/memory/sqlc/queries/archival.sql @@ -21,54 +21,66 @@ VALUES ( datetime('now') ); -- name: GetArchivalChunk :one -SELECT id, - recall_id, - chunk_index, - content, - embedding, - source, - hash, - created_at -FROM archival_chunks -WHERE id = sqlc.arg(id); +SELECT ac.id, + ac.recall_id, + ac.chunk_index, + ac.content, + ac.embedding, + ac.source, + ac.hash, + ac.created_at +FROM archival_chunks ac + JOIN recall_items ri ON ac.recall_id = ri.id +WHERE ac.id = sqlc.arg(id) + AND ri.agent_id = sqlc.arg(agent_id) +LIMIT 1; -- name: ListArchivalChunks :many -SELECT id, - recall_id, - chunk_index, - content, - embedding, - source, - hash, - created_at -FROM archival_chunks -WHERE recall_id = sqlc.arg(recall_id) -ORDER BY chunk_index; +SELECT ac.id, + ac.recall_id, + ac.chunk_index, + ac.content, + ac.embedding, + ac.source, + ac.hash, + ac.created_at +FROM archival_chunks ac + JOIN recall_items ri ON ac.recall_id = ri.id +WHERE ac.recall_id = sqlc.arg(recall_id) + AND ri.agent_id = sqlc.arg(agent_id) +ORDER BY ac.chunk_index +LIMIT sqlc.arg(lim); -- name: DeleteArchivalChunksByRecall :exec DELETE FROM archival_chunks WHERE recall_id = sqlc.arg(recall_id); -- name: CountArchivalChunks :one SELECT COUNT(*) -FROM archival_chunks; +FROM archival_chunks ac + JOIN recall_items ri ON ac.recall_id = ri.id +WHERE ri.agent_id = sqlc.arg(agent_id); -- name: ListAllArchivalChunks :many -SELECT id, - recall_id, - chunk_index, - content, - embedding, - source, - hash, - created_at -FROM archival_chunks -ORDER BY created_at DESC +SELECT ac.id, + ac.recall_id, + ac.chunk_index, + ac.content, + ac.embedding, + ac.source, + ac.hash, + ac.created_at +FROM archival_chunks ac + JOIN recall_items ri ON ac.recall_id = ri.id +WHERE ri.agent_id = sqlc.arg(agent_id) +ORDER BY ac.created_at DESC LIMIT sqlc.arg(lim) OFFSET sqlc.arg(off); -- name: GetArchivalChunksByIDs :many -SELECT id, - recall_id, - chunk_index, - content, - embedding, - source, - hash, - created_at -FROM archival_chunks -WHERE id IN (sqlc.slice('ids')); \ No newline at end of file +SELECT ac.id, + ac.recall_id, + ac.chunk_index, + ac.content, + ac.embedding, + ac.source, + ac.hash, + ac.created_at +FROM archival_chunks ac + JOIN recall_items ri ON ac.recall_id = ri.id +WHERE ac.id IN (sqlc.slice('ids')) + AND ri.agent_id = sqlc.arg(agent_id); \ No newline at end of file diff --git a/pkg/memory/sqlc/queries/jobs.sql b/pkg/memory/sqlc/queries/jobs.sql new file mode 100644 index 000000000..aff2dd946 --- /dev/null +++ b/pkg/memory/sqlc/queries/jobs.sql @@ -0,0 +1,96 @@ +-- name: EnqueueJob :one +INSERT INTO jobs ( + id, + kind, + status, + run_at, + max_attempts, + payload_json, + dedupe_key + ) +VALUES ( + sqlc.arg(id), + sqlc.arg(kind), + 'queued', + coalesce( + sqlc.arg(run_at), + strftime('%Y-%m-%dT%H:%M:%fZ', 'now') + ), + coalesce(sqlc.arg(max_attempts), 3), + coalesce(sqlc.arg(payload_json), '{}'), + sqlc.arg(dedupe_key) + ) ON CONFLICT(kind, dedupe_key) +WHERE dedupe_key IS NOT NULL DO +UPDATE +SET status = 'queued', + run_at = excluded.run_at, + max_attempts = excluded.max_attempts, + payload_json = excluded.payload_json, + attempts = 0, + last_error = NULL, + updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') +RETURNING *; +-- name: FindNextRunnableJob :one +SELECT id +FROM jobs +WHERE status = 'queued' + AND run_at <= strftime('%Y-%m-%dT%H:%M:%fZ', 'now') +ORDER BY run_at ASC, + created_at ASC +LIMIT 1; +-- name: ClaimJobByID :one +UPDATE jobs +SET status = 'running', + attempts = attempts + 1, + locked_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + locked_by = sqlc.arg(locked_by), + updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + last_error = NULL +WHERE id = sqlc.arg(id) + AND status = 'queued' +RETURNING *; +-- name: MarkJobSucceeded :one +UPDATE jobs +SET status = 'succeeded', + completed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + last_error = NULL +WHERE id = sqlc.arg(id) +RETURNING *; +-- name: RequeueJob :one +UPDATE jobs +SET status = 'queued', + run_at = coalesce( + sqlc.arg(run_at), + strftime('%Y-%m-%dT%H:%M:%fZ', 'now') + ), + updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + last_error = sqlc.arg(last_error), + locked_at = NULL, + locked_by = NULL, + completed_at = NULL +WHERE id = sqlc.arg(id) +RETURNING *; +-- name: MarkJobFailed :one +UPDATE jobs +SET status = 'failed', + completed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now'), + last_error = sqlc.arg(last_error) +WHERE id = sqlc.arg(id) +RETURNING *; +-- name: GetJob :one +SELECT * +FROM jobs +WHERE id = sqlc.arg(id) +LIMIT 1; +-- name: ListJobs :many +SELECT * +FROM jobs +ORDER BY created_at DESC +LIMIT sqlc.arg(lim) OFFSET sqlc.arg(off); +-- name: CountJobsByStatus :many +SELECT status, + count(*) AS count +FROM jobs +GROUP BY status; \ No newline at end of file diff --git a/pkg/memory/sqlc/queries/recall.sql b/pkg/memory/sqlc/queries/recall.sql index 73b9e535e..74051cd9e 100644 --- a/pkg/memory/sqlc/queries/recall.sql +++ b/pkg/memory/sqlc/queries/recall.sql @@ -42,7 +42,9 @@ SELECT id, created_at, updated_at FROM recall_items -WHERE id = sqlc.arg(id); +WHERE id = sqlc.arg(id) + AND agent_id = sqlc.arg(agent_id) +LIMIT 1; -- name: UpdateRecallItem :exec UPDATE recall_items SET role = sqlc.arg(role), @@ -53,10 +55,12 @@ SET role = sqlc.arg(role), content = sqlc.arg(content), tags = sqlc.arg(tags), updated_at = datetime('now') -WHERE id = sqlc.arg(id); +WHERE id = sqlc.arg(id) + AND agent_id = sqlc.arg(agent_id); -- name: DeleteRecallItem :exec DELETE FROM recall_items -WHERE id = sqlc.arg(id); +WHERE id = sqlc.arg(id) + AND agent_id = sqlc.arg(agent_id); -- name: ListRecallItems :many SELECT id, agent_id, @@ -118,7 +122,8 @@ SELECT id, created_at, updated_at FROM recall_items -WHERE id IN (sqlc.slice('ids')); +WHERE id IN (sqlc.slice('ids')) + AND agent_id = sqlc.arg(agent_id); -- name: InsertSessionMessage :exec INSERT INTO recall_items ( id, diff --git a/pkg/memory/sqlc/queries/working_context.sql b/pkg/memory/sqlc/queries/working_context.sql index 489d1286e..58da9014d 100644 --- a/pkg/memory/sqlc/queries/working_context.sql +++ b/pkg/memory/sqlc/queries/working_context.sql @@ -6,7 +6,8 @@ SELECT agent_id, updated_at FROM working_context WHERE agent_id = sqlc.arg(agent_id) - AND session_key = sqlc.arg(session_key); + AND session_key = sqlc.arg(session_key) +LIMIT 1; -- name: UpsertWorkingContext :exec INSERT INTO working_context (agent_id, session_key, content, updated_at) VALUES ( diff --git a/pkg/memory/sqlc/recall.sql.go b/pkg/memory/sqlc/recall.sql.go index 32ac3e1ef..dc7ebc0de 100644 --- a/pkg/memory/sqlc/recall.sql.go +++ b/pkg/memory/sqlc/recall.sql.go @@ -24,8 +24,8 @@ WHERE agent_id = ?1 ` type CountRecallItemsParams struct { - AgentID string `json:"agent_id"` - SessionKey string `json:"session_key"` + AgentID string `db:"agent_id" json:"agent_id"` + SessionKey string `db:"session_key" json:"session_key"` } // CountRecallItems @@ -53,8 +53,8 @@ WHERE agent_id = ?1 ` type CountSessionMessagesParams struct { - AgentID string `json:"agent_id"` - SessionKey string `json:"session_key"` + AgentID string `db:"agent_id" json:"agent_id"` + SessionKey string `db:"session_key" json:"session_key"` } // CountSessionMessages @@ -74,18 +74,21 @@ func (q *Queries) CountSessionMessages(ctx context.Context, arg CountSessionMess const DeleteRecallItem = `-- name: DeleteRecallItem :exec DELETE FROM recall_items WHERE id = ?1 + AND agent_id = ?2 ` type DeleteRecallItemParams struct { - ID ids.UUID `json:"id"` + ID ids.UUID `db:"id" json:"id"` + AgentID string `db:"agent_id" json:"agent_id"` } // DeleteRecallItem // // DELETE FROM recall_items // WHERE id = ?1 +// AND agent_id = ?2 func (q *Queries) DeleteRecallItem(ctx context.Context, arg DeleteRecallItemParams) error { - _, err := q.db.ExecContext(ctx, DeleteRecallItem, arg.ID) + _, err := q.db.ExecContext(ctx, DeleteRecallItem, arg.ID, arg.AgentID) return err } @@ -104,10 +107,13 @@ SELECT id, updated_at FROM recall_items WHERE id = ?1 + AND agent_id = ?2 +LIMIT 1 ` type GetRecallItemParams struct { - ID ids.UUID `json:"id"` + ID ids.UUID `db:"id" json:"id"` + AgentID string `db:"agent_id" json:"agent_id"` } // GetRecallItem @@ -126,8 +132,10 @@ type GetRecallItemParams struct { // updated_at // FROM recall_items // WHERE id = ?1 +// AND agent_id = ?2 +// LIMIT 1 func (q *Queries) GetRecallItem(ctx context.Context, arg GetRecallItemParams) (RecallItem, error) { - row := q.db.QueryRowContext(ctx, GetRecallItem, arg.ID) + row := q.db.QueryRowContext(ctx, GetRecallItem, arg.ID, arg.AgentID) var i RecallItem err := row.Scan( &i.ID, @@ -161,10 +169,12 @@ SELECT id, updated_at FROM recall_items WHERE id IN (/*SLICE:ids*/?) + AND agent_id = ?2 ` type GetRecallItemsByIDsParams struct { - Ids []ids.UUID `json:"ids"` + Ids []ids.UUID `db:"ids" json:"ids"` + AgentID string `db:"agent_id" json:"agent_id"` } // GetRecallItemsByIDs @@ -183,6 +193,7 @@ type GetRecallItemsByIDsParams struct { // updated_at // FROM recall_items // WHERE id IN (/*SLICE:ids*/?) +// AND agent_id = ?2 func (q *Queries) GetRecallItemsByIDs(ctx context.Context, arg GetRecallItemsByIDsParams) ([]RecallItem, error) { query := GetRecallItemsByIDs var queryParams []interface{} @@ -194,6 +205,7 @@ func (q *Queries) GetRecallItemsByIDs(ctx context.Context, arg GetRecallItemsByI } else { query = strings.Replace(query, "/*SLICE:ids*/?", "NULL", 1) } + queryParams = append(queryParams, arg.AgentID) rows, err := q.db.QueryContext(ctx, query, queryParams...) if err != nil { return nil, err @@ -261,16 +273,16 @@ VALUES ( ` type InsertRecallItemParams struct { - ID ids.UUID `json:"id"` - AgentID string `json:"agent_id"` - SessionKey string `json:"session_key"` - Role string `json:"role"` - Sector memory.Sector `json:"sector"` - Importance float64 `json:"importance"` - Salience float64 `json:"salience"` - DecayRate float64 `json:"decay_rate"` - Content string `json:"content"` - Tags string `json:"tags"` + ID ids.UUID `db:"id" json:"id"` + AgentID string `db:"agent_id" json:"agent_id"` + SessionKey string `db:"session_key" json:"session_key"` + Role string `db:"role" json:"role"` + Sector memory.Sector `db:"sector" json:"sector"` + Importance float64 `db:"importance" json:"importance"` + Salience float64 `db:"salience" json:"salience"` + DecayRate float64 `db:"decay_rate" json:"decay_rate"` + Content string `db:"content" json:"content"` + Tags string `db:"tags" json:"tags"` } // Recall Item queries @@ -351,11 +363,11 @@ VALUES ( ` type InsertSessionMessageParams struct { - ID ids.UUID `json:"id"` - AgentID string `json:"agent_id"` - SessionKey string `json:"session_key"` - Role string `json:"role"` - Content string `json:"content"` + ID ids.UUID `db:"id" json:"id"` + AgentID string `db:"agent_id" json:"agent_id"` + SessionKey string `db:"session_key" json:"session_key"` + Role string `db:"role" json:"role"` + Content string `db:"content" json:"content"` } // InsertSessionMessage @@ -423,10 +435,10 @@ LIMIT ?4 OFFSET ?3 ` type ListRecallItemsParams struct { - AgentID string `json:"agent_id"` - SessionKey string `json:"session_key"` - Off int64 `json:"off"` - Lim int64 `json:"lim"` + AgentID string `db:"agent_id" json:"agent_id"` + SessionKey string `db:"session_key" json:"session_key"` + Off int64 `db:"off" json:"off"` + Lim int64 `db:"lim" json:"lim"` } // ListRecallItems @@ -518,10 +530,10 @@ LIMIT ?4 ` type ListSessionMessagesParams struct { - AgentID string `json:"agent_id"` - SessionKey string `json:"session_key"` - Role string `json:"role"` - Lim int64 `json:"lim"` + AgentID string `db:"agent_id" json:"agent_id"` + SessionKey string `db:"session_key" json:"session_key"` + Role string `db:"role" json:"role"` + Lim int64 `db:"lim" json:"lim"` } // ListSessionMessages @@ -610,9 +622,9 @@ LIMIT ?3 ` type SearchRecallByKeywordParams struct { - Keyword *string `json:"keyword"` - AgentID string `json:"agent_id"` - Lim int64 `json:"lim"` + Keyword *string `db:"keyword" json:"keyword"` + AgentID string `db:"agent_id" json:"agent_id"` + Lim int64 `db:"lim" json:"lim"` } // SearchRecallByKeyword @@ -681,17 +693,19 @@ SET role = ?1, tags = ?7, updated_at = datetime('now') WHERE id = ?8 + AND agent_id = ?9 ` type UpdateRecallItemParams struct { - Role string `json:"role"` - Sector memory.Sector `json:"sector"` - Importance float64 `json:"importance"` - Salience float64 `json:"salience"` - DecayRate float64 `json:"decay_rate"` - Content string `json:"content"` - Tags string `json:"tags"` - ID ids.UUID `json:"id"` + Role string `db:"role" json:"role"` + Sector memory.Sector `db:"sector" json:"sector"` + Importance float64 `db:"importance" json:"importance"` + Salience float64 `db:"salience" json:"salience"` + DecayRate float64 `db:"decay_rate" json:"decay_rate"` + Content string `db:"content" json:"content"` + Tags string `db:"tags" json:"tags"` + ID ids.UUID `db:"id" json:"id"` + AgentID string `db:"agent_id" json:"agent_id"` } // UpdateRecallItem @@ -706,6 +720,7 @@ type UpdateRecallItemParams struct { // tags = ?7, // updated_at = datetime('now') // WHERE id = ?8 +// AND agent_id = ?9 func (q *Queries) UpdateRecallItem(ctx context.Context, arg UpdateRecallItemParams) error { _, err := q.db.ExecContext(ctx, UpdateRecallItem, arg.Role, @@ -716,6 +731,7 @@ func (q *Queries) UpdateRecallItem(ctx context.Context, arg UpdateRecallItemPara arg.Content, arg.Tags, arg.ID, + arg.AgentID, ) return err } diff --git a/pkg/memory/sqlc/schema.sql b/pkg/memory/sqlc/schema.sql index 5eacba709..fb3a6a583 100644 --- a/pkg/memory/sqlc/schema.sql +++ b/pkg/memory/sqlc/schema.sql @@ -95,4 +95,190 @@ CREATE TABLE IF NOT EXISTS agent_audit_log ( created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_audit_agent_time ON agent_audit_log(agent_id, created_at DESC); -CREATE INDEX IF NOT EXISTS idx_audit_action ON agent_audit_log(action); \ No newline at end of file +CREATE INDEX IF NOT EXISTS idx_audit_action ON agent_audit_log(action); +-- ============================================================================ +-- Agent Runtime State Tables +-- ============================================================================ +-- Conversations: minimal parent entity for agent runs and messages. +CREATE TABLE IF NOT EXISTS agent_conversations ( + id BLOB PRIMARY KEY, + title TEXT, + created_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + updated_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) +); +-- Messages: conversation turns. +CREATE TABLE IF NOT EXISTS agent_messages ( + id BLOB PRIMARY KEY, + conversation_id BLOB NOT NULL REFERENCES agent_conversations(id) ON DELETE CASCADE, + role TEXT NOT NULL, + content TEXT NOT NULL, + metadata_json JSON NOT NULL DEFAULT '{}', + created_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + updated_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) +); +CREATE INDEX IF NOT EXISTS idx_agent_messages_conversation_created_at ON agent_messages(conversation_id, created_at); +-- Runs: a single invocation of the agent runtime (one RunTurn call). +CREATE TABLE IF NOT EXISTS agent_runs ( + id BLOB PRIMARY KEY, + conversation_id BLOB NOT NULL REFERENCES agent_conversations(id) ON DELETE CASCADE, + status TEXT NOT NULL DEFAULT 'running', + metadata_json JSON NOT NULL DEFAULT '{}', + created_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + updated_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) +); +CREATE INDEX IF NOT EXISTS idx_agent_runs_conversation_created_at ON agent_runs(conversation_id, created_at); +-- Run states: snapshots captured per step. +CREATE TABLE IF NOT EXISTS agent_run_states ( + id BLOB PRIMARY KEY, + run_id BLOB NOT NULL REFERENCES agent_runs(id) ON DELETE CASCADE, + step_index INTEGER NOT NULL, + state TEXT NOT NULL, + snapshot_json JSON NOT NULL DEFAULT '{}', + created_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + updated_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + UNIQUE(run_id, step_index) +); +CREATE INDEX IF NOT EXISTS idx_agent_run_states_run_step ON agent_run_states(run_id, step_index); +-- Transition log: debugging and resumability. +CREATE TABLE IF NOT EXISTS agent_state_transitions ( + id BLOB PRIMARY KEY, + run_id BLOB NOT NULL REFERENCES agent_runs(id) ON DELETE CASCADE, + step_index INTEGER NOT NULL, + from_state TEXT NOT NULL, + to_state TEXT NOT NULL, + trigger TEXT NOT NULL, + at DATETIME NOT NULL, + meta_json JSON NOT NULL DEFAULT '{}', + error TEXT, + created_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + updated_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) +); +CREATE INDEX IF NOT EXISTS idx_agent_state_transitions_run_step_at ON agent_state_transitions(run_id, step_index, at); +-- Checkpoints: named snapshots for later restore. +CREATE TABLE IF NOT EXISTS agent_checkpoints ( + id BLOB PRIMARY KEY, + conversation_id BLOB NOT NULL REFERENCES agent_conversations(id) ON DELETE CASCADE, + name TEXT NOT NULL, + run_state_id BLOB NOT NULL REFERENCES agent_run_states(id) ON DELETE RESTRICT, + metadata_json JSON NOT NULL DEFAULT '{}', + created_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + updated_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + UNIQUE(conversation_id, name) +); +CREATE INDEX IF NOT EXISTS idx_agent_checkpoints_conversation_created_at ON agent_checkpoints(conversation_id, created_at); +-- Tool results: offloaded tool outputs. +CREATE TABLE IF NOT EXISTS agent_tool_results ( + id BLOB PRIMARY KEY, + conversation_id BLOB NOT NULL REFERENCES agent_conversations(id) ON DELETE CASCADE, + run_id BLOB NOT NULL REFERENCES agent_runs(id) ON DELETE CASCADE, + step_index INTEGER NOT NULL, + tool_call_id TEXT NOT NULL, + tool_name TEXT NOT NULL, + full_key TEXT NOT NULL, + preview TEXT, + chunk_count INTEGER NOT NULL DEFAULT 0, + metadata_json JSON NOT NULL DEFAULT '{}', + created_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + updated_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + UNIQUE(run_id, tool_call_id) +); +CREATE INDEX IF NOT EXISTS idx_agent_tool_results_conversation_created_at ON agent_tool_results(conversation_id, created_at); +CREATE INDEX IF NOT EXISTS idx_agent_tool_results_run_step ON agent_tool_results(run_id, step_index); +CREATE INDEX IF NOT EXISTS idx_agent_tool_results_tool_name ON agent_tool_results(tool_name); +-- ============================================================================ +-- Job Queue +-- ============================================================================ +CREATE TABLE IF NOT EXISTS jobs ( + id BLOB PRIMARY KEY, + kind TEXT NOT NULL, + status TEXT NOT NULL, + run_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + attempts INTEGER NOT NULL DEFAULT 0, + max_attempts INTEGER NOT NULL DEFAULT 3, + locked_at DATETIME, + locked_by TEXT, + payload_json JSON NOT NULL DEFAULT '{}', + dedupe_key TEXT, + last_error TEXT, + created_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + updated_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + completed_at DATETIME +); +CREATE INDEX IF NOT EXISTS idx_jobs_status_run_at ON jobs(status, run_at); +CREATE UNIQUE INDEX IF NOT EXISTS idx_jobs_kind_dedupe ON jobs(kind, dedupe_key) +WHERE dedupe_key IS NOT NULL; +-- ============================================================================ +-- Conversation Graph (forks, links, threads, mentions, edits) +-- ============================================================================ +-- Forks: parent→child conversation via checkpoint. +CREATE TABLE IF NOT EXISTS agent_conversation_forks ( + id BLOB PRIMARY KEY, + parent_conversation_id BLOB NOT NULL REFERENCES agent_conversations(id) ON DELETE CASCADE, + child_conversation_id BLOB NOT NULL REFERENCES agent_conversations(id) ON DELETE CASCADE, + checkpoint_id BLOB NOT NULL REFERENCES agent_checkpoints(id) ON DELETE RESTRICT, + metadata_json JSON NOT NULL DEFAULT '{}', + created_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + updated_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + UNIQUE(child_conversation_id) +); +CREATE INDEX IF NOT EXISTS idx_agent_conversation_forks_parent_created_at ON agent_conversation_forks(parent_conversation_id, created_at DESC); +-- Links: user-created relationships between conversations (merge, reference, etc.). +CREATE TABLE IF NOT EXISTS agent_conversation_links ( + id BLOB PRIMARY KEY, + conversation_id BLOB NOT NULL REFERENCES agent_conversations(id) ON DELETE CASCADE, + linked_conversation_id BLOB NOT NULL REFERENCES agent_conversations(id) ON DELETE CASCADE, + kind TEXT NOT NULL DEFAULT 'merge', + metadata_json JSON NOT NULL DEFAULT '{}', + created_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + updated_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + UNIQUE(conversation_id, linked_conversation_id, kind) +); +CREATE INDEX IF NOT EXISTS idx_agent_conversation_links_conversation_id_created_at ON agent_conversation_links(conversation_id, created_at DESC); +-- Threads: sub-conversations within a main conversation. +CREATE TABLE IF NOT EXISTS agent_threads ( + id BLOB PRIMARY KEY, + conversation_id BLOB NOT NULL REFERENCES agent_conversations(id) ON DELETE CASCADE, + title TEXT, + metadata_json JSON NOT NULL DEFAULT '{}', + created_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + updated_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) +); +CREATE INDEX IF NOT EXISTS idx_agent_threads_conversation_id_created_at ON agent_threads(conversation_id, created_at DESC); +-- Thread messages. +CREATE TABLE IF NOT EXISTS agent_thread_messages ( + id BLOB PRIMARY KEY, + thread_id BLOB NOT NULL REFERENCES agent_threads(id) ON DELETE CASCADE, + role TEXT NOT NULL, + content TEXT NOT NULL, + metadata_json JSON NOT NULL DEFAULT '{}', + created_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + updated_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) +); +CREATE INDEX IF NOT EXISTS idx_agent_thread_messages_thread_id_created_at ON agent_thread_messages(thread_id, created_at ASC); +-- Mentions: captured from messages (conversation, thread, file references). +CREATE TABLE IF NOT EXISTS agent_mentions ( + id BLOB PRIMARY KEY, + conversation_id BLOB NOT NULL REFERENCES agent_conversations(id) ON DELETE CASCADE, + message_id BLOB REFERENCES agent_messages(id) ON DELETE + SET NULL, + kind TEXT NOT NULL, + target_id BLOB NOT NULL, + raw TEXT NOT NULL, + metadata_json JSON NOT NULL DEFAULT '{}', + created_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + updated_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) +); +CREATE INDEX IF NOT EXISTS idx_agent_mentions_conversation_id_created_at ON agent_mentions(conversation_id, created_at DESC); +CREATE INDEX IF NOT EXISTS idx_agent_mentions_kind_target_id ON agent_mentions(kind, target_id); +-- Message revisions: edit history. +CREATE TABLE IF NOT EXISTS agent_message_revisions ( + id BLOB PRIMARY KEY, + message_id BLOB NOT NULL REFERENCES agent_messages(id) ON DELETE CASCADE, + editor TEXT NOT NULL, + old_content TEXT NOT NULL, + new_content TEXT NOT NULL, + metadata_json JSON NOT NULL DEFAULT '{}', + created_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + updated_at DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) +); +CREATE INDEX IF NOT EXISTS idx_agent_message_revisions_message_id_created_at ON agent_message_revisions(message_id, created_at DESC); \ No newline at end of file diff --git a/pkg/memory/sqlc/sqlc.yaml b/pkg/memory/sqlc/sqlc.yaml index 17c52018b..8fb52adff 100644 --- a/pkg/memory/sqlc/sqlc.yaml +++ b/pkg/memory/sqlc/sqlc.yaml @@ -9,6 +9,7 @@ sql: out: "." sql_package: "database/sql" emit_json_tags: true + emit_db_tags: true json_tags_case_style: "snake" emit_empty_slices: true emit_interface: true @@ -22,7 +23,7 @@ sql: query_parameter_limit: 0 initialisms: ["id", "url", "api", "sql", "fts", "uuid"] overrides: - # Entity IDs: UUIDv7 via ids.UUID (TEXT storage with Valuer/Scanner) + # Entity IDs: UUIDv7 via ids.UUID (BLOB storage with Valuer/Scanner) # Only entity-owned PKs and their FKs — NOT agent_id/session_key (external identifiers) - column: "recall_items.id" go_type: @@ -48,6 +49,152 @@ sql: go_type: import: "github.com/sipeed/picoclaw/pkg/ids" type: "UUID" + # Agent conversations + - column: "agent_conversations.id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + # Agent messages + - column: "agent_messages.id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + - column: "agent_messages.conversation_id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + # Agent runs + - column: "agent_runs.id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + - column: "agent_runs.conversation_id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + # Agent run states + - column: "agent_run_states.id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + - column: "agent_run_states.run_id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + # Agent state transitions + - column: "agent_state_transitions.id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + - column: "agent_state_transitions.run_id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + # Agent checkpoints + - column: "agent_checkpoints.id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + - column: "agent_checkpoints.conversation_id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + - column: "agent_checkpoints.run_state_id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + # Agent tool results + - column: "agent_tool_results.id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + - column: "agent_tool_results.conversation_id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + - column: "agent_tool_results.run_id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + # Jobs + - column: "jobs.id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + # Conversation forks + - column: "agent_conversation_forks.id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + - column: "agent_conversation_forks.parent_conversation_id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + - column: "agent_conversation_forks.child_conversation_id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + - column: "agent_conversation_forks.checkpoint_id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + # Conversation links + - column: "agent_conversation_links.id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + - column: "agent_conversation_links.conversation_id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + - column: "agent_conversation_links.linked_conversation_id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + # Threads + - column: "agent_threads.id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + - column: "agent_threads.conversation_id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + # Thread messages + - column: "agent_thread_messages.id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + - column: "agent_thread_messages.thread_id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + # Mentions + - column: "agent_mentions.id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + - column: "agent_mentions.conversation_id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + - column: "agent_mentions.message_id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + - column: "agent_mentions.target_id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + # Message revisions + - column: "agent_message_revisions.id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" + - column: "agent_message_revisions.message_id" + go_type: + import: "github.com/sipeed/picoclaw/pkg/ids" + type: "UUID" # Domain type: recall_items.sector → memory.Sector - column: "recall_items.sector" go_type: @@ -77,3 +224,18 @@ sql: go_type: import: "encoding/json" type: "RawMessage" + rules: + - no-unbounded-delete + - one-select-requires-limit-1 +rules: + - name: no-unbounded-delete + message: "DELETE statements must include a WHERE clause" + rule: > + query.sql.contains("DELETE") && !query.sql.contains("WHERE") + - name: one-select-requires-limit-1 + message: ":one SELECT queries must include LIMIT 1 (except aggregates)" + rule: > + query.cmd == "one" + && query.sql.contains("SELECT") + && !query.sql.contains("LIMIT 1") + && !query.sql.contains("COUNT(") diff --git a/pkg/memory/sqlc/summaries.sql.go b/pkg/memory/sqlc/summaries.sql.go index 563a2a4e1..2b68fcdc6 100644 --- a/pkg/memory/sqlc/summaries.sql.go +++ b/pkg/memory/sqlc/summaries.sql.go @@ -33,12 +33,12 @@ VALUES ( ` type InsertSummaryParams struct { - ID ids.UUID `json:"id"` - AgentID string `json:"agent_id"` - SessionKey string `json:"session_key"` - Content string `json:"content"` - FromMsgIdx int64 `json:"from_msg_idx"` - ToMsgIdx int64 `json:"to_msg_idx"` + ID ids.UUID `db:"id" json:"id"` + AgentID string `db:"agent_id" json:"agent_id"` + SessionKey string `db:"session_key" json:"session_key"` + Content string `db:"content" json:"content"` + FromMsgIdx int64 `db:"from_msg_idx" json:"from_msg_idx"` + ToMsgIdx int64 `db:"to_msg_idx" json:"to_msg_idx"` } // Memory Summary queries @@ -92,9 +92,9 @@ LIMIT ?3 ` type ListSummariesParams struct { - AgentID string `json:"agent_id"` - SessionKey string `json:"session_key"` - Lim int64 `json:"lim"` + AgentID string `db:"agent_id" json:"agent_id"` + SessionKey string `db:"session_key" json:"session_key"` + Lim int64 `db:"lim" json:"lim"` } // ListSummaries diff --git a/pkg/memory/sqlc/working_context.sql.go b/pkg/memory/sqlc/working_context.sql.go index f76818665..40dd318fd 100644 --- a/pkg/memory/sqlc/working_context.sql.go +++ b/pkg/memory/sqlc/working_context.sql.go @@ -17,11 +17,12 @@ SELECT agent_id, FROM working_context WHERE agent_id = ?1 AND session_key = ?2 +LIMIT 1 ` type GetWorkingContextParams struct { - AgentID string `json:"agent_id"` - SessionKey string `json:"session_key"` + AgentID string `db:"agent_id" json:"agent_id"` + SessionKey string `db:"session_key" json:"session_key"` } // Working Context queries @@ -33,6 +34,7 @@ type GetWorkingContextParams struct { // FROM working_context // WHERE agent_id = ?1 // AND session_key = ?2 +// LIMIT 1 func (q *Queries) GetWorkingContext(ctx context.Context, arg GetWorkingContextParams) (WorkingContext, error) { row := q.db.QueryRowContext(ctx, GetWorkingContext, arg.AgentID, arg.SessionKey) var i WorkingContext @@ -59,9 +61,9 @@ SET content = excluded.content, ` type UpsertWorkingContextParams struct { - AgentID string `json:"agent_id"` - SessionKey string `json:"session_key"` - Content string `json:"content"` + AgentID string `db:"agent_id" json:"agent_id"` + SessionKey string `db:"session_key" json:"session_key"` + Content string `db:"content" json:"content"` } // UpsertWorkingContext