Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion PRIVACY.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,13 @@ message, on the same terms and bounds (see "Files you attach to a coding
agent"): an answer carrying one is always handed to the agent, and our
service also keeps each file, with its name, in our database beside the item,
where every member of the task's workspace can open it, until the answer is
taken back or the agent posts the item again. While you are signed in, Luke
taken back or the agent posts the item again. Any member of the task's
workspace can also add a message to an item's thread at any point, answered
or not, with files on the same terms: our service keeps the message's words
and files with the item under the account of the member who wrote it, which
every member of the workspace sees by name beside it, and hands it to the
agent as one of your messages, kept in its transcript. A thread stays until
the item is deleted with its task. While you are signed in, Luke
on your Mac keeps a read open to our service for every item waiting in your
workspaces, each in brief (its task, agent, title, and the account that started
the agent), and for every task holding an item; it counts in its sidebar and on
Expand Down
4 changes: 4 additions & 0 deletions apps/web/server/api-rewrites.json
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@
"src": "/api/inbox/([^/]+)/attachments/([^/]+)",
"dest": "/api/default.js?route=inbox/attachment&id=$1&attachment=$2"
},
{
"src": "/api/inbox/([^/]+)/messages",
"dest": "/api/default.js?route=inbox/messages&id=$1"
},
{
"src": "/api/inbox/([^/]+)",
"dest": "/api/default.js?route=inbox/item&id=$1"
Expand Down
1 change: 1 addition & 0 deletions apps/web/server/function-rewrites.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ const SEGMENT_REWRITES: readonly SegmentRewrite[] = [
route: "inbox/attachment",
query: "id=$1&attachment=$2",
},
{ src: "/api/inbox/([^/]+)/messages", route: "inbox/messages", query: "id=$1" },
{ src: "/api/inbox/([^/]+)", route: "inbox/item", query: "id=$1" },
{ src: "/api/workspaces/([^/]+)/photo", route: "workspaces/photo", query: "id=$1" },
{ src: "/api/automations/([^/]+)/webhook", route: "automations/webhook", query: "id=$1" },
Expand Down
9 changes: 9 additions & 0 deletions apps/web/server/routes/inbox/messages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { deploymentEveOrigin } from "@luke/service/agents/brain-host/eve-origin";
import { codingAgentsApp } from "@luke/service/agents/coding-agents-app";
import { resolveHostedUserId } from "@luke/service/auth/bearer";
import { routeFromHttpRouter } from "../../route-effect.js";

/** A member's message on one inbox item's thread (POST), handed to the agent that posted the item. The logic lives in `packages/service/src/agents/coding-agents-app.ts`; this file only hands it the deployment's real seams. */
export default routeFromHttpRouter(
codingAgentsApp({ resolveUserId: resolveHostedUserId, eveOrigin: deploymentEveOrigin }),
);
1 change: 1 addition & 0 deletions apps/web/tests/storage-schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const DECLARED_TABLES = [
"hosted_usage",
"inbox_attachment",
"inbox_item",
"inbox_message",
"integration_connection",
"invitation",
"issue",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
CREATE TABLE IF NOT EXISTS "inbox_message" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
"item_id" uuid NOT NULL,
"author" text NOT NULL,
"user_id" text,
"text" text NOT NULL,
"client_key" text,
"created_at" timestamp with time zone NOT NULL
);
--> statement-breakpoint
-- A nullable column with no default is written to the catalog alone, so no row of the table is rewritten; the same holds below.
ALTER TABLE "inbox_attachment" ADD COLUMN IF NOT EXISTS "message_id" uuid;--> statement-breakpoint
-- No backfill: a read takes a settled item's last change for a null settled_at, and the first message on its thread keeps that instant here before moving the change (`inbox-store.ts`).
ALTER TABLE "inbox_item" ADD COLUMN IF NOT EXISTS "settled_at" timestamp with time zone;--> statement-breakpoint
-- The table holds a few rows per item, their bytes stored out of line, and the column is null on every one, so the build reads little under its lock.
CREATE INDEX IF NOT EXISTS "inbox_attachment_message" ON "inbox_attachment" ("message_id");--> statement-breakpoint
CREATE INDEX IF NOT EXISTS "inbox_message_item_created" ON "inbox_message" ("item_id","created_at");--> statement-breakpoint
CREATE UNIQUE INDEX IF NOT EXISTS "inbox_message_client_key" ON "inbox_message" ("item_id","client_key") WHERE "client_key" is not null;--> statement-breakpoint
ALTER TABLE "inbox_attachment" DROP CONSTRAINT IF EXISTS "inbox_attachment_message_id_inbox_message_id_fkey";--> statement-breakpoint
-- Every row standing names no message, so the constraint is not validated over them: a validation would read the whole table under a lock that holds back every attachment written meanwhile.
ALTER TABLE "inbox_attachment" ADD CONSTRAINT "inbox_attachment_message_id_inbox_message_id_fkey" FOREIGN KEY ("message_id") REFERENCES "inbox_message"("id") ON DELETE CASCADE NOT VALID;--> statement-breakpoint
ALTER TABLE "inbox_message" DROP CONSTRAINT IF EXISTS "inbox_message_item_id_inbox_item_id_fkey";--> statement-breakpoint
ALTER TABLE "inbox_message" ADD CONSTRAINT "inbox_message_item_id_inbox_item_id_fkey" FOREIGN KEY ("item_id") REFERENCES "inbox_item"("id") ON DELETE CASCADE;--> statement-breakpoint
ALTER TABLE "inbox_message" DROP CONSTRAINT IF EXISTS "inbox_message_user_id_user_id_fkey";--> statement-breakpoint
ALTER TABLE "inbox_message" ADD CONSTRAINT "inbox_message_user_id_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE SET NULL;--> statement-breakpoint
ALTER TABLE "inbox_attachment" DROP CONSTRAINT IF EXISTS "inbox_attachment_kind_bound";--> statement-breakpoint
-- Every row standing is a picture or an answer's file and names no message, which the old check already held of it, so the new one is not validated over them, for the reason above.
ALTER TABLE "inbox_attachment" ADD CONSTRAINT "inbox_attachment_kind_bound" CHECK (("kind" = 'image' and octet_length("bytes") <= 1500000 and "name" is null and "position" is null and "message_id" is null) or ("kind" = 'answer' and octet_length("bytes") <= 3000000 and "name" is not null and "position" is not null and "message_id" is null) or ("kind" = 'message' and octet_length("bytes") <= 3000000 and "name" is not null and "position" is not null and "message_id" is not null)) NOT VALID;
Loading
Loading