@hatchet-dev/typescript-sdk 1.21.2 → 1.22.0
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/clients/hatchet-client/client-config.d.ts +27 -133
- package/clients/hatchet-client/client-config.js +24 -24
- package/legacy/legacy-client.js +2 -2
- package/legacy/step.d.ts +22 -121
- package/legacy/step.js +7 -4
- package/legacy/workflow.d.ts +46 -420
- package/legacy/workflow.js +5 -5
- package/package.json +12 -6
- package/util/config-loader/config-loader.js +2 -2
- package/util/uuid.js +2 -2
- package/v1/agent/claude.d.ts +17 -0
- package/v1/agent/claude.js +83 -0
- package/v1/agent/openai.d.ts +4 -0
- package/v1/agent/openai.js +83 -0
- package/v1/client/client.js +2 -2
- package/v1/client/features/crons.d.ts +3 -15
- package/v1/client/features/crons.js +8 -8
- package/v1/client/features/schedules.d.ts +6 -20
- package/v1/client/features/schedules.js +10 -10
- package/v1/client/worker/context.d.ts +1 -1
- package/v1/client/worker/worker-internal.js +2 -2
- package/v1/declaration.d.ts +18 -1
- package/v1/declaration.js +10 -0
- package/v1/examples/agent/agent-claude.d.ts +1 -0
- package/v1/examples/agent/agent-claude.js +68 -0
- package/v1/examples/agent/agent-openai.d.ts +1 -0
- package/v1/examples/agent/agent-openai.js +72 -0
- package/v1/examples/agent/worker.d.ts +1 -0
- package/v1/examples/agent/worker.js +24 -0
- package/v1/examples/agent/workflow.d.ts +26 -0
- package/v1/examples/agent/workflow.js +48 -0
- package/v1/examples/durable/workflow.d.ts +6 -2
- package/v1/examples/durable/workflow.js +9 -6
- package/v1/examples/durable_eviction/workflow.js +6 -0
- package/v1/examples/e2e-worker.js +7 -0
- package/v1/examples/simple/run.js +5 -0
- package/v1/examples/simple/zod.d.ts +2 -6
- package/v1/examples/simple/zod.js +1 -1
- package/v1/examples/support_agent/run.d.ts +1 -0
- package/v1/examples/support_agent/run.js +42 -0
- package/v1/examples/support_agent/workflow.d.ts +43 -0
- package/v1/examples/support_agent/workflow.js +140 -0
- package/v1/examples/welcome_email/run.d.ts +1 -0
- package/v1/examples/welcome_email/run.js +40 -0
- package/v1/examples/welcome_email/workflow.d.ts +11 -0
- package/v1/examples/welcome_email/workflow.js +52 -0
- package/version.d.ts +1 -1
- package/version.js +1 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.getTemperatureWorkflow = exports.getTemperature = void 0;
|
|
13
|
+
// > Declaring a Task
|
|
14
|
+
const hatchet_client_1 = require("../hatchet-client");
|
|
15
|
+
const v4_1 = require("zod/v4");
|
|
16
|
+
// Note that for agent tools, Zod must be used to create the input and output types for workflows/tasks
|
|
17
|
+
const TemperatureCoordinates = v4_1.z.object({
|
|
18
|
+
latitude: v4_1.z.number(),
|
|
19
|
+
longitude: v4_1.z.number(),
|
|
20
|
+
});
|
|
21
|
+
const TemperatureInput = v4_1.z.object({
|
|
22
|
+
locationName: v4_1.z.string(),
|
|
23
|
+
coords: TemperatureCoordinates,
|
|
24
|
+
});
|
|
25
|
+
const temperatureRequest = (input) => __awaiter(void 0, void 0, void 0, function* () {
|
|
26
|
+
const response = yield fetch(`https://api.open-meteo.com/v1/forecast?latitude=${input.coords.latitude}&longitude=${input.coords.longitude}¤t=temperature_2m&temperature_unit=fahrenheit`);
|
|
27
|
+
const data = yield response.json();
|
|
28
|
+
return {
|
|
29
|
+
text: `Temperature: ${data.current.temperature_2m}°F`,
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
exports.getTemperature = hatchet_client_1.hatchet.task({
|
|
33
|
+
name: 'getTemperature',
|
|
34
|
+
retries: 3,
|
|
35
|
+
fn: temperatureRequest,
|
|
36
|
+
inputValidator: TemperatureInput,
|
|
37
|
+
description: 'Get the current temperature at a location',
|
|
38
|
+
});
|
|
39
|
+
exports.getTemperatureWorkflow = hatchet_client_1.hatchet.workflow({
|
|
40
|
+
name: 'getTemperatureWorkflow',
|
|
41
|
+
inputValidator: TemperatureInput,
|
|
42
|
+
description: 'Get the current temperature at a location',
|
|
43
|
+
});
|
|
44
|
+
exports.getTemperatureWorkflow.task({
|
|
45
|
+
name: 'getTemperature',
|
|
46
|
+
fn: temperatureRequest,
|
|
47
|
+
});
|
|
48
|
+
// !!
|
|
@@ -71,8 +71,12 @@ export declare const waitForTwoEventsSecondPushedFirst: import("../..").TaskWork
|
|
|
71
71
|
scope: string;
|
|
72
72
|
}, {
|
|
73
73
|
elapsed: number;
|
|
74
|
-
event1:
|
|
75
|
-
|
|
74
|
+
event1: {
|
|
75
|
+
order: string;
|
|
76
|
+
};
|
|
77
|
+
event2: {
|
|
78
|
+
order: string;
|
|
79
|
+
};
|
|
76
80
|
}, {}, {}, {}, {}>;
|
|
77
81
|
export declare const memoNowCaching: import("../..").TaskWorkflowDeclaration<import("../..").JsonObject, {
|
|
78
82
|
start_time: string;
|
|
@@ -13,7 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.durableSpawnDag = exports.dagChildWorkflow = exports.memoNowCaching = exports.waitForTwoEventsSecondPushedFirst = exports.waitForOrEventLookback = exports.waitForEventLookback = exports.LOOKBACK_WINDOW = exports.durableReplayReset = exports.REPLAY_RESET_MEMOIZED_MAX_SECONDS = exports.REPLAY_RESET_SLEEP_SECONDS = exports.durableNonDeterminism = exports.durableWithExplicitSpawn = exports.durableSleepEventSpawn = exports.durableWithBulkSpawn = exports.durableWithSpawn = exports.spawnChildTask = exports.waitForSleepTwice = exports.durableWorkflow = exports.SLEEP_TIME = exports.SLEEP_TIME_SECONDS = exports.EVENT_KEY = void 0;
|
|
16
|
-
const
|
|
16
|
+
const v4_1 = require("zod/v4");
|
|
17
17
|
const conditions_1 = require("../../conditions");
|
|
18
18
|
const non_determinism_error_1 = require("../../../util/errors/non-determinism-error");
|
|
19
19
|
const sleep_1 = __importDefault(require("../../../util/sleep"));
|
|
@@ -226,9 +226,12 @@ exports.durableReplayReset = hatchet_client_1.hatchet.durableTask({
|
|
|
226
226
|
}),
|
|
227
227
|
});
|
|
228
228
|
exports.LOOKBACK_WINDOW = '1m';
|
|
229
|
-
const lookbackEventPayloadSchema =
|
|
230
|
-
order:
|
|
231
|
-
user_id:
|
|
229
|
+
const lookbackEventPayloadSchema = v4_1.z.object({
|
|
230
|
+
order: v4_1.z.string(),
|
|
231
|
+
user_id: v4_1.z.number(),
|
|
232
|
+
});
|
|
233
|
+
const twoEventsPayloadSchema = v4_1.z.object({
|
|
234
|
+
order: v4_1.z.string(),
|
|
232
235
|
});
|
|
233
236
|
exports.waitForEventLookback = hatchet_client_1.hatchet.durableTask({
|
|
234
237
|
name: 'wait-for-event-lookback',
|
|
@@ -262,8 +265,8 @@ exports.waitForTwoEventsSecondPushedFirst = hatchet_client_1.hatchet.durableTask
|
|
|
262
265
|
executionTimeout: '10m',
|
|
263
266
|
fn: (input, ctx) => __awaiter(void 0, void 0, void 0, function* () {
|
|
264
267
|
const start = Date.now();
|
|
265
|
-
const event1 = yield ctx.waitForEvent('key1', undefined,
|
|
266
|
-
const event2 = yield ctx.waitForEvent('key2', undefined,
|
|
268
|
+
const event1 = yield ctx.waitForEvent('key1', undefined, twoEventsPayloadSchema, input.scope, exports.LOOKBACK_WINDOW);
|
|
269
|
+
const event2 = yield ctx.waitForEvent('key2', undefined, twoEventsPayloadSchema, input.scope, exports.LOOKBACK_WINDOW);
|
|
267
270
|
return {
|
|
268
271
|
elapsed: (Date.now() - start) / 1000,
|
|
269
272
|
event1,
|
|
@@ -18,11 +18,13 @@ const hatchet_client_1 = require("../hatchet-client");
|
|
|
18
18
|
exports.EVICTION_TTL_SECONDS = 5;
|
|
19
19
|
exports.LONG_SLEEP_SECONDS = 15;
|
|
20
20
|
exports.EVENT_KEY = 'durable-eviction:event';
|
|
21
|
+
// > Eviction Policy
|
|
21
22
|
const EVICTION_POLICY = {
|
|
22
23
|
ttl: `${exports.EVICTION_TTL_SECONDS}s`,
|
|
23
24
|
allowCapacityEviction: true,
|
|
24
25
|
priority: 0,
|
|
25
26
|
};
|
|
27
|
+
// !!
|
|
26
28
|
exports.childTask = hatchet_client_1.hatchet.task({
|
|
27
29
|
name: 'eviction-child-task',
|
|
28
30
|
fn: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -30,6 +32,7 @@ exports.childTask = hatchet_client_1.hatchet.task({
|
|
|
30
32
|
return { child_status: 'completed' };
|
|
31
33
|
}),
|
|
32
34
|
});
|
|
35
|
+
// > Evictable Sleep
|
|
33
36
|
exports.evictableSleep = hatchet_client_1.hatchet.durableTask({
|
|
34
37
|
name: 'evictable-sleep',
|
|
35
38
|
executionTimeout: '5m',
|
|
@@ -39,6 +42,7 @@ exports.evictableSleep = hatchet_client_1.hatchet.durableTask({
|
|
|
39
42
|
return { status: 'completed' };
|
|
40
43
|
}),
|
|
41
44
|
});
|
|
45
|
+
// !!
|
|
42
46
|
// NOTE: DO NOT REGISTER ON E2E TEST WORKER
|
|
43
47
|
exports.evictableSleepForGracefulTermination = hatchet_client_1.hatchet.durableTask({
|
|
44
48
|
name: 'evictable-sleep-for-graceful-termination',
|
|
@@ -114,6 +118,7 @@ exports.capacityEvictableSleep = hatchet_client_1.hatchet.durableTask({
|
|
|
114
118
|
return { status: 'completed' };
|
|
115
119
|
}),
|
|
116
120
|
});
|
|
121
|
+
// > Non Evictable Sleep
|
|
117
122
|
exports.nonEvictableSleep = hatchet_client_1.hatchet.durableTask({
|
|
118
123
|
name: 'non-evictable-sleep',
|
|
119
124
|
executionTimeout: '5m',
|
|
@@ -127,3 +132,4 @@ exports.nonEvictableSleep = hatchet_client_1.hatchet.durableTask({
|
|
|
127
132
|
return { status: 'completed' };
|
|
128
133
|
}),
|
|
129
134
|
});
|
|
135
|
+
// !!
|
|
@@ -40,6 +40,8 @@ const workflow_18 = require("./streaming/workflow");
|
|
|
40
40
|
const workflow_19 = require("./timeout/workflow");
|
|
41
41
|
const workflow_20 = require("./webhooks/workflow");
|
|
42
42
|
const workflow_21 = require("./child_index/workflow");
|
|
43
|
+
const workflow_22 = require("./support_agent/workflow");
|
|
44
|
+
const workflow_23 = require("./welcome_email/workflow");
|
|
43
45
|
const workflows = [
|
|
44
46
|
workflow_1.bulkChild,
|
|
45
47
|
workflow_1.bulkParentWorkflow,
|
|
@@ -94,6 +96,11 @@ const workflows = [
|
|
|
94
96
|
workflow_21.childIndexParent,
|
|
95
97
|
workflow_21.scenarioTask,
|
|
96
98
|
workflow_21.orchestratorTask,
|
|
99
|
+
workflow_22.supportAgent,
|
|
100
|
+
workflow_22.triageTicket,
|
|
101
|
+
workflow_22.generateReply,
|
|
102
|
+
workflow_22.escalateTicket,
|
|
103
|
+
workflow_23.welcomeEmail,
|
|
97
104
|
];
|
|
98
105
|
function main() {
|
|
99
106
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -13,6 +13,7 @@ exports.extra = extra;
|
|
|
13
13
|
const hatchet_client_1 = require("../hatchet-client");
|
|
14
14
|
const workflow_1 = require("./workflow");
|
|
15
15
|
const workflow_with_child_1 = require("./workflow-with-child");
|
|
16
|
+
const zod_1 = require("./zod");
|
|
16
17
|
function main() {
|
|
17
18
|
return __awaiter(this, void 0, void 0, function* () {
|
|
18
19
|
// > Running a Task
|
|
@@ -23,6 +24,10 @@ function main() {
|
|
|
23
24
|
test: 'test',
|
|
24
25
|
},
|
|
25
26
|
});
|
|
27
|
+
const res3 = yield zod_1.simpleWithZod.run({
|
|
28
|
+
Message: 'HeLlO WoRlD',
|
|
29
|
+
});
|
|
30
|
+
console.log(res3.TransformedMessage);
|
|
26
31
|
// 👀 Access the results of the Task
|
|
27
32
|
console.log(res.TransformedMessage);
|
|
28
33
|
// !!
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import * as z from 'zod';
|
|
1
|
+
import * as z from 'zod/v4';
|
|
2
2
|
declare const SimpleInputSchema: z.ZodObject<{
|
|
3
3
|
Message: z.ZodString;
|
|
4
|
-
},
|
|
5
|
-
Message: string;
|
|
6
|
-
}, {
|
|
7
|
-
Message: string;
|
|
8
|
-
}>;
|
|
4
|
+
}, z.core.$strip>;
|
|
9
5
|
export type SimpleInputWithZod = z.infer<typeof SimpleInputSchema>;
|
|
10
6
|
export declare const simpleWithZod: import("../..").TaskWorkflowDeclaration<{
|
|
11
7
|
Message: string;
|
|
@@ -44,7 +44,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
45
|
exports.simpleWithZod = void 0;
|
|
46
46
|
// > Declaring a Task
|
|
47
|
-
const z = __importStar(require("zod"));
|
|
47
|
+
const z = __importStar(require("zod/v4"));
|
|
48
48
|
const hatchet_client_1 = require("../hatchet-client");
|
|
49
49
|
const SimpleInputSchema = z.object({
|
|
50
50
|
Message: z.string(),
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const hatchet_client_1 = require("../hatchet-client");
|
|
13
|
+
const workflow_1 = require("./workflow");
|
|
14
|
+
// > Trigger the workflow
|
|
15
|
+
function main() {
|
|
16
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
17
|
+
const input = {
|
|
18
|
+
ticketId: 'ticket-42',
|
|
19
|
+
customerEmail: 'alice@example.com',
|
|
20
|
+
subject: 'Login broken',
|
|
21
|
+
body: "I can't log in since this morning.",
|
|
22
|
+
};
|
|
23
|
+
// Start the support agent workflow
|
|
24
|
+
const ref = yield workflow_1.supportAgent.runNoWait(input);
|
|
25
|
+
const runId = yield ref.getWorkflowRunId();
|
|
26
|
+
console.log(`Started workflow run: ${runId}`);
|
|
27
|
+
// Push a customer reply event (scoped to this ticket)
|
|
28
|
+
console.log('Pushing customer reply event...');
|
|
29
|
+
yield hatchet_client_1.hatchet.events.push(workflow_1.REPLY_EVENT_KEY, { message: 'I cleared my cookies and it works now. Thanks!' }, { scope: input.ticketId });
|
|
30
|
+
// Wait for the workflow to complete
|
|
31
|
+
const result = yield ref.output;
|
|
32
|
+
console.log('Workflow completed:', result);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
// !!
|
|
36
|
+
if (require.main === module) {
|
|
37
|
+
main()
|
|
38
|
+
.catch(console.error)
|
|
39
|
+
.finally(() => {
|
|
40
|
+
process.exit(0);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export declare const REPLY_EVENT_KEY = "support:customer-reply";
|
|
2
|
+
export declare const TIMEOUT_SECONDS = 5;
|
|
3
|
+
export type SupportTicketInput = {
|
|
4
|
+
ticketId: string;
|
|
5
|
+
customerEmail: string;
|
|
6
|
+
subject: string;
|
|
7
|
+
body: string;
|
|
8
|
+
};
|
|
9
|
+
export type TriageOutput = {
|
|
10
|
+
category: string;
|
|
11
|
+
priority: string;
|
|
12
|
+
};
|
|
13
|
+
export type ReplyOutput = {
|
|
14
|
+
message: string;
|
|
15
|
+
};
|
|
16
|
+
export type EscalationOutput = {
|
|
17
|
+
reason: string;
|
|
18
|
+
assignedTo: string;
|
|
19
|
+
};
|
|
20
|
+
export declare const triageTicket: import("../..").TaskWorkflowDeclaration<SupportTicketInput, {
|
|
21
|
+
category: string;
|
|
22
|
+
priority: string;
|
|
23
|
+
}, {}, {}, {}, {}>;
|
|
24
|
+
export declare const generateReply: import("../..").TaskWorkflowDeclaration<SupportTicketInput, {
|
|
25
|
+
message: any;
|
|
26
|
+
}, {}, {}, {}, {}>;
|
|
27
|
+
export declare const escalateTicket: import("../..").TaskWorkflowDeclaration<SupportTicketInput, {
|
|
28
|
+
reason: string;
|
|
29
|
+
assignedTo: string;
|
|
30
|
+
}, {}, {}, {}, {}>;
|
|
31
|
+
export declare const supportAgent: import("../..").TaskWorkflowDeclaration<SupportTicketInput, {
|
|
32
|
+
ticketId: string;
|
|
33
|
+
status: "escalated";
|
|
34
|
+
triageCategory: string;
|
|
35
|
+
triagePriority: string;
|
|
36
|
+
initialReply: any;
|
|
37
|
+
} | {
|
|
38
|
+
ticketId: string;
|
|
39
|
+
status: "resolved";
|
|
40
|
+
triageCategory: string;
|
|
41
|
+
triagePriority: string;
|
|
42
|
+
initialReply: any;
|
|
43
|
+
}, {}, {}, {}, {}>;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.supportAgent = exports.escalateTicket = exports.generateReply = exports.triageTicket = exports.TIMEOUT_SECONDS = exports.REPLY_EVENT_KEY = void 0;
|
|
13
|
+
const conditions_1 = require("../../conditions");
|
|
14
|
+
const duration_1 = require("../../client/duration");
|
|
15
|
+
const hatchet_client_1 = require("../hatchet-client");
|
|
16
|
+
exports.REPLY_EVENT_KEY = 'support:customer-reply';
|
|
17
|
+
const REPLY_LABEL = 'reply';
|
|
18
|
+
const TIMEOUT_LABEL = 'timeout';
|
|
19
|
+
exports.TIMEOUT_SECONDS = 5;
|
|
20
|
+
const LOOKBACK_WINDOW = '5m';
|
|
21
|
+
// !!
|
|
22
|
+
// > Triage task
|
|
23
|
+
// Classify the ticket into a category and priority.
|
|
24
|
+
exports.triageTicket = hatchet_client_1.hatchet.task({
|
|
25
|
+
name: 'triage-ticket',
|
|
26
|
+
fn: (input) => __awaiter(void 0, void 0, void 0, function* () {
|
|
27
|
+
const text = `${input.subject} ${input.body}`.toLowerCase();
|
|
28
|
+
let category;
|
|
29
|
+
if (['bill', 'charge', 'payment', 'invoice'].some((w) => text.includes(w))) {
|
|
30
|
+
category = 'billing';
|
|
31
|
+
}
|
|
32
|
+
else if (['login', 'password', 'auth', 'access'].some((w) => text.includes(w))) {
|
|
33
|
+
category = 'account';
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
category = 'technical';
|
|
37
|
+
}
|
|
38
|
+
let priority;
|
|
39
|
+
if (['urgent', 'critical', 'down', 'outage'].some((w) => text.includes(w))) {
|
|
40
|
+
priority = 'high';
|
|
41
|
+
}
|
|
42
|
+
else if (['twice', 'broken', 'error'].some((w) => text.includes(w))) {
|
|
43
|
+
priority = 'medium';
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
priority = 'low';
|
|
47
|
+
}
|
|
48
|
+
return { category, priority };
|
|
49
|
+
}),
|
|
50
|
+
});
|
|
51
|
+
// !!
|
|
52
|
+
// > Generate reply task
|
|
53
|
+
// Generate an initial support reply using Claude.
|
|
54
|
+
exports.generateReply = hatchet_client_1.hatchet.task({
|
|
55
|
+
name: 'generate-reply',
|
|
56
|
+
fn: (input) => __awaiter(void 0, void 0, void 0, function* () {
|
|
57
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
58
|
+
if (!apiKey) {
|
|
59
|
+
return {
|
|
60
|
+
message: `Thank you for contacting support about: ${input.subject}. We are looking into this and will get back to you shortly.`,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
64
|
+
const anthropic = require('@anthropic-ai/sdk');
|
|
65
|
+
const Anthropic = anthropic.default || anthropic;
|
|
66
|
+
const client = new Anthropic({ apiKey });
|
|
67
|
+
const response = yield client.messages.create({
|
|
68
|
+
model: 'claude-sonnet-4-20250514',
|
|
69
|
+
max_tokens: 300,
|
|
70
|
+
messages: [
|
|
71
|
+
{
|
|
72
|
+
role: 'user',
|
|
73
|
+
content: `You are a friendly support agent. Write a brief, helpful initial ` +
|
|
74
|
+
`reply to this support ticket.\n\n` +
|
|
75
|
+
`Subject: ${input.subject}\n` +
|
|
76
|
+
`Message: ${input.body}\n\n` +
|
|
77
|
+
`Keep the reply under 3 sentences.`,
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
});
|
|
81
|
+
const [block] = response.content;
|
|
82
|
+
const text = (block === null || block === void 0 ? void 0 : block.type) === 'text' ? block.text : '';
|
|
83
|
+
return { message: text };
|
|
84
|
+
}),
|
|
85
|
+
});
|
|
86
|
+
// !!
|
|
87
|
+
// > Escalate task
|
|
88
|
+
// Escalate an unresolved ticket to the human support team.
|
|
89
|
+
exports.escalateTicket = hatchet_client_1.hatchet.task({
|
|
90
|
+
name: 'escalate-ticket',
|
|
91
|
+
fn: (input) => __awaiter(void 0, void 0, void 0, function* () {
|
|
92
|
+
return {
|
|
93
|
+
reason: `No customer reply within ${exports.TIMEOUT_SECONDS}s timeout`,
|
|
94
|
+
assignedTo: 'support-team@example.com',
|
|
95
|
+
};
|
|
96
|
+
}),
|
|
97
|
+
});
|
|
98
|
+
// !!
|
|
99
|
+
// > Support agent workflow
|
|
100
|
+
exports.supportAgent = hatchet_client_1.hatchet.durableTask({
|
|
101
|
+
name: 'support-agent',
|
|
102
|
+
executionTimeout: '10m',
|
|
103
|
+
fn: (input, ctx) => __awaiter(void 0, void 0, void 0, function* () {
|
|
104
|
+
var _a, _b;
|
|
105
|
+
// Step 1: Triage the ticket
|
|
106
|
+
const triage = yield exports.triageTicket.run(input);
|
|
107
|
+
// Step 2: Generate an initial reply
|
|
108
|
+
const reply = yield exports.generateReply.run(input);
|
|
109
|
+
// Step 3: Wait for a customer reply or timeout
|
|
110
|
+
const now = yield ctx.now();
|
|
111
|
+
const considerEventsSince = new Date(now.getTime() - (0, duration_1.durationToMs)(LOOKBACK_WINDOW)).toISOString();
|
|
112
|
+
const waitResult = yield ctx.waitFor((0, conditions_1.Or)(new conditions_1.SleepCondition(`${exports.TIMEOUT_SECONDS}s`, TIMEOUT_LABEL), new conditions_1.UserEventCondition(exports.REPLY_EVENT_KEY, '', REPLY_LABEL, undefined, input.ticketId, considerEventsSince)));
|
|
113
|
+
// Determine which condition fired. ctx.waitFor returns
|
|
114
|
+
// { CREATE: { <label>: ... } } where <label> is the readableDataKey
|
|
115
|
+
// we assigned above ('timeout' or 'reply').
|
|
116
|
+
const create = (_a = waitResult['CREATE']) !== null && _a !== void 0 ? _a : waitResult;
|
|
117
|
+
const resolvedLabel = (_b = Object.keys(create)[0]) !== null && _b !== void 0 ? _b : '';
|
|
118
|
+
const customerReplied = resolvedLabel === REPLY_LABEL;
|
|
119
|
+
if (!customerReplied) {
|
|
120
|
+
// Step 4a: Timeout -> escalate
|
|
121
|
+
yield exports.escalateTicket.run(input);
|
|
122
|
+
return {
|
|
123
|
+
ticketId: input.ticketId,
|
|
124
|
+
status: 'escalated',
|
|
125
|
+
triageCategory: triage.category,
|
|
126
|
+
triagePriority: triage.priority,
|
|
127
|
+
initialReply: reply.message,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
// Step 4b: Customer replied -> resolve
|
|
131
|
+
return {
|
|
132
|
+
ticketId: input.ticketId,
|
|
133
|
+
status: 'resolved',
|
|
134
|
+
triageCategory: triage.category,
|
|
135
|
+
triagePriority: triage.priority,
|
|
136
|
+
initialReply: reply.message,
|
|
137
|
+
};
|
|
138
|
+
}),
|
|
139
|
+
});
|
|
140
|
+
// !!
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
// > Trigger the workflow
|
|
13
|
+
const hatchet_client_1 = require("../hatchet-client");
|
|
14
|
+
const workflow_1 = require("./workflow");
|
|
15
|
+
function main() {
|
|
16
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
17
|
+
const input = {
|
|
18
|
+
email: 'alice@example.com',
|
|
19
|
+
user_id: 'user-123',
|
|
20
|
+
};
|
|
21
|
+
// Start the welcome-email workflow
|
|
22
|
+
const ref = yield workflow_1.welcomeEmail.runNoWait(input);
|
|
23
|
+
const runId = yield ref.getWorkflowRunId();
|
|
24
|
+
console.log(`Started workflow run: ${runId}`);
|
|
25
|
+
// Push onboarding-completed event (scoped to this user)
|
|
26
|
+
console.log('Pushing onboarding-completed event...');
|
|
27
|
+
yield hatchet_client_1.hatchet.events.push(workflow_1.ONBOARDING_EVENT_KEY, { status: 'done' }, { scope: input.user_id });
|
|
28
|
+
// Wait for the workflow to complete
|
|
29
|
+
const result = yield ref.output;
|
|
30
|
+
console.log('Workflow completed:', result);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
// !!
|
|
34
|
+
if (require.main === module) {
|
|
35
|
+
main()
|
|
36
|
+
.catch(console.error)
|
|
37
|
+
.finally(() => {
|
|
38
|
+
process.exit(0);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const ONBOARDING_EVENT_KEY = "user:onboarding-completed";
|
|
2
|
+
export type SignupInput = {
|
|
3
|
+
email: string;
|
|
4
|
+
user_id: string;
|
|
5
|
+
};
|
|
6
|
+
export type WelcomeEmailResult = {
|
|
7
|
+
userId: string;
|
|
8
|
+
welcomeSent: boolean;
|
|
9
|
+
followUpSent: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare const welcomeEmail: import("../..").TaskWorkflowDeclaration<SignupInput, WelcomeEmailResult, {}, {}, {}, {}>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.welcomeEmail = exports.ONBOARDING_EVENT_KEY = void 0;
|
|
13
|
+
const conditions_1 = require("../../conditions");
|
|
14
|
+
const duration_1 = require("../../client/duration");
|
|
15
|
+
const hatchet_client_1 = require("../hatchet-client");
|
|
16
|
+
exports.ONBOARDING_EVENT_KEY = 'user:onboarding-completed';
|
|
17
|
+
const TIMEOUT_SECONDS = 5;
|
|
18
|
+
const LOOKBACK_WINDOW = '5m';
|
|
19
|
+
// !!
|
|
20
|
+
// > Welcome email task
|
|
21
|
+
exports.welcomeEmail = hatchet_client_1.hatchet.durableTask({
|
|
22
|
+
name: 'welcome-email',
|
|
23
|
+
onEvents: ['user:signup'],
|
|
24
|
+
executionTimeout: '5m',
|
|
25
|
+
fn: (input, ctx) => __awaiter(void 0, void 0, void 0, function* () {
|
|
26
|
+
var _a, _b;
|
|
27
|
+
// Step 1: Send the welcome email
|
|
28
|
+
console.log(`Sending welcome email to ${input.email}: finish your first onboarding step`);
|
|
29
|
+
// Step 2: Wait for the user to complete onboarding, or time out
|
|
30
|
+
// (use a longer duration for a more realistic workflow)
|
|
31
|
+
const now = yield ctx.now();
|
|
32
|
+
const considerEventsSince = new Date(now.getTime() - (0, duration_1.durationToMs)(LOOKBACK_WINDOW)).toISOString();
|
|
33
|
+
const waitResult = yield ctx.waitFor((0, conditions_1.Or)({ sleepFor: `${TIMEOUT_SECONDS}s` },
|
|
34
|
+
// Scope the event condition to this user so that another user's
|
|
35
|
+
// onboarding-completed event does not resolve this wait.
|
|
36
|
+
{ eventKey: exports.ONBOARDING_EVENT_KEY, scope: input.user_id, considerEventsSince }));
|
|
37
|
+
// The or-group result is { CREATE: { <condition_key>: ... } }.
|
|
38
|
+
// Check whether the onboarding event was the one that resolved.
|
|
39
|
+
const create = (_a = waitResult['CREATE']) !== null && _a !== void 0 ? _a : waitResult;
|
|
40
|
+
const resolvedKey = (_b = Object.keys(create)[0]) !== null && _b !== void 0 ? _b : '';
|
|
41
|
+
const onboardingCompleted = resolvedKey === exports.ONBOARDING_EVENT_KEY;
|
|
42
|
+
if (onboardingCompleted) {
|
|
43
|
+
// Step 3a: User completed onboarding -> skip follow-up
|
|
44
|
+
console.log(`User ${input.user_id} completed onboarding, skipping follow-up`);
|
|
45
|
+
return { userId: input.user_id, welcomeSent: true, followUpSent: false };
|
|
46
|
+
}
|
|
47
|
+
// Step 3b: Timeout -> send follow-up email
|
|
48
|
+
console.log(`Sending follow-up email to ${input.email}: need help finishing onboarding?`);
|
|
49
|
+
return { userId: input.user_id, welcomeSent: true, followUpSent: true };
|
|
50
|
+
}),
|
|
51
|
+
});
|
|
52
|
+
// !!
|
package/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const HATCHET_VERSION = "1.
|
|
1
|
+
export declare const HATCHET_VERSION = "1.22.0";
|
package/version.js
CHANGED