wip: Phase 1 protocol + CAS types for Merkle call stack
小橘 <xiaoju@shazhou.work>
This commit is contained in:
@@ -9,5 +9,8 @@ export function collectRefs(payload: StateNode["payload"]): string[] {
|
|||||||
if (payload.compact !== null) {
|
if (payload.compact !== null) {
|
||||||
out.push(payload.compact);
|
out.push(payload.compact);
|
||||||
}
|
}
|
||||||
|
if (payload.childThread !== null) {
|
||||||
|
out.push(payload.childThread);
|
||||||
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ function isStartPayload(value: unknown): value is StartNodePayload {
|
|||||||
if (!isRecord(value)) {
|
if (!isRecord(value)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const parentState = value.parentState;
|
||||||
|
if (parentState !== undefined && parentState !== null && typeof parentState !== "string") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
typeof value.name === "string" &&
|
typeof value.name === "string" &&
|
||||||
typeof value.hash === "string" &&
|
typeof value.hash === "string" &&
|
||||||
@@ -25,6 +29,16 @@ function isStartPayload(value: unknown): value is StartNodePayload {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Normalizes a raw start payload, defaulting `parentState` to `null` for legacy nodes. */
|
||||||
|
function normalizeStartPayload(raw: StartNodePayload): StartNodePayload {
|
||||||
|
return {
|
||||||
|
name: raw.name,
|
||||||
|
hash: raw.hash,
|
||||||
|
depth: raw.depth,
|
||||||
|
parentState: raw.parentState ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function isStatePayload(value: unknown): value is StateNodePayload {
|
function isStatePayload(value: unknown): value is StateNodePayload {
|
||||||
if (!isRecord(value)) {
|
if (!isRecord(value)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -41,6 +55,10 @@ function isStatePayload(value: unknown): value is StateNodePayload {
|
|||||||
if (!isRecord(meta)) {
|
if (!isRecord(meta)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const childThread = value.childThread;
|
||||||
|
if (childThread !== undefined && childThread !== null && typeof childThread !== "string") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
typeof value.role === "string" &&
|
typeof value.role === "string" &&
|
||||||
typeof value.start === "string" &&
|
typeof value.start === "string" &&
|
||||||
@@ -49,6 +67,20 @@ function isStatePayload(value: unknown): value is StateNodePayload {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Normalizes a raw state payload, defaulting `childThread` to `null` for legacy nodes. */
|
||||||
|
function normalizeStatePayload(raw: StateNodePayload): StateNodePayload {
|
||||||
|
return {
|
||||||
|
role: raw.role,
|
||||||
|
meta: raw.meta,
|
||||||
|
start: raw.start,
|
||||||
|
content: raw.content,
|
||||||
|
ancestors: raw.ancestors,
|
||||||
|
compact: raw.compact,
|
||||||
|
timestamp: raw.timestamp,
|
||||||
|
childThread: raw.childThread ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/** Parses a YAML CAS blob into a typed RFC v3 thread node (or legacy content layout with `children`). */
|
/** Parses a YAML CAS blob into a typed RFC v3 thread node (or legacy content layout with `children`). */
|
||||||
export function parseCasThreadNode(yamlText: string): ParsedCasThreadNode | null {
|
export function parseCasThreadNode(yamlText: string): ParsedCasThreadNode | null {
|
||||||
let raw: unknown;
|
let raw: unknown;
|
||||||
@@ -86,14 +118,14 @@ export function parseCasThreadNode(yamlText: string): ParsedCasThreadNode | null
|
|||||||
if (!isStartPayload(raw.payload)) {
|
if (!isStartPayload(raw.payload)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const node: StartNode = { type: "start", payload: raw.payload, refs: [...refs] };
|
const node: StartNode = { type: "start", payload: normalizeStartPayload(raw.payload), refs: [...refs] };
|
||||||
return { kind: "start", node };
|
return { kind: "start", node };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isStatePayload(raw.payload)) {
|
if (!isStatePayload(raw.payload)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const node: StateNode = { type: "state", payload: raw.payload, refs: [...refs] };
|
const node: StateNode = { type: "state", payload: normalizeStatePayload(raw.payload), refs: [...refs] };
|
||||||
return { kind: "state", node };
|
return { kind: "state", node };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,10 +175,14 @@ export async function putStartNode(
|
|||||||
payload: StartNode["payload"],
|
payload: StartNode["payload"],
|
||||||
promptHash: string,
|
promptHash: string,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
|
const refs = [promptHash];
|
||||||
|
if (payload.parentState !== null) {
|
||||||
|
refs.push(payload.parentState);
|
||||||
|
}
|
||||||
const node: StartNode = {
|
const node: StartNode = {
|
||||||
type: "start",
|
type: "start",
|
||||||
payload,
|
payload,
|
||||||
refs: [promptHash],
|
refs,
|
||||||
};
|
};
|
||||||
return store.put(serializeCasNode(node));
|
return store.put(serializeCasNode(node));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ export type StartNodePayload = {
|
|||||||
name: string;
|
name: string;
|
||||||
hash: string;
|
hash: string;
|
||||||
depth: number;
|
depth: number;
|
||||||
|
/** Parent thread's head state hash at spawn time. `null` for top-level workflows. */
|
||||||
|
parentState: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type StartNode = {
|
export type StartNode = {
|
||||||
@@ -20,6 +22,8 @@ export type StateNodePayload = {
|
|||||||
ancestors: string[];
|
ancestors: string[];
|
||||||
compact: string | null;
|
compact: string | null;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
|
/** Child thread's final state hash (workflow-as-agent). `null` when no child spawned. */
|
||||||
|
childThread: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type StateNode = {
|
export type StateNode = {
|
||||||
|
|||||||
Reference in New Issue
Block a user