mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
feat(agent): refine pico core task capabilities
This commit is contained in:
parent
d11c4b7281
commit
1915b35a9b
5 changed files with 283 additions and 200 deletions
|
|
@ -12,17 +12,17 @@ export interface Address<T extends JsonValue = JsonValue> {
|
|||
readonly scope: Scope;
|
||||
readonly namespace: string;
|
||||
readonly key?: string;
|
||||
readonly collection: "value" | "list";
|
||||
readonly kind: "value" | "list";
|
||||
readonly rewind: boolean;
|
||||
readonly [addressType]?: T;
|
||||
}
|
||||
|
||||
export interface Value<T extends JsonValue> extends Address<T> {
|
||||
readonly collection: "value";
|
||||
readonly kind: "value";
|
||||
}
|
||||
|
||||
export interface List<T extends JsonValue> extends Address<T> {
|
||||
readonly collection: "list";
|
||||
readonly kind: "list";
|
||||
}
|
||||
|
||||
export interface Element<T extends JsonValue> {
|
||||
|
|
@ -51,7 +51,7 @@ export function defineValue<T extends JsonValue>(scope: Scope, namespace: string
|
|||
...options,
|
||||
scope,
|
||||
namespace,
|
||||
collection: "value",
|
||||
kind: "value",
|
||||
rewind: options?.rewind ?? false,
|
||||
});
|
||||
}
|
||||
|
|
@ -71,7 +71,7 @@ export function defineList<T extends JsonValue>(scope: Scope, namespace: string,
|
|||
...options,
|
||||
scope,
|
||||
namespace,
|
||||
collection: "list",
|
||||
kind: "list",
|
||||
rewind: options?.rewind ?? false,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,3 +6,4 @@ export interface JsonObject {
|
|||
}
|
||||
|
||||
export type Id = number;
|
||||
export type Seq = number;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import type { Element, List, Value } from "./addresses.ts";
|
|||
import type { Id, JsonValue } from "./core.ts";
|
||||
import type { ContextEdit, Conversation, Entry, EntryInput, EntryKind } from "./entries.ts";
|
||||
import type {
|
||||
AnyDefinedTaskKind,
|
||||
AnyTaskKind,
|
||||
Completion,
|
||||
ExactJsonInput,
|
||||
|
|
@ -17,29 +18,13 @@ import type {
|
|||
TaskOutputRef,
|
||||
} from "./tasks.ts";
|
||||
|
||||
export interface Cursor {
|
||||
readonly after?: Id;
|
||||
readonly before?: Id;
|
||||
readonly limit: number;
|
||||
}
|
||||
|
||||
export interface Page<T> {
|
||||
readonly items: readonly T[];
|
||||
readonly next?: Id;
|
||||
readonly readAt: Id;
|
||||
}
|
||||
|
||||
export interface ListQuery extends Cursor {
|
||||
readonly at?: Id;
|
||||
}
|
||||
|
||||
export interface TxReaders {
|
||||
getConversation(id: Id): Promise<Conversation | undefined>;
|
||||
getEntry(id: Id): Promise<Entry | undefined>;
|
||||
getEntry<E extends Entry>(kind: EntryKind<E>, id: Id): Promise<E | undefined>;
|
||||
getEntries(ids: readonly Id[]): Promise<ReadonlyMap<Id, Entry>>;
|
||||
getTask(id: Id): Promise<Task | undefined>;
|
||||
getTask<K extends AnyTaskKind>(kind: K, id: Id): Promise<TaskOf<K> | undefined>;
|
||||
getTask<K extends AnyDefinedTaskKind>(kind: K, id: Id): Promise<TaskOf<K> | undefined>;
|
||||
getTasks(ids: readonly Id[]): Promise<ReadonlyMap<Id, Task>>;
|
||||
}
|
||||
|
||||
|
|
@ -50,7 +35,8 @@ export interface TxValue<T extends JsonValue> {
|
|||
}
|
||||
|
||||
export interface TxList<T extends JsonValue> {
|
||||
read(query: ListQuery): Promise<Page<Element<T>>>;
|
||||
read(): Promise<readonly Element<T>[]>;
|
||||
read(at: Id): Promise<readonly Element<T>[]>;
|
||||
append(value: T): Id;
|
||||
remove(id: Id): void;
|
||||
clear(): void;
|
||||
|
|
@ -87,7 +73,7 @@ export interface StoredEntryDraft {
|
|||
}
|
||||
|
||||
export type QueuedInput =
|
||||
| { readonly mode: "steer" | "followUp" | "nextRun"; readonly input: UserInput; readonly requestId?: string }
|
||||
| { readonly mode: "steer" | "followUp"; readonly input: UserInput; readonly requestId?: string }
|
||||
| { readonly mode: "write"; readonly entry: StoredEntryDraft; readonly requestId?: string };
|
||||
|
||||
export type InputResult =
|
||||
|
|
@ -104,24 +90,46 @@ export type InputResult =
|
|||
|
||||
export type TerminalInputResult = Extract<InputResult, { status: "done" | "unanswered" }>;
|
||||
|
||||
export interface AcceptOptions {
|
||||
export interface InternalAcceptOptions {
|
||||
readonly input: UserInput;
|
||||
readonly requestId?: string;
|
||||
readonly whenBusy?: "followUp" | "steer" | "reject";
|
||||
}
|
||||
|
||||
export interface BaseTaskTx<C extends TaskCheckpoint> extends TxReaders {
|
||||
checkpoint(value: C): void;
|
||||
task<K extends AnyTaskKind, S extends TaskSpec<InputOf<K>, OutputOf<K>>>(
|
||||
export interface SendInput {
|
||||
readonly requestId?: string;
|
||||
readonly content: UserInput;
|
||||
readonly whenBusy?: "followUp" | "steer" | "reject";
|
||||
}
|
||||
|
||||
export interface InputHandle {
|
||||
readonly id: Id;
|
||||
readonly conversationId: Id;
|
||||
result(ctx: Context): Promise<InputResult | undefined>;
|
||||
wait(ctx: Context): Promise<TerminalInputResult>;
|
||||
abort(ctx: Context): Promise<"aborted" | "already_placed" | "not_found">;
|
||||
}
|
||||
|
||||
interface TaskCreator<Kinds extends AnyDefinedTaskKind> {
|
||||
task<K extends Kinds, S extends TaskSpec<InputOf<K>, OutputOf<K>>>(
|
||||
kind: K,
|
||||
spec: NoExtra<TaskSpec<InputOf<K>, OutputOf<K>>, S> & {
|
||||
readonly input: ExactJsonInput<InputOf<K>, S["input"]>;
|
||||
},
|
||||
): Id;
|
||||
}
|
||||
|
||||
interface TaskTxBase<C extends TaskCheckpoint> extends TxReaders {
|
||||
checkpoint(value: C): void;
|
||||
createConversation(spec: ConversationCreateSpec): Id;
|
||||
value<T extends JsonValue>(address: Value<T>): TxValue<T>;
|
||||
list<T extends JsonValue>(address: List<T>): TxList<T>;
|
||||
accept(conversationId: Id, options: AcceptOptions): Promise<Acceptance>;
|
||||
}
|
||||
|
||||
export type BaseTaskTx<C extends TaskCheckpoint> = TaskTxBase<C> & TaskCreator<AnyTaskKind>;
|
||||
|
||||
export interface InternalAdmissionTx {
|
||||
accept(conversationId: Id, options: InternalAcceptOptions): Promise<Acceptance>;
|
||||
queueInput(conversationId: Id, input: QueuedInput): Promise<Acceptance>;
|
||||
write<E extends Entry>(
|
||||
conversationId: Id,
|
||||
|
|
@ -131,18 +139,22 @@ export interface BaseTaskTx<C extends TaskCheckpoint> extends TxReaders {
|
|||
): Promise<Acceptance>;
|
||||
}
|
||||
|
||||
export interface TurnTaskTx<C extends TaskCheckpoint> extends BaseTaskTx<C> {
|
||||
entry<E extends Entry>(kind: EntryKind<E>, conversationId: Id, input: EntryInput<E>): Id;
|
||||
}
|
||||
export type CoreTaskTx<C extends TaskCheckpoint> = TaskTxBase<C> &
|
||||
TaskCreator<AnyDefinedTaskKind> &
|
||||
InternalAdmissionTx & {
|
||||
entry<E extends Entry>(kind: EntryKind<E>, conversationId: Id, input: EntryInput<E>): Id;
|
||||
};
|
||||
|
||||
export interface PublicValue<T extends JsonValue> {
|
||||
get(at: Id | undefined, ctx: Context): Promise<T | undefined>;
|
||||
get(ctx: Context): Promise<T | undefined>;
|
||||
get(at: Id, ctx: Context): Promise<T | undefined>;
|
||||
set(value: T, ctx: Context): Promise<void>;
|
||||
delete(ctx: Context): Promise<void>;
|
||||
}
|
||||
|
||||
export interface PublicList<T extends JsonValue> {
|
||||
read(query: ListQuery, ctx: Context): Promise<Page<Element<T>>>;
|
||||
read(ctx: Context): Promise<readonly Element<T>[]>;
|
||||
read(at: Id, ctx: Context): Promise<readonly Element<T>[]>;
|
||||
append(value: T, ctx: Context): Promise<Id>;
|
||||
remove(id: Id, ctx: Context): Promise<void>;
|
||||
clear(ctx: Context): Promise<void>;
|
||||
|
|
@ -160,11 +172,11 @@ export interface ReadonlyTaskOutput<O extends object> {
|
|||
read(ctx: Context): Promise<O>;
|
||||
}
|
||||
|
||||
type RuntimeOutput<O extends object> = [O] extends [never] ? unknown : { readonly output: TaskOutput<O> };
|
||||
|
||||
type AbortRuntimeOutput<O extends object> = [O] extends [never] ? unknown : { readonly output: ReadonlyTaskOutput<O> };
|
||||
|
||||
type FinalOutput<O extends object> = [O] extends [never] ? unknown : { readonly output: O };
|
||||
export type RuntimeOutput<O extends object> = [O] extends [never] ? unknown : { readonly output: TaskOutput<O> };
|
||||
export type AbortRuntimeOutput<O extends object> = [O] extends [never]
|
||||
? unknown
|
||||
: { readonly output: ReadonlyTaskOutput<O> };
|
||||
export type FinalOutput<O extends object> = [O] extends [never] ? unknown : { readonly output: O };
|
||||
|
||||
export interface ScratchTx {
|
||||
value<T extends JsonValue>(address: Value<T>): TxValue<T>;
|
||||
|
|
@ -178,38 +190,19 @@ export interface ScratchReader {
|
|||
|
||||
export interface TaskConversation {
|
||||
readonly id: Id;
|
||||
accept(options: AcceptOptions, ctx: Context): Promise<Acceptance>;
|
||||
queueInput(input: QueuedInput, ctx: Context): Promise<Acceptance>;
|
||||
write<E extends Entry>(
|
||||
kind: EntryKind<E>,
|
||||
input: EntryInput<E>,
|
||||
requestId: string | undefined,
|
||||
ctx: Context,
|
||||
): Promise<Acceptance>;
|
||||
result(inputId: Id, ctx: Context): Promise<InputResult | undefined>;
|
||||
waitForInput(inputId: Id, ctx: Context): Promise<TerminalInputResult>;
|
||||
abortInput(inputId: Id, ctx: Context): Promise<"aborted" | "already_placed" | "not_found">;
|
||||
send(input: SendInput, ctx: Context): Promise<InputHandle>;
|
||||
value<T extends JsonValue>(address: Value<T>): PublicValue<T>;
|
||||
list<T extends JsonValue>(address: List<T>): PublicList<T>;
|
||||
}
|
||||
|
||||
export interface AbortTaskConversation {
|
||||
readonly id: Id;
|
||||
write<E extends Entry>(
|
||||
kind: EntryKind<E>,
|
||||
input: EntryInput<E>,
|
||||
requestId: string | undefined,
|
||||
ctx: Context,
|
||||
): Promise<Acceptance>;
|
||||
result(inputId: Id, ctx: Context): Promise<InputResult | undefined>;
|
||||
waitForInput(inputId: Id, ctx: Context): Promise<TerminalInputResult>;
|
||||
value<T extends JsonValue>(address: Value<T>): PublicValue<T>;
|
||||
list<T extends JsonValue>(address: List<T>): PublicList<T>;
|
||||
}
|
||||
|
||||
export interface BaseTaskRuntime<I extends JsonValue, C extends TaskCheckpoint, O extends object = never> {
|
||||
interface TaskRuntimeBase {
|
||||
readonly taskId: Id;
|
||||
commit<T>(build: (tx: BaseTaskTx<C>, current: RunningTask<I, C, O>) => T | Promise<T>, ctx: Context): Promise<T>;
|
||||
scratch<T>(build: (tx: ScratchTx) => T | Promise<T>, ctx: Context): Promise<T>;
|
||||
conversation(id: Id, ctx: Context): Promise<TaskConversation | undefined>;
|
||||
waitForTask(id: Id, ctx: Context): Promise<Task>;
|
||||
|
|
@ -218,68 +211,83 @@ export interface BaseTaskRuntime<I extends JsonValue, C extends TaskCheckpoint,
|
|||
sleep(untilMs: number, ctx: Context): Promise<void>;
|
||||
}
|
||||
|
||||
export interface TurnTaskRuntime<I extends JsonValue, C extends TaskCheckpoint, O extends object = never>
|
||||
extends Omit<BaseTaskRuntime<I, C, O>, "commit"> {
|
||||
commit<T>(build: (tx: TurnTaskTx<C>, current: RunningTask<I, C, O>) => T | Promise<T>, ctx: Context): Promise<T>;
|
||||
interface CommitRuntime<I extends JsonValue, C extends TaskCheckpoint, O extends object, Tx> {
|
||||
commit<T>(build: (tx: Tx, current: RunningTask<I, C, O>) => T | Promise<T>, ctx: Context): Promise<T>;
|
||||
}
|
||||
|
||||
export type RuntimeFor<
|
||||
export type TaskRuntime<I extends JsonValue, C extends TaskCheckpoint, O extends object = never> = TaskRuntimeBase &
|
||||
CommitRuntime<I, C, O, BaseTaskTx<C>> &
|
||||
RuntimeOutput<O>;
|
||||
|
||||
export type CoreTaskRuntime<I extends JsonValue, C extends TaskCheckpoint, O extends object = never> = TaskRuntimeBase &
|
||||
CommitRuntime<I, C, O, CoreTaskTx<C>> &
|
||||
RuntimeOutput<O>;
|
||||
|
||||
type TerminalClosureFor<
|
||||
Tx,
|
||||
I extends JsonValue,
|
||||
C extends TaskCheckpoint,
|
||||
R extends JsonValue,
|
||||
F extends JsonValue,
|
||||
O extends object,
|
||||
T extends false | true,
|
||||
> = (T extends true ? TurnTaskRuntime<I, C, O> : BaseTaskRuntime<I, C, O>) & RuntimeOutput<O>;
|
||||
|
||||
export interface TerminalTx<C extends TaskCheckpoint> extends BaseTaskTx<C> {}
|
||||
export interface TurnTerminalTx<C extends TaskCheckpoint> extends TurnTaskTx<C> {}
|
||||
|
||||
export type FinalTx<C extends TaskCheckpoint, O extends object, T extends false | true> = (T extends true
|
||||
? TurnTerminalTx<C>
|
||||
: TerminalTx<C>) &
|
||||
FinalOutput<O>;
|
||||
> = (tx: Tx & FinalOutput<O>, current: RunningTask<I, C, O>) => Completion<R, F> | Promise<Completion<R, F>>;
|
||||
|
||||
export type FinalTx<C extends TaskCheckpoint, O extends object> = BaseTaskTx<C> & FinalOutput<O>;
|
||||
export type CoreFinalTx<C extends TaskCheckpoint, O extends object> = CoreTaskTx<C> & FinalOutput<O>;
|
||||
export type TerminalClosure<
|
||||
I extends JsonValue,
|
||||
C extends TaskCheckpoint,
|
||||
R extends JsonValue,
|
||||
F extends JsonValue,
|
||||
O extends object,
|
||||
T extends false | true,
|
||||
> = (tx: FinalTx<C, O, T>, current: RunningTask<I, C, O>) => Completion<R, F> | Promise<Completion<R, F>>;
|
||||
> = TerminalClosureFor<BaseTaskTx<C>, I, C, R, F, O>;
|
||||
export type CoreTerminalClosure<
|
||||
I extends JsonValue,
|
||||
C extends TaskCheckpoint,
|
||||
R extends JsonValue,
|
||||
F extends JsonValue,
|
||||
O extends object,
|
||||
> = TerminalClosureFor<CoreTaskTx<C>, I, C, R, F, O>;
|
||||
|
||||
export interface AbortTx<C extends TaskCheckpoint> extends TxReaders {
|
||||
checkpoint(value: C): void;
|
||||
value<T extends JsonValue>(address: Value<T>): TxValue<T>;
|
||||
list<T extends JsonValue>(address: List<T>): TxList<T>;
|
||||
markTask(id: Id): Promise<"marked" | "terminal">;
|
||||
}
|
||||
|
||||
export interface CoreAbortTx<C extends TaskCheckpoint> extends AbortTx<C> {
|
||||
entry<E extends Entry>(kind: EntryKind<E>, conversationId: Id, input: EntryInput<E>): Id;
|
||||
write<E extends Entry>(
|
||||
conversationId: Id,
|
||||
kind: EntryKind<E>,
|
||||
input: EntryInput<E>,
|
||||
requestId?: string,
|
||||
): Promise<Acceptance>;
|
||||
markTask(id: Id): Promise<"marked" | "terminal">;
|
||||
}
|
||||
|
||||
export interface TurnAbortTx<C extends TaskCheckpoint> extends AbortTx<C> {
|
||||
entry<E extends Entry>(kind: EntryKind<E>, conversationId: Id, input: EntryInput<E>): Id;
|
||||
}
|
||||
|
||||
export type AbortFinalTx<C extends TaskCheckpoint, O extends object, T extends false | true> = (T extends true
|
||||
? TurnAbortTx<C>
|
||||
: AbortTx<C>) &
|
||||
FinalOutput<O>;
|
||||
type AbortClosureFor<Tx, I extends JsonValue, C extends TaskCheckpoint, A extends JsonValue, O extends object> = (
|
||||
tx: Tx & FinalOutput<O>,
|
||||
current: RunningTask<I, C, O>,
|
||||
) => A | Promise<A>;
|
||||
|
||||
export type AbortFinalTx<C extends TaskCheckpoint, O extends object> = AbortTx<C> & FinalOutput<O>;
|
||||
export type CoreAbortFinalTx<C extends TaskCheckpoint, O extends object> = CoreAbortTx<C> & FinalOutput<O>;
|
||||
export type AbortClosure<
|
||||
I extends JsonValue,
|
||||
C extends TaskCheckpoint,
|
||||
A extends JsonValue,
|
||||
O extends object,
|
||||
T extends false | true,
|
||||
> = (tx: AbortFinalTx<C, O, T>, current: RunningTask<I, C, O>) => A | Promise<A>;
|
||||
> = AbortClosureFor<AbortTx<C>, I, C, A, O>;
|
||||
export type CoreAbortClosure<
|
||||
I extends JsonValue,
|
||||
C extends TaskCheckpoint,
|
||||
A extends JsonValue,
|
||||
O extends object,
|
||||
> = AbortClosureFor<CoreAbortTx<C>, I, C, A, O>;
|
||||
|
||||
export interface BaseAbortRuntime<I extends JsonValue, C extends TaskCheckpoint, O extends object = never> {
|
||||
interface AbortRuntimeBase {
|
||||
readonly taskId: Id;
|
||||
commit<T>(build: (tx: AbortTx<C>, current: RunningTask<I, C, O>) => T | Promise<T>, ctx: Context): Promise<T>;
|
||||
scratch<T>(read: (scratch: ScratchReader) => T | Promise<T>, ctx: Context): Promise<T>;
|
||||
conversation(id: Id, ctx: Context): Promise<AbortTaskConversation | undefined>;
|
||||
waitForTask(id: Id, ctx: Context): Promise<Task>;
|
||||
|
|
@ -287,14 +295,14 @@ export interface BaseAbortRuntime<I extends JsonValue, C extends TaskCheckpoint,
|
|||
now(): number;
|
||||
}
|
||||
|
||||
export interface TurnAbortRuntime<I extends JsonValue, C extends TaskCheckpoint, O extends object = never>
|
||||
extends Omit<BaseAbortRuntime<I, C, O>, "commit"> {
|
||||
commit<T>(build: (tx: TurnAbortTx<C>, current: RunningTask<I, C, O>) => T | Promise<T>, ctx: Context): Promise<T>;
|
||||
}
|
||||
|
||||
export type AbortRuntimeFor<
|
||||
export type AbortTaskRuntime<
|
||||
I extends JsonValue,
|
||||
C extends TaskCheckpoint,
|
||||
O extends object,
|
||||
T extends false | true,
|
||||
> = (T extends true ? TurnAbortRuntime<I, C, O> : BaseAbortRuntime<I, C, O>) & AbortRuntimeOutput<O>;
|
||||
O extends object = never,
|
||||
> = AbortRuntimeBase & CommitRuntime<I, C, O, AbortTx<C>> & AbortRuntimeOutput<O>;
|
||||
|
||||
export type CoreAbortTaskRuntime<
|
||||
I extends JsonValue,
|
||||
C extends TaskCheckpoint,
|
||||
O extends object = never,
|
||||
> = AbortRuntimeBase & CommitRuntime<I, C, O, CoreAbortTx<C>> & AbortRuntimeOutput<O>;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,15 @@
|
|||
import type { Context } from "@earendil-works/chord";
|
||||
import type { Id, JsonObject, JsonValue } from "./core.ts";
|
||||
import type { AbortClosure, AbortRuntimeFor, RuntimeFor, TerminalClosure } from "./runtime.ts";
|
||||
import type {
|
||||
AbortClosure,
|
||||
AbortTaskRuntime,
|
||||
CoreAbortClosure,
|
||||
CoreAbortTaskRuntime,
|
||||
CoreTaskRuntime,
|
||||
CoreTerminalClosure,
|
||||
TaskRuntime,
|
||||
TerminalClosure,
|
||||
} from "./runtime.ts";
|
||||
|
||||
export interface TaskCheckpoint extends JsonObject {
|
||||
readonly phase: string;
|
||||
|
|
@ -35,7 +44,7 @@ export interface TaskOutputRef<O extends object> extends StoredTaskOutputRef {
|
|||
|
||||
export type TaskOutputField<O extends object> = [O] extends [never] ? unknown : { readonly output: TaskOutputRef<O> };
|
||||
|
||||
type TaskOutputDefinition<I extends JsonValue, O extends object> = [O] extends [never]
|
||||
export type TaskOutputDefinition<I extends JsonValue, O extends object> = [O] extends [never]
|
||||
? { readonly output?: never }
|
||||
: { readonly output: TaskOutputSpec<I, O> };
|
||||
|
||||
|
|
@ -53,7 +62,6 @@ export interface TaskBase<I extends JsonValue, C extends TaskCheckpoint> {
|
|||
readonly checkpoint?: C;
|
||||
readonly after: readonly Id[];
|
||||
readonly background?: true;
|
||||
readonly turn?: true;
|
||||
readonly owns: readonly Id[];
|
||||
readonly output?: StoredTaskOutputRef;
|
||||
readonly abort?: true;
|
||||
|
|
@ -88,8 +96,12 @@ const taskKindBrand: unique symbol = Symbol("pico.taskKind");
|
|||
|
||||
export interface TaskKindBase {
|
||||
readonly kind: string;
|
||||
readonly turn: false | true;
|
||||
readonly [taskKindBrand]: true;
|
||||
readonly [taskKindBrand]: "ordinary";
|
||||
}
|
||||
|
||||
export interface CoreTaskKindBase {
|
||||
readonly kind: string;
|
||||
readonly [taskKindBrand]: "core";
|
||||
}
|
||||
|
||||
interface TaskKindMethods<
|
||||
|
|
@ -99,24 +111,47 @@ interface TaskKindMethods<
|
|||
F extends JsonValue,
|
||||
A extends JsonValue,
|
||||
O extends object,
|
||||
T extends false | true,
|
||||
> extends TaskKindBase {
|
||||
readonly turn: T;
|
||||
execute(
|
||||
task: RunningTask<I, C, O>,
|
||||
runtime: RuntimeFor<I, C, O, T>,
|
||||
runtime: TaskRuntime<I, C, O>,
|
||||
ctx: Context,
|
||||
): Promise<TerminalClosure<I, C, R, F, O, T>>;
|
||||
): Promise<TerminalClosure<I, C, R, F, O>>;
|
||||
recover(
|
||||
task: RunningTask<I, C, O>,
|
||||
runtime: RuntimeFor<I, C, O, T>,
|
||||
runtime: TaskRuntime<I, C, O>,
|
||||
ctx: Context,
|
||||
): Promise<TerminalClosure<I, C, R, F, O, T>>;
|
||||
): Promise<TerminalClosure<I, C, R, F, O>>;
|
||||
abort(
|
||||
task: RunningTask<I, C, O>,
|
||||
runtime: AbortRuntimeFor<I, C, O, T>,
|
||||
runtime: AbortTaskRuntime<I, C, O>,
|
||||
ctx: Context,
|
||||
): Promise<AbortClosure<I, C, A, O, T>>;
|
||||
): Promise<AbortClosure<I, C, A, O>>;
|
||||
}
|
||||
|
||||
interface CoreTaskKindMethods<
|
||||
I extends JsonValue,
|
||||
C extends TaskCheckpoint,
|
||||
R extends JsonValue,
|
||||
F extends JsonValue,
|
||||
A extends JsonValue,
|
||||
O extends object,
|
||||
> extends CoreTaskKindBase {
|
||||
execute(
|
||||
task: RunningTask<I, C, O>,
|
||||
runtime: CoreTaskRuntime<I, C, O>,
|
||||
ctx: Context,
|
||||
): Promise<CoreTerminalClosure<I, C, R, F, O>>;
|
||||
recover(
|
||||
task: RunningTask<I, C, O>,
|
||||
runtime: CoreTaskRuntime<I, C, O>,
|
||||
ctx: Context,
|
||||
): Promise<CoreTerminalClosure<I, C, R, F, O>>;
|
||||
abort(
|
||||
task: RunningTask<I, C, O>,
|
||||
runtime: CoreAbortTaskRuntime<I, C, O>,
|
||||
ctx: Context,
|
||||
): Promise<CoreAbortClosure<I, C, A, O>>;
|
||||
}
|
||||
|
||||
export type TaskKind<
|
||||
|
|
@ -126,8 +161,16 @@ export type TaskKind<
|
|||
F extends JsonValue,
|
||||
A extends JsonValue,
|
||||
O extends object = never,
|
||||
T extends false | true = false,
|
||||
> = TaskKindMethods<I, C, R, F, A, O, T> & TaskOutputDefinition<I, O>;
|
||||
> = TaskKindMethods<I, C, R, F, A, O> & TaskOutputDefinition<I, O>;
|
||||
|
||||
export type CoreTaskKind<
|
||||
I extends JsonValue,
|
||||
C extends TaskCheckpoint,
|
||||
R extends JsonValue,
|
||||
F extends JsonValue,
|
||||
A extends JsonValue,
|
||||
O extends object = never,
|
||||
> = CoreTaskKindMethods<I, C, R, F, A, O> & TaskOutputDefinition<I, O>;
|
||||
|
||||
export type NoExtra<Expected, Actual extends Expected> = Actual & Record<Exclude<keyof Actual, keyof Expected>, never>;
|
||||
|
||||
|
|
@ -137,39 +180,26 @@ export type ExactJsonInput<Expected extends JsonValue, Actual extends Expected>
|
|||
? Actual & Record<Exclude<keyof Actual, keyof Expected>, never>
|
||||
: Actual;
|
||||
|
||||
export type NonTurnKindDefinition<
|
||||
I extends JsonValue,
|
||||
C extends TaskCheckpoint,
|
||||
R extends JsonValue,
|
||||
F extends JsonValue,
|
||||
A extends JsonValue,
|
||||
O extends object,
|
||||
> = Omit<TaskKind<I, C, R, F, A, O, false>, "turn" | typeof taskKindBrand> & { readonly turn?: false };
|
||||
type KindDefinition<K> = Omit<K, typeof taskKindBrand>;
|
||||
type KindFactory<K> = <D extends KindDefinition<K>>(definition: NoExtra<KindDefinition<K>, D>) => D & K;
|
||||
|
||||
export type TurnKindDefinition<
|
||||
export type TaskKindFactory<
|
||||
I extends JsonValue,
|
||||
C extends TaskCheckpoint,
|
||||
R extends JsonValue,
|
||||
F extends JsonValue,
|
||||
A extends JsonValue,
|
||||
O extends object,
|
||||
> = Omit<TaskKind<I, C, R, F, A, O, true>, typeof taskKindBrand>;
|
||||
> = KindFactory<TaskKind<I, C, R, F, A, O>>;
|
||||
|
||||
export interface TaskKindFactory<
|
||||
export type CoreTaskKindFactory<
|
||||
I extends JsonValue,
|
||||
C extends TaskCheckpoint,
|
||||
R extends JsonValue,
|
||||
F extends JsonValue,
|
||||
A extends JsonValue,
|
||||
O extends object,
|
||||
> {
|
||||
<D extends NonTurnKindDefinition<I, C, R, F, A, O>>(
|
||||
definition: NoExtra<NonTurnKindDefinition<I, C, R, F, A, O>, D>,
|
||||
): D & TaskKind<I, C, R, F, A, O, false>;
|
||||
<D extends TurnKindDefinition<I, C, R, F, A, O>>(
|
||||
definition: NoExtra<TurnKindDefinition<I, C, R, F, A, O>, D>,
|
||||
): D & TaskKind<I, C, R, F, A, O, true>;
|
||||
}
|
||||
> = KindFactory<CoreTaskKind<I, C, R, F, A, O>>;
|
||||
|
||||
export function defineTask<
|
||||
I extends JsonValue,
|
||||
|
|
@ -179,29 +209,49 @@ export function defineTask<
|
|||
A extends JsonValue,
|
||||
O extends object = never,
|
||||
>(): TaskKindFactory<I, C, R, F, A, O> {
|
||||
const define = (definition: NonTurnKindDefinition<I, C, R, F, A, O> | TurnKindDefinition<I, C, R, F, A, O>) =>
|
||||
const define = (definition: KindDefinition<TaskKind<I, C, R, F, A, O>>) =>
|
||||
Object.freeze({
|
||||
...definition,
|
||||
turn: definition.turn ?? false,
|
||||
[taskKindBrand]: true as const,
|
||||
[taskKindBrand]: "ordinary" as const,
|
||||
});
|
||||
return define as TaskKindFactory<I, C, R, F, A, O>;
|
||||
}
|
||||
|
||||
/** Internal authoring helper for Pico's fixed privileged task kinds. */
|
||||
export function defineCoreTask<
|
||||
I extends JsonValue,
|
||||
C extends TaskCheckpoint,
|
||||
R extends JsonValue,
|
||||
F extends JsonValue,
|
||||
A extends JsonValue,
|
||||
O extends object = never,
|
||||
>(): CoreTaskKindFactory<I, C, R, F, A, O> {
|
||||
const define = (definition: KindDefinition<CoreTaskKind<I, C, R, F, A, O>>) =>
|
||||
Object.freeze({
|
||||
...definition,
|
||||
[taskKindBrand]: "core" as const,
|
||||
});
|
||||
return define as CoreTaskKindFactory<I, C, R, F, A, O>;
|
||||
}
|
||||
|
||||
export type AnyTaskKind = TaskKindBase;
|
||||
export type PayloadsOf<K> = K extends TaskKind<infer I, infer C, infer R, infer F, infer A, infer O, false | true>
|
||||
export type AnyCoreTaskKind = CoreTaskKindBase;
|
||||
export type AnyDefinedTaskKind = AnyTaskKind | AnyCoreTaskKind;
|
||||
|
||||
export type PayloadsOf<K> = K extends TaskKind<infer I, infer C, infer R, infer F, infer A, infer O>
|
||||
? { input: I; checkpoint: C; result: R; failure: F; aborted: A; output: O }
|
||||
: never;
|
||||
: K extends CoreTaskKind<infer I, infer C, infer R, infer F, infer A, infer O>
|
||||
? { input: I; checkpoint: C; result: R; failure: F; aborted: A; output: O }
|
||||
: never;
|
||||
export type InputOf<K> = PayloadsOf<K>["input"];
|
||||
export type CheckpointOf<K> = PayloadsOf<K>["checkpoint"];
|
||||
export type ResultOf<K> = PayloadsOf<K>["result"];
|
||||
export type FailureOf<K> = PayloadsOf<K>["failure"];
|
||||
export type AbortedOf<K> = PayloadsOf<K>["aborted"];
|
||||
export type OutputOf<K> = PayloadsOf<K>["output"];
|
||||
export type TurnOf<K extends TaskKindBase> = K["turn"];
|
||||
type WithoutOutput<T> = T extends unknown ? Omit<T, "output"> : never;
|
||||
|
||||
export type TaskOf<K extends TaskKindBase> = WithoutOutput<
|
||||
export type TaskOf<K extends AnyDefinedTaskKind> = WithoutOutput<
|
||||
Task<InputOf<K>, CheckpointOf<K>, ResultOf<K>, FailureOf<K>, AbortedOf<K>>
|
||||
> &
|
||||
TaskOutputField<OutputOf<K>>;
|
||||
|
|
|
|||
|
|
@ -2,11 +2,14 @@ import type { Context } from "@earendil-works/chord";
|
|||
import type {
|
||||
AbortClosure,
|
||||
AbortedOf,
|
||||
AbortRuntimeFor,
|
||||
BaseAbortRuntime,
|
||||
BaseTaskRuntime,
|
||||
AbortTaskRuntime,
|
||||
BaseTaskTx,
|
||||
CheckpointOf,
|
||||
CoreAbortClosure,
|
||||
CoreAbortTaskRuntime,
|
||||
CoreTaskRuntime,
|
||||
CoreTaskTx,
|
||||
CoreTerminalClosure,
|
||||
Entry,
|
||||
EntryInput,
|
||||
ExactJsonInput,
|
||||
|
|
@ -18,19 +21,24 @@ import type {
|
|||
PayloadsOf,
|
||||
ReadonlyTaskOutput,
|
||||
ResultOf,
|
||||
RuntimeFor,
|
||||
SummaryEntry,
|
||||
TaskKindBase,
|
||||
TaskOf,
|
||||
TaskOutcome,
|
||||
TaskOutput,
|
||||
TaskOutputRef,
|
||||
TaskRuntime,
|
||||
TerminalClosure,
|
||||
TurnAbortRuntime,
|
||||
TurnOf,
|
||||
TurnTaskRuntime,
|
||||
Value,
|
||||
} from "../../../src/harness/pico/index.ts";
|
||||
import { defineEntry, defineList, defineTask, defineTaskOutput, defineValue } from "../../../src/harness/pico/index.ts";
|
||||
import {
|
||||
defineCoreTask,
|
||||
defineEntry,
|
||||
defineList,
|
||||
defineTask,
|
||||
defineTaskOutput,
|
||||
defineValue,
|
||||
} from "../../../src/harness/pico/index.ts";
|
||||
|
||||
type Equal<Left, Right> = (<Type>() => Type extends Left ? 1 : 2) extends <Type>() => Type extends Right ? 1 : 2
|
||||
? (<Type>() => Type extends Right ? 1 : 2) extends <Type>() => Type extends Left ? 1 : 2
|
||||
|
|
@ -69,8 +77,8 @@ type JsonAssertions = [
|
|||
Assert<{ readonly value: undefined } extends JsonValue ? false : true>,
|
||||
];
|
||||
|
||||
const nonTurnKind = defineTask<TestInput, TestCheckpoint, TestResult, TestFailure, TestAborted>()({
|
||||
kind: "test.non_turn",
|
||||
const ordinaryKind = defineTask<TestInput, TestCheckpoint, TestResult, TestFailure, TestAborted>()({
|
||||
kind: "test.ordinary",
|
||||
execute() {
|
||||
return Promise.resolve(() => ({ status: "completed", result: { answer: "done" } }));
|
||||
},
|
||||
|
|
@ -82,9 +90,8 @@ const nonTurnKind = defineTask<TestInput, TestCheckpoint, TestResult, TestFailur
|
|||
},
|
||||
});
|
||||
|
||||
const turnKind = defineTask<TestInput, TestCheckpoint, TestResult, TestFailure, TestAborted>()({
|
||||
kind: "test.turn",
|
||||
turn: true,
|
||||
const coreKind = defineCoreTask<TestInput, TestCheckpoint, TestResult, TestFailure, TestAborted>()({
|
||||
kind: "pi.test_core",
|
||||
execute() {
|
||||
return Promise.resolve(() => ({ status: "completed", result: { answer: "done" } }));
|
||||
},
|
||||
|
|
@ -148,55 +155,53 @@ const incompatibleCheckpointKind = defineTask<
|
|||
},
|
||||
});
|
||||
|
||||
type NonTurnRuntimeTx = Parameters<Parameters<RuntimeFor<TestInput, TestCheckpoint, never, false>["commit"]>[0]>[0];
|
||||
type TurnRuntimeTx = Parameters<Parameters<RuntimeFor<TestInput, TestCheckpoint, never, true>["commit"]>[0]>[0];
|
||||
type NonTurnAbortTx = Parameters<Parameters<AbortRuntimeFor<TestInput, TestCheckpoint, never, false>["commit"]>[0]>[0];
|
||||
type TurnRuntimeAbortTx = Parameters<
|
||||
Parameters<AbortRuntimeFor<TestInput, TestCheckpoint, never, true>["commit"]>[0]
|
||||
>[0];
|
||||
type NonTurnFinalTx = Parameters<TerminalClosure<TestInput, TestCheckpoint, TestResult, TestFailure, never, false>>[0];
|
||||
type TurnFinalTx = Parameters<TerminalClosure<TestInput, TestCheckpoint, TestResult, TestFailure, never, true>>[0];
|
||||
type NonTurnAbortFinalTx = Parameters<AbortClosure<TestInput, TestCheckpoint, TestAborted, never, false>>[0];
|
||||
type TurnAbortFinalTx = Parameters<AbortClosure<TestInput, TestCheckpoint, TestAborted, never, true>>[0];
|
||||
type OutputFinalTx = Parameters<
|
||||
TerminalClosure<TestInput, TestCheckpoint, TestResult, TestFailure, TestOutput, false>
|
||||
>[0];
|
||||
type OutputAbortFinalTx = Parameters<AbortClosure<TestInput, TestCheckpoint, TestAborted, TestOutput, false>>[0];
|
||||
type OrdinaryRuntimeTx = Parameters<Parameters<TaskRuntime<TestInput, TestCheckpoint>["commit"]>[0]>[0];
|
||||
type CoreRuntimeTx = Parameters<Parameters<CoreTaskRuntime<TestInput, TestCheckpoint>["commit"]>[0]>[0];
|
||||
type OrdinaryAbortTx = Parameters<Parameters<AbortTaskRuntime<TestInput, TestCheckpoint>["commit"]>[0]>[0];
|
||||
type CoreRuntimeAbortTx = Parameters<Parameters<CoreAbortTaskRuntime<TestInput, TestCheckpoint>["commit"]>[0]>[0];
|
||||
type OrdinaryFinalTx = Parameters<TerminalClosure<TestInput, TestCheckpoint, TestResult, TestFailure, never>>[0];
|
||||
type CoreFinalTx = Parameters<CoreTerminalClosure<TestInput, TestCheckpoint, TestResult, TestFailure, never>>[0];
|
||||
type OrdinaryAbortFinalTx = Parameters<AbortClosure<TestInput, TestCheckpoint, TestAborted, never>>[0];
|
||||
type CoreAbortFinalTx = Parameters<CoreAbortClosure<TestInput, TestCheckpoint, TestAborted, never>>[0];
|
||||
type OutputFinalTx = Parameters<TerminalClosure<TestInput, TestCheckpoint, TestResult, TestFailure, TestOutput>>[0];
|
||||
type OutputAbortFinalTx = Parameters<AbortClosure<TestInput, TestCheckpoint, TestAborted, TestOutput>>[0];
|
||||
|
||||
type KindAssertions = [
|
||||
Assert<Equal<TurnOf<typeof nonTurnKind>, false>>,
|
||||
Assert<Equal<TurnOf<typeof turnKind>, true>>,
|
||||
Assert<Equal<InputOf<typeof nonTurnKind>, TestInput>>,
|
||||
Assert<Equal<CheckpointOf<typeof nonTurnKind>, TestCheckpoint>>,
|
||||
Assert<Equal<ResultOf<typeof nonTurnKind>, TestResult>>,
|
||||
Assert<Equal<FailureOf<typeof nonTurnKind>, TestFailure>>,
|
||||
Assert<Equal<AbortedOf<typeof nonTurnKind>, TestAborted>>,
|
||||
Assert<Equal<PayloadsOf<typeof nonTurnKind>["aborted"], TestAborted>>,
|
||||
Assert<Equal<OutputOf<typeof nonTurnKind>, never>>,
|
||||
Assert<"turn" extends keyof typeof ordinaryKind ? false : true>,
|
||||
Assert<"turn" extends keyof typeof coreKind ? false : true>,
|
||||
Assert<"busy" extends keyof TaskOf<typeof ordinaryKind> ? false : true>,
|
||||
Assert<Equal<InputOf<typeof ordinaryKind>, TestInput>>,
|
||||
Assert<Equal<InputOf<typeof coreKind>, TestInput>>,
|
||||
Assert<Equal<CheckpointOf<typeof ordinaryKind>, TestCheckpoint>>,
|
||||
Assert<Equal<ResultOf<typeof ordinaryKind>, TestResult>>,
|
||||
Assert<Equal<FailureOf<typeof ordinaryKind>, TestFailure>>,
|
||||
Assert<Equal<AbortedOf<typeof ordinaryKind>, TestAborted>>,
|
||||
Assert<Equal<PayloadsOf<typeof ordinaryKind>["aborted"], TestAborted>>,
|
||||
Assert<Equal<OutputOf<typeof ordinaryKind>, never>>,
|
||||
Assert<Equal<OutputOf<typeof outputTaskKind>, TestOutput>>,
|
||||
Assert<Equal<Parameters<typeof nonTurnKind.execute>[1], BaseTaskRuntime<TestInput, TestCheckpoint>>>,
|
||||
Assert<Equal<Parameters<typeof ordinaryKind.execute>[1], TaskRuntime<TestInput, TestCheckpoint>>>,
|
||||
Assert<Equal<Parameters<typeof coreKind.execute>[1], CoreTaskRuntime<TestInput, TestCheckpoint>>>,
|
||||
Assert<Equal<Parameters<typeof outputTaskKind.execute>[1]["output"], TaskOutput<TestOutput>>>,
|
||||
Assert<Equal<ReturnType<Parameters<TaskOutput<TestOutput>["mutate"]>[0]>, undefined>>,
|
||||
Assert<Equal<Parameters<typeof turnKind.execute>[1], TurnTaskRuntime<TestInput, TestCheckpoint>>>,
|
||||
Assert<Equal<Parameters<typeof nonTurnKind.abort>[1], BaseAbortRuntime<TestInput, TestCheckpoint>>>,
|
||||
Assert<Equal<Parameters<typeof turnKind.abort>[1], TurnAbortRuntime<TestInput, TestCheckpoint>>>,
|
||||
Assert<Equal<Parameters<typeof ordinaryKind.abort>[1], AbortTaskRuntime<TestInput, TestCheckpoint>>>,
|
||||
Assert<Equal<Parameters<typeof coreKind.abort>[1], CoreAbortTaskRuntime<TestInput, TestCheckpoint>>>,
|
||||
Assert<Equal<Parameters<typeof outputTaskKind.abort>[1]["output"], ReadonlyTaskOutput<TestOutput>>>,
|
||||
Assert<Equal<HasOutput<Parameters<typeof nonTurnKind.execute>[1]>, false>>,
|
||||
Assert<Equal<HasOutput<Parameters<typeof ordinaryKind.execute>[1]>, false>>,
|
||||
Assert<Equal<HasOutput<Parameters<typeof outputTaskKind.execute>[1]>, true>>,
|
||||
Assert<Equal<OutputFinalTx["output"], TestOutput>>,
|
||||
Assert<Equal<OutputAbortFinalTx["output"], TestOutput>>,
|
||||
Assert<Equal<HasEntry<NonTurnRuntimeTx>, false>>,
|
||||
Assert<Equal<HasEntry<TurnRuntimeTx>, true>>,
|
||||
Assert<Equal<HasEntry<NonTurnFinalTx>, false>>,
|
||||
Assert<Equal<HasEntry<TurnFinalTx>, true>>,
|
||||
Assert<Equal<HasEntry<NonTurnAbortTx>, false>>,
|
||||
Assert<Equal<HasEntry<TurnRuntimeAbortTx>, true>>,
|
||||
Assert<Equal<HasEntry<NonTurnAbortFinalTx>, false>>,
|
||||
Assert<Equal<HasEntry<TurnAbortFinalTx>, true>>,
|
||||
Assert<"sleep" extends keyof BaseAbortRuntime<TestInput, TestCheckpoint> ? false : true>,
|
||||
Assert<Equal<HasEntry<OrdinaryRuntimeTx>, false>>,
|
||||
Assert<Equal<HasEntry<CoreRuntimeTx>, true>>,
|
||||
Assert<Equal<HasEntry<OrdinaryFinalTx>, false>>,
|
||||
Assert<Equal<HasEntry<CoreFinalTx>, true>>,
|
||||
Assert<Equal<HasEntry<OrdinaryAbortTx>, false>>,
|
||||
Assert<Equal<HasEntry<CoreRuntimeAbortTx>, true>>,
|
||||
Assert<Equal<HasEntry<OrdinaryAbortFinalTx>, false>>,
|
||||
Assert<Equal<HasEntry<CoreAbortFinalTx>, true>>,
|
||||
Assert<"sleep" extends keyof AbortTaskRuntime<TestInput, TestCheckpoint> ? false : true>,
|
||||
];
|
||||
|
||||
type KindOutcome = Extract<TaskOf<typeof nonTurnKind>, { readonly status: "terminal" }>["outcome"];
|
||||
type KindOutcome = Extract<TaskOf<typeof ordinaryKind>, { readonly status: "terminal" }>["outcome"];
|
||||
type OutcomeAssertions = [
|
||||
Assert<Equal<KindOutcome, TaskOutcome<TestResult, TestFailure, TestAborted>>>,
|
||||
Assert<Equal<Extract<KindOutcome, { readonly status: "completed" }>["result"], TestResult>>,
|
||||
|
|
@ -220,9 +225,11 @@ function verifyTaskInputs(
|
|||
outputRef: TaskOutputRef<TestOutput>,
|
||||
wrongOutputRef: TaskOutputRef<{ count: number }>,
|
||||
): void {
|
||||
tx.task(nonTurnKind, {
|
||||
tx.task(ordinaryKind, {
|
||||
input: { prompt: "hello", nested: { count: 1 } },
|
||||
});
|
||||
// @ts-expect-error ordinary task transactions cannot instantiate fixed core task kinds
|
||||
tx.task(coreKind, { input: { prompt: "core", nested: { count: 1 } } });
|
||||
tx.task(outputTaskKind, {
|
||||
input: { prompt: "own output", nested: { count: 1 } },
|
||||
});
|
||||
|
|
@ -231,7 +238,7 @@ function verifyTaskInputs(
|
|||
output: outputRef,
|
||||
});
|
||||
// @ts-expect-error a no-output kind cannot receive an output reference
|
||||
tx.task(nonTurnKind, { input: { prompt: "hello", nested: { count: 1 } }, output: outputRef });
|
||||
tx.task(ordinaryKind, { input: { prompt: "hello", nested: { count: 1 } }, output: outputRef });
|
||||
tx.task(outputTaskKind, {
|
||||
input: { prompt: "wrong output", nested: { count: 1 } },
|
||||
// @ts-expect-error a shared output reference must have the kind's output type
|
||||
|
|
@ -243,30 +250,30 @@ function verifyTaskInputs(
|
|||
input: { prompt: "hello", nested: { count: 1 } },
|
||||
background: true,
|
||||
} as const;
|
||||
tx.task(nonTurnKind, exactVariable);
|
||||
tx.task(ordinaryKind, exactVariable);
|
||||
|
||||
// Top-level exactness intentionally does not recursively reject extra nested fields.
|
||||
tx.task(nonTurnKind, {
|
||||
tx.task(ordinaryKind, {
|
||||
input: { prompt: "hello", nested: { count: 1, extension: true } },
|
||||
});
|
||||
|
||||
// @ts-expect-error task input literals reject extra top-level fields
|
||||
tx.task(nonTurnKind, { input: { prompt: "hello", nested: { count: 1 }, extension: true } });
|
||||
tx.task(ordinaryKind, { input: { prompt: "hello", nested: { count: 1 }, extension: true } });
|
||||
|
||||
const extraInputVariable = { prompt: "hello", nested: { count: 1 }, extension: true };
|
||||
// @ts-expect-error task input variables reject visible extra top-level fields
|
||||
tx.task(nonTurnKind, { input: extraInputVariable });
|
||||
tx.task(ordinaryKind, { input: extraInputVariable });
|
||||
|
||||
const inputBase = { prompt: "hello", nested: { count: 1 } };
|
||||
// @ts-expect-error task input spreads reject visible extra top-level fields
|
||||
tx.task(nonTurnKind, { input: { ...inputBase, extension: true } });
|
||||
tx.task(ordinaryKind, { input: { ...inputBase, extension: true } });
|
||||
|
||||
const extraSpecVariable = {
|
||||
input: { prompt: "hello", nested: { count: 1 } },
|
||||
priority: 1,
|
||||
};
|
||||
// @ts-expect-error task specs reject visible extra top-level fields
|
||||
tx.task(nonTurnKind, extraSpecVariable);
|
||||
tx.task(ordinaryKind, extraSpecVariable);
|
||||
|
||||
tx.checkpoint({ phase: "ready", cursor: 1 });
|
||||
// @ts-expect-error every checkpoint requires its phase
|
||||
|
|
@ -277,6 +284,21 @@ function verifyTaskInputs(
|
|||
tx.checkpoint({ phase: "ready", token: "cursor" });
|
||||
}
|
||||
|
||||
function verifyOrdinaryRegistration(register: (kind: TaskKindBase) => void): void {
|
||||
register(ordinaryKind);
|
||||
// @ts-expect-error fixed core kinds cannot enter the ordinary task registry
|
||||
register(coreKind);
|
||||
}
|
||||
|
||||
function verifyCoreTaskInputs(tx: CoreTaskTx<TestCheckpoint>): void {
|
||||
tx.task(ordinaryKind, {
|
||||
input: { prompt: "ordinary child", nested: { count: 1 } },
|
||||
});
|
||||
tx.task(coreKind, {
|
||||
input: { prompt: "core child", nested: { count: 1 } },
|
||||
});
|
||||
}
|
||||
|
||||
function verifyOutputMutation(
|
||||
output: TaskOutput<TestOutput>,
|
||||
readonlyOutput: ReadonlyTaskOutput<TestOutput>,
|
||||
|
|
@ -298,10 +320,10 @@ function verifyCheckpointCompatibility(
|
|||
incompatible: CheckpointOf<typeof incompatibleCheckpointKind>,
|
||||
): void {
|
||||
// Checkpoints are structural: an independently declared identical shape is assignable.
|
||||
const structurallyCompatible: CheckpointOf<typeof nonTurnKind> = checkpoint;
|
||||
const structurallyCompatible: CheckpointOf<typeof ordinaryKind> = checkpoint;
|
||||
void structurallyCompatible;
|
||||
// @ts-expect-error a differently shaped kind checkpoint is not assignable
|
||||
const structurallyIncompatible: CheckpointOf<typeof nonTurnKind> = incompatible;
|
||||
const structurallyIncompatible: CheckpointOf<typeof ordinaryKind> = incompatible;
|
||||
void structurallyIncompatible;
|
||||
}
|
||||
|
||||
|
|
@ -373,6 +395,8 @@ void entryAssertions;
|
|||
void exactInputAssertion;
|
||||
void entry;
|
||||
void verifyTaskInputs;
|
||||
void verifyOrdinaryRegistration;
|
||||
void verifyCoreTaskInputs;
|
||||
void verifyOutputMutation;
|
||||
void verifyCheckpointCompatibility;
|
||||
void verifyOutcomes;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue