feat: Slice 1 polish - MiMo V2.5 model, visible reasoning, image attachments, mobile keyboard fix

- Switch conversation agent to xiaomi/mimo-v2.5 (multimodal: text + image)
- Render native reasoning parts as live 'Thinking trace' (streaming open,
  collapsed after completion); inline <think> extraction for streaming models
- Image attachments: picker (up to 4, 10MB each), base64 to Flue
  AgentPromptImage, authenticated blob-URL replay for historical images
- Mobile keyboard viewport fix: visual-viewport hook, fixed shell,
  interactive-widget=resizes-content, header pinned, composer follows keyboard
- Conversation to Signal to proposed Work: Convex persistence, Effect
  validation in @code/work-os, Work cards with exact source provenance
- Streamdown markdown + Mermaid chart rendering in chat messages
- Flue tool turns hidden, reasoning-containing turns remain visible
- Frontend regression tests: keyboard viewport, responsive shell,
  attachment overflow, authenticated images, reasoning traces, transforms
- .env.example updated to xiaomi/mimo-v2.5 config
This commit is contained in:
-Puter
2026-07-27 12:22:54 +05:30
parent 58ff7942bd
commit d8383a788e
55 changed files with 6487 additions and 2006 deletions

View File

@@ -30,6 +30,7 @@ import type * as signalRouting from "../signalRouting.js";
import type * as signals from "../signals.js";
import type * as todos from "../todos.js";
import type * as workflows from "../workflows.js";
import type * as works from "../works.js";
import type {
ApiFromModules,
@@ -60,6 +61,7 @@ declare const fullApi: ApiFromModules<{
signals: typeof signals;
todos: typeof todos;
workflows: typeof workflows;
works: typeof works;
}>;
/**

View File

@@ -282,6 +282,41 @@ export default defineSchema({
"messageId",
]),
works: defineTable({
organizationId: v.id("organizations"),
projectId: v.id("projects"),
title: v.string(),
objective: v.string(),
status: v.literal("proposed"),
createdAt: v.number(),
updatedAt: v.number(),
})
.index("by_project_and_createdAt", ["projectId", "createdAt"])
.index("by_organization_and_createdAt", ["organizationId", "createdAt"]),
signalWorkAttachments: defineTable({
organizationId: v.id("organizations"),
projectId: v.id("projects"),
signalId: v.id("signals"),
workId: v.id("works"),
createdAt: v.number(),
})
.index("by_signal", ["signalId"])
.index("by_work", ["workId"])
.index("by_signal_and_work", ["signalId", "workId"]),
workEvents: defineTable({
organizationId: v.id("organizations"),
projectId: v.id("projects"),
workId: v.id("works"),
kind: v.union(v.literal("work.proposed"), v.literal("signal.attached")),
idempotencyKey: v.string(),
data: v.any(),
createdAt: v.number(),
})
.index("by_work_and_createdAt", ["workId", "createdAt"])
.index("by_work_and_idempotencyKey", ["workId", "idempotencyKey"]),
// -----------------------------------------------------------------
// Signal-to-issue attachments. A proper relation that links one Signal
// to one ProjectIssue, idempotent by (signalId, issueId). Multiple

View File

@@ -0,0 +1,224 @@
import { env } from "@code/env/convex";
import { convexTest } from "convex-test";
import { anyApi } from "convex/server";
import { describe, expect, test } from "vitest";
import { internal } from "./_generated/api";
import schema from "./schema";
declare global {
interface ImportMeta {
readonly glob: (pattern: string) => Record<string, () => Promise<unknown>>;
}
}
const modules = import.meta.glob("./**/*.ts");
const api = anyApi;
const identity = { tokenIdentifier: "https://convex.test|slice-one" };
describe("Slice 1 Work routing", () => {
test("creates one proposed Work and preserves exact source text", async () => {
const t = convexTest({ schema, modules });
const organization = await t
.withIdentity(identity)
.mutation(api.organizations.ensurePersonalOrganization, {});
const project = await t
.withIdentity(identity)
.mutation(internal.projects.persistPublicGitImport, {
remote: { defaultBranch: "main", documents: [], warnings: [] },
source: {
host: "github.com",
normalizedUrl: "https://github.com/example/slice-one",
projectName: "slice-one",
repositoryPath: "example/slice-one",
url: "https://github.com/example/slice-one",
},
userId: identity.tokenIdentifier,
});
const rawText = " Add a phone-ready Slice 1 experience. ";
const message = await t
.withIdentity(identity)
.mutation(api.conversationMessages.beginUserMessage, {
clientRequestId: "slice-one-request",
organizationId: organization._id,
rawText,
});
await t
.withIdentity(identity)
.mutation(api.conversationMessages.markAdmitted, {
clientRequestId: "slice-one-request",
organizationId: organization._id,
submissionId: "slice-one-submission",
});
const signal = await t.mutation(api.signalRouting.createSignal, {
messageIds: [message.messageId],
organizationId: organization._id,
problemStatement: {
constraints: ["Mobile web first"],
desiredOutcome: "The Slice 1 loop works on a phone.",
summary: "The current product is not ready for phone testing.",
title: "Make Slice 1 phone-ready",
},
processedByAgentInstanceId: organization._id,
projectId: project.id,
token: env.FLUE_DB_TOKEN,
});
const first = await t.mutation(api.works.createFromSignal, {
organizationId: organization._id,
signalId: signal.signalId,
token: env.FLUE_DB_TOKEN,
});
const repeated = await t.mutation(api.works.createFromSignal, {
organizationId: organization._id,
signalId: signal.signalId,
token: env.FLUE_DB_TOKEN,
});
expect(repeated).toEqual({ created: false, workId: first.workId });
const works = await t
.withIdentity(identity)
.query(api.works.listForProject, { projectId: project.id });
expect(works).toHaveLength(1);
expect(works[0]?.status).toBe("proposed");
expect(works[0]?.signals[0]?.sources[0]?.rawText).toBe(rawText);
});
test("keeps casual conversation out of Work", async () => {
const t = convexTest({ schema, modules });
const organization = await t
.withIdentity(identity)
.mutation(api.organizations.ensurePersonalOrganization, {});
const project = await t
.withIdentity(identity)
.mutation(internal.projects.persistPublicGitImport, {
remote: { defaultBranch: "main", documents: [], warnings: [] },
source: {
host: "git.openputer.com",
normalizedUrl: "https://git.openputer.com/puter/zopu-code",
projectName: "zopu-code",
repositoryPath: "puter/zopu-code",
url: "https://git.openputer.com/puter/zopu-code",
},
userId: identity.tokenIdentifier,
});
await t
.withIdentity(identity)
.mutation(api.conversationMessages.beginUserMessage, {
clientRequestId: "slice-one-casual",
organizationId: organization._id,
rawText: "Yo, how are you?",
});
await t
.withIdentity(identity)
.mutation(api.conversationMessages.markAdmitted, {
clientRequestId: "slice-one-casual",
organizationId: organization._id,
submissionId: "slice-one-casual-submission",
});
const works = await t
.withIdentity(identity)
.query(api.works.listForProject, { projectId: project.id });
expect(works).toEqual([]);
});
test("attaches another Signal to existing Work idempotently", async () => {
const t = convexTest({ schema, modules });
const organization = await t
.withIdentity(identity)
.mutation(api.organizations.ensurePersonalOrganization, {});
const project = await t
.withIdentity(identity)
.mutation(internal.projects.persistPublicGitImport, {
remote: { defaultBranch: "main", documents: [], warnings: [] },
source: {
host: "git.openputer.com",
normalizedUrl: "https://git.openputer.com/puter/zopu-code",
projectName: "zopu-code",
repositoryPath: "puter/zopu-code",
url: "https://git.openputer.com/puter/zopu-code",
},
userId: identity.tokenIdentifier,
});
const seedSignal = async (
clientRequestId: string,
rawText: string,
title: string
) => {
const message = await t
.withIdentity(identity)
.mutation(api.conversationMessages.beginUserMessage, {
clientRequestId,
organizationId: organization._id,
rawText,
});
await t
.withIdentity(identity)
.mutation(api.conversationMessages.markAdmitted, {
clientRequestId,
organizationId: organization._id,
submissionId: `${clientRequestId}-submission`,
});
return await t.mutation(api.signalRouting.createSignal, {
messageIds: [message.messageId],
organizationId: organization._id,
problemStatement: {
constraints: [],
desiredOutcome: "The Slice 1 phone flow is polished.",
summary: "The messages describe the same desired outcome.",
title,
},
processedByAgentInstanceId: organization._id,
projectId: project.id,
token: env.FLUE_DB_TOKEN,
});
};
const firstSignal = await seedSignal(
"slice-one-first",
"Polish the Slice 1 phone experience.",
"Polish Slice 1"
);
const secondSignal = await seedSignal(
"slice-one-second",
"Make the same Slice 1 experience easier to inspect on mobile.",
"Improve Slice 1 inspection"
);
const created = await t.mutation(api.works.createFromSignal, {
organizationId: organization._id,
signalId: firstSignal.signalId,
token: env.FLUE_DB_TOKEN,
});
const firstAttach = await t.mutation(api.works.attachSignalToWork, {
organizationId: organization._id,
signalId: secondSignal.signalId,
token: env.FLUE_DB_TOKEN,
workId: created.workId,
});
const repeatedAttach = await t.mutation(api.works.attachSignalToWork, {
organizationId: organization._id,
signalId: secondSignal.signalId,
token: env.FLUE_DB_TOKEN,
workId: created.workId,
});
expect(firstAttach).toEqual({ attached: true, workId: created.workId });
expect(repeatedAttach).toEqual({
attached: false,
workId: created.workId,
});
const works = await t
.withIdentity(identity)
.query(api.works.listForProject, { projectId: project.id });
expect(works).toHaveLength(1);
expect(works[0]?.signals).toHaveLength(2);
expect(
works[0]?.events.filter(
(event: { readonly kind: string }) => event.kind === "signal.attached"
)
).toHaveLength(1);
});
});

View File

@@ -0,0 +1,267 @@
import { env } from "@code/env/convex";
import {
signalAttachedEvent,
workDraftFromSignal,
workProposedEventFromSignal,
} from "@code/work-os";
import { ConvexError, v } from "convex/values";
import { Effect } from "effect";
import type { Doc, Id } from "./_generated/dataModel";
import { mutation, type MutationCtx, query } from "./_generated/server";
import { requireProjectMember } from "./authz";
const requireAgent = (token: string) => {
if (token !== env.FLUE_DB_TOKEN) {
throw new ConvexError("Invalid agent control token");
}
};
const requireSignal = async (
ctx: MutationCtx,
organizationId: Id<"organizations">,
signalId: Id<"signals">
): Promise<Doc<"signals">> => {
const signal = await ctx.db.get(signalId);
if (!signal || signal.organizationId !== organizationId) {
throw new ConvexError("Signal not found");
}
if (!signal.projectId) {
throw new ConvexError("Signal must belong to a project");
}
return signal;
};
const requireWork = async (
ctx: MutationCtx,
organizationId: Id<"organizations">,
workId: Id<"works">
): Promise<Doc<"works">> => {
const work = await ctx.db.get(workId);
if (!work || work.organizationId !== organizationId) {
throw new ConvexError("Work not found");
}
return work;
};
const attachSignal = async (
ctx: MutationCtx,
signal: Doc<"signals">,
work: Doc<"works">
): Promise<boolean> => {
const event = await Effect.runPromise(
signalAttachedEvent({
signal: {
organizationId: String(signal.organizationId),
projectId: String(signal.projectId),
signalId: String(signal._id),
title: signal.problemStatement.title,
},
work: {
organizationId: String(work.organizationId),
projectId: String(work.projectId),
workId: String(work._id),
},
})
).catch((error: unknown) => {
throw new ConvexError(
error instanceof Error ? error.message : "Invalid Work attachment"
);
});
const existing = await ctx.db
.query("signalWorkAttachments")
.withIndex("by_signal_and_work", (q) =>
q.eq("signalId", signal._id).eq("workId", work._id)
)
.unique();
if (existing) {
return false;
}
const createdAt = Date.now();
await ctx.db.insert("signalWorkAttachments", {
createdAt,
organizationId: work.organizationId,
projectId: work.projectId,
signalId: signal._id,
workId: work._id,
});
await ctx.db.insert("workEvents", {
createdAt,
data: event.data,
idempotencyKey: event.idempotencyKey,
kind: event.kind,
organizationId: work.organizationId,
projectId: work.projectId,
workId: work._id,
});
await ctx.db.patch(work._id, { updatedAt: createdAt });
return true;
};
export const listProposedForAgent = query({
args: {
organizationId: v.id("organizations"),
projectId: v.id("projects"),
token: v.string(),
},
handler: async (ctx, args) => {
requireAgent(args.token);
const project = await ctx.db.get(args.projectId);
if (!project || project.organizationId !== args.organizationId) {
throw new ConvexError("Project not found");
}
return await ctx.db
.query("works")
.withIndex("by_project_and_createdAt", (q) =>
q.eq("projectId", args.projectId)
)
.order("desc")
.take(50);
},
});
export const createFromSignal = mutation({
args: {
organizationId: v.id("organizations"),
signalId: v.id("signals"),
token: v.string(),
},
handler: async (ctx, args) => {
requireAgent(args.token);
const signal = await requireSignal(ctx, args.organizationId, args.signalId);
const existingAttachment = await ctx.db
.query("signalWorkAttachments")
.withIndex("by_signal", (q) => q.eq("signalId", signal._id))
.first();
if (existingAttachment) {
return { created: false, workId: existingAttachment.workId };
}
const draft = await Effect.runPromise(
workDraftFromSignal(signal.problemStatement)
).catch((error: unknown) => {
throw new ConvexError(
error instanceof Error ? error.message : "Invalid Work proposal"
);
});
const createdAt = Date.now();
const event = await Effect.runPromise(
workProposedEventFromSignal({
organizationId: String(signal.organizationId),
projectId: String(signal.projectId),
signalId: String(signal._id),
title: draft.title,
})
).catch((error: unknown) => {
throw new ConvexError(
error instanceof Error ? error.message : "Invalid Work event"
);
});
const workId = await ctx.db.insert("works", {
createdAt,
objective: draft.objective,
organizationId: signal.organizationId,
projectId: signal.projectId as Id<"projects">,
status: "proposed",
title: draft.title,
updatedAt: createdAt,
});
await ctx.db.insert("signalWorkAttachments", {
createdAt,
organizationId: signal.organizationId,
projectId: signal.projectId as Id<"projects">,
signalId: signal._id,
workId,
});
await ctx.db.insert("workEvents", {
createdAt,
data: event.data,
idempotencyKey: event.idempotencyKey,
kind: event.kind,
organizationId: signal.organizationId,
projectId: signal.projectId as Id<"projects">,
workId,
});
return { created: true, workId };
},
});
export const attachSignalToWork = mutation({
args: {
organizationId: v.id("organizations"),
signalId: v.id("signals"),
token: v.string(),
workId: v.id("works"),
},
handler: async (ctx, args) => {
requireAgent(args.token);
const signal = await requireSignal(ctx, args.organizationId, args.signalId);
const work = await requireWork(ctx, args.organizationId, args.workId);
return {
attached: await attachSignal(ctx, signal, work),
workId: work._id,
};
},
});
export const listForProject = query({
args: { projectId: v.id("projects") },
handler: async (ctx, args) => {
await requireProjectMember(ctx, args.projectId);
const works = await ctx.db
.query("works")
.withIndex("by_project_and_createdAt", (q) =>
q.eq("projectId", args.projectId)
)
.order("desc")
.take(100);
return await Promise.all(
works.map(async (work) => {
const attachments = await ctx.db
.query("signalWorkAttachments")
.withIndex("by_work", (q) => q.eq("workId", work._id))
.collect();
const signals = await Promise.all(
attachments.map(async (attachment) => {
const signal = await ctx.db.get(attachment.signalId);
if (!signal) {
return null;
}
const sources = await ctx.db
.query("signalSources")
.withIndex("by_signal_and_ordinal", (q) =>
q.eq("signalId", signal._id)
)
.collect();
return {
createdAt: signal.createdAt,
signalId: signal._id,
summary: signal.problemStatement.summary,
sources: sources
.sort((a, b) => a.ordinal - b.ordinal)
.map((source) => ({
createdAt: source.sourceCreatedAt,
messageId: source.messageId,
rawText: source.rawTextSnapshot,
submissionId: source.submissionId ?? null,
})),
title: signal.problemStatement.title,
};
})
);
const events = await ctx.db
.query("workEvents")
.withIndex("by_work_and_createdAt", (q) => q.eq("workId", work._id))
.order("desc")
.collect();
return {
...work,
events,
signals: signals.filter((signal) => signal !== null),
};
})
);
},
});