Skip to content

Commit daeed27

Browse files
committed
feat: add Agent Runs API and related models, activities, and tests
1 parent 29b5b10 commit daeed27

File tree

10 files changed

+396
-0
lines changed

10 files changed

+396
-0
lines changed

env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,6 @@ TEST_WORK_ITEM_TYPE_ID=your-work-item-type-id
2222

2323
# Custom property IDs
2424
TEST_CUSTOM_TEXT_PROPERTY_ID=your-custom-text-property-id
25+
26+
# Agent configuration
27+
TEST_AGENT_SLUG=your-agent-slug

src/api/AgentRuns/Activities.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { BaseResource } from "../BaseResource";
2+
import { Configuration } from "../../Configuration";
3+
import { AgentRunActivity, CreateAgentRunActivityRequest } from "../../models/AgentRun";
4+
import { PaginatedResponse } from "../../models/common";
5+
6+
/**
7+
* Agent Run Activities API resource
8+
* Handles all agent run activity operations
9+
*/
10+
export class Activities extends BaseResource {
11+
constructor(config: Configuration) {
12+
super(config);
13+
}
14+
15+
/**
16+
* List activities for an agent run
17+
* @param workspaceSlug - The workspace slug
18+
* @param runId - The agent run ID
19+
* @param params - Optional query parameters for pagination
20+
* @returns Paginated list of agent run activities
21+
*/
22+
async list(
23+
workspaceSlug: string,
24+
runId: string,
25+
params?: { per_page?: number; cursor?: string }
26+
): Promise<PaginatedResponse<AgentRunActivity>> {
27+
return this.get<PaginatedResponse<AgentRunActivity>>(
28+
`/workspaces/${workspaceSlug}/runs/${runId}/activities/`,
29+
params
30+
);
31+
}
32+
33+
/**
34+
* Retrieve a specific agent run activity by ID
35+
* @param workspaceSlug - The workspace slug
36+
* @param runId - The agent run ID
37+
* @param activityId - The activity ID
38+
* @returns The agent run activity
39+
*/
40+
async retrieve(workspaceSlug: string, runId: string, activityId: string): Promise<AgentRunActivity> {
41+
return this.get<AgentRunActivity>(`/workspaces/${workspaceSlug}/runs/${runId}/activities/${activityId}/`);
42+
}
43+
44+
/**
45+
* Create a new agent run activity
46+
* @param workspaceSlug - The workspace slug
47+
* @param runId - The agent run ID
48+
* @param data - The activity data to create
49+
* @returns The created agent run activity
50+
*/
51+
async create(workspaceSlug: string, runId: string, data: CreateAgentRunActivityRequest): Promise<AgentRunActivity> {
52+
return this.post<AgentRunActivity>(`/workspaces/${workspaceSlug}/runs/${runId}/activities/`, data);
53+
}
54+
}
55+

src/api/AgentRuns/index.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { BaseResource } from "../BaseResource";
2+
import { Configuration } from "../../Configuration";
3+
import { AgentRun, CreateAgentRunRequest } from "../../models/AgentRun";
4+
import { Activities } from "./Activities";
5+
6+
/**
7+
* Agent Runs API resource
8+
* Handles all agent run operations
9+
*/
10+
export class AgentRuns extends BaseResource {
11+
public activities: Activities;
12+
13+
constructor(config: Configuration) {
14+
super(config);
15+
this.activities = new Activities(config);
16+
}
17+
18+
/**
19+
* Create a new agent run
20+
* @param workspaceSlug - The workspace slug
21+
* @param data - The agent run data to create
22+
* @returns The created agent run
23+
*/
24+
async create(workspaceSlug: string, data: CreateAgentRunRequest): Promise<AgentRun> {
25+
return this.post<AgentRun>(`/workspaces/${workspaceSlug}/runs/`, data);
26+
}
27+
28+
/**
29+
* Retrieve an agent run by ID
30+
* @param workspaceSlug - The workspace slug
31+
* @param runId - The agent run ID
32+
* @returns The agent run
33+
*/
34+
async retrieve(workspaceSlug: string, runId: string): Promise<AgentRun> {
35+
return this.get<AgentRun>(`/workspaces/${workspaceSlug}/runs/${runId}/`);
36+
}
37+
38+
/**
39+
* Resume an agent run
40+
* @param workspaceSlug - The workspace slug
41+
* @param runId - The agent run ID
42+
* @returns The resumed agent run
43+
*/
44+
async resume(workspaceSlug: string, runId: string): Promise<AgentRun> {
45+
return this.post<AgentRun>(`/workspaces/${workspaceSlug}/runs/${runId}/`);
46+
}
47+
}
48+

src/client/plane-client.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { Intake } from "../api/Intake";
1818
import { Stickies } from "../api/Stickies";
1919
import { Teamspaces } from "../api/Teamspaces";
2020
import { Initiatives } from "../api/Initiatives";
21+
import { AgentRuns } from "../api/AgentRuns";
2122

