@love-moon/conductor-sdk 0.13.1 → 0.13.2
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/CHANGELOG.md +37 -0
- package/dist/api/tasks.d.ts +7 -0
- package/dist/api/tasks.js +17 -4
- package/dist/backend/client.d.ts +23 -1
- package/dist/backend/client.js +55 -7
- package/dist/client.d.ts +7 -0
- package/dist/client.js +11 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# @love-moon/conductor-sdk
|
|
2
2
|
|
|
3
|
+
## 0.13.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 6547b2c: Add read-only daemon queries and archived-task search to the CLI:
|
|
8
|
+
`conductor daemon list [--all]` (online daemons, version, AI backends),
|
|
9
|
+
`conductor daemon tools <host>` (installed AI tools and reachability),
|
|
10
|
+
`conductor daemon quota <host> [--tool] [--refresh]` (usage windows / balance
|
|
11
|
+
per tool), and `conductor task list --archived [--search] [--all-projects]
|
|
12
|
+
[--page]`. The SDK's `BackendApiClient` gains `listAgents`,
|
|
13
|
+
`getAiManagerStatus`, `getAiManagerQuota` and `listAchievedTasks` over the
|
|
14
|
+
existing web API routes; no server change.
|
|
15
|
+
- 8e3a4c3: Report how many model tokens each turn consumed. After every turn (or `/goal`)
|
|
16
|
+
`conductor fire` sends the turn's token count (fresh input + cache reads/writes +
|
|
17
|
+
output) over the new `task_turn_usage` websocket event, and the server adds it
|
|
18
|
+
to the task's running total shown in the task detail card. A persistent task's
|
|
19
|
+
total restarts with each round.
|
|
20
|
+
|
|
21
|
+
Failed or interrupted turns and `/compact` are counted too; when a turn's usage
|
|
22
|
+
is unknown the fire reports `null`, which clears the task's last-turn count.
|
|
23
|
+
|
|
24
|
+
- `@love-moon/conductor-sdk`: new `ConductorClient.sendTurnUsage(taskId, { tokens })`.
|
|
25
|
+
- `@love-moon/ai-sdk`: the Codex app-server provider's turn/goal `usage` now
|
|
26
|
+
carries `turnTotalTokens`, the turn's share of Codex's thread-cumulative total.
|
|
27
|
+
Claude and Codex turn errors carry the tokens spent before the failure as
|
|
28
|
+
`error.usage` (Claude sums streamed usage when an interrupted query ends
|
|
29
|
+
without a result).
|
|
30
|
+
- Claude and Codex backends are counted; other backends report nothing yet.
|
|
31
|
+
|
|
32
|
+
- 6989999: `conductor task list --include-moved` also lists tasks moved into the project
|
|
33
|
+
from other projects (`project_id == P OR second_project_id == P`), with a MOVED
|
|
34
|
+
column naming the origin/target project. `--json` output now carries
|
|
35
|
+
`secondProjectId`. The SDK gains `TasksApi.listTasks({ includeMoved })`,
|
|
36
|
+
`BackendApiClient.listTasks({ projectScope: 'display' })` and
|
|
37
|
+
`Task/TaskSummary.secondProjectId`; it merges the server's existing real and
|
|
38
|
+
display scopes, so no server change is needed.
|
|
39
|
+
|
|
3
40
|
## 0.13.1
|
|
4
41
|
|
|
5
42
|
## 0.13.0
|
package/dist/api/tasks.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { ApiClient, SdkClientOptions } from './shared.js';
|
|
|
2
2
|
export interface Task {
|
|
3
3
|
id: string;
|
|
4
4
|
projectId: string | null;
|
|
5
|
+
/** Project the task was moved to in the web UI (display-only); null if not moved. */
|
|
6
|
+
secondProjectId?: string | null;
|
|
5
7
|
issueId?: string | null;
|
|
6
8
|
title: string;
|
|
7
9
|
status: string;
|
|
@@ -91,6 +93,11 @@ export interface ListTasksInput {
|
|
|
91
93
|
projectId?: string;
|
|
92
94
|
issueId?: string;
|
|
93
95
|
status?: string | string[];
|
|
96
|
+
/**
|
|
97
|
+
* Also include tasks moved into `projectId` from other projects (their
|
|
98
|
+
* `secondProjectId` is `projectId`), i.e. `projectId == P OR secondProjectId == P`.
|
|
99
|
+
*/
|
|
100
|
+
includeMoved?: boolean;
|
|
94
101
|
}
|
|
95
102
|
export interface CreateTaskInput {
|
|
96
103
|
projectId: string;
|
package/dist/api/tasks.js
CHANGED
|
@@ -30,6 +30,7 @@ const normalizeTask = (payload) => {
|
|
|
30
30
|
? null
|
|
31
31
|
: String(payload.project_id)
|
|
32
32
|
: null,
|
|
33
|
+
secondProjectId: payload.secondProjectId ?? payload.second_project_id ?? null,
|
|
33
34
|
issueId: payload.issueId ?? payload.issue_id ?? null,
|
|
34
35
|
title: String(payload.title ?? ''),
|
|
35
36
|
status: String(payload.status ?? ''),
|
|
@@ -134,10 +135,22 @@ export class TasksApi {
|
|
|
134
135
|
// The server `/api/tasks` only filters on `project_id`; status / issue
|
|
135
136
|
// filtering happens client-side here so we can support both string and
|
|
136
137
|
// array `status` shapes from the facade input.
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
138
|
+
const fetchNormalized = async (projectScope) => (await this.client.listTasks({ projectId: input.projectId, projectScope })).map((entry) => normalizeTask(typeof entry.asObject === 'function' ? entry.asObject() : entry));
|
|
139
|
+
let normalized;
|
|
140
|
+
if (input.includeMoved && input.projectId) {
|
|
141
|
+
// Real scope has every task whose projectId is P; display scope adds the
|
|
142
|
+
// ones moved in. Merging the two existing scopes needs no server change.
|
|
143
|
+
const [real, display] = await Promise.all([
|
|
144
|
+
fetchNormalized('real'),
|
|
145
|
+
fetchNormalized('display'),
|
|
146
|
+
]);
|
|
147
|
+
const seen = new Set(real.map((task) => task.id));
|
|
148
|
+
const sortTime = (task) => Date.parse(task.updatedAt ?? task.createdAt ?? '') || 0;
|
|
149
|
+
normalized = [...real, ...display.filter((task) => !seen.has(task.id))].sort((a, b) => sortTime(b) - sortTime(a));
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
normalized = await fetchNormalized('real');
|
|
153
|
+
}
|
|
141
154
|
if (input.issueId) {
|
|
142
155
|
normalized = normalized.filter((task) => task.issueId === input.issueId);
|
|
143
156
|
}
|
package/dist/backend/client.d.ts
CHANGED
|
@@ -78,7 +78,8 @@ export declare class TaskSummary {
|
|
|
78
78
|
readonly createdAt?: string | null | undefined;
|
|
79
79
|
readonly updatedAt?: string | null | undefined;
|
|
80
80
|
readonly grouping?: (TaskGroupingSummary | null) | undefined;
|
|
81
|
-
|
|
81
|
+
readonly secondProjectId?: string | null | undefined;
|
|
82
|
+
constructor(id: string, projectId: string | null, title: string, status: string, backendType?: string | null | undefined, sessionId?: string | null | undefined, sessionFilePath?: string | null | undefined, createdAt?: string | null | undefined, updatedAt?: string | null | undefined, grouping?: (TaskGroupingSummary | null) | undefined, secondProjectId?: string | null | undefined);
|
|
82
83
|
static fromJSON(payload: Record<string, any>): TaskSummary;
|
|
83
84
|
}
|
|
84
85
|
export declare class BackendApiClient {
|
|
@@ -104,6 +105,7 @@ export declare class BackendApiClient {
|
|
|
104
105
|
listTasks(params?: {
|
|
105
106
|
projectId?: string;
|
|
106
107
|
status?: string;
|
|
108
|
+
projectScope?: 'real' | 'display';
|
|
107
109
|
}): Promise<TaskSummary[]>;
|
|
108
110
|
createTask(params: BackendCreateTaskParams): Promise<TaskSummary>;
|
|
109
111
|
/**
|
|
@@ -245,6 +247,26 @@ export declare class BackendApiClient {
|
|
|
245
247
|
listScheduledMessages(taskId: string): Promise<Record<string, any>[]>;
|
|
246
248
|
createScheduledMessage(taskId: string, params: Record<string, unknown>): Promise<Record<string, any>>;
|
|
247
249
|
deleteScheduledMessage(taskId: string, scheduleId: string): Promise<void>;
|
|
250
|
+
/** Daemons (agents) currently connected for this user. */
|
|
251
|
+
listAgents(): Promise<Record<string, any>[]>;
|
|
252
|
+
/** Install / network status of the AI tools on a daemon. */
|
|
253
|
+
getAiManagerStatus(agentHost: string): Promise<Record<string, any>>;
|
|
254
|
+
/** Usage-window / balance quota of the AI tools on a daemon. */
|
|
255
|
+
getAiManagerQuota(agentHost: string, params?: {
|
|
256
|
+
tool?: string;
|
|
257
|
+
forceRefresh?: boolean;
|
|
258
|
+
}): Promise<Record<string, any>>;
|
|
259
|
+
/** Search archived (achieved) tasks by title and transcript; 10 per page. */
|
|
260
|
+
listAchievedTasks(params?: {
|
|
261
|
+
query?: string;
|
|
262
|
+
projectId?: string;
|
|
263
|
+
page?: number;
|
|
264
|
+
}): Promise<{
|
|
265
|
+
tasks: Record<string, any>[];
|
|
266
|
+
total: number;
|
|
267
|
+
page: number;
|
|
268
|
+
totalPages: number;
|
|
269
|
+
}>;
|
|
248
270
|
private request;
|
|
249
271
|
private buildUrl;
|
|
250
272
|
private sendRequest;
|
package/dist/backend/client.js
CHANGED
|
@@ -75,7 +75,8 @@ export class TaskSummary {
|
|
|
75
75
|
createdAt;
|
|
76
76
|
updatedAt;
|
|
77
77
|
grouping;
|
|
78
|
-
|
|
78
|
+
secondProjectId;
|
|
79
|
+
constructor(id, projectId, title, status, backendType, sessionId, sessionFilePath, createdAt, updatedAt, grouping, secondProjectId) {
|
|
79
80
|
this.id = id;
|
|
80
81
|
this.projectId = projectId;
|
|
81
82
|
this.title = title;
|
|
@@ -86,6 +87,7 @@ export class TaskSummary {
|
|
|
86
87
|
this.createdAt = createdAt;
|
|
87
88
|
this.updatedAt = updatedAt;
|
|
88
89
|
this.grouping = grouping;
|
|
90
|
+
this.secondProjectId = secondProjectId;
|
|
89
91
|
}
|
|
90
92
|
static fromJSON(payload) {
|
|
91
93
|
const id = payload.id ? String(payload.id) : '';
|
|
@@ -112,7 +114,7 @@ export class TaskSummary {
|
|
|
112
114
|
: null,
|
|
113
115
|
}
|
|
114
116
|
: null;
|
|
115
|
-
return new TaskSummary(id, payload.project_id ? String(payload.project_id) : null, title, status, payload.backend_type ?? payload.backendType ?? null, payload.session_id ?? payload.sessionId ?? null, payload.session_file_path ?? payload.sessionFilePath ?? null, payload.created_at ?? null, payload.updated_at ?? null, grouping);
|
|
117
|
+
return new TaskSummary(id, payload.project_id ? String(payload.project_id) : null, title, status, payload.backend_type ?? payload.backendType ?? null, payload.session_id ?? payload.sessionId ?? null, payload.session_file_path ?? payload.sessionFilePath ?? null, payload.created_at ?? null, payload.updated_at ?? null, grouping, payload.second_project_id ?? payload.secondProjectId ?? null);
|
|
116
118
|
}
|
|
117
119
|
}
|
|
118
120
|
export class BackendApiClient {
|
|
@@ -157,10 +159,12 @@ export class BackendApiClient {
|
|
|
157
159
|
const query = new URLSearchParams();
|
|
158
160
|
if (params.projectId) {
|
|
159
161
|
query.set('project_id', params.projectId);
|
|
160
|
-
// Match the task's REAL project
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
|
|
162
|
+
// Match the task's REAL project unless the caller asks for the web UI's
|
|
163
|
+
// display grouping (the server default), which hides this project's
|
|
164
|
+
// moved-out tasks and includes the ones moved in via `second_project_id`.
|
|
165
|
+
if (params.projectScope !== 'display') {
|
|
166
|
+
query.set('project_scope', 'real');
|
|
167
|
+
}
|
|
164
168
|
}
|
|
165
169
|
if (params.status) {
|
|
166
170
|
query.set('status', params.status);
|
|
@@ -503,10 +507,54 @@ export class BackendApiClient {
|
|
|
503
507
|
headers: agentActorHeaders(),
|
|
504
508
|
});
|
|
505
509
|
}
|
|
510
|
+
/** Daemons (agents) currently connected for this user. */
|
|
511
|
+
async listAgents() {
|
|
512
|
+
const response = await this.request('GET', '/api/agents');
|
|
513
|
+
const payload = await this.parseJson(response);
|
|
514
|
+
if (!Array.isArray(payload)) {
|
|
515
|
+
throw new BackendApiError('Invalid agents response: expected list', response.status, payload);
|
|
516
|
+
}
|
|
517
|
+
return payload;
|
|
518
|
+
}
|
|
519
|
+
/** Install / network status of the AI tools on a daemon. */
|
|
520
|
+
async getAiManagerStatus(agentHost) {
|
|
521
|
+
const query = new URLSearchParams({ agentHost });
|
|
522
|
+
const response = await this.request('GET', '/api/ai-manager/status', { query, timeoutMs: 45_000 });
|
|
523
|
+
return this.parseJson(response);
|
|
524
|
+
}
|
|
525
|
+
/** Usage-window / balance quota of the AI tools on a daemon. */
|
|
526
|
+
async getAiManagerQuota(agentHost, params = {}) {
|
|
527
|
+
const query = new URLSearchParams({ agentHost });
|
|
528
|
+
if (params.tool)
|
|
529
|
+
query.set('tool', params.tool);
|
|
530
|
+
if (params.forceRefresh)
|
|
531
|
+
query.set('forceRefresh', '1');
|
|
532
|
+
// The server waits up to 30s for the daemon to query every provider.
|
|
533
|
+
const response = await this.request('GET', '/api/ai-manager/quota', { query, timeoutMs: 45_000 });
|
|
534
|
+
return this.parseJson(response);
|
|
535
|
+
}
|
|
536
|
+
/** Search archived (achieved) tasks by title and transcript; 10 per page. */
|
|
537
|
+
async listAchievedTasks(params = {}) {
|
|
538
|
+
const query = new URLSearchParams();
|
|
539
|
+
if (params.query)
|
|
540
|
+
query.set('q', params.query);
|
|
541
|
+
if (params.projectId)
|
|
542
|
+
query.set('projectId', params.projectId);
|
|
543
|
+
if (params.page)
|
|
544
|
+
query.set('page', String(params.page));
|
|
545
|
+
const response = await this.request('GET', '/api/tasks/achieved', { query });
|
|
546
|
+
const payload = await this.parseJson(response);
|
|
547
|
+
if (!payload || !Array.isArray(payload.tasks)) {
|
|
548
|
+
throw new BackendApiError('Invalid achieved tasks response', response.status, payload);
|
|
549
|
+
}
|
|
550
|
+
return payload;
|
|
551
|
+
}
|
|
506
552
|
async request(method, pathname, opts = {}) {
|
|
507
553
|
const url = this.buildUrl(pathname, opts.query);
|
|
508
554
|
const controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
|
|
509
|
-
const timeout = controller
|
|
555
|
+
const timeout = controller
|
|
556
|
+
? setTimeout(() => controller.abort(), opts.timeoutMs ?? this.timeoutMs)
|
|
557
|
+
: null;
|
|
510
558
|
try {
|
|
511
559
|
let response = await this.sendRequest(url, method, opts.body, controller, opts.headers);
|
|
512
560
|
if (response.status === 404 &&
|
package/dist/client.d.ts
CHANGED
|
@@ -118,6 +118,13 @@ export declare class ConductorClient {
|
|
|
118
118
|
sendMessage(taskId: string, content: string, metadata?: Record<string, any>): Promise<Record<string, any>>;
|
|
119
119
|
sendTaskStatus(taskId: string, payload: Record<string, any>): Promise<Record<string, any>>;
|
|
120
120
|
sendRuntimeStatus(taskId: string, payload: Record<string, any>): Promise<Record<string, any>>;
|
|
121
|
+
/**
|
|
122
|
+
* Report the tokens one finished turn consumed; the server adds them to the task total.
|
|
123
|
+
* `null` marks a failed turn whose usage is unknown.
|
|
124
|
+
*/
|
|
125
|
+
sendTurnUsage(taskId: string, payload: {
|
|
126
|
+
tokens: number | null;
|
|
127
|
+
}): Promise<Record<string, any>>;
|
|
121
128
|
sendAgentResume(payload?: {
|
|
122
129
|
active_tasks?: string[];
|
|
123
130
|
source?: string;
|
package/dist/client.js
CHANGED
|
@@ -305,6 +305,17 @@ export class ConductorClient {
|
|
|
305
305
|
});
|
|
306
306
|
return { delivered: true };
|
|
307
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* Report the tokens one finished turn consumed; the server adds them to the task total.
|
|
310
|
+
* `null` marks a failed turn whose usage is unknown.
|
|
311
|
+
*/
|
|
312
|
+
async sendTurnUsage(taskId, payload) {
|
|
313
|
+
await this.sendEnvelope({
|
|
314
|
+
type: 'task_turn_usage',
|
|
315
|
+
payload: { task_id: taskId, tokens: payload.tokens },
|
|
316
|
+
});
|
|
317
|
+
return { delivered: true };
|
|
318
|
+
}
|
|
308
319
|
async sendAgentResume(payload = {}) {
|
|
309
320
|
const storedCursor = this.downstreamCursorStore.get(this.agentHost);
|
|
310
321
|
const lastAppliedCursor = payload.last_applied_cursor ?? (storedCursor
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@love-moon/conductor-sdk",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.2",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/lovemoon-ai/conductor.git"
|
|
@@ -33,5 +33,5 @@
|
|
|
33
33
|
"typescript": "^5.6.3",
|
|
34
34
|
"vitest": "^2.1.4"
|
|
35
35
|
},
|
|
36
|
-
"gitCommitId": "
|
|
36
|
+
"gitCommitId": "d60a6a2"
|
|
37
37
|
}
|