feat(chat): migrate from flow.kind=definition hack to flow.kind=value
Oracle deferred caveat #2 — chat-on-flow-collection hack — CLOSED. Previous design used flow.kind=definition + source_context=EA2_CHAT_THREAD for chat threads and EA2_CHAT_MSG for messages, then filtered them out of hub/wizard surfaces via a NON_BUSINESS_SOURCE_CONTEXTS exclusion. That was containment, not a clean model. Verified EA2 contract: flow.kind='value' is a valid enum value (probed against ea2.baseline.svc — kind=value returns 200, kind=conversation/ thread/message/channel/note all return 400 schema violation). - Thread: flow.kind=value, source_context=CANVAS_CHAT_THREAD - Message: flow.kind=value, source_context=CANVAS_CHAT_MSG - Linked via defines edges (already validated in round 2) Because curatedPublishedFlows already requires kind='definition', chat docs are now invisible to business surfaces by SCHEMA, not by source_ context regex filter. The filter is kept as defence-in-depth (+ legacy docs created during round 2 still need to be filtered out). Tried data collection first; it requires relates(role=sourced_from) edge to an sdx_connections handle per the P19 invariant — wrong model for chat, so flow.kind=value is the right home.
This commit is contained in:
+9
-9
@@ -48,12 +48,12 @@ async function jsonFetch(path: string, init?: RequestInit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const chatApi = {
|
export const chatApi = {
|
||||||
/** List existing chat-thread flow docs for the current tenant. */
|
/** List existing chat-thread flow docs (kind=value, source_context=CANVAS_CHAT_THREAD). */
|
||||||
async listThreads(signal?: AbortSignal): Promise<ChatThread[]> {
|
async listThreads(signal?: AbortSignal): Promise<ChatThread[]> {
|
||||||
const res = await jsonFetch(`/api/ea2/flow/processes?limit=200`, { signal });
|
const res = await jsonFetch(`/api/ea2/flow/processes?limit=500&kind=value`, { signal });
|
||||||
const items = (res?.items || []) as any[];
|
const items = (res?.items || []) as any[];
|
||||||
return items
|
return items
|
||||||
.filter((it) => it?.source_context === "EA2_CHAT_THREAD")
|
.filter((it) => it?.source_context === "CANVAS_CHAT_THREAD")
|
||||||
.map((it) => ({
|
.map((it) => ({
|
||||||
_key: it._key,
|
_key: it._key,
|
||||||
display_name: it.display_name || "Chat",
|
display_name: it.display_name || "Chat",
|
||||||
@@ -64,17 +64,17 @@ export const chatApi = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a thread between two persona emails. Returns the new thread key.
|
* Create a thread as flow.kind=value so it is invisible to process-definition
|
||||||
* Falls back to a deterministic key built from the participants when present.
|
* surfaces by EA2 schema rather than by source_context filtering.
|
||||||
*/
|
*/
|
||||||
async createThread(displayName: string, participants: string[], signal?: AbortSignal): Promise<string> {
|
async createThread(displayName: string, participants: string[], signal?: AbortSignal): Promise<string> {
|
||||||
const payload = {
|
const payload = {
|
||||||
kind: "definition",
|
kind: "value",
|
||||||
status: "published",
|
status: "published",
|
||||||
name: `chat_${Date.now()}`,
|
name: `chat_${Date.now()}`,
|
||||||
display_name: displayName,
|
display_name: displayName,
|
||||||
description: `Conversation between ${participants.join(" & ")}`,
|
description: `Conversation between ${participants.join(" & ")}`,
|
||||||
source_context: "EA2_CHAT_THREAD",
|
source_context: "CANVAS_CHAT_THREAD",
|
||||||
config: { chat: { participants } },
|
config: { chat: { participants } },
|
||||||
};
|
};
|
||||||
const res = await fetch(`${api.config.baseUrl}/api/ea2/flow`, {
|
const res = await fetch(`${api.config.baseUrl}/api/ea2/flow`, {
|
||||||
@@ -124,12 +124,12 @@ export const chatApi = {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
headers: authHeaders(),
|
headers: authHeaders(),
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
kind: "definition",
|
kind: "value",
|
||||||
status: "published",
|
status: "published",
|
||||||
name: `msg_${Date.now()}`,
|
name: `msg_${Date.now()}`,
|
||||||
display_name: body.slice(0, 80),
|
display_name: body.slice(0, 80),
|
||||||
description: body,
|
description: body,
|
||||||
source_context: "EA2_CHAT_MSG",
|
source_context: "CANVAS_CHAT_MSG",
|
||||||
config: { chat: { author_email: authorEmail, thread_key: threadKey } },
|
config: { chat: { author_email: authorEmail, thread_key: threadKey } },
|
||||||
}),
|
}),
|
||||||
signal,
|
signal,
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ const DEV_ARTEFACT_PATTERNS = [
|
|||||||
const NON_BUSINESS_SOURCE_CONTEXTS = new Set([
|
const NON_BUSINESS_SOURCE_CONTEXTS = new Set([
|
||||||
"EA2_CHAT_THREAD",
|
"EA2_CHAT_THREAD",
|
||||||
"EA2_CHAT_MSG",
|
"EA2_CHAT_MSG",
|
||||||
|
"CANVAS_CHAT_THREAD",
|
||||||
|
"CANVAS_CHAT_MSG",
|
||||||
"EA2_WIZARD_STEP",
|
"EA2_WIZARD_STEP",
|
||||||
"EA2_DRAFT_PROCESS:process_creation",
|
"EA2_DRAFT_PROCESS:process_creation",
|
||||||
]);
|
]);
|
||||||
|
|||||||
Reference in New Issue
Block a user