2223
/**
2324
* Main Plane Client class
@@ -44,6 +45,7 @@ export class PlaneClient {
4445
public stickies: Stickies;
4546
public teamspaces: Teamspaces;
4647
public initiatives: Initiatives;
48+
public agentRuns: AgentRuns;
4749

4850
constructor(config: { baseUrl?: string; apiKey?: string; accessToken?: string; enableLogging?: boolean }) {
4951
this.config = new Configuration({
@@ -76,5 +78,6 @@ export class PlaneClient {
7678
this.stickies = new Stickies(this.config);
7779
this.teamspaces = new Teamspaces(this.config);
7880
this.initiatives = new Initiatives(this.config);
81+
this.agentRuns = new AgentRuns(this.config);
7982
}
8083
}

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export { Intake } from "./api/Intake";
2727
export { Stickies } from "./api/Stickies";
2828
export { Teamspaces } from "./api/Teamspaces";
2929
export { Initiatives } from "./api/Initiatives";
30+
export { AgentRuns } from "./api/AgentRuns";
3031

3132
// Sub-resources
3233
export { Relations as WorkItemRelations } from "./api/WorkItems/Relations";
@@ -43,6 +44,7 @@ export { Members as TeamspaceMembers } from "./api/Teamspaces/Members";
4344
export { Labels as InitiativeLabels } from "./api/Initiatives/Labels";
4445
export { Projects as InitiativeProjects } from "./api/Initiatives/Projects";
4546
export { Epics as InitiativeEpics } from "./api/Initiatives/Epics";
47+
export { Activities as AgentRunActivities } from "./api/AgentRuns/Activities";
4648

4749
// Models
4850
export * from "./models";

src/models/AgentRun.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { BaseModel } from "./common";
2+
3+
/**
4+
* Agent run status enum
5+
*/
6+
export type AgentRunStatus =
7+
| "created"
8+
| "in_progress"
9+
| "awaiting"
10+
| "completed"
11+
| "stopping"
12+
| "stopped"
13+
| "failed"
14+
| "stale";
15+
16+
/**
17+
* Agent run type enum
18+
*/
19+
export type AgentRunType = "comment_thread";
20+
21+
/**
22+
* Agent run activity signal enum
23+
*/
24+
export type AgentRunActivitySignal = "auth_request" | "continue" | "select" | "stop";
25+
26+
/**
27+
* Agent run activity type enum
28+
*/
29+
export type AgentRunActivityType = "action" | "elicitation" | "error" | "prompt" | "response" | "thought";
30+
31+
/**
32+
* Agent Run interface
33+
*/
34+
export interface AgentRun extends BaseModel {
35+
agent_user: string;
36+
comment?: string;
37+
source_comment?: string;
38+
creator: string;
39+
stopped_at?: string;
40+
stopped_by?: string;
41+
started_at: string;
42+
ended_at?: string;
43+
external_link?: string;
44+
issue?: string;
45+
workspace: string;
46+
project?: string;
47+
status: AgentRunStatus;
48+
error_metadata?: Record<string, any>;
49+
type: AgentRunType;
50+
}
51+
52+
/**
53+
* Create agent run request interface
54+
*/
55+
export interface CreateAgentRunRequest {
56+
agent_slug: string;
57+
issue?: string;
58+
project?: string;
59+
comment?: string;
60+
source_comment?: string;
61+
external_link?: string;
62+
type?: AgentRunType;
63+
}
64+
65+
/**
66+
* Agent run activity content for action type
67+
*/
68+
export interface AgentRunActivityActionContent {
69+
type: "action";
70+
action: string;
71+
parameters: Record<string, string>;
72+
}
73+
74+
/**
75+
* Agent run activity content for other types
76+
*/
77+
export interface AgentRunActivityTextContent {
78+
type: Exclude<AgentRunActivityType, "action">;
79+
body: string;
80+
}
81+
82+
/**
83+
* Agent run activity content
84+
*/
85+
export type AgentRunActivityContent = AgentRunActivityActionContent | AgentRunActivityTextContent;
86+
87+
/**
88+
* Agent Run Activity interface
89+
*/
90+
export interface AgentRunActivity extends BaseModel {
91+
agent_run: string;
92+
content: AgentRunActivityContent;
93+
content_metadata?: Record<string, any>;
94+
ephemeral: boolean;
95+
signal: AgentRunActivitySignal;
96+
signal_metadata?: Record<string, any>;
97+
comment?: string;
98+
actor?: string;
99+
type: AgentRunActivityType;
100+
project?: string;
101+
workspace: string;
102+
}
103+
104+
/**
105+
* Create agent run activity request interface
106+
*/
107+
export interface CreateAgentRunActivityRequest {
108+
content: AgentRunActivityContent;
109+
content_metadata?: Record<string, any>;
110+
signal?: AgentRunActivitySignal;
111+
signal_metadata?: Record<string, any>;
112+
type: Exclude<AgentRunActivityType, "prompt">;
113+
project?: string;
114+
}
115+

