@love-moon/conductor-sdk 0.15.2 → 0.17.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/CHANGELOG.md +76 -0
- package/dist/api/issues.d.ts +7 -0
- package/dist/api/issues.js +22 -9
- package/dist/api/projects.d.ts +2 -0
- package/dist/api/projects.js +9 -2
- package/dist/api/tasks.js +5 -4
- package/dist/backend/client.js +10 -1
- package/dist/client.js +2 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,81 @@
|
|
|
1
1
|
# @love-moon/conductor-sdk
|
|
2
2
|
|
|
3
|
+
## 0.17.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 88e1ae0: Support `/stop` in task chats. A bare `/stop` message, sent from the web, the
|
|
8
|
+
CLI or a channel, makes the server interrupt the AI's running turn right away
|
|
9
|
+
instead of waiting for fire to read the message once that turn ends. When fire
|
|
10
|
+
does read the queued `/stop`, it posts a confirmation and does not send it to
|
|
11
|
+
the model. `/stop` also drops every message queued before it (except the
|
|
12
|
+
server's end-of-round summary request); resend what you still want. The server
|
|
13
|
+
targets the reply it knows is running, so older fires stop it too;
|
|
14
|
+
`interrupt_turn` no longer needs a reply target, and without one fire
|
|
15
|
+
interrupts the turn it is running, including the initial prompt and pre_prompt
|
|
16
|
+
turns. In the web chat, the ⋯ menu gains `/stop`, `/clear` and `/compact`,
|
|
17
|
+
which send those commands as messages, and Interrupt (Esc) sends `/stop`,
|
|
18
|
+
ignored while an earlier `/stop` is unanswered. The Interrupt action is gone
|
|
19
|
+
from the message toolbar.
|
|
20
|
+
|
|
21
|
+
## 0.16.0
|
|
22
|
+
|
|
23
|
+
### Minor Changes
|
|
24
|
+
|
|
25
|
+
- 7c45a56: `conductor issue start` can pick the AI for the task it spawns: `--backend <b>`,
|
|
26
|
+
or `--global-backend <backend>@<host>` to run the AI on one of your global AI
|
|
27
|
+
backends while the code stays on the project's daemon (same as the web "Move
|
|
28
|
+
Issue To Doing" dialog). The SDK's `updateIssue` forwards `globalBackend`.
|
|
29
|
+
|
|
30
|
+
Requires a Conductor web server that ships issue global backends (the same
|
|
31
|
+
release): deploy the web server before publishing the CLI/SDK. An older server
|
|
32
|
+
ignores `--global-backend` and starts a plain task on the project's daemon; the
|
|
33
|
+
CLI then prints a warning instead of failing silently.
|
|
34
|
+
|
|
35
|
+
### Patch Changes
|
|
36
|
+
|
|
37
|
+
- 0495b79: The CLI can now do what the web app can:
|
|
38
|
+
|
|
39
|
+
- `conductor task`: `stop`, `interrupt`, `restart`, `delete`, `archive`/`unarchive`,
|
|
40
|
+
`rename`, `pin`/`unpin`, `move`, `labels`, `share`/`unshare`, `persistent`,
|
|
41
|
+
`round start|end`, `cleanup-worktree`, `terminal open|show|close`, `resume`,
|
|
42
|
+
`attachment download`, `shared <token|link>` (read a shared task), `transcribe <audio>`
|
|
43
|
+
(speech to text), and `schedule update`. `create` also takes `--daemon-host`, `--agent` (multi-agent group),
|
|
44
|
+
`--global-backend`, `--worktree`, `--remote-worktree` and `--persistent`.
|
|
45
|
+
`send --attach` uploads files, `messages --follow` streams new messages, and
|
|
46
|
+
`list --all-projects` / `--project-ids` lists tasks across projects.
|
|
47
|
+
- `conductor project`: `update`, `refresh`, `delete`, `reorder`, `agents`,
|
|
48
|
+
`collab invite|show-invite|join|leave`, `labels list|add|rename|remove`.
|
|
49
|
+
- `conductor issue`: `delete`, and `list --all-projects` / `--project-ids`.
|
|
50
|
+
- `conductor daemon`: `restart`, `upgrade [--status|--wait]`, `sessions`, `accounts`,
|
|
51
|
+
`switch-account`, `commands list|run|status`, `share create|list|revoke|show-invite|accept`.
|
|
52
|
+
- New `conductor search`, `conductor settings` (global backends, catchphrases, daily
|
|
53
|
+
report, task-list and card-group preferences, generated reports) and `conductor auth`
|
|
54
|
+
(`whoami`, `tokens list|create|revoke`).
|
|
55
|
+
|
|
56
|
+
Fix: `conductor project hide/unhide` (and the SDK's `setProjectHidden`) no longer erase
|
|
57
|
+
the project's metadata (task labels, memos, binding data).
|
|
58
|
+
|
|
59
|
+
On a cross-daemon merged project, `project hide/unhide`, `delete` and
|
|
60
|
+
`update --merge-opt-out` act on every daemon, like the web. `hide/unhide --json`
|
|
61
|
+
still prints the target project, plus `ids` for every project changed.
|
|
62
|
+
`delete --daemon-host <h>` deletes only that copy; a group delete stops at the
|
|
63
|
+
first failure and says which ids were deleted. `update --merge-opt-out` lists the
|
|
64
|
+
same-name projects it also split or re-merged.
|
|
65
|
+
|
|
66
|
+
- b68fadf: `conductor issue` now matches the web issue board:
|
|
67
|
+
`start --daemon <host> [--project <id>]` picks the daemon, or the merged-group
|
|
68
|
+
sibling project, that the task runs on (on another member's shared project the
|
|
69
|
+
server decides). `--priority` takes `P0|P1|P2`, and `--status` uses
|
|
70
|
+
`todo|doing|done`; `create` no longer offers `doing`. `list --status` is sent to
|
|
71
|
+
the server and still applied locally, so an older server that ignores it filters
|
|
72
|
+
too. An invalid argument now exits 2 without sending a request. The SDK's
|
|
73
|
+
`updateIssue` forwards `projectId`, and `listIssues` sends multiple statuses to
|
|
74
|
+
the server and filters the result the same way.
|
|
75
|
+
|
|
76
|
+
The matching web server changes (metadata patches merge instead of replacing,
|
|
77
|
+
`GET /api/issues?status=` filtering) should be deployed before publishing.
|
|
78
|
+
|
|
3
79
|
## 0.15.2
|
|
4
80
|
|
|
5
81
|
## 0.15.1
|
package/dist/api/issues.d.ts
CHANGED
|
@@ -31,12 +31,19 @@ export interface CreateIssueInput {
|
|
|
31
31
|
clientRequestId?: string;
|
|
32
32
|
}
|
|
33
33
|
export interface UpdateIssueInput {
|
|
34
|
+
/** Re-parent the issue (e.g. onto a merged-group sibling when starting it). */
|
|
35
|
+
projectId?: string;
|
|
34
36
|
title?: string;
|
|
35
37
|
description?: string;
|
|
36
38
|
priority?: string;
|
|
37
39
|
type?: string;
|
|
38
40
|
status?: string;
|
|
39
41
|
metadata?: Record<string, unknown>;
|
|
42
|
+
/** RFC 0041: on todo→doing, run the spawned task's AI on this global backend. */
|
|
43
|
+
globalBackend?: {
|
|
44
|
+
host: string;
|
|
45
|
+
backend: string;
|
|
46
|
+
};
|
|
40
47
|
}
|
|
41
48
|
export interface UpdateIssueStatusOptions {
|
|
42
49
|
evidence?: string;
|
package/dist/api/issues.js
CHANGED
|
@@ -21,6 +21,13 @@ const normalizeIssue = (payload) => {
|
|
|
21
21
|
raw: payload,
|
|
22
22
|
};
|
|
23
23
|
};
|
|
24
|
+
// Legacy statuses the server serves as their canonical value
|
|
25
|
+
// (web/src/lib/issues/config.ts LEGACY_ISSUE_STATUS_ALIASES).
|
|
26
|
+
const LEGACY_ISSUE_STATUS_ALIASES = { backlog: 'todo', review: 'doing' };
|
|
27
|
+
const canonicalIssueStatus = (value) => {
|
|
28
|
+
const normalized = String(value ?? '').trim().toLowerCase();
|
|
29
|
+
return LEGACY_ISSUE_STATUS_ALIASES[normalized] ?? normalized;
|
|
30
|
+
};
|
|
24
31
|
const extractIssue = (payload) => {
|
|
25
32
|
// The PATCH /issues/[issueId] route returns `{ issue, activeTask, ... }`.
|
|
26
33
|
// Other routes return the issue itself. Normalize both shapes here so
|
|
@@ -58,9 +65,9 @@ export class IssuesApi {
|
|
|
58
65
|
if (!input.projectId) {
|
|
59
66
|
throw new Error('projectId is required');
|
|
60
67
|
}
|
|
61
|
-
// The server
|
|
62
|
-
//
|
|
63
|
-
//
|
|
68
|
+
// The server filters on a comma-separated `status` (and maps legacy
|
|
69
|
+
// aliases such as `backlog` → `todo`); servers that predate that ignore
|
|
70
|
+
// it, so the result is filtered here too.
|
|
64
71
|
const statusFilter = Array.isArray(input.status)
|
|
65
72
|
? input.status.map((value) => String(value).trim()).filter(Boolean)
|
|
66
73
|
: input.status
|
|
@@ -68,12 +75,12 @@ export class IssuesApi {
|
|
|
68
75
|
: [];
|
|
69
76
|
const issues = await this.client.listIssues({
|
|
70
77
|
projectId: input.projectId,
|
|
71
|
-
status: statusFilter.length
|
|
78
|
+
status: statusFilter.length > 0 ? statusFilter.join(',') : undefined,
|
|
72
79
|
});
|
|
73
80
|
let normalized = issues.map((entry) => normalizeIssue(entry));
|
|
74
|
-
if (statusFilter.length >
|
|
75
|
-
const
|
|
76
|
-
normalized = normalized.filter((issue) =>
|
|
81
|
+
if (statusFilter.length > 0) {
|
|
82
|
+
const wanted = new Set(statusFilter.map(canonicalIssueStatus));
|
|
83
|
+
normalized = normalized.filter((issue) => wanted.has(canonicalIssueStatus(issue.status)));
|
|
77
84
|
}
|
|
78
85
|
if (typeof input.limit === 'number' && Number.isFinite(input.limit)) {
|
|
79
86
|
normalized = normalized.slice(0, Math.max(0, Math.floor(input.limit)));
|
|
@@ -125,6 +132,9 @@ export class IssuesApi {
|
|
|
125
132
|
throw new Error('issueId is required');
|
|
126
133
|
}
|
|
127
134
|
const params = {};
|
|
135
|
+
if (patch.projectId !== undefined) {
|
|
136
|
+
params.projectId = patch.projectId;
|
|
137
|
+
}
|
|
128
138
|
if (patch.title !== undefined) {
|
|
129
139
|
params.title = patch.title;
|
|
130
140
|
}
|
|
@@ -140,6 +150,9 @@ export class IssuesApi {
|
|
|
140
150
|
if (patch.status !== undefined) {
|
|
141
151
|
params.status = patch.status;
|
|
142
152
|
}
|
|
153
|
+
if (patch.globalBackend !== undefined) {
|
|
154
|
+
params.globalBackend = patch.globalBackend;
|
|
155
|
+
}
|
|
143
156
|
// We always include audit metadata on writes. If the caller passes a
|
|
144
157
|
// `metadata` patch, we merge our audit keys with theirs (caller wins).
|
|
145
158
|
params.metadata = buildAuditMetadata(patch.metadata, this.options);
|
|
@@ -157,8 +170,8 @@ export class IssuesApi {
|
|
|
157
170
|
});
|
|
158
171
|
}
|
|
159
172
|
// Pull the existing metadata so we can merge `qa.evidence` without
|
|
160
|
-
// clobbering
|
|
161
|
-
// we have to round-trip the current state (matches RFC 0025 §3 wording
|
|
173
|
+
// clobbering sibling `qa.*` keys. The PATCH route only shallow-merges
|
|
174
|
+
// metadata, so we have to round-trip the current state (matches RFC 0025 §3 wording
|
|
162
175
|
// for `done --evidence`).
|
|
163
176
|
const existing = await this.getIssue(issueId);
|
|
164
177
|
const existingMetadata = existing.metadata && typeof existing.metadata === 'object'
|
package/dist/api/projects.d.ts
CHANGED
|
@@ -43,6 +43,8 @@ export interface CreateProjectInput {
|
|
|
43
43
|
name?: string;
|
|
44
44
|
workspacePath?: string;
|
|
45
45
|
daemonHost?: string;
|
|
46
|
+
/** Ask the daemon to mkdir `workspacePath` when it does not exist yet. */
|
|
47
|
+
createWorkspaceIfMissing?: boolean;
|
|
46
48
|
isDefault?: boolean;
|
|
47
49
|
clientRequestId?: string;
|
|
48
50
|
metadata?: Record<string, unknown>;
|
package/dist/api/projects.js
CHANGED
|
@@ -139,6 +139,9 @@ export class ProjectsApi {
|
|
|
139
139
|
if (input.daemonHost) {
|
|
140
140
|
params.daemonHost = input.daemonHost;
|
|
141
141
|
}
|
|
142
|
+
if (input.createWorkspaceIfMissing) {
|
|
143
|
+
params.createWorkspaceIfMissing = true;
|
|
144
|
+
}
|
|
142
145
|
}
|
|
143
146
|
params.metadata = metadata;
|
|
144
147
|
const summary = await this.client.createProject(params);
|
|
@@ -156,8 +159,12 @@ export class ProjectsApi {
|
|
|
156
159
|
}
|
|
157
160
|
async setProjectHidden(idOrName, hidden, options = {}) {
|
|
158
161
|
const targetId = looksLikeProjectId(idOrName) ? idOrName.trim() : (await this.resolveById(idOrName)).id;
|
|
159
|
-
|
|
160
|
-
|
|
162
|
+
// Send `hidden` only, exactly like the web UI. The project PATCH route
|
|
163
|
+
// *replaces* `project.metadata` with any `metadata` it receives, so the
|
|
164
|
+
// audit-only blob this used to send wiped the project's task labels, memos
|
|
165
|
+
// and binding data on every hide/unhide.
|
|
166
|
+
void options;
|
|
167
|
+
const payload = await this.client.patchProjectByQuery(targetId, { hidden });
|
|
161
168
|
return normalizeProject(payload);
|
|
162
169
|
}
|
|
163
170
|
async resolveProject(input = {}) {
|
package/dist/api/tasks.js
CHANGED
|
@@ -226,14 +226,15 @@ export class TasksApi {
|
|
|
226
226
|
if (typeof content !== 'string' || content.length === 0) {
|
|
227
227
|
throw new Error('content is required');
|
|
228
228
|
}
|
|
229
|
-
const
|
|
230
|
-
? { clientRequestId: options.clientRequestId, ...(options.metadata ?? {}) }
|
|
231
|
-
: options.metadata;
|
|
232
|
-
const metadata = buildAuditMetadata(userMetadata, this.options);
|
|
229
|
+
const metadata = buildAuditMetadata(options.metadata, this.options);
|
|
233
230
|
const body = {
|
|
234
231
|
content,
|
|
235
232
|
metadata,
|
|
236
233
|
};
|
|
234
|
+
// The server dedupes on the top-level key and stamps it onto the message metadata.
|
|
235
|
+
if (options.clientRequestId) {
|
|
236
|
+
body.clientRequestId = options.clientRequestId;
|
|
237
|
+
}
|
|
237
238
|
if (options.role) {
|
|
238
239
|
body.role = options.role;
|
|
239
240
|
}
|
package/dist/backend/client.js
CHANGED
|
@@ -19,6 +19,12 @@ export class BackendApiError extends Error {
|
|
|
19
19
|
this.details = details;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Server payload per summary, so `asObject()` keeps REST-only fields (hidden,
|
|
24
|
+
* mergeOptOut, gitRemoteUrl, metadata). A WeakMap keeps it off the instance's
|
|
25
|
+
* own (serialized) properties.
|
|
26
|
+
*/
|
|
27
|
+
const projectSummaryPayloads = new WeakMap();
|
|
22
28
|
export class ProjectSummary {
|
|
23
29
|
id;
|
|
24
30
|
name;
|
|
@@ -47,10 +53,13 @@ export class ProjectSummary {
|
|
|
47
53
|
if (!id) {
|
|
48
54
|
throw new Error('Project payload missing id');
|
|
49
55
|
}
|
|
50
|
-
|
|
56
|
+
const summary = new ProjectSummary(id, payload.name ?? undefined, payload.daemonHost ?? payload.daemon_host ?? null, payload.workspacePath ?? payload.workspace_path ?? null, payload.repoRoot ?? payload.repo_root ?? null, payload.worktreeBranch ?? payload.worktree_branch ?? null, payload.lastCommit ?? payload.last_commit ?? null, payload.fileCount ?? payload.file_count ?? null, payload.isDefault ?? payload.is_default ?? undefined, payload.lastCommitAt ?? payload.last_commit_at ?? null);
|
|
57
|
+
projectSummaryPayloads.set(summary, payload);
|
|
58
|
+
return summary;
|
|
51
59
|
}
|
|
52
60
|
asObject() {
|
|
53
61
|
return {
|
|
62
|
+
...projectSummaryPayloads.get(this),
|
|
54
63
|
id: this.id,
|
|
55
64
|
name: this.name,
|
|
56
65
|
daemonHost: this.daemonHost,
|
package/dist/client.js
CHANGED
|
@@ -870,7 +870,8 @@ export class ConductorClient {
|
|
|
870
870
|
: typeof data.targetReplyTo === 'string'
|
|
871
871
|
? data.targetReplyTo.trim()
|
|
872
872
|
: '';
|
|
873
|
-
|
|
873
|
+
// An empty target interrupts whatever turn is running (`/stop`).
|
|
874
|
+
if (!taskId) {
|
|
874
875
|
return false;
|
|
875
876
|
}
|
|
876
877
|
if (!this.onInterruptTurn) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@love-moon/conductor-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
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": "2799bc0"
|
|
37
37
|
}
|