feat: v0.3.0 — State, Memory, message_status, MCP instructions
Some checks failed
Some checks failed
Phase B + C + message delivery status. State: shared key-value store per mesh. set_state pushes changes to all peers. get_state/list_state for reads. Peers coordinate through shared facts instead of messages. Memory: persistent knowledge with full-text search (tsvector). remember/recall/forget. New peers recall context from past sessions. message_status: check delivery status with per-recipient detail (delivered/held/disconnected). Multicast fix: broadcast and @group messages now push directly to all connected peers instead of racing through queue drain. MCP instructions: dynamic identity injection (name, groups, role), comprehensive tool reference, group coordination guide. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
27
packages/db/migrations/0008_add-state-and-memory.sql
Normal file
27
packages/db/migrations/0008_add-state-and-memory.sql
Normal file
@@ -0,0 +1,27 @@
|
||||
CREATE TABLE "mesh"."memory" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"mesh_id" text NOT NULL,
|
||||
"content" text NOT NULL,
|
||||
"tags" text[] DEFAULT '{}',
|
||||
"remembered_by" text,
|
||||
"remembered_by_name" text,
|
||||
"remembered_at" timestamp DEFAULT now() NOT NULL,
|
||||
"forgotten_at" timestamp
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "mesh"."state" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"mesh_id" text NOT NULL,
|
||||
"key" text NOT NULL,
|
||||
"value" jsonb NOT NULL,
|
||||
"updated_by_presence" text,
|
||||
"updated_by_name" text,
|
||||
"updated_at" timestamp DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "mesh"."memory" ADD CONSTRAINT "memory_mesh_id_mesh_id_fk" FOREIGN KEY ("mesh_id") REFERENCES "mesh"."mesh"("id") ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint
|
||||
ALTER TABLE "mesh"."memory" ADD CONSTRAINT "memory_remembered_by_member_id_fk" FOREIGN KEY ("remembered_by") REFERENCES "mesh"."member"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "mesh"."state" ADD CONSTRAINT "state_mesh_id_mesh_id_fk" FOREIGN KEY ("mesh_id") REFERENCES "mesh"."mesh"("id") ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "state_mesh_key_idx" ON "mesh"."state" USING btree ("mesh_id","key");--> statement-breakpoint
|
||||
ALTER TABLE "mesh"."memory" ADD COLUMN IF NOT EXISTS "search_vector" tsvector GENERATED ALWAYS AS (to_tsvector('english', content)) STORED;--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "memory_search_idx" ON "mesh"."memory" USING gin("search_vector");
|
||||
3049
packages/db/migrations/meta/0008_snapshot.json
Normal file
3049
packages/db/migrations/meta/0008_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -57,6 +57,13 @@
|
||||
"when": 1775476994511,
|
||||
"tag": "0007_add-presence-groups",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"version": "7",
|
||||
"when": 1775477883426,
|
||||
"tag": "0008_add-state-and-memory",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
pgSchema,
|
||||
timestamp,
|
||||
text,
|
||||
uniqueIndex,
|
||||
} from "drizzle-orm/pg-core";
|
||||
|
||||
import { generateId } from "@turbostarter/shared/utils";
|
||||
@@ -251,6 +252,43 @@ export const pendingStatus = meshSchema.table("pending_status", {
|
||||
appliedAt: timestamp(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Shared key-value state scoped to a mesh. Any peer can read/write.
|
||||
* Changes push to all connected peers in real time.
|
||||
*/
|
||||
export const meshState = meshSchema.table(
|
||||
"state",
|
||||
{
|
||||
id: text().primaryKey().notNull().$defaultFn(generateId),
|
||||
meshId: text()
|
||||
.references(() => mesh.id, { onDelete: "cascade", onUpdate: "cascade" })
|
||||
.notNull(),
|
||||
key: text().notNull(),
|
||||
value: jsonb().notNull(),
|
||||
updatedByPresence: text(),
|
||||
updatedByName: text(),
|
||||
updatedAt: timestamp().defaultNow().notNull(),
|
||||
},
|
||||
(table) => [uniqueIndex("state_mesh_key_idx").on(table.meshId, table.key)],
|
||||
);
|
||||
|
||||
/**
|
||||
* Persistent shared memory for a mesh. Full-text searchable via a
|
||||
* tsvector generated column + GIN index added in raw SQL migration.
|
||||
*/
|
||||
export const meshMemory = meshSchema.table("memory", {
|
||||
id: text().primaryKey().notNull().$defaultFn(generateId),
|
||||
meshId: text()
|
||||
.references(() => mesh.id, { onDelete: "cascade", onUpdate: "cascade" })
|
||||
.notNull(),
|
||||
content: text().notNull(),
|
||||
tags: text().array().default([]),
|
||||
rememberedBy: text().references(() => meshMember.id),
|
||||
rememberedByName: text(),
|
||||
rememberedAt: timestamp().defaultNow().notNull(),
|
||||
forgottenAt: timestamp(),
|
||||
});
|
||||
|
||||
export const meshRelations = relations(mesh, ({ one, many }) => ({
|
||||
owner: one(user, {
|
||||
fields: [mesh.ownerUserId],
|
||||
@@ -311,6 +349,24 @@ export const auditLogRelations = relations(auditLog, ({ one }) => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
export const meshStateRelations = relations(meshState, ({ one }) => ({
|
||||
mesh: one(mesh, {
|
||||
fields: [meshState.meshId],
|
||||
references: [mesh.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const meshMemoryRelations = relations(meshMemory, ({ one }) => ({
|
||||
mesh: one(mesh, {
|
||||
fields: [meshMemory.meshId],
|
||||
references: [mesh.id],
|
||||
}),
|
||||
member: one(meshMember, {
|
||||
fields: [meshMemory.rememberedBy],
|
||||
references: [meshMember.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const selectMeshSchema = createSelectSchema(mesh);
|
||||
export const insertMeshSchema = createInsertSchema(mesh);
|
||||
export const selectMemberSchema = createSelectSchema(meshMember);
|
||||
@@ -340,3 +396,11 @@ export type SelectMessageQueue = typeof messageQueue.$inferSelect;
|
||||
export type InsertMessageQueue = typeof messageQueue.$inferInsert;
|
||||
export type SelectPendingStatus = typeof pendingStatus.$inferSelect;
|
||||
export type InsertPendingStatus = typeof pendingStatus.$inferInsert;
|
||||
export const selectMeshStateSchema = createSelectSchema(meshState);
|
||||
export const insertMeshStateSchema = createInsertSchema(meshState);
|
||||
export const selectMeshMemorySchema = createSelectSchema(meshMemory);
|
||||
export const insertMeshMemorySchema = createInsertSchema(meshMemory);
|
||||
export type SelectMeshState = typeof meshState.$inferSelect;
|
||||
export type InsertMeshState = typeof meshState.$inferInsert;
|
||||
export type SelectMeshMemory = typeof meshMemory.$inferSelect;
|
||||
export type InsertMeshMemory = typeof meshMemory.$inferInsert;
|
||||
|
||||
Reference in New Issue
Block a user