src/models/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ export * from "./Sticky";
1414
export * from "./Teamspace";
1515
export * from "./InitiativeLabel";
1616
export * from "./Initiative";
17+
export * from "./AgentRun";
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { PlaneClient } from "../../../src/client/plane-client";
2+
import { AgentRun, AgentRunActivity } from "../../../src/models";
3+
import { config } from "../constants";
4+
import { createTestClient } from "../../helpers/test-utils";
5+
import { describeIf as describe } from "../../helpers/conditional-tests";
6+
7+
describe(!!(config.workspaceSlug && config.agentSlug), "Agent Run Activities API Tests", () => {
8+
let client: PlaneClient;
9+
let workspaceSlug: string;
10+
let agentSlug: string;
11+
let projectId: string;
12+
let agentRun: AgentRun;
13+
let activity: AgentRunActivity;
14+
15+
beforeAll(async () => {
16+
client = createTestClient();
17+
workspaceSlug = config.workspaceSlug;
18+
agentSlug = config.agentSlug;
19+
projectId = config.projectId;
20+
21+
// Create an agent run for testing activities
22+
agentRun = await client.agentRuns.create(workspaceSlug, {
23+
agent_slug: agentSlug,
24+
project: projectId,
25+
});
26+
});
27+
28+
it("should create an agent run activity", async () => {
29+
activity = await client.agentRuns.activities.create(workspaceSlug, agentRun.id, {
30+
content: {
31+
type: "thought",
32+
body: "Test thought activity",
33+
},
34+
type: "thought",
35+
project: projectId,
36+
});
37+
38+
expect(activity).toBeDefined();
39+
expect(activity.id).toBeDefined();
40+
expect(activity.type).toBe("thought");
41+
expect(activity.agent_run).toBe(agentRun.id);
42+
});
43+
44+
it("should list agent run activities", async () => {
45+
const activitiesResponse = await client.agentRuns.activities.list(workspaceSlug, agentRun.id);
46+
47+
expect(activitiesResponse).toBeDefined();
48+
expect(Array.isArray(activitiesResponse.results)).toBe(true);
49+
expect(activitiesResponse.results.length).toBeGreaterThan(0);
50+
51+
const foundActivity = activitiesResponse.results.find((a) => a.id === activity.id);
52+
expect(foundActivity).toBeDefined();
53+
});
54+
55+
it("should retrieve an agent run activity", async () => {
56+
const retrievedActivity = await client.agentRuns.activities.retrieve(
57+
workspaceSlug,
58+
agentRun.id,
59+
activity.id
60+
);
61+
62+
expect(retrievedActivity).toBeDefined();
63+
expect(retrievedActivity.id).toBe(activity.id);
64+
expect(retrievedActivity.type).toBe("thought");
65+
});
66+
67+
it("should create an action type activity", async () => {
68+
const actionActivity = await client.agentRuns.activities.create(workspaceSlug, agentRun.id, {
69+
content: {
70+
type: "action",
71+
action: "create_comment",
72+
parameters: {
73+
comment: "Test comment from agent",
74+
},
75+
},
76+
type: "action",
77+
project: projectId,
78+
});
79+
80+
expect(actionActivity).toBeDefined();
81+
expect(actionActivity.id).toBeDefined();
82+
expect(actionActivity.type).toBe("action");
83+
});
84+
85+
it("should create a response type activity", async () => {
86+
const responseActivity = await client.agentRuns.activities.create(workspaceSlug, agentRun.id, {
87+
content: {
88+
type: "response",
89+
body: "This is a response from the agent",
90+
},
91+
type: "response",
92+
project: projectId,
93+
});
94+
95+
expect(responseActivity).toBeDefined();
96+
expect(responseActivity.id).toBeDefined();
97+
expect(responseActivity.type).toBe("response");
98+
});
99+
100+
it("should create an elicitation type activity", async () => {
101+
const elicitationActivity = await client.agentRuns.activities.create(workspaceSlug, agentRun.id, {
102+
content: {
103+
type: "elicitation",
104+
body: "What would you like me to do next?",
105+
},
106+
type: "elicitation",
107+
signal: "select",
108+
signal_metadata: {
109+
options: ["option1", "option2"],
110+
},
111+
project: projectId,
112+
});
113+
114+
expect(elicitationActivity).toBeDefined();
115+
expect(elicitationActivity.id).toBeDefined();
116+
expect(elicitationActivity.type).toBe("elicitation");
117+
expect(elicitationActivity.signal).toBe("select");
118+
});
119+
});
120+

0 commit comments

Comments
 (0)