From 1915b35a9b03a1203d4bf0bd2fd143cce1562853 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Sat, 12 Sep 2026 22:31:16 +0200 Subject: [PATCH] feat(agent): refine pico core task capabilities --- packages/agent/src/harness/pico/addresses.ts | 10 +- packages/agent/src/harness/pico/core.ts | 1 + packages/agent/src/harness/pico/runtime.ts | 194 +++++++++--------- packages/agent/src/harness/pico/tasks.ts | 132 ++++++++---- .../agent/test/harness/pico/types.compile.ts | 146 +++++++------ 5 files changed, 283 insertions(+), 200 deletions(-) diff --git a/packages/agent/src/harness/pico/addresses.ts b/packages/agent/src/harness/pico/addresses.ts index 9d12acc09..a9bd69afa 100644 --- a/packages/agent/src/harness/pico/addresses.ts +++ b/packages/agent/src/harness/pico/addresses.ts @@ -12,17 +12,17 @@ export interface Address { 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 extends Address { - readonly collection: "value"; + readonly kind: "value"; } export interface List extends Address { - readonly collection: "list"; + readonly kind: "list"; } export interface Element { @@ -51,7 +51,7 @@ export function defineValue(scope: Scope, namespace: string ...options, scope, namespace, - collection: "value", + kind: "value", rewind: options?.rewind ?? false, }); } @@ -71,7 +71,7 @@ export function defineList(scope: Scope, namespace: string, ...options, scope, namespace, - collection: "list", + kind: "list", rewind: options?.rewind ?? false, }); } diff --git a/packages/agent/src/harness/pico/core.ts b/packages/agent/src/harness/pico/core.ts index 495a66e7c..fa71922d9 100644 --- a/packages/agent/src/harness/pico/core.ts +++ b/packages/agent/src/harness/pico/core.ts @@ -6,3 +6,4 @@ export interface JsonObject { } export type Id = number; +export type Seq = number; diff --git a/packages/agent/src/harness/pico/runtime.ts b/packages/agent/src/harness/pico/runtime.ts index 3d563ad82..a4ae202a3 100644 --- a/packages/agent/src/harness/pico/runtime.ts +++ b/packages/agent/src/harness/pico/runtime.ts @@ -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 { - 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; getEntry(id: Id): Promise; getEntry(kind: EntryKind, id: Id): Promise; getEntries(ids: readonly Id[]): Promise>; getTask(id: Id): Promise; - getTask(kind: K, id: Id): Promise | undefined>; + getTask(kind: K, id: Id): Promise | undefined>; getTasks(ids: readonly Id[]): Promise>; } @@ -50,7 +35,8 @@ export interface TxValue { } export interface TxList { - read(query: ListQuery): Promise>>; + read(): Promise[]>; + read(at: Id): Promise[]>; 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; -export interface AcceptOptions { +export interface InternalAcceptOptions { readonly input: UserInput; readonly requestId?: string; readonly whenBusy?: "followUp" | "steer" | "reject"; } -export interface BaseTaskTx extends TxReaders { - checkpoint(value: C): void; - task, OutputOf>>( +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; + wait(ctx: Context): Promise; + abort(ctx: Context): Promise<"aborted" | "already_placed" | "not_found">; +} + +interface TaskCreator { + task, OutputOf>>( kind: K, spec: NoExtra, OutputOf>, S> & { readonly input: ExactJsonInput, S["input"]>; }, ): Id; +} + +interface TaskTxBase extends TxReaders { + checkpoint(value: C): void; createConversation(spec: ConversationCreateSpec): Id; value(address: Value): TxValue; list(address: List): TxList; - accept(conversationId: Id, options: AcceptOptions): Promise; +} + +export type BaseTaskTx = TaskTxBase & TaskCreator; + +export interface InternalAdmissionTx { + accept(conversationId: Id, options: InternalAcceptOptions): Promise; queueInput(conversationId: Id, input: QueuedInput): Promise; write( conversationId: Id, @@ -131,18 +139,22 @@ export interface BaseTaskTx extends TxReaders { ): Promise; } -export interface TurnTaskTx extends BaseTaskTx { - entry(kind: EntryKind, conversationId: Id, input: EntryInput): Id; -} +export type CoreTaskTx = TaskTxBase & + TaskCreator & + InternalAdmissionTx & { + entry(kind: EntryKind, conversationId: Id, input: EntryInput): Id; + }; export interface PublicValue { - get(at: Id | undefined, ctx: Context): Promise; + get(ctx: Context): Promise; + get(at: Id, ctx: Context): Promise; set(value: T, ctx: Context): Promise; delete(ctx: Context): Promise; } export interface PublicList { - read(query: ListQuery, ctx: Context): Promise>>; + read(ctx: Context): Promise[]>; + read(at: Id, ctx: Context): Promise[]>; append(value: T, ctx: Context): Promise; remove(id: Id, ctx: Context): Promise; clear(ctx: Context): Promise; @@ -160,11 +172,11 @@ export interface ReadonlyTaskOutput { read(ctx: Context): Promise; } -type RuntimeOutput = [O] extends [never] ? unknown : { readonly output: TaskOutput }; - -type AbortRuntimeOutput = [O] extends [never] ? unknown : { readonly output: ReadonlyTaskOutput }; - -type FinalOutput = [O] extends [never] ? unknown : { readonly output: O }; +export type RuntimeOutput = [O] extends [never] ? unknown : { readonly output: TaskOutput }; +export type AbortRuntimeOutput = [O] extends [never] + ? unknown + : { readonly output: ReadonlyTaskOutput }; +export type FinalOutput = [O] extends [never] ? unknown : { readonly output: O }; export interface ScratchTx { value(address: Value): TxValue; @@ -178,38 +190,19 @@ export interface ScratchReader { export interface TaskConversation { readonly id: Id; - accept(options: AcceptOptions, ctx: Context): Promise; - queueInput(input: QueuedInput, ctx: Context): Promise; - write( - kind: EntryKind, - input: EntryInput, - requestId: string | undefined, - ctx: Context, - ): Promise; - result(inputId: Id, ctx: Context): Promise; - waitForInput(inputId: Id, ctx: Context): Promise; - abortInput(inputId: Id, ctx: Context): Promise<"aborted" | "already_placed" | "not_found">; + send(input: SendInput, ctx: Context): Promise; value(address: Value): PublicValue; list(address: List): PublicList; } export interface AbortTaskConversation { readonly id: Id; - write( - kind: EntryKind, - input: EntryInput, - requestId: string | undefined, - ctx: Context, - ): Promise; - result(inputId: Id, ctx: Context): Promise; - waitForInput(inputId: Id, ctx: Context): Promise; value(address: Value): PublicValue; list(address: List): PublicList; } -export interface BaseTaskRuntime { +interface TaskRuntimeBase { readonly taskId: Id; - commit(build: (tx: BaseTaskTx, current: RunningTask) => T | Promise, ctx: Context): Promise; scratch(build: (tx: ScratchTx) => T | Promise, ctx: Context): Promise; conversation(id: Id, ctx: Context): Promise; waitForTask(id: Id, ctx: Context): Promise; @@ -218,68 +211,83 @@ export interface BaseTaskRuntime; } -export interface TurnTaskRuntime - extends Omit, "commit"> { - commit(build: (tx: TurnTaskTx, current: RunningTask) => T | Promise, ctx: Context): Promise; +interface CommitRuntime { + commit(build: (tx: Tx, current: RunningTask) => T | Promise, ctx: Context): Promise; } -export type RuntimeFor< +export type TaskRuntime = TaskRuntimeBase & + CommitRuntime> & + RuntimeOutput; + +export type CoreTaskRuntime = TaskRuntimeBase & + CommitRuntime> & + RuntimeOutput; + +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 : BaseTaskRuntime) & RuntimeOutput; - -export interface TerminalTx extends BaseTaskTx {} -export interface TurnTerminalTx extends TurnTaskTx {} - -export type FinalTx = (T extends true - ? TurnTerminalTx - : TerminalTx) & - FinalOutput; +> = (tx: Tx & FinalOutput, current: RunningTask) => Completion | Promise>; +export type FinalTx = BaseTaskTx & FinalOutput; +export type CoreFinalTx = CoreTaskTx & FinalOutput; export type TerminalClosure< I extends JsonValue, C extends TaskCheckpoint, R extends JsonValue, F extends JsonValue, O extends object, - T extends false | true, -> = (tx: FinalTx, current: RunningTask) => Completion | Promise>; +> = TerminalClosureFor, I, C, R, F, O>; +export type CoreTerminalClosure< + I extends JsonValue, + C extends TaskCheckpoint, + R extends JsonValue, + F extends JsonValue, + O extends object, +> = TerminalClosureFor, I, C, R, F, O>; export interface AbortTx extends TxReaders { checkpoint(value: C): void; value(address: Value): TxValue; list(address: List): TxList; + markTask(id: Id): Promise<"marked" | "terminal">; +} + +export interface CoreAbortTx extends AbortTx { + entry(kind: EntryKind, conversationId: Id, input: EntryInput): Id; write( conversationId: Id, kind: EntryKind, input: EntryInput, requestId?: string, ): Promise; - markTask(id: Id): Promise<"marked" | "terminal">; } -export interface TurnAbortTx extends AbortTx { - entry(kind: EntryKind, conversationId: Id, input: EntryInput): Id; -} - -export type AbortFinalTx = (T extends true - ? TurnAbortTx - : AbortTx) & - FinalOutput; +type AbortClosureFor = ( + tx: Tx & FinalOutput, + current: RunningTask, +) => A | Promise; +export type AbortFinalTx = AbortTx & FinalOutput; +export type CoreAbortFinalTx = CoreAbortTx & FinalOutput; export type AbortClosure< I extends JsonValue, C extends TaskCheckpoint, A extends JsonValue, O extends object, - T extends false | true, -> = (tx: AbortFinalTx, current: RunningTask) => A | Promise; +> = AbortClosureFor, I, C, A, O>; +export type CoreAbortClosure< + I extends JsonValue, + C extends TaskCheckpoint, + A extends JsonValue, + O extends object, +> = AbortClosureFor, I, C, A, O>; -export interface BaseAbortRuntime { +interface AbortRuntimeBase { readonly taskId: Id; - commit(build: (tx: AbortTx, current: RunningTask) => T | Promise, ctx: Context): Promise; scratch(read: (scratch: ScratchReader) => T | Promise, ctx: Context): Promise; conversation(id: Id, ctx: Context): Promise; waitForTask(id: Id, ctx: Context): Promise; @@ -287,14 +295,14 @@ export interface BaseAbortRuntime - extends Omit, "commit"> { - commit(build: (tx: TurnAbortTx, current: RunningTask) => T | Promise, ctx: Context): Promise; -} - -export type AbortRuntimeFor< +export type AbortTaskRuntime< I extends JsonValue, C extends TaskCheckpoint, - O extends object, - T extends false | true, -> = (T extends true ? TurnAbortRuntime : BaseAbortRuntime) & AbortRuntimeOutput; + O extends object = never, +> = AbortRuntimeBase & CommitRuntime> & AbortRuntimeOutput; + +export type CoreAbortTaskRuntime< + I extends JsonValue, + C extends TaskCheckpoint, + O extends object = never, +> = AbortRuntimeBase & CommitRuntime> & AbortRuntimeOutput; diff --git a/packages/agent/src/harness/pico/tasks.ts b/packages/agent/src/harness/pico/tasks.ts index 30b2d88e4..9066fabab 100644 --- a/packages/agent/src/harness/pico/tasks.ts +++ b/packages/agent/src/harness/pico/tasks.ts @@ -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 extends StoredTaskOutputRef { export type TaskOutputField = [O] extends [never] ? unknown : { readonly output: TaskOutputRef }; -type TaskOutputDefinition = [O] extends [never] +export type TaskOutputDefinition = [O] extends [never] ? { readonly output?: never } : { readonly output: TaskOutputSpec }; @@ -53,7 +62,6 @@ export interface TaskBase { 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, - runtime: RuntimeFor, + runtime: TaskRuntime, ctx: Context, - ): Promise>; + ): Promise>; recover( task: RunningTask, - runtime: RuntimeFor, + runtime: TaskRuntime, ctx: Context, - ): Promise>; + ): Promise>; abort( task: RunningTask, - runtime: AbortRuntimeFor, + runtime: AbortTaskRuntime, ctx: Context, - ): Promise>; + ): Promise>; +} + +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, + runtime: CoreTaskRuntime, + ctx: Context, + ): Promise>; + recover( + task: RunningTask, + runtime: CoreTaskRuntime, + ctx: Context, + ): Promise>; + abort( + task: RunningTask, + runtime: CoreAbortTaskRuntime, + ctx: Context, + ): Promise>; } 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 & TaskOutputDefinition; +> = TaskKindMethods & TaskOutputDefinition; + +export type CoreTaskKind< + I extends JsonValue, + C extends TaskCheckpoint, + R extends JsonValue, + F extends JsonValue, + A extends JsonValue, + O extends object = never, +> = CoreTaskKindMethods & TaskOutputDefinition; export type NoExtra = Actual & Record, never>; @@ -137,39 +180,26 @@ export type ExactJsonInput ? Actual & Record, never> : Actual; -export type NonTurnKindDefinition< - I extends JsonValue, - C extends TaskCheckpoint, - R extends JsonValue, - F extends JsonValue, - A extends JsonValue, - O extends object, -> = Omit, "turn" | typeof taskKindBrand> & { readonly turn?: false }; +type KindDefinition = Omit; +type KindFactory = >(definition: NoExtra, 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, typeof taskKindBrand>; +> = KindFactory>; -export interface TaskKindFactory< +export type CoreTaskKindFactory< I extends JsonValue, C extends TaskCheckpoint, R extends JsonValue, F extends JsonValue, A extends JsonValue, O extends object, -> { - >( - definition: NoExtra, D>, - ): D & TaskKind; - >( - definition: NoExtra, D>, - ): D & TaskKind; -} +> = KindFactory>; export function defineTask< I extends JsonValue, @@ -179,29 +209,49 @@ export function defineTask< A extends JsonValue, O extends object = never, >(): TaskKindFactory { - const define = (definition: NonTurnKindDefinition | TurnKindDefinition) => + const define = (definition: KindDefinition>) => Object.freeze({ ...definition, - turn: definition.turn ?? false, - [taskKindBrand]: true as const, + [taskKindBrand]: "ordinary" as const, }); return define as TaskKindFactory; } +/** 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 { + const define = (definition: KindDefinition>) => + Object.freeze({ + ...definition, + [taskKindBrand]: "core" as const, + }); + return define as CoreTaskKindFactory; +} + export type AnyTaskKind = TaskKindBase; -export type PayloadsOf = K extends TaskKind +export type AnyCoreTaskKind = CoreTaskKindBase; +export type AnyDefinedTaskKind = AnyTaskKind | AnyCoreTaskKind; + +export type PayloadsOf = K extends TaskKind ? { input: I; checkpoint: C; result: R; failure: F; aborted: A; output: O } - : never; + : K extends CoreTaskKind + ? { input: I; checkpoint: C; result: R; failure: F; aborted: A; output: O } + : never; export type InputOf = PayloadsOf["input"]; export type CheckpointOf = PayloadsOf["checkpoint"]; export type ResultOf = PayloadsOf["result"]; export type FailureOf = PayloadsOf["failure"]; export type AbortedOf = PayloadsOf["aborted"]; export type OutputOf = PayloadsOf["output"]; -export type TurnOf = K["turn"]; type WithoutOutput = T extends unknown ? Omit : never; -export type TaskOf = WithoutOutput< +export type TaskOf = WithoutOutput< Task, CheckpointOf, ResultOf, FailureOf, AbortedOf> > & TaskOutputField>; diff --git a/packages/agent/test/harness/pico/types.compile.ts b/packages/agent/test/harness/pico/types.compile.ts index 54b77ce68..e2131739a 100644 --- a/packages/agent/test/harness/pico/types.compile.ts +++ b/packages/agent/test/harness/pico/types.compile.ts @@ -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 = (() => Type extends Left ? 1 : 2) extends () => Type extends Right ? 1 : 2 ? (() => Type extends Right ? 1 : 2) extends () => Type extends Left ? 1 : 2 @@ -69,8 +77,8 @@ type JsonAssertions = [ Assert<{ readonly value: undefined } extends JsonValue ? false : true>, ]; -const nonTurnKind = defineTask()({ - kind: "test.non_turn", +const ordinaryKind = defineTask()({ + kind: "test.ordinary", execute() { return Promise.resolve(() => ({ status: "completed", result: { answer: "done" } })); }, @@ -82,9 +90,8 @@ const nonTurnKind = defineTask()({ - kind: "test.turn", - turn: true, +const coreKind = defineCoreTask()({ + kind: "pi.test_core", execute() { return Promise.resolve(() => ({ status: "completed", result: { answer: "done" } })); }, @@ -148,55 +155,53 @@ const incompatibleCheckpointKind = defineTask< }, }); -type NonTurnRuntimeTx = Parameters["commit"]>[0]>[0]; -type TurnRuntimeTx = Parameters["commit"]>[0]>[0]; -type NonTurnAbortTx = Parameters["commit"]>[0]>[0]; -type TurnRuntimeAbortTx = Parameters< - Parameters["commit"]>[0] ->[0]; -type NonTurnFinalTx = Parameters>[0]; -type TurnFinalTx = Parameters>[0]; -type NonTurnAbortFinalTx = Parameters>[0]; -type TurnAbortFinalTx = Parameters>[0]; -type OutputFinalTx = Parameters< - TerminalClosure ->[0]; -type OutputAbortFinalTx = Parameters>[0]; +type OrdinaryRuntimeTx = Parameters["commit"]>[0]>[0]; +type CoreRuntimeTx = Parameters["commit"]>[0]>[0]; +type OrdinaryAbortTx = Parameters["commit"]>[0]>[0]; +type CoreRuntimeAbortTx = Parameters["commit"]>[0]>[0]; +type OrdinaryFinalTx = Parameters>[0]; +type CoreFinalTx = Parameters>[0]; +type OrdinaryAbortFinalTx = Parameters>[0]; +type CoreAbortFinalTx = Parameters>[0]; +type OutputFinalTx = Parameters>[0]; +type OutputAbortFinalTx = Parameters>[0]; type KindAssertions = [ - Assert, false>>, - Assert, true>>, - Assert, TestInput>>, - Assert, TestCheckpoint>>, - Assert, TestResult>>, - Assert, TestFailure>>, - Assert, TestAborted>>, - Assert["aborted"], TestAborted>>, - Assert, never>>, + Assert<"turn" extends keyof typeof ordinaryKind ? false : true>, + Assert<"turn" extends keyof typeof coreKind ? false : true>, + Assert<"busy" extends keyof TaskOf ? false : true>, + Assert, TestInput>>, + Assert, TestInput>>, + Assert, TestCheckpoint>>, + Assert, TestResult>>, + Assert, TestFailure>>, + Assert, TestAborted>>, + Assert["aborted"], TestAborted>>, + Assert, never>>, Assert, TestOutput>>, - Assert[1], BaseTaskRuntime>>, + Assert[1], TaskRuntime>>, + Assert[1], CoreTaskRuntime>>, Assert[1]["output"], TaskOutput>>, Assert["mutate"]>[0]>, undefined>>, - Assert[1], TurnTaskRuntime>>, - Assert[1], BaseAbortRuntime>>, - Assert[1], TurnAbortRuntime>>, + Assert[1], AbortTaskRuntime>>, + Assert[1], CoreAbortTaskRuntime>>, Assert[1]["output"], ReadonlyTaskOutput>>, - Assert[1]>, false>>, + Assert[1]>, false>>, Assert[1]>, true>>, Assert>, Assert>, - Assert, false>>, - Assert, true>>, - Assert, false>>, - Assert, true>>, - Assert, false>>, - Assert, true>>, - Assert, false>>, - Assert, true>>, - Assert<"sleep" extends keyof BaseAbortRuntime ? false : true>, + Assert, false>>, + Assert, true>>, + Assert, false>>, + Assert, true>>, + Assert, false>>, + Assert, true>>, + Assert, false>>, + Assert, true>>, + Assert<"sleep" extends keyof AbortTaskRuntime ? false : true>, ]; -type KindOutcome = Extract, { readonly status: "terminal" }>["outcome"]; +type KindOutcome = Extract, { readonly status: "terminal" }>["outcome"]; type OutcomeAssertions = [ Assert>>, Assert["result"], TestResult>>, @@ -220,9 +225,11 @@ function verifyTaskInputs( outputRef: TaskOutputRef, 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): 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, readonlyOutput: ReadonlyTaskOutput, @@ -298,10 +320,10 @@ function verifyCheckpointCompatibility( incompatible: CheckpointOf, ): void { // Checkpoints are structural: an independently declared identical shape is assignable. - const structurallyCompatible: CheckpointOf = checkpoint; + const structurallyCompatible: CheckpointOf = checkpoint; void structurallyCompatible; // @ts-expect-error a differently shaped kind checkpoint is not assignable - const structurallyIncompatible: CheckpointOf = incompatible; + const structurallyIncompatible: CheckpointOf = 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;