@happyrobot-ai/sdk 0.1.8 → 0.1.9

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.
@@ -10,5 +10,7 @@ export { createVoiceAgent } from "./voice-agent";
10
10
  export type { CreateVoiceAgentOptions, CreateVoiceAgentResult } from "./voice-agent";
11
11
  export { triggerAndWait } from "./trigger-and-wait";
12
12
  export type { TriggerAndWaitOptions, TriggerAndWaitResult } from "./trigger-and-wait";
13
+ export { triggerAndWaitForNodeOutput } from "./trigger-and-wait-for-node-output";
14
+ export type { TriggerAndWaitForNodeOutputOptions, TriggerAndWaitForNodeOutputResult, } from "./trigger-and-wait-for-node-output";
13
15
  export { createFromTemplate } from "./template-workflows";
14
16
  export type { CreateFromTemplateOptions, CreateFromTemplateResult } from "./template-workflows";
package/helpers/index.js CHANGED
@@ -8,11 +8,13 @@
8
8
  * ```
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
- exports.createFromTemplate = exports.triggerAndWait = exports.createVoiceAgent = void 0;
11
+ exports.createFromTemplate = exports.triggerAndWaitForNodeOutput = exports.triggerAndWait = exports.createVoiceAgent = void 0;
12
12
  var voice_agent_1 = require("./voice-agent");
13
13
  Object.defineProperty(exports, "createVoiceAgent", { enumerable: true, get: function () { return voice_agent_1.createVoiceAgent; } });
14
14
  var trigger_and_wait_1 = require("./trigger-and-wait");
15
15
  Object.defineProperty(exports, "triggerAndWait", { enumerable: true, get: function () { return trigger_and_wait_1.triggerAndWait; } });
16
+ var trigger_and_wait_for_node_output_1 = require("./trigger-and-wait-for-node-output");
17
+ Object.defineProperty(exports, "triggerAndWaitForNodeOutput", { enumerable: true, get: function () { return trigger_and_wait_for_node_output_1.triggerAndWaitForNodeOutput; } });
16
18
  var template_workflows_1 = require("./template-workflows");
17
19
  Object.defineProperty(exports, "createFromTemplate", { enumerable: true, get: function () { return template_workflows_1.createFromTemplate; } });
18
20
  //# sourceMappingURL=index.js.map
package/helpers/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  import * as _cjs from "./index.js";
2
- const { createVoiceAgent, triggerAndWait, createFromTemplate } = _cjs;
3
- export { createVoiceAgent, triggerAndWait, createFromTemplate };
2
+ const { createVoiceAgent, triggerAndWait, triggerAndWaitForNodeOutput, createFromTemplate } = _cjs;
3
+ export { createVoiceAgent, triggerAndWait, triggerAndWaitForNodeOutput, createFromTemplate };
@@ -0,0 +1,18 @@
1
+ import type { HappyRobotClient } from "../client";
2
+ import type { RunNodeOutput } from "../types/runs.types";
3
+ export interface TriggerAndWaitForNodeOutputOptions {
4
+ workflowId: string;
5
+ payload?: Record<string, unknown>;
6
+ environment?: "production" | "staging" | "development";
7
+ nodePersistentId: string;
8
+ timeoutMs?: number;
9
+ pollIntervalMs?: number;
10
+ }
11
+ export interface TriggerAndWaitForNodeOutputResult {
12
+ ok: boolean;
13
+ runId: string;
14
+ status: string;
15
+ nodeOutput: RunNodeOutput;
16
+ completedAt: string | null;
17
+ }
18
+ export declare function triggerAndWaitForNodeOutput(client: HappyRobotClient, options: TriggerAndWaitForNodeOutputOptions): Promise<TriggerAndWaitForNodeOutputResult>;
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.triggerAndWaitForNodeOutput = triggerAndWaitForNodeOutput;
4
+ const errors_1 = require("../core/errors");
5
+ const node_output_1 = require("@/shared/constants/node-output");
6
+ const RUN_TERMINAL_STATUS_SET = new Set(node_output_1.RUN_TERMINAL_STATUSES);
7
+ const NODE_READY_STATUS_SET = new Set(node_output_1.NODE_READY_STATUSES);
8
+ async function triggerAndWaitForNodeOutput(client, options) {
9
+ const { workflowId, payload, environment = "production", nodePersistentId, timeoutMs = 300_000, pollIntervalMs = 2_000, } = options;
10
+ const triggerResponse = await client.workflows.triggerRun(workflowId, {
11
+ payload,
12
+ environment,
13
+ });
14
+ // NOTE: V3 trigger responses use `run_id`, while some V2 workflows return
15
+ // `queued_run_ids`. We support both for now and can remove the fallback once
16
+ // all workflows are upgraded.
17
+ const run_id = triggerResponse.run_id ??
18
+ (Array.isArray(triggerResponse.queued_run_ids)
19
+ ? triggerResponse.queued_run_ids[0]
20
+ : undefined);
21
+ if (!run_id) {
22
+ throw new Error("No run_id returned from trigger");
23
+ }
24
+ const deadline = Date.now() + timeoutMs;
25
+ let lastRunStatus = null;
26
+ let lastRunCompletedAt = null;
27
+ while (Date.now() < deadline) {
28
+ const nodes = await client.runs.listNodes(run_id, {
29
+ node_persistent_id: nodePersistentId,
30
+ sort: "asc",
31
+ });
32
+ const matchingNodes = nodes.data.filter((item) => item.node_persistent_id === nodePersistentId);
33
+ const node = pickLatestNodeExecution(matchingNodes);
34
+ if (node?.output_id && node.status && NODE_READY_STATUS_SET.has(node.status)) {
35
+ const output = await client.runs.getOutput(run_id, node.output_id);
36
+ return {
37
+ ok: true,
38
+ runId: run_id,
39
+ status: lastRunStatus ?? node.status,
40
+ nodeOutput: output.data,
41
+ completedAt: lastRunCompletedAt,
42
+ };
43
+ }
44
+ // TODO: 2 HTTP calls per poll (listNodes + get(run)). Future fix: include
45
+ // run_status in listNodes response to eliminate get(run) entirely.
46
+ const run = await client.runs.get(run_id);
47
+ lastRunStatus = run.status;
48
+ lastRunCompletedAt = run.completed_at;
49
+ if (RUN_TERMINAL_STATUS_SET.has(run.status)) {
50
+ const finalNodes = await client.runs.listNodes(run_id, {
51
+ node_persistent_id: nodePersistentId,
52
+ sort: "asc",
53
+ });
54
+ const finalMatchingNodes = finalNodes.data.filter((item) => item.node_persistent_id === nodePersistentId);
55
+ const finalNode = pickLatestNodeExecution(finalMatchingNodes);
56
+ if (finalNode?.output_id &&
57
+ finalNode.status &&
58
+ NODE_READY_STATUS_SET.has(finalNode.status)) {
59
+ const output = await client.runs.getOutput(run_id, finalNode.output_id);
60
+ return {
61
+ ok: true,
62
+ runId: run_id,
63
+ status: run.status,
64
+ nodeOutput: output.data,
65
+ completedAt: run.completed_at,
66
+ };
67
+ }
68
+ throw new Error(`Run reached terminal status '${run.status}' before node '${nodePersistentId}' became ready`);
69
+ }
70
+ // TODO: add response-node mode when is_response_node filtering is implemented server-side.
71
+ await sleep(pollIntervalMs);
72
+ }
73
+ throw new errors_1.TimeoutError(timeoutMs);
74
+ }
75
+ function sleep(ms) {
76
+ return new Promise((resolve) => setTimeout(resolve, ms));
77
+ }
78
+ function pickLatestNodeExecution(nodes) {
79
+ // TODO: should return FIRST completed execution by default.
80
+ // Consider adding executionStrategy: "first" | "latest" to options.
81
+ if (nodes.length === 0) {
82
+ return undefined;
83
+ }
84
+ return nodes.reduce((latest, current) => {
85
+ const latestTs = Date.parse(latest.timestamp);
86
+ const currentTs = Date.parse(current.timestamp);
87
+ if (Number.isNaN(latestTs) || Number.isNaN(currentTs)) {
88
+ return current.output_id > latest.output_id ? current : latest;
89
+ }
90
+ if (currentTs > latestTs) {
91
+ return current;
92
+ }
93
+ if (currentTs < latestTs) {
94
+ return latest;
95
+ }
96
+ return current.output_id > latest.output_id ? current : latest;
97
+ });
98
+ }
99
+ //# sourceMappingURL=trigger-and-wait-for-node-output.js.map
@@ -0,0 +1,3 @@
1
+ import * as _cjs from "./trigger-and-wait-for-node-output.js";
2
+ const { triggerAndWaitForNodeOutput } = _cjs;
3
+ export { triggerAndWaitForNodeOutput };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyrobot-ai/sdk",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "TypeScript SDK for the HappyRobot Public API",
5
5
  "main": "./index.js",
6
6
  "module": "./index.mjs",
@@ -59,4 +59,4 @@
59
59
  "directory": "packages/public_api"
60
60
  },
61
61
  "dependencies": {}
62
- }
62
+ }
@@ -4,6 +4,8 @@
4
4
  * Maps to:
5
5
  * GET /runs
6
6
  * GET /runs/:run_id
7
+ * GET /runs/:run_id/nodes
8
+ * GET /runs/:run_id/outputs/:output_id
7
9
  * GET /runs/:run_id/sessions
8
10
  * GET /runs/:run_id/recordings
9
11
  * GET /runs/:run_id/flags
@@ -11,13 +13,17 @@
11
13
  * POST /runs/:run_id/mark
12
14
  */
13
15
  import type { HttpClient } from "../core/http";
14
- import type { RunDetail, MarkRunBody, MarkRunResponse, ListFlagsQuery, PaginatedFlagsResponse } from "../types/runs.types";
16
+ import type { RunDetail, MarkRunBody, MarkRunResponse, ListFlagsQuery, PaginatedFlagsResponse, GetRunOutputResponse, ListRunNodesQuery, RunNodesResponse } from "../types/runs.types";
15
17
  import type { PaginatedSessionsResponse, ListSessionsQuery } from "../types/sessions.types";
16
18
  export declare class RunsResource {
17
19
  private readonly http;
18
20
  constructor(http: HttpClient);
19
21
  /** Get a run by ID. */
20
22
  get(runId: string): Promise<RunDetail>;
23
+ /** List node executions for a run in timestamp order. */
24
+ listNodes(runId: string, query?: ListRunNodesQuery): Promise<RunNodesResponse>;
25
+ /** Get one output payload by output ID within a run. */
26
+ getOutput(runId: string, outputId: string): Promise<GetRunOutputResponse>;
21
27
  /** Get sessions for a run. */
22
28
  getSessions(runId: string, query?: ListSessionsQuery): Promise<PaginatedSessionsResponse>;
23
29
  /** Get recordings for a run. */
package/resources/runs.js CHANGED
@@ -5,6 +5,8 @@
5
5
  * Maps to:
6
6
  * GET /runs
7
7
  * GET /runs/:run_id
8
+ * GET /runs/:run_id/nodes
9
+ * GET /runs/:run_id/outputs/:output_id
8
10
  * GET /runs/:run_id/sessions
9
11
  * GET /runs/:run_id/recordings
10
12
  * GET /runs/:run_id/flags
@@ -25,6 +27,21 @@ class RunsResource {
25
27
  path: `/runs/${enc(runId)}`,
26
28
  });
27
29
  }
30
+ /** List node executions for a run in timestamp order. */
31
+ async listNodes(runId, query) {
32
+ return this.http.request({
33
+ method: "GET",
34
+ path: `/runs/${enc(runId)}/nodes`,
35
+ query: query,
36
+ });
37
+ }
38
+ /** Get one output payload by output ID within a run. */
39
+ async getOutput(runId, outputId) {
40
+ return this.http.request({
41
+ method: "GET",
42
+ path: `/runs/${enc(runId)}/outputs/${enc(outputId)}`,
43
+ });
44
+ }
28
45
  /** Get sessions for a run. */
29
46
  async getSessions(runId, query) {
30
47
  return this.http.request({
@@ -2,7 +2,7 @@
2
2
  * Run types extracted from Zod schemas.
3
3
  */
4
4
  import type { z } from "zod";
5
- import type { RunDetailSchema, MarkRunBodySchema, MarkRunResponseSchema, ListFlagsQuerySchema, PaginatedFlagsResponseSchema, FlagItemSchema } from "../../routes/runs/schemas";
5
+ import type { RunDetailSchema, MarkRunBodySchema, MarkRunResponseSchema, ListFlagsQuerySchema, PaginatedFlagsResponseSchema, FlagItemSchema, GetRunOutputResponseSchema, ListRunNodesQuerySchema, ListRunNodesResponseSchema, RunNodeOutputSchema } from "../../routes/runs/schemas";
6
6
  import type { WorkflowRunsQuerySchema, PaginatedWorkflowRunsResponseSchema, TriggerRunBodySchema, TriggerRunResponseSchema } from "../../routes/workflows/:workflow_id/runs/schemas";
7
7
  export type WorkflowRunsQuery = z.input<typeof WorkflowRunsQuerySchema>;
8
8
  export type PaginatedWorkflowRunsResponse = z.infer<typeof PaginatedWorkflowRunsResponseSchema>;
@@ -14,3 +14,7 @@ export type MarkRunResponse = z.infer<typeof MarkRunResponseSchema>;
14
14
  export type ListFlagsQuery = z.input<typeof ListFlagsQuerySchema>;
15
15
  export type PaginatedFlagsResponse = z.infer<typeof PaginatedFlagsResponseSchema>;
16
16
  export type FlagItem = z.infer<typeof FlagItemSchema>;
17
+ export type ListRunNodesQuery = z.input<typeof ListRunNodesQuerySchema>;
18
+ export type RunNodesResponse = z.infer<typeof ListRunNodesResponseSchema>;
19
+ export type GetRunOutputResponse = z.infer<typeof GetRunOutputResponseSchema>;
20
+ export type RunNodeOutput = z.infer<typeof RunNodeOutputSchema>;
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import type { z } from "zod";
6
6
  import type { ListWorkflowsQuerySchema, WorkflowListItemSchema, PaginatedWorkflowsResponseSchema, CreateWorkflowBodySchema, CreateWorkflowResponseSchema, WorkflowDetailResponseSchema, UpdateWorkflowBodySchema, UpdateWorkflowResponseSchema, DuplicateWorkflowBodySchema, DuplicateWorkflowResponseSchema, PaginatedTemplatesResponseSchema, ListTemplatesQuerySchema, WorkflowPublishResponseSchema, WorkflowPublishErrorResponseSchema, WorkflowUnpublishResponseSchema, CancelRunsResponseSchema, ListWorkflowVersionsQuerySchema, PaginatedWorkflowVersionsResponseSchema } from "../../routes/workflows/schemas";
7
- export type ListWorkflowsQuery = z.input<typeof ListWorkflowsQuerySchema>;
7
+ export type ListWorkflowsQuery = z.output<typeof ListWorkflowsQuerySchema>;
8
8
  export type WorkflowListItem = z.infer<typeof WorkflowListItemSchema>;
9
9
  export type PaginatedWorkflowsResponse = z.infer<typeof PaginatedWorkflowsResponseSchema>;
10
10
  export type CreateWorkflowBody = z.input<typeof CreateWorkflowBodySchema>;