@ekairos/tasks 1.22.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { tasksDomain } from "./domain.js";
2
+ export { createApprovalOutcome, approvalOutcomeForCommand, approvalOutcomeSchema, } from "./approval.js";
3
+ export { parseTaskOutcome, taskOutcomeToken, toStoredOutcomeSchema, } from "./task.js";
4
+ export { Task, } from "./service.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,GAGtB,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,GAOtB,MAAM,WAAW,CAAA;AAClB,OAAO,EACL,IAAI,GAOL,MAAM,cAAc,CAAA"}
@@ -0,0 +1,111 @@
1
+ import { type TaskData, type TaskHandle, type TaskOutcomeSchema, type TaskStoredOutcomeSchema } from "./task.js";
2
+ export type ServiceResult<T = unknown> = {
3
+ ok: true;
4
+ data: T;
5
+ } | {
6
+ ok: false;
7
+ error: string;
8
+ issues?: unknown;
9
+ };
10
+ export type TasksRuntime = {
11
+ db: any;
12
+ };
13
+ export type TasksClientRuntime = {
14
+ use(domain: unknown): Promise<any>;
15
+ };
16
+ export type TaskRecord<TContext = unknown, TOutcome = unknown> = TaskData<TContext, TOutcome> & {
17
+ errorText?: string;
18
+ };
19
+ export type TaskOpenActionInput = {
20
+ id?: string;
21
+ kind: string;
22
+ key: string;
23
+ instructions: string;
24
+ context?: unknown;
25
+ outcomeKind?: string;
26
+ outcomeSchema: TaskStoredOutcomeSchema;
27
+ };
28
+ export type TaskDecideActionInput = {
29
+ id: string;
30
+ outcome: unknown;
31
+ resumeWorkflow?: boolean;
32
+ runId?: string;
33
+ };
34
+ export type TaskCancelActionInput = {
35
+ id: string;
36
+ reason?: string;
37
+ runId?: string;
38
+ };
39
+ export type TaskFailActionInput = {
40
+ id: string;
41
+ errorText: string;
42
+ runId?: string;
43
+ };
44
+ export type TaskStartActionInput = {
45
+ id: string;
46
+ runId: string;
47
+ };
48
+ export type TaskReleaseActionInput = {
49
+ id: string;
50
+ runId: string;
51
+ actor?: unknown;
52
+ comment?: string;
53
+ };
54
+ export type TaskGetActionInput = {
55
+ id: string;
56
+ };
57
+ export type TaskAwaitOutcomeActionInput = {
58
+ id: string;
59
+ };
60
+ export type TaskOpenOptions = {
61
+ id?: string;
62
+ outcomeKind?: string;
63
+ };
64
+ export type TaskOpenInput<TOutcome, TContext = unknown> = {
65
+ id?: string;
66
+ kind: string;
67
+ key: string;
68
+ outcome: TaskOutcomeSchema<TOutcome>;
69
+ instructions: string;
70
+ context: TContext;
71
+ outcomeKind?: string;
72
+ };
73
+ export type TaskApprovalInput<TContext = unknown> = {
74
+ id?: string;
75
+ key: string;
76
+ instructions: string;
77
+ context: TContext;
78
+ };
79
+ export declare class TaskService {
80
+ private readonly runtime;
81
+ constructor(runtime: TasksRuntime);
82
+ private db;
83
+ private getTaskRecord;
84
+ openRaw(input: TaskOpenActionInput): Promise<ServiceResult<TaskRecord>>;
85
+ decide(input: TaskDecideActionInput): Promise<ServiceResult<TaskRecord>>;
86
+ cancel(input: TaskCancelActionInput): Promise<ServiceResult<TaskRecord>>;
87
+ fail(input: TaskFailActionInput): Promise<ServiceResult<TaskRecord>>;
88
+ getRaw(input: TaskGetActionInput): Promise<ServiceResult<TaskRecord>>;
89
+ awaitOutcome(input: TaskAwaitOutcomeActionInput): Promise<ServiceResult<unknown>>;
90
+ start(input: TaskStartActionInput): Promise<ServiceResult<TaskRecord>>;
91
+ release(input: TaskReleaseActionInput): Promise<ServiceResult<TaskRecord>>;
92
+ }
93
+ export declare class Task {
94
+ static create<TOutcome, TContext = unknown>(runtime: TasksClientRuntime, input: TaskOpenInput<TOutcome, TContext>): Promise<TaskHandle<TOutcome, TContext>>;
95
+ static open<TOutcome, TContext = unknown>(runtime: TasksClientRuntime, input: TaskOpenInput<TOutcome, TContext>): Promise<TaskHandle<TOutcome, TContext>>;
96
+ static approval<TContext = unknown>(runtime: TasksClientRuntime, input: TaskApprovalInput<TContext>): Promise<TaskHandle<{
97
+ outcome: "approved";
98
+ approved: true;
99
+ decidedBy?: string | undefined;
100
+ decidedAt?: string | undefined;
101
+ note?: string | undefined;
102
+ } | {
103
+ outcome: "rejected";
104
+ approved: false;
105
+ reason: string;
106
+ decidedBy?: string | undefined;
107
+ decidedAt?: string | undefined;
108
+ }, TContext>>;
109
+ static get<TOutcome, TContext = unknown>(runtime: TasksClientRuntime, id: string, outcomeSchema: TaskOutcomeSchema<TOutcome>): Promise<TaskHandle<TOutcome, TContext>>;
110
+ }
111
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,521 @@
1
+ import Ajv from "ajv";
2
+ import { id as instantId } from "@instantdb/core";
3
+ import { createHook } from "workflow";
4
+ import { approvalOutcomeSchema } from "./approval.js";
5
+ import { tasksDomain } from "./domain.js";
6
+ import { assertStoredOutcomeSchemaMatches, createTaskHandle, storedOutcomeSchemasEqual, taskOutcomeToken, toStoredOutcomeSchema, } from "./task.js";
7
+ async function runTaskDomainAction(runtime, action, input) {
8
+ const tasks = await tasksDomain(runtime);
9
+ const actions = tasks.actions;
10
+ return await actions[action](input);
11
+ }
12
+ function unwrapTaskResult(result) {
13
+ if (!result.ok)
14
+ throw new Error(result.error);
15
+ return result.data;
16
+ }
17
+ async function getTaskRecordThroughDomain(runtime, id) {
18
+ return unwrapTaskResult(await runTaskDomainAction(runtime, "getTask", { id }));
19
+ }
20
+ function unwrapOutcomeResult(result) {
21
+ if (!result.ok)
22
+ throw new Error(result.error);
23
+ return result.data;
24
+ }
25
+ function errorText(error) {
26
+ if (error instanceof Error)
27
+ return error.message;
28
+ return String(error);
29
+ }
30
+ function createDomainTaskHandle(runtime, record, outcomeSchema) {
31
+ assertStoredOutcomeSchemaMatches(outcomeSchema, record.outcomeSchema);
32
+ return createTaskHandle(record, outcomeSchema, {
33
+ async awaitOutcome() {
34
+ return unwrapOutcomeResult(await runTaskDomainAction(runtime, "awaitOutcome", { id: record.id }));
35
+ },
36
+ async start(data, work) {
37
+ const runId = newEntityId();
38
+ let terminal = false;
39
+ let lastRecord = null;
40
+ const completeWith = async (outcome) => {
41
+ lastRecord = unwrapTaskResult(await runTaskDomainAction(runtime, "completeTask", {
42
+ id: data.id,
43
+ runId,
44
+ outcome,
45
+ }));
46
+ terminal = true;
47
+ return lastRecord;
48
+ };
49
+ const releaseWith = async (input) => {
50
+ lastRecord = unwrapTaskResult(await runTaskDomainAction(runtime, "releaseTask", {
51
+ id: data.id,
52
+ runId,
53
+ actor: input?.actor,
54
+ comment: input?.comment,
55
+ }));
56
+ terminal = true;
57
+ return lastRecord;
58
+ };
59
+ await runTaskDomainAction(runtime, "startTask", {
60
+ id: data.id,
61
+ runId,
62
+ });
63
+ const run = {
64
+ id: runId,
65
+ taskId: data.id,
66
+ completed: completeWith,
67
+ release: releaseWith,
68
+ };
69
+ try {
70
+ const returned = await work(run);
71
+ if (returned) {
72
+ if (returned.state === "in_progress") {
73
+ throw new Error("Task.start callback returned an in_progress task. A started task must finish with completed outcome or release.");
74
+ }
75
+ return returned;
76
+ }
77
+ if (!terminal) {
78
+ throw new Error("Task.start callback finished without completing or releasing the task.");
79
+ }
80
+ if (!lastRecord) {
81
+ throw new Error("Task.start callback did not return a task record.");
82
+ }
83
+ return lastRecord;
84
+ }
85
+ catch (error) {
86
+ if (!terminal) {
87
+ try {
88
+ await runTaskDomainAction(runtime, "failTask", {
89
+ id: data.id,
90
+ runId,
91
+ errorText: errorText(error),
92
+ });
93
+ }
94
+ catch {
95
+ // Preserve the original runner failure.
96
+ }
97
+ }
98
+ throw error;
99
+ }
100
+ },
101
+ });
102
+ }
103
+ let ajvInstance = null;
104
+ function getAjv() {
105
+ if (!ajvInstance) {
106
+ ajvInstance = new Ajv({ allErrors: true, strict: false });
107
+ }
108
+ return ajvInstance;
109
+ }
110
+ function newEntityId() {
111
+ return instantId();
112
+ }
113
+ function normalizeTaskState(state) {
114
+ if (state === "open" ||
115
+ state === "completed" ||
116
+ state === "in_progress" ||
117
+ state === "cancelled" ||
118
+ state === "failed") {
119
+ return state;
120
+ }
121
+ return "open";
122
+ }
123
+ function rowToTaskRecord(row) {
124
+ return {
125
+ id: String(row.id),
126
+ kind: String(row.kind),
127
+ key: String(row.key),
128
+ state: normalizeTaskState(row.state),
129
+ instructions: String(row.instructions ?? ""),
130
+ context: row.context,
131
+ outcomeKind: typeof row.outcomeKind === "string" ? row.outcomeKind : undefined,
132
+ outcomeSchema: row.outcomeSchema,
133
+ resolvedOutcome: row.outcome,
134
+ activeRunId: typeof row.activeRunId === "string" ? row.activeRunId : undefined,
135
+ lastProgress: row.lastProgress,
136
+ errorText: typeof row.errorText === "string" ? row.errorText : undefined,
137
+ createdAt: row.createdAt,
138
+ updatedAt: row.updatedAt,
139
+ resolvedAt: row.resolvedAt,
140
+ };
141
+ }
142
+ function validateStoredOutcome(storedSchema, outcome) {
143
+ if (!storedSchema || storedSchema.type !== "json-schema") {
144
+ return { ok: false, error: "task_outcome_schema_missing" };
145
+ }
146
+ try {
147
+ const validate = getAjv().compile(storedSchema.schema);
148
+ if (!validate(outcome)) {
149
+ return {
150
+ ok: false,
151
+ error: "task_outcome_invalid",
152
+ issues: validate.errors ?? [],
153
+ };
154
+ }
155
+ }
156
+ catch (error) {
157
+ return {
158
+ ok: false,
159
+ error: "task_outcome_schema_invalid",
160
+ issues: error instanceof Error ? error.message : String(error),
161
+ };
162
+ }
163
+ return { ok: true, data: outcome };
164
+ }
165
+ async function resumeTaskOutcome(id, outcome) {
166
+ const { resumeHook } = await import("workflow/api");
167
+ try {
168
+ await resumeHook(taskOutcomeToken(id), outcome);
169
+ }
170
+ catch (error) {
171
+ if (error instanceof Error && error.name === "HookNotFoundError") {
172
+ return;
173
+ }
174
+ throw error;
175
+ }
176
+ }
177
+ async function resumeTaskFailure(id, error) {
178
+ const { resumeHook } = await import("workflow/api");
179
+ try {
180
+ await resumeHook(taskOutcomeToken(id), {
181
+ __ekairosTaskFailure: true,
182
+ error,
183
+ });
184
+ }
185
+ catch (error) {
186
+ if (error instanceof Error && error.name === "HookNotFoundError") {
187
+ return;
188
+ }
189
+ throw error;
190
+ }
191
+ }
192
+ function failureOutcomeError(task) {
193
+ if (task.state === "failed") {
194
+ return task.errorText || "task_failed";
195
+ }
196
+ if (task.state === "cancelled") {
197
+ return task.errorText || "task_cancelled";
198
+ }
199
+ return `task_not_open:${task.state}`;
200
+ }
201
+ function isFailureHookPayload(value) {
202
+ return !!value &&
203
+ typeof value === "object" &&
204
+ value.__ekairosTaskFailure === true;
205
+ }
206
+ function createTaskOutcomeHook(id) {
207
+ return createHook({
208
+ token: taskOutcomeToken(id),
209
+ });
210
+ }
211
+ export class TaskService {
212
+ constructor(runtime) {
213
+ this.runtime = runtime;
214
+ }
215
+ db() {
216
+ return this.runtime.db;
217
+ }
218
+ async getTaskRecord(id) {
219
+ const db = this.db();
220
+ const result = await db.query({
221
+ task_tasks: {
222
+ $: { where: { id }, limit: 1 },
223
+ },
224
+ });
225
+ const row = result?.task_tasks?.[0];
226
+ return row?.id ? rowToTaskRecord(row) : null;
227
+ }
228
+ async openRaw(input) {
229
+ if (!input.kind.trim())
230
+ return { ok: false, error: "task_kind_required" };
231
+ if (!input.key.trim())
232
+ return { ok: false, error: "task_key_required" };
233
+ if (!input.outcomeSchema)
234
+ return { ok: false, error: "task_outcome_schema_required" };
235
+ const db = this.db();
236
+ const existing = await db.query({
237
+ task_tasks: {
238
+ $: { where: { key: input.key }, limit: 1 },
239
+ },
240
+ });
241
+ const existingTask = existing?.task_tasks?.[0];
242
+ if (existingTask?.id) {
243
+ const existingSchema = existingTask.outcomeSchema;
244
+ if (!storedOutcomeSchemasEqual(existingSchema, input.outcomeSchema)) {
245
+ return { ok: false, error: "task_outcome_schema_mismatch" };
246
+ }
247
+ return { ok: true, data: rowToTaskRecord(existingTask) };
248
+ }
249
+ const now = new Date();
250
+ const id = input.id ?? newEntityId();
251
+ await db.transact([
252
+ db.tx.task_tasks[id].update({
253
+ kind: input.kind,
254
+ key: input.key,
255
+ state: "open",
256
+ instructions: input.instructions,
257
+ context: input.context,
258
+ outcomeKind: input.outcomeKind,
259
+ outcomeSchema: input.outcomeSchema,
260
+ createdAt: now,
261
+ updatedAt: now,
262
+ }),
263
+ ]);
264
+ const created = await this.getTaskRecord(id);
265
+ if (!created)
266
+ return { ok: false, error: "task_open_failed" };
267
+ return { ok: true, data: created };
268
+ }
269
+ async decide(input) {
270
+ if (!input.id.trim())
271
+ return { ok: false, error: "task_id_required" };
272
+ const task = await this.getTaskRecord(input.id);
273
+ if (!task)
274
+ return { ok: false, error: "task_not_found" };
275
+ if (task.state !== "open" && task.state !== "in_progress") {
276
+ return { ok: false, error: `task_not_decidable:${task.state}` };
277
+ }
278
+ if (task.state === "in_progress" && !input.runId?.trim()) {
279
+ return { ok: false, error: "task_run_id_required" };
280
+ }
281
+ if (task.state === "in_progress" && task.activeRunId !== input.runId) {
282
+ return { ok: false, error: "task_run_mismatch" };
283
+ }
284
+ const validation = validateStoredOutcome(task.outcomeSchema, input.outcome);
285
+ if (!validation.ok)
286
+ return validation;
287
+ const db = this.db();
288
+ const now = new Date();
289
+ await db.transact([
290
+ db.tx.task_tasks[input.id].update({
291
+ state: "completed",
292
+ activeRunId: "",
293
+ outcome: validation.data,
294
+ lastProgress: {
295
+ type: "completed",
296
+ runId: input.runId,
297
+ createdAt: now.toISOString(),
298
+ },
299
+ resolvedAt: now,
300
+ updatedAt: now,
301
+ }),
302
+ ]);
303
+ if (input.resumeWorkflow !== false) {
304
+ await resumeTaskOutcome(input.id, validation.data);
305
+ }
306
+ const completed = await this.getTaskRecord(input.id);
307
+ if (!completed)
308
+ return { ok: false, error: "task_not_found_after_decide" };
309
+ return { ok: true, data: completed };
310
+ }
311
+ async cancel(input) {
312
+ if (!input.id.trim())
313
+ return { ok: false, error: "task_id_required" };
314
+ const task = await this.getTaskRecord(input.id);
315
+ if (!task)
316
+ return { ok: false, error: "task_not_found" };
317
+ if (task.state !== "open" && task.state !== "in_progress") {
318
+ return { ok: false, error: `task_not_cancellable:${task.state}` };
319
+ }
320
+ if (task.state === "in_progress" && !input.runId?.trim()) {
321
+ return { ok: false, error: "task_run_id_required" };
322
+ }
323
+ if (task.state === "in_progress" && task.activeRunId !== input.runId) {
324
+ return { ok: false, error: "task_run_mismatch" };
325
+ }
326
+ const db = this.db();
327
+ const now = new Date();
328
+ await db.transact([
329
+ db.tx.task_tasks[input.id].update({
330
+ state: "cancelled",
331
+ activeRunId: "",
332
+ errorText: input.reason,
333
+ lastProgress: {
334
+ type: "cancelled",
335
+ runId: input.runId,
336
+ comment: input.reason,
337
+ createdAt: now.toISOString(),
338
+ },
339
+ resolvedAt: now,
340
+ updatedAt: now,
341
+ }),
342
+ ]);
343
+ const cancelled = await this.getTaskRecord(input.id);
344
+ if (!cancelled)
345
+ return { ok: false, error: "task_not_found_after_cancel" };
346
+ return { ok: true, data: cancelled };
347
+ }
348
+ async fail(input) {
349
+ if (!input.id.trim())
350
+ return { ok: false, error: "task_id_required" };
351
+ if (!input.errorText.trim())
352
+ return { ok: false, error: "task_error_text_required" };
353
+ const task = await this.getTaskRecord(input.id);
354
+ if (!task)
355
+ return { ok: false, error: "task_not_found" };
356
+ if (task.state !== "open" && task.state !== "in_progress") {
357
+ return { ok: false, error: `task_not_failable:${task.state}` };
358
+ }
359
+ if (task.state === "in_progress" && !input.runId?.trim()) {
360
+ return { ok: false, error: "task_run_id_required" };
361
+ }
362
+ if (task.state === "in_progress" && task.activeRunId !== input.runId) {
363
+ return { ok: false, error: "task_run_mismatch" };
364
+ }
365
+ const db = this.db();
366
+ const now = new Date();
367
+ await db.transact([
368
+ db.tx.task_tasks[input.id].update({
369
+ state: "failed",
370
+ activeRunId: "",
371
+ errorText: input.errorText,
372
+ lastProgress: {
373
+ type: "failed",
374
+ runId: input.runId,
375
+ comment: input.errorText,
376
+ createdAt: now.toISOString(),
377
+ },
378
+ resolvedAt: now,
379
+ updatedAt: now,
380
+ }),
381
+ ]);
382
+ await resumeTaskFailure(input.id, input.errorText);
383
+ const failed = await this.getTaskRecord(input.id);
384
+ if (!failed)
385
+ return { ok: false, error: "task_not_found_after_fail" };
386
+ return { ok: true, data: failed };
387
+ }
388
+ async getRaw(input) {
389
+ if (!input.id.trim())
390
+ return { ok: false, error: "task_id_required" };
391
+ const task = await this.getTaskRecord(input.id);
392
+ if (!task)
393
+ return { ok: false, error: "task_not_found" };
394
+ return { ok: true, data: task };
395
+ }
396
+ async awaitOutcome(input) {
397
+ if (!input.id.trim())
398
+ return { ok: false, error: "task_id_required" };
399
+ const task = await this.getTaskRecord(input.id);
400
+ if (!task)
401
+ return { ok: false, error: "task_not_found" };
402
+ if (task.state === "completed") {
403
+ return validateStoredOutcome(task.outcomeSchema, task.resolvedOutcome);
404
+ }
405
+ if (task.state === "failed" || task.state === "cancelled") {
406
+ return { ok: false, error: failureOutcomeError(task) };
407
+ }
408
+ if (task.state !== "open" && task.state !== "in_progress") {
409
+ return { ok: false, error: `task_not_open:${task.state}` };
410
+ }
411
+ const hook = createTaskOutcomeHook(input.id);
412
+ try {
413
+ const outcome = await hook;
414
+ if (isFailureHookPayload(outcome)) {
415
+ return { ok: false, error: String(outcome.error || "task_failed") };
416
+ }
417
+ return validateStoredOutcome(task.outcomeSchema, outcome);
418
+ }
419
+ finally {
420
+ hook.dispose();
421
+ }
422
+ }
423
+ async start(input) {
424
+ if (!input.id.trim())
425
+ return { ok: false, error: "task_id_required" };
426
+ if (!input.runId.trim())
427
+ return { ok: false, error: "task_run_id_required" };
428
+ const task = await this.getTaskRecord(input.id);
429
+ if (!task)
430
+ return { ok: false, error: "task_not_found" };
431
+ if (task.state !== "open") {
432
+ return { ok: false, error: `task_not_open:${task.state}` };
433
+ }
434
+ const db = this.db();
435
+ const now = new Date();
436
+ await db.transact([
437
+ db.tx.task_tasks[input.id].update({
438
+ state: "in_progress",
439
+ activeRunId: input.runId,
440
+ lastProgress: {
441
+ type: "started",
442
+ runId: input.runId,
443
+ createdAt: now.toISOString(),
444
+ },
445
+ updatedAt: now,
446
+ }),
447
+ ]);
448
+ const started = await this.getTaskRecord(input.id);
449
+ if (!started)
450
+ return { ok: false, error: "task_not_found_after_start" };
451
+ return { ok: true, data: started };
452
+ }
453
+ async release(input) {
454
+ if (!input.id.trim())
455
+ return { ok: false, error: "task_id_required" };
456
+ if (!input.runId.trim())
457
+ return { ok: false, error: "task_run_id_required" };
458
+ const task = await this.getTaskRecord(input.id);
459
+ if (!task)
460
+ return { ok: false, error: "task_not_found" };
461
+ if (task.state !== "in_progress") {
462
+ return { ok: false, error: `task_not_in_progress:${task.state}` };
463
+ }
464
+ if (task.activeRunId !== input.runId) {
465
+ return { ok: false, error: "task_run_mismatch" };
466
+ }
467
+ const db = this.db();
468
+ const now = new Date();
469
+ await db.transact([
470
+ db.tx.task_tasks[input.id].update({
471
+ state: "open",
472
+ activeRunId: "",
473
+ lastProgress: {
474
+ type: "released",
475
+ runId: input.runId,
476
+ actor: input.actor,
477
+ comment: input.comment?.trim() || undefined,
478
+ createdAt: now.toISOString(),
479
+ },
480
+ updatedAt: now,
481
+ }),
482
+ ]);
483
+ const released = await this.getTaskRecord(input.id);
484
+ if (!released)
485
+ return { ok: false, error: "task_not_found_after_release" };
486
+ return { ok: true, data: released };
487
+ }
488
+ }
489
+ export class Task {
490
+ static async create(runtime, input) {
491
+ return await Task.open(runtime, input);
492
+ }
493
+ static async open(runtime, input) {
494
+ const opened = unwrapTaskResult(await runTaskDomainAction(runtime, "createTask", {
495
+ id: input.id,
496
+ kind: input.kind,
497
+ key: input.key,
498
+ instructions: input.instructions,
499
+ context: input.context,
500
+ outcomeKind: input.outcomeKind,
501
+ outcomeSchema: toStoredOutcomeSchema(input.outcome),
502
+ }));
503
+ return createDomainTaskHandle(runtime, opened, input.outcome);
504
+ }
505
+ static async approval(runtime, input) {
506
+ return await Task.open(runtime, {
507
+ id: input.id,
508
+ kind: "approval",
509
+ key: input.key,
510
+ outcome: approvalOutcomeSchema,
511
+ instructions: input.instructions,
512
+ context: input.context,
513
+ outcomeKind: "approval",
514
+ });
515
+ }
516
+ static async get(runtime, id, outcomeSchema) {
517
+ const task = await getTaskRecordThroughDomain(runtime, id);
518
+ return createDomainTaskHandle(runtime, task, outcomeSchema);
519
+ }
520
+ }
521
+ //# sourceMappingURL=service.js.map