@controlflow-ai/daemon 0.1.2 → 0.1.3
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/README.md +54 -6
- package/package.json +3 -1
- package/src/agent-avatar.ts +30 -0
- package/src/agent-key.ts +28 -0
- package/src/agent-permissions.ts +359 -0
- package/src/agent-runtime.ts +795 -28
- package/src/agent-workspace.ts +183 -0
- package/src/app.ts +1970 -79
- package/src/args.ts +54 -7
- package/src/cli.ts +873 -14
- package/src/client.ts +472 -10
- package/src/coco.ts +9 -40
- package/src/codex.ts +33 -5
- package/src/config.ts +28 -4
- package/src/console.ts +230 -20
- package/src/daemon-client.ts +116 -3
- package/src/daemon.ts +936 -98
- package/src/db.ts +3128 -122
- package/src/delivery-ws.ts +269 -0
- package/src/format.ts +4 -1
- package/src/lark/cli.ts +3 -3
- package/src/lark/event-router.ts +60 -4
- package/src/lark/inbound-events.ts +156 -3
- package/src/lark/server-integration.ts +659 -111
- package/src/lark/ws-daemon.ts +136 -10
- package/src/local-api.ts +545 -15
- package/src/local-auth.ts +33 -1
- package/src/message-attachments.ts +71 -0
- package/src/messaging-cli.ts +741 -0
- package/src/messaging-status.ts +669 -0
- package/src/migrations/024_agents_model.ts +10 -0
- package/src/migrations/025_room_archive.ts +44 -0
- package/src/migrations/026_project_archive.ts +44 -0
- package/src/migrations/027_agent_permission_profiles.ts +16 -0
- package/src/migrations/028_lark_websocket_restart_state.ts +16 -0
- package/src/migrations/029_held_message_drafts.ts +32 -0
- package/src/migrations/030_agent_room_read_state.ts +25 -0
- package/src/migrations/031_room_tasks.ts +29 -0
- package/src/migrations/032_room_reminders.ts +29 -0
- package/src/migrations/033_room_saved_messages.ts +25 -0
- package/src/migrations/034_agent_activity_events.ts +27 -0
- package/src/migrations/035_agent_avatars.ts +17 -0
- package/src/migrations/036_project_agent_defaults.ts +21 -0
- package/src/migrations/037_message_attachments.ts +36 -0
- package/src/migrations/038_agent_activity_room_scope.ts +64 -0
- package/src/migrations/039_message_attachments_path.ts +34 -0
- package/src/migrations/040_message_attachments_file_schema.ts +80 -0
- package/src/migrations/041_room_system_events.ts +30 -0
- package/src/migrations/042_message_attachment_file_kind.ts +52 -0
- package/src/migrations/043_room_mode_skill_registry.ts +92 -0
- package/src/migrations/044_workflow_runtime.ts +69 -0
- package/src/migrations/045_skill_repository_ownership.ts +64 -0
- package/src/migrations.ts +69 -1
- package/src/neeko.ts +40 -4
- package/src/runtime-env.ts +179 -0
- package/src/runtime-registry.ts +83 -13
- package/src/server.ts +244 -4
- package/src/token-file.ts +13 -6
- package/src/types.ts +362 -0
- package/src/workflow-runtime.ts +275 -0
- package/src/web.ts +0 -904
package/src/client.ts
CHANGED
|
@@ -1,6 +1,73 @@
|
|
|
1
1
|
import { defaultServerToken, defaultServerUrl } from './config.js';
|
|
2
|
+
import { HttpError } from './http.js';
|
|
2
3
|
import { serverAuthHeaders } from './server-auth.js';
|
|
3
|
-
import type { AgentRoomSubscriptionMode, AgentRun, AgentSession, ApiResponse, Chat, Computer, ComputerAgentAssignment, ComputerConnection, DaemonInstance, Message, MessageDelivery, ProvisionedComputer, RemoteFileList, RoomChannel, RoomParticipant, RunAction } from './types.js';
|
|
4
|
+
import type { AgentActivityEvent, AgentActivityKind, AgentDeletionResult, AgentPermissionProfile, AgentRoomSubscription, AgentRoomSubscriptionMode, AgentRun, AgentSession, AgentWorkbench, ApiResponse, Chat, Computer, ComputerAgentAssignment, ComputerConnection, ComputerDeletionImpact, ComputerDeletionResult, DaemonInstance, DeliveryContext, EnabledSkill, HeldDraftStatus, HeldMessageDraft, Message, MessageDelivery, MessageFreshnessInput, ProvisionedComputer, RemoteFileList, RoomChannel, RoomParticipant, RoomReminder, RoomReminderStatus, RoomSavedMessage, RoomTask, RoomTaskStatus, RunAction } from './types.js';
|
|
5
|
+
export type { DeliveryBacklogSummary, DeliveryConnectionStats as DeliveryConnectionStatus, DeliveryWebSocketSummary, LarkMessagingStatus, MessagingDiagnostic, MessagingHealth, MessagingHealthDomain, MessagingStatus, MessagingStatusSummary } from './messaging-status.js';
|
|
6
|
+
import type { DeliveryBacklogSummary, DeliveryConnectionStats as DeliveryConnectionStatus, MessagingHealth, MessagingStatus } from './messaging-status.js';
|
|
7
|
+
|
|
8
|
+
export interface LarkRestartResult {
|
|
9
|
+
bots: string[];
|
|
10
|
+
restarted: string[];
|
|
11
|
+
missing: string[];
|
|
12
|
+
skipped_recent?: string[];
|
|
13
|
+
skipped_ineffective?: string[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface LarkProbeEventResult {
|
|
17
|
+
app_id: string;
|
|
18
|
+
envelope: string;
|
|
19
|
+
raw_event_id: string;
|
|
20
|
+
inserted: boolean;
|
|
21
|
+
duplicate: boolean;
|
|
22
|
+
parse_ok: 0 | 1;
|
|
23
|
+
handled: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface LarkRecentEventSummary {
|
|
27
|
+
id: string;
|
|
28
|
+
received_at: string;
|
|
29
|
+
app_id: string;
|
|
30
|
+
event_type: string;
|
|
31
|
+
event_id: string;
|
|
32
|
+
parse_ok: 0 | 1;
|
|
33
|
+
bytes: number;
|
|
34
|
+
is_probe: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface LarkRepairParseResult {
|
|
38
|
+
dry_run: boolean;
|
|
39
|
+
scanned: number;
|
|
40
|
+
repaired: number;
|
|
41
|
+
unchanged: number;
|
|
42
|
+
conflicts: number;
|
|
43
|
+
errors: number;
|
|
44
|
+
rows: Array<{
|
|
45
|
+
id: string;
|
|
46
|
+
app_id: string;
|
|
47
|
+
old_event_id: string;
|
|
48
|
+
old_event_type: string;
|
|
49
|
+
new_event_id: string;
|
|
50
|
+
new_event_type: string;
|
|
51
|
+
status: 'repaired' | 'would_repair' | 'unchanged' | 'conflict' | 'error';
|
|
52
|
+
error?: string;
|
|
53
|
+
}>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface DeliveryProbeResult {
|
|
57
|
+
message: Message;
|
|
58
|
+
deliveries: MessageDelivery[];
|
|
59
|
+
notify: {
|
|
60
|
+
deliveries: number;
|
|
61
|
+
target_connections: number;
|
|
62
|
+
open_sockets: number;
|
|
63
|
+
websocket_frames: number;
|
|
64
|
+
};
|
|
65
|
+
probe: {
|
|
66
|
+
token: string;
|
|
67
|
+
agent: string;
|
|
68
|
+
room: string;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
4
71
|
|
|
5
72
|
export interface SendRequest {
|
|
6
73
|
chat?: string;
|
|
@@ -15,6 +82,102 @@ export interface SendRequest {
|
|
|
15
82
|
type?: 'message' | 'system';
|
|
16
83
|
idempotency_key?: string | null;
|
|
17
84
|
mentions?: string[];
|
|
85
|
+
mention_lint?: 'strict' | 'literal' | 'infer';
|
|
86
|
+
attachments?: SendMessageAttachmentRequest[];
|
|
87
|
+
messages?: SendBatchMessageRequest[];
|
|
88
|
+
freshness?: MessageFreshnessInput;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface SendBatchMessageRequest {
|
|
92
|
+
parent_id?: number;
|
|
93
|
+
channel_id?: string | null;
|
|
94
|
+
recipient?: string | null;
|
|
95
|
+
content: string;
|
|
96
|
+
type?: 'message' | 'system';
|
|
97
|
+
idempotency_key?: string | null;
|
|
98
|
+
mentions?: string[];
|
|
99
|
+
attachments?: SendMessageAttachmentRequest[];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface SendMessageAttachmentRequest {
|
|
103
|
+
kind: 'image' | 'file';
|
|
104
|
+
mime_type: string;
|
|
105
|
+
filename?: string | null;
|
|
106
|
+
content_base64: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface SendBatchRequest extends Omit<SendRequest, 'content' | 'recipient' | 'mentions' | 'idempotency_key' | 'type' | 'parent_id' | 'channel_id'> {
|
|
110
|
+
messages: SendBatchMessageRequest[];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface SendMessageResult {
|
|
114
|
+
message?: Message;
|
|
115
|
+
messages?: Message[];
|
|
116
|
+
deliveries?: MessageDelivery[];
|
|
117
|
+
notify?: unknown;
|
|
118
|
+
draft?: HeldMessageDraft;
|
|
119
|
+
drafts?: HeldMessageDraft[];
|
|
120
|
+
intervening_messages?: Message[];
|
|
121
|
+
invalid_mentions?: string[];
|
|
122
|
+
available_mentions?: string[];
|
|
123
|
+
mention_lint?: {
|
|
124
|
+
missing_mentions: string[];
|
|
125
|
+
available_mentions: string[];
|
|
126
|
+
message: string;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface HeldDraftResolutionResult {
|
|
131
|
+
draft: HeldMessageDraft;
|
|
132
|
+
message?: Message;
|
|
133
|
+
deliveries?: MessageDelivery[];
|
|
134
|
+
notify?: unknown;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface ListHeldDraftsRequest {
|
|
138
|
+
agent?: string;
|
|
139
|
+
room_id?: string | null;
|
|
140
|
+
status?: HeldDraftStatus | 'all';
|
|
141
|
+
limit?: number;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface CreateRoomTasksRequest {
|
|
145
|
+
title?: string;
|
|
146
|
+
tasks?: Array<{ title: string }>;
|
|
147
|
+
created_by?: string | null;
|
|
148
|
+
source_message_id?: number | null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface CreateRoomReminderRequest {
|
|
152
|
+
msg_id: number;
|
|
153
|
+
title: string;
|
|
154
|
+
created_by?: string | null;
|
|
155
|
+
fire_at?: string | null;
|
|
156
|
+
delay_seconds?: number | null;
|
|
157
|
+
repeat?: string | null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface ListRoomRemindersRequest {
|
|
161
|
+
status?: RoomReminderStatus | 'all';
|
|
162
|
+
created_by?: string | null;
|
|
163
|
+
room_id?: string | null;
|
|
164
|
+
limit?: number;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export interface CreateRoomSavedMessageRequest {
|
|
168
|
+
msg_id: number;
|
|
169
|
+
saved_by: string;
|
|
170
|
+
note?: string | null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface ListRoomSavedMessagesRequest {
|
|
174
|
+
saved_by?: string | null;
|
|
175
|
+
room_id?: string | null;
|
|
176
|
+
limit?: number;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface ClaimRoomTaskRequest {
|
|
180
|
+
assignee: string;
|
|
18
181
|
}
|
|
19
182
|
|
|
20
183
|
export interface DaemonAuth {
|
|
@@ -65,6 +228,15 @@ export interface ProvisionComputerRequest {
|
|
|
65
228
|
package_name?: string;
|
|
66
229
|
}
|
|
67
230
|
|
|
231
|
+
export interface UpdateComputerRequest {
|
|
232
|
+
name: string;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export interface RegenerateComputerCommandRequest {
|
|
236
|
+
server_url?: string;
|
|
237
|
+
package_name?: string;
|
|
238
|
+
}
|
|
239
|
+
|
|
68
240
|
export interface CreateDeliveryRequest {
|
|
69
241
|
message_id: number;
|
|
70
242
|
agent: string;
|
|
@@ -75,6 +247,7 @@ export interface ClaimDeliveryRequest {
|
|
|
75
247
|
connection_id?: string | null;
|
|
76
248
|
computer_id?: string | null;
|
|
77
249
|
lease_ms?: number;
|
|
250
|
+
steer_run_id?: string | null;
|
|
78
251
|
}
|
|
79
252
|
|
|
80
253
|
export interface GetOrCreateSessionRequest {
|
|
@@ -106,6 +279,23 @@ export interface FinishRunRequest {
|
|
|
106
279
|
output?: string;
|
|
107
280
|
}
|
|
108
281
|
|
|
282
|
+
export interface RecordAgentActivityRequest {
|
|
283
|
+
kind: AgentActivityKind;
|
|
284
|
+
title: string;
|
|
285
|
+
detail?: string | null;
|
|
286
|
+
metadata?: Record<string, unknown> | null;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export interface CreateHandoffRoomRequest {
|
|
290
|
+
source_room_id: string;
|
|
291
|
+
source_message_id?: number;
|
|
292
|
+
actor: string;
|
|
293
|
+
purpose: string;
|
|
294
|
+
name: string;
|
|
295
|
+
team?: Array<{ agent: string; mode?: AgentRoomSubscriptionMode }>;
|
|
296
|
+
handoff_content?: string;
|
|
297
|
+
}
|
|
298
|
+
|
|
109
299
|
export interface CreateArtifactRequest {
|
|
110
300
|
content_base64: string;
|
|
111
301
|
mime_type: string;
|
|
@@ -132,13 +322,17 @@ export class LockClient {
|
|
|
132
322
|
return data.chats;
|
|
133
323
|
}
|
|
134
324
|
|
|
135
|
-
async listRooms(): Promise<Chat[]> {
|
|
136
|
-
const
|
|
325
|
+
async listRooms(agentScope?: string): Promise<Chat[]> {
|
|
326
|
+
const params = new URLSearchParams();
|
|
327
|
+
if (agentScope) params.set('agent_scope', agentScope);
|
|
328
|
+
const data = await this.get<{ rooms: Chat[] }>(`/api/rooms${params.size ? `?${params}` : ''}`);
|
|
137
329
|
return data.rooms;
|
|
138
330
|
}
|
|
139
331
|
|
|
140
|
-
async listRoomMembers(room: string): Promise<{ room: Chat; participants: RoomParticipant[]; completeness: string }> {
|
|
141
|
-
const
|
|
332
|
+
async listRoomMembers(room: string, agent?: string): Promise<{ room: Chat; participants: RoomParticipant[]; agent_subscriptions?: AgentRoomSubscription[]; enabled_skills?: EnabledSkill[]; completeness: string }> {
|
|
333
|
+
const params = new URLSearchParams();
|
|
334
|
+
if (agent) params.set('agent', agent);
|
|
335
|
+
const data = await this.get<{ room: Chat; participants: RoomParticipant[]; agent_subscriptions?: AgentRoomSubscription[]; enabled_skills?: EnabledSkill[]; completeness: string }>(`/api/rooms/${encodeURIComponent(room)}/members${params.size ? `?${params}` : ''}`);
|
|
142
336
|
return data;
|
|
143
337
|
}
|
|
144
338
|
|
|
@@ -146,32 +340,183 @@ export class LockClient {
|
|
|
146
340
|
return this.post<{ participant: RoomParticipant; subscription: unknown }>(`/api/rooms/${encodeURIComponent(room)}/agents`, input);
|
|
147
341
|
}
|
|
148
342
|
|
|
343
|
+
async createHandoffRoom(input: CreateHandoffRoomRequest): Promise<{ room: Chat; message: Message | null; deliveries: MessageDelivery[]; notify: unknown }> {
|
|
344
|
+
return this.post<{ room: Chat; message: Message | null; deliveries: MessageDelivery[]; notify: unknown }>('/api/rooms/handoff', input);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async leaveAgentRoom(room: string, agent: string): Promise<{ room: Chat; agent: string; participant_removed: boolean; subscriptions_removed: number; active_deliveries_canceled: number }> {
|
|
348
|
+
return this.delete<{ room: Chat; agent: string; participant_removed: boolean; subscriptions_removed: number; active_deliveries_canceled: number }>(`/api/rooms/${encodeURIComponent(room)}/agents/${encodeURIComponent(agent)}`);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async deleteAgent(agent: string, input: { confirm_delete: boolean; leave_rooms: boolean; unbind_lark: boolean } = { confirm_delete: true, leave_rooms: true, unbind_lark: true }): Promise<{ deletion: AgentDeletionResult; lark: { unbound: boolean; app_id: string | null; error: string | null } }> {
|
|
352
|
+
return this.delete<{ deletion: AgentDeletionResult; lark: { unbound: boolean; app_id: string | null; error: string | null } }>(`/api/agents/${encodeURIComponent(agent)}`, undefined, input);
|
|
353
|
+
}
|
|
354
|
+
|
|
149
355
|
async createTopic(room: string, input: { name: string; created_by?: string | null }): Promise<RoomChannel> {
|
|
150
356
|
const data = await this.post<{ channel: RoomChannel }>(`/api/rooms/${encodeURIComponent(room)}/topics`, input);
|
|
151
357
|
return data.channel;
|
|
152
358
|
}
|
|
153
359
|
|
|
360
|
+
async listRoomTasks(room: string, status: RoomTaskStatus | 'all' = 'all', limit = 50): Promise<RoomTask[]> {
|
|
361
|
+
const params = new URLSearchParams({ status, limit: String(limit) });
|
|
362
|
+
const data = await this.get<{ tasks: RoomTask[] }>(`/api/rooms/${encodeURIComponent(room)}/tasks?${params}`);
|
|
363
|
+
return data.tasks;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async createRoomTasks(room: string, input: CreateRoomTasksRequest): Promise<RoomTask[]> {
|
|
367
|
+
const data = await this.post<{ tasks: RoomTask[] }>(`/api/rooms/${encodeURIComponent(room)}/tasks`, input);
|
|
368
|
+
return data.tasks;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async claimRoomTask(room: string, taskNumber: number, input: ClaimRoomTaskRequest): Promise<RoomTask> {
|
|
372
|
+
const data = await this.post<{ task: RoomTask }>(`/api/rooms/${encodeURIComponent(room)}/tasks/${taskNumber}/claim`, input);
|
|
373
|
+
return data.task;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
async unclaimRoomTask(room: string, taskNumber: number): Promise<RoomTask> {
|
|
377
|
+
const data = await this.post<{ task: RoomTask }>(`/api/rooms/${encodeURIComponent(room)}/tasks/${taskNumber}/unclaim`, {});
|
|
378
|
+
return data.task;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
async updateRoomTaskStatus(room: string, taskNumber: number, status: RoomTaskStatus): Promise<RoomTask> {
|
|
382
|
+
const data = await this.patch<{ task: RoomTask }>(`/api/rooms/${encodeURIComponent(room)}/tasks/${taskNumber}`, { status });
|
|
383
|
+
return data.task;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
async restartRoomAgent(room: string, agent: string): Promise<AgentRun> {
|
|
387
|
+
const data = await this.post<{ run: AgentRun }>(`/api/rooms/${encodeURIComponent(room)}/agents/${encodeURIComponent(agent)}/restart`, {});
|
|
388
|
+
return data.run;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
async scheduleReminder(input: CreateRoomReminderRequest): Promise<RoomReminder> {
|
|
392
|
+
const data = await this.post<{ reminder: RoomReminder }>('/api/reminders', input);
|
|
393
|
+
return data.reminder;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
async listReminders(input: ListRoomRemindersRequest = {}): Promise<RoomReminder[]> {
|
|
397
|
+
const params = new URLSearchParams();
|
|
398
|
+
params.set('status', input.status ?? 'scheduled');
|
|
399
|
+
if (input.created_by) params.set('created_by', input.created_by);
|
|
400
|
+
if (input.room_id) params.set('room_id', input.room_id);
|
|
401
|
+
params.set('limit', String(input.limit ?? 50));
|
|
402
|
+
const data = await this.get<{ reminders: RoomReminder[] }>(`/api/reminders?${params}`);
|
|
403
|
+
return data.reminders;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
async cancelReminder(id: string): Promise<RoomReminder> {
|
|
407
|
+
const data = await this.post<{ reminder: RoomReminder }>(`/api/reminders/${encodeURIComponent(id)}/cancel`, {});
|
|
408
|
+
return data.reminder;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
async saveMessage(input: CreateRoomSavedMessageRequest): Promise<RoomSavedMessage> {
|
|
412
|
+
const data = await this.post<{ saved_message: RoomSavedMessage }>('/api/saved-messages', input);
|
|
413
|
+
return data.saved_message;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
async listSavedMessages(input: ListRoomSavedMessagesRequest = {}): Promise<RoomSavedMessage[]> {
|
|
417
|
+
const params = new URLSearchParams();
|
|
418
|
+
if (input.saved_by) params.set('saved_by', input.saved_by);
|
|
419
|
+
if (input.room_id) params.set('room_id', input.room_id);
|
|
420
|
+
params.set('limit', String(input.limit ?? 50));
|
|
421
|
+
const data = await this.get<{ saved_messages: RoomSavedMessage[] }>(`/api/saved-messages?${params}`);
|
|
422
|
+
return data.saved_messages;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
async removeSavedMessage(id: string): Promise<RoomSavedMessage> {
|
|
426
|
+
const data = await this.post<{ saved_message: RoomSavedMessage }>(`/api/saved-messages/${encodeURIComponent(id)}/delete`, {});
|
|
427
|
+
return data.saved_message;
|
|
428
|
+
}
|
|
429
|
+
|
|
154
430
|
async getMessages(params: URLSearchParams): Promise<Message[]> {
|
|
155
431
|
const data = await this.get<{ messages: Message[] }>(`/api/messages?${params}`);
|
|
156
432
|
return data.messages;
|
|
157
433
|
}
|
|
158
434
|
|
|
435
|
+
async getDeliveryContext(input: { agent: string; chatId: string; messageId: number; limit?: number }): Promise<DeliveryContext> {
|
|
436
|
+
const params = new URLSearchParams({
|
|
437
|
+
agent: input.agent,
|
|
438
|
+
chat_id: input.chatId,
|
|
439
|
+
message_id: String(input.messageId),
|
|
440
|
+
limit: String(input.limit ?? 50),
|
|
441
|
+
});
|
|
442
|
+
const data = await this.get<{ context: DeliveryContext }>(`/api/messages/context?${params}`);
|
|
443
|
+
return data.context;
|
|
444
|
+
}
|
|
445
|
+
|
|
159
446
|
async getInbox(agent: string, after = 0, limit = 50): Promise<Message[]> {
|
|
160
447
|
const params = new URLSearchParams({ agent, after: String(after), limit: String(limit) });
|
|
161
448
|
const data = await this.get<{ messages: Message[] }>(`/api/inbox?${params}`);
|
|
162
449
|
return data.messages;
|
|
163
450
|
}
|
|
164
451
|
|
|
165
|
-
async getMessage(id: number): Promise<Message> {
|
|
166
|
-
const
|
|
452
|
+
async getMessage(id: number, agentScope?: string): Promise<Message> {
|
|
453
|
+
const suffix = agentScope ? `?agent_scope=${encodeURIComponent(agentScope)}` : '';
|
|
454
|
+
const data = await this.get<{ message: Message }>(`/api/messages/${id}${suffix}`);
|
|
167
455
|
return data.message;
|
|
168
456
|
}
|
|
169
457
|
|
|
458
|
+
async getMessageAttachmentContent(id: string, agentScope?: string): Promise<{ content: Uint8Array; mimeType: string; filename: string }> {
|
|
459
|
+
const suffix = agentScope ? `?agent_scope=${encodeURIComponent(agentScope)}` : '';
|
|
460
|
+
const response = await fetch(`${this.baseUrl}/api/message-attachments/${encodeURIComponent(id)}/content${suffix}`);
|
|
461
|
+
if (!response.ok) {
|
|
462
|
+
let message = `request failed: ${response.status}`;
|
|
463
|
+
let code = 'REQUEST_FAILED';
|
|
464
|
+
try {
|
|
465
|
+
const payload = await response.json() as ApiResponse<unknown>;
|
|
466
|
+
if ('code' in payload && typeof payload.code === 'string') code = payload.code;
|
|
467
|
+
message = payload.message ?? message;
|
|
468
|
+
} catch {
|
|
469
|
+
// Binary endpoint errors should be JSON, but keep a fallback.
|
|
470
|
+
}
|
|
471
|
+
throw new HttpError(response.status, code, message);
|
|
472
|
+
}
|
|
473
|
+
const filename = response.headers.get('content-disposition')?.match(/filename="([^"]+)"/)?.[1] ?? id;
|
|
474
|
+
return {
|
|
475
|
+
content: new Uint8Array(await response.arrayBuffer()),
|
|
476
|
+
mimeType: response.headers.get('content-type') ?? 'application/octet-stream',
|
|
477
|
+
filename,
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
|
|
170
481
|
async sendMessage(input: SendRequest): Promise<Message> {
|
|
171
|
-
const data = await this.
|
|
482
|
+
const data = await this.sendMessageResult(input);
|
|
483
|
+
if (!data.message) throw new HttpError(202, 'MESSAGE_HELD', 'message was held as a draft');
|
|
172
484
|
return data.message;
|
|
173
485
|
}
|
|
174
486
|
|
|
487
|
+
async sendMessageResult(input: SendRequest): Promise<SendMessageResult> {
|
|
488
|
+
return this.post<SendMessageResult>('/api/messages', input);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
async getAgentWorkbench(agent: string): Promise<AgentWorkbench> {
|
|
492
|
+
const data = await this.get<{ workbench: AgentWorkbench }>(`/api/agents/${encodeURIComponent(agent)}/workbench`);
|
|
493
|
+
return data.workbench;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
async listHeldDrafts(input: ListHeldDraftsRequest = {}): Promise<HeldMessageDraft[]> {
|
|
497
|
+
const agent = input.agent?.trim();
|
|
498
|
+
if (!agent) throw new Error('agent is required');
|
|
499
|
+
const workbench = await this.getAgentWorkbench(agent);
|
|
500
|
+
const status = input.status ?? 'held';
|
|
501
|
+
const limit = input.limit ?? 50;
|
|
502
|
+
return workbench.held_drafts
|
|
503
|
+
.filter((draft) => status === 'all' || draft.status === status)
|
|
504
|
+
.filter((draft) => !input.room_id || draft.chat_id === input.room_id)
|
|
505
|
+
.slice(0, limit);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
async abandonHeldDraft(id: string): Promise<HeldDraftResolutionResult> {
|
|
509
|
+
return this.post<HeldDraftResolutionResult>(`/api/held-drafts/${encodeURIComponent(id)}/abandon`, {});
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
async sendHeldDraftAnyway(id: string): Promise<HeldDraftResolutionResult> {
|
|
513
|
+
return this.post<HeldDraftResolutionResult>(`/api/held-drafts/${encodeURIComponent(id)}/send-anyway`, {});
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
async reviseHeldDraft(id: string, content: string): Promise<HeldDraftResolutionResult> {
|
|
517
|
+
return this.post<HeldDraftResolutionResult>(`/api/held-drafts/${encodeURIComponent(id)}/revise`, { content });
|
|
518
|
+
}
|
|
519
|
+
|
|
175
520
|
async provisionComputer(input: ProvisionComputerRequest = {}): Promise<ProvisionedComputer> {
|
|
176
521
|
const data = await this.post<ProvisionedComputer>('/api/computers/provision', input);
|
|
177
522
|
return data;
|
|
@@ -182,6 +527,29 @@ export class LockClient {
|
|
|
182
527
|
return data.computers;
|
|
183
528
|
}
|
|
184
529
|
|
|
530
|
+
async updateComputer(computerId: string, input: UpdateComputerRequest): Promise<Computer> {
|
|
531
|
+
const data = await this.patch<{ computer: Computer }>(`/api/computers/${encodeURIComponent(computerId)}`, input);
|
|
532
|
+
return data.computer;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
async regenerateComputerCommand(computerId: string, input: RegenerateComputerCommandRequest = {}): Promise<ProvisionedComputer> {
|
|
536
|
+
return this.post<ProvisionedComputer>(`/api/computers/${encodeURIComponent(computerId)}/reconnect-command`, input);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
async getComputerDeleteImpact(computerId: string): Promise<ComputerDeletionImpact> {
|
|
540
|
+
const data = await this.get<{ impact: ComputerDeletionImpact }>(`/api/computers/${encodeURIComponent(computerId)}/delete-impact`);
|
|
541
|
+
return data.impact;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
async deleteComputer(computerId: string, input: { remove_projects?: boolean; remove_agents?: boolean } = {}): Promise<ComputerDeletionResult & { deleted: boolean }> {
|
|
545
|
+
return this.delete<ComputerDeletionResult & { deleted: boolean }>(`/api/computers/${encodeURIComponent(computerId)}`, undefined, input);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
async fetchPermissionProfile(agent: string): Promise<AgentPermissionProfile> {
|
|
549
|
+
const data = await this.get<{ profile: AgentPermissionProfile }>(`/api/agents/${encodeURIComponent(agent)}/permissions`);
|
|
550
|
+
return data.profile;
|
|
551
|
+
}
|
|
552
|
+
|
|
185
553
|
async connectComputer(input: ConnectComputerRequest): Promise<{ computer: Computer; connection: ComputerConnection; token: string; local_control_token?: string; daemon: DaemonInstance; agents: ComputerAgentAssignment[] }> {
|
|
186
554
|
const data = await this.post<{ computer: Computer; connection: ComputerConnection; token: string; local_control_token?: string; daemon: DaemonInstance; agents: ComputerAgentAssignment[] }>('/api/computers/connect', input);
|
|
187
555
|
return data;
|
|
@@ -205,12 +573,80 @@ export class LockClient {
|
|
|
205
573
|
return data.daemon;
|
|
206
574
|
}
|
|
207
575
|
|
|
208
|
-
async listDeliveries(agent: string, status = 'pending', limit = 50): Promise<MessageDelivery[]> {
|
|
576
|
+
async listDeliveries(agent: string, status = 'pending', limit = 50, options: { distinctChat?: boolean; excludeRunningComputerId?: string | null; connectionId?: string | null } = {}): Promise<MessageDelivery[]> {
|
|
209
577
|
const params = new URLSearchParams({ agent, status, limit: String(limit) });
|
|
578
|
+
if (options.distinctChat) params.set('distinct_chat', 'true');
|
|
579
|
+
if (options.excludeRunningComputerId) params.set('exclude_running_computer_id', options.excludeRunningComputerId);
|
|
580
|
+
if (options.connectionId) params.set('connection_id', options.connectionId);
|
|
210
581
|
const data = await this.get<{ deliveries: MessageDelivery[] }>(`/api/deliveries?${params}`);
|
|
211
582
|
return data.deliveries;
|
|
212
583
|
}
|
|
213
584
|
|
|
585
|
+
async getDelivery(id: string): Promise<MessageDelivery> {
|
|
586
|
+
const data = await this.get<{ delivery: MessageDelivery }>(`/api/deliveries/${encodeURIComponent(id)}`);
|
|
587
|
+
return data.delivery;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
async listPendingDeliveryAgents(): Promise<Array<{ agent: string; pending: number }>> {
|
|
591
|
+
const data = await this.get<{ agents: Array<{ agent: string; pending: number }> }>('/api/deliveries/pending-agents');
|
|
592
|
+
return data.agents;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
async listDeliveryBacklog(): Promise<DeliveryBacklogSummary[]> {
|
|
596
|
+
const data = await this.get<{ backlog: DeliveryBacklogSummary[] }>('/api/deliveries/backlog');
|
|
597
|
+
return data.backlog;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
async getDeliveryWebSocketStatus(): Promise<DeliveryConnectionStatus> {
|
|
601
|
+
const data = await this.get<{ websocket: DeliveryConnectionStatus }>('/api/daemon/ws/status');
|
|
602
|
+
return data.websocket;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
async getMessagingStatus(): Promise<MessagingStatus> {
|
|
606
|
+
return this.get<MessagingStatus>('/api/messaging/status');
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
async getMessagingHealth(): Promise<MessagingHealth> {
|
|
610
|
+
return this.get<MessagingHealth>('/api/messaging/health');
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
async probeDelivery(input: { agent?: string; room?: string; sender?: string; content?: string; idempotencyKey?: string | null } = {}, token = defaultServerToken()): Promise<DeliveryProbeResult> {
|
|
614
|
+
return this.post<DeliveryProbeResult>('/api/messaging/probe-delivery', {
|
|
615
|
+
agent: input.agent,
|
|
616
|
+
room: input.room,
|
|
617
|
+
sender: input.sender,
|
|
618
|
+
content: input.content,
|
|
619
|
+
idempotency_key: input.idempotencyKey,
|
|
620
|
+
}, serverAuthHeaders(token));
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
async restartLarkWebSocket(options: { appId?: string; appIds?: string[] } = {}): Promise<LarkRestartResult> {
|
|
624
|
+
const body = options.appId ? { app_id: options.appId } : options.appIds ? { app_ids: options.appIds } : {};
|
|
625
|
+
return this.post<LarkRestartResult>('/api/lark/restart', body);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
async probeLarkEvent(input: { appId: string; envelope?: string; data: unknown }, token = defaultServerToken()): Promise<LarkProbeEventResult> {
|
|
629
|
+
return this.post<LarkProbeEventResult>('/api/lark/probe-event', {
|
|
630
|
+
app_id: input.appId,
|
|
631
|
+
envelope: input.envelope,
|
|
632
|
+
data: input.data,
|
|
633
|
+
}, serverAuthHeaders(token));
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
async listRecentLarkEvents(limit = 20): Promise<LarkRecentEventSummary[]> {
|
|
637
|
+
const params = new URLSearchParams({ limit: String(limit) });
|
|
638
|
+
const data = await this.get<{ events: LarkRecentEventSummary[] }>(`/api/lark/events/recent?${params}`);
|
|
639
|
+
return data.events;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
async repairLarkEventParseFailures(input: { appId?: string; limit?: number; dryRun?: boolean } = {}, token = defaultServerToken()): Promise<LarkRepairParseResult> {
|
|
643
|
+
return this.post<LarkRepairParseResult>('/api/lark/events/repair-parse', {
|
|
644
|
+
app_id: input.appId,
|
|
645
|
+
limit: input.limit,
|
|
646
|
+
dry_run: input.dryRun,
|
|
647
|
+
}, serverAuthHeaders(token));
|
|
648
|
+
}
|
|
649
|
+
|
|
214
650
|
async createDelivery(input: CreateDeliveryRequest): Promise<MessageDelivery> {
|
|
215
651
|
const data = await this.post<{ delivery: MessageDelivery }>('/api/deliveries', input);
|
|
216
652
|
return data.delivery;
|
|
@@ -226,6 +662,11 @@ export class LockClient {
|
|
|
226
662
|
return data.delivery;
|
|
227
663
|
}
|
|
228
664
|
|
|
665
|
+
async markDeliveryProcessingCompleted(id: string, input: FinishDeliveryRequest): Promise<MessageDelivery> {
|
|
666
|
+
const data = await this.post<{ delivery: MessageDelivery }>(`/api/deliveries/${id}/processing-completed`, input);
|
|
667
|
+
return data.delivery;
|
|
668
|
+
}
|
|
669
|
+
|
|
229
670
|
async getOrCreateSession(input: GetOrCreateSessionRequest): Promise<AgentSession> {
|
|
230
671
|
const data = await this.post<{ session: AgentSession }>('/api/sessions', input);
|
|
231
672
|
return data.session;
|
|
@@ -271,6 +712,11 @@ export class LockClient {
|
|
|
271
712
|
return data.run;
|
|
272
713
|
}
|
|
273
714
|
|
|
715
|
+
async recordRunActivity(runId: string, input: RecordAgentActivityRequest): Promise<AgentActivityEvent> {
|
|
716
|
+
const data = await this.post<{ event: AgentActivityEvent }>(`/api/runs/${runId}/activity`, input);
|
|
717
|
+
return data.event;
|
|
718
|
+
}
|
|
719
|
+
|
|
274
720
|
async finishRun(runId: string, input: FinishRunRequest): Promise<AgentRun> {
|
|
275
721
|
const data = await this.post<{ run: AgentRun }>(`/api/runs/${runId}/finish`, input);
|
|
276
722
|
return data.run;
|
|
@@ -298,6 +744,22 @@ export class LockClient {
|
|
|
298
744
|
});
|
|
299
745
|
}
|
|
300
746
|
|
|
747
|
+
private async patch<T>(path: string, body: unknown, headers?: HeadersInit): Promise<T> {
|
|
748
|
+
return this.request<T>(path, {
|
|
749
|
+
method: 'PATCH',
|
|
750
|
+
headers: { 'content-type': 'application/json', ...this.daemonAuthHeaders(), ...headers },
|
|
751
|
+
body: JSON.stringify(body),
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
private async delete<T>(path: string, headers?: HeadersInit, body?: unknown): Promise<T> {
|
|
756
|
+
return this.request<T>(path, {
|
|
757
|
+
method: 'DELETE',
|
|
758
|
+
headers: { ...(body === undefined ? {} : { 'content-type': 'application/json' }), ...this.daemonAuthHeaders(), ...headers },
|
|
759
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
|
|
301
763
|
private daemonAuthHeaders(): HeadersInit {
|
|
302
764
|
if (!this.daemonAuth) return {};
|
|
303
765
|
return {
|
|
@@ -311,7 +773,7 @@ export class LockClient {
|
|
|
311
773
|
const response = await fetch(`${this.baseUrl}${path}`, init);
|
|
312
774
|
const payload = await response.json() as ApiResponse<T>;
|
|
313
775
|
if (!response.ok || !payload.ok) {
|
|
314
|
-
throw new
|
|
776
|
+
throw new HttpError(response.status, 'code' in payload && typeof payload.code === 'string' ? payload.code : 'REQUEST_FAILED', payload.message ?? `request failed: ${response.status}`);
|
|
315
777
|
}
|
|
316
778
|
return payload.data as T;
|
|
317
779
|
}
|
package/src/coco.ts
CHANGED
|
@@ -1,52 +1,21 @@
|
|
|
1
1
|
import { buildPalPrompt, runtimeCwd, type AgentRuntime, type AgentRuntimeRunInput } from './agent-runtime.js';
|
|
2
|
+
import { buildCocoPermissionArgs, buildRuntimeLaunchContext, effectivePermissionProfile } from './agent-permissions.js';
|
|
2
3
|
export { runAgentRuntime } from './agent-runtime.js';
|
|
3
4
|
|
|
4
|
-
function
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
try {
|
|
9
|
-
const event = JSON.parse(trimmed) as { session_id?: unknown };
|
|
10
|
-
if (typeof event.session_id === 'string' && event.session_id.trim()) {
|
|
11
|
-
return event.session_id;
|
|
12
|
-
}
|
|
13
|
-
} catch {
|
|
14
|
-
// Non-JSON stdout is kept in output; it just is not a JSONL event.
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function makeCocoRuntime(_agentUuid: string): AgentRuntime {
|
|
5
|
+
export function makeCocoRuntime(_agentUuid: string, model?: string | null): AgentRuntime {
|
|
6
|
+
// Coco ACP exits before initialize when passed --model. Per-invocation model
|
|
7
|
+
// selection works through config overrides.
|
|
8
|
+
const modelArgs = model?.trim() ? ['-c', `model.name=${model.trim()}`] : [];
|
|
21
9
|
return {
|
|
22
10
|
name: 'coco',
|
|
23
|
-
capabilities: { protocol: 'acp', resume: 'runtime-session-id', busyDeliveryMode: 'queue', supportsMcp: true },
|
|
24
|
-
command: 'coco',
|
|
25
|
-
buildPrompt: buildPalPrompt,
|
|
26
|
-
buildCwd: runtimeCwd,
|
|
27
|
-
buildArgs(input: AgentRuntimeRunInput): string[] {
|
|
28
|
-
return ['acp', 'serve', '-y', ...input.extraArgs];
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function makeCocoStreamJsonRuntime(_agentUuid: string): AgentRuntime {
|
|
34
|
-
return {
|
|
35
|
-
name: 'coco-stream-json',
|
|
36
|
-
capabilities: { protocol: 'json-stream', resume: 'runtime-session-id', busyDeliveryMode: 'queue', supportsMcp: false },
|
|
11
|
+
capabilities: { protocol: 'acp', resume: 'runtime-session-id', busyDeliveryMode: 'queue', supportsMcp: true, supportsSteer: false },
|
|
37
12
|
command: 'coco',
|
|
38
13
|
buildPrompt: buildPalPrompt,
|
|
39
14
|
buildCwd: runtimeCwd,
|
|
40
15
|
buildArgs(input: AgentRuntimeRunInput): string[] {
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
return ['
|
|
44
|
-
},
|
|
45
|
-
parseOutput({ stdout, stderr, input }) {
|
|
46
|
-
return {
|
|
47
|
-
output: [stdout.trim(), stderr.trim()].filter(Boolean).join('\n'),
|
|
48
|
-
runtimeSessionId: extractSessionId(stdout) ?? input.runtimeSessionId ?? null,
|
|
49
|
-
};
|
|
16
|
+
const context = input.launchContext ?? buildRuntimeLaunchContext(input);
|
|
17
|
+
const permissionArgs = buildCocoPermissionArgs(effectivePermissionProfile(input), context);
|
|
18
|
+
return ['acp', 'serve', ...permissionArgs, ...modelArgs, ...input.extraArgs];
|
|
50
19
|
},
|
|
51
20
|
};
|
|
52
21
|
}
|