ticlawk 0.1.17-dev.2 → 0.1.17-dev.20
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 +26 -59
- package/bin/ticlawk.mjs +31 -301
- package/package.json +4 -2
- package/scripts/publish-dev.sh +77 -0
- package/src/adapters/ticlawk/api.mjs +50 -378
- package/src/adapters/ticlawk/credentials.mjs +1 -43
- package/src/adapters/ticlawk/index.mjs +61 -565
- package/src/adapters/ticlawk/wake-client.mjs +1 -1
- package/src/cli/agent-commands.mjs +18 -715
- package/src/core/adapter-registry.mjs +1 -19
- package/src/core/agent-cli-handlers.mjs +18 -556
- package/src/core/agent-home.mjs +1 -81
- package/src/core/events/worker-events.mjs +36 -32
- package/src/core/http.mjs +0 -152
- package/src/core/profiles.mjs +0 -1
- package/src/core/runtime-contract.mjs +1 -0
- package/src/core/runtime-env.mjs +0 -8
- package/src/core/runtime-support.mjs +78 -130
- package/src/runtimes/_shared/incoming-message-prompt.mjs +232 -0
- package/src/runtimes/_shared/runtime-base-instructions.mjs +34 -0
- package/src/runtimes/claude-code/index.mjs +48 -21
- package/src/runtimes/claude-code/session.mjs +7 -2
- package/src/runtimes/codex/index.mjs +64 -116
- package/src/runtimes/codex/session.mjs +12 -2
- package/src/runtimes/openclaw/index.mjs +30 -17
- package/src/runtimes/opencode/index.mjs +64 -42
- package/src/runtimes/opencode/session.mjs +14 -14
- package/src/runtimes/pi/index.mjs +64 -42
- package/src/runtimes/pi/session.mjs +8 -11
- package/ticlawk.mjs +32 -5
- package/src/runtimes/_shared/agent-handbook.mjs +0 -45
- package/src/runtimes/_shared/brand.mjs +0 -2
- package/src/runtimes/_shared/goal-step-prompt.mjs +0 -98
- package/src/runtimes/_shared/goal-task-protocol.mjs +0 -50
- package/src/runtimes/_shared/handbook/BASICS.md +0 -27
- package/src/runtimes/_shared/handbook/COLLABORATION.md +0 -37
- package/src/runtimes/_shared/handbook/COMMUNICATION.md +0 -55
- package/src/runtimes/_shared/handbook/DM_SCOPE.md +0 -13
- package/src/runtimes/_shared/handbook/GOAL_AUTHORITY.md +0 -47
- package/src/runtimes/_shared/handbook/GOAL_TASK_CORE.md +0 -43
- package/src/runtimes/_shared/handbook/GROUP_ADMIN_SCOPE.md +0 -21
- package/src/runtimes/_shared/handbook/GROUP_MEMBER_SCOPE.md +0 -15
- package/src/runtimes/_shared/handbook/SURFACES.md +0 -41
- package/src/runtimes/_shared/handbook/TASK_WORKER.md +0 -14
- package/src/runtimes/_shared/standing-prompt.mjs +0 -171
- package/src/runtimes/_shared/wake-prompt.mjs +0 -268
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
* and forwards to the ticlawk backend using the connector API key.
|
|
8
8
|
*
|
|
9
9
|
* Targets are parsed in the daemon (not on the wire to backend) so the
|
|
10
|
-
* CLI can speak `#<group>` / `dm
|
|
11
|
-
* `#<group>:<short-msg-id>`
|
|
10
|
+
* CLI can speak `#<group>` / `dm:@<user>` / `#<group>:<short-msg-id>`
|
|
12
11
|
* while backend keeps a flat conversation_id contract.
|
|
13
12
|
*/
|
|
14
13
|
|
|
@@ -35,7 +34,7 @@ export function invalidateServerInfoCache(actingAgentId = null) {
|
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
/**
|
|
38
|
-
* Parse a target string into { conversationId,
|
|
37
|
+
* Parse a target string into { conversationId, threadRootMsgId } using a
|
|
39
38
|
* cached server-info lookup. Returns null fields if the target cannot be
|
|
40
39
|
* resolved; callers should treat that as a 404.
|
|
41
40
|
*
|
|
@@ -44,29 +43,29 @@ export function invalidateServerInfoCache(actingAgentId = null) {
|
|
|
44
43
|
* dm:@<handle> -> find DM conversation whose other member is <handle>
|
|
45
44
|
* #<uuid> -> conversation_id = <uuid>
|
|
46
45
|
* #<group-name> -> find group conversation by name
|
|
47
|
-
* <foo>:<short-msg-id> ->
|
|
46
|
+
* <foo>:<short-msg-id> -> thread under <foo>, root = first message whose
|
|
48
47
|
* id startsWith <short-msg-id>
|
|
49
48
|
*/
|
|
50
49
|
export async function resolveTarget(actingAgentId, target) {
|
|
51
|
-
if (!target) return { conversationId: null,
|
|
50
|
+
if (!target) return { conversationId: null, threadRootMsgId: null, error: 'target is required' };
|
|
52
51
|
|
|
53
|
-
// Strip optional
|
|
52
|
+
// Strip optional thread suffix.
|
|
54
53
|
let base = target;
|
|
55
|
-
let
|
|
54
|
+
let threadShort = null;
|
|
56
55
|
const colonIdx = target.indexOf(':', target.startsWith('dm:') ? 3 : 1);
|
|
57
56
|
if (colonIdx > 0 && colonIdx < target.length - 1) {
|
|
58
|
-
|
|
57
|
+
threadShort = target.slice(colonIdx + 1);
|
|
59
58
|
base = target.slice(0, colonIdx);
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
if (/^[0-9a-f-]{36}$/i.test(base)) {
|
|
63
|
-
return { conversationId: base,
|
|
62
|
+
return { conversationId: base, threadRootMsgId: threadShort, error: null };
|
|
64
63
|
}
|
|
65
64
|
if (base.startsWith('dm:') && /^[0-9a-f-]{36}$/i.test(base.slice(3))) {
|
|
66
|
-
return { conversationId: base.slice(3),
|
|
65
|
+
return { conversationId: base.slice(3), threadRootMsgId: threadShort, error: null };
|
|
67
66
|
}
|
|
68
67
|
if (base.startsWith('#') && /^[0-9a-f-]{36}$/i.test(base.slice(1))) {
|
|
69
|
-
return { conversationId: base.slice(1),
|
|
68
|
+
return { conversationId: base.slice(1), threadRootMsgId: threadShort, error: null };
|
|
70
69
|
}
|
|
71
70
|
|
|
72
71
|
const info = await getCachedServerInfo(actingAgentId);
|
|
@@ -77,30 +76,19 @@ export async function resolveTarget(actingAgentId, target) {
|
|
|
77
76
|
const match = convs.find((c) =>
|
|
78
77
|
c.type === 'dm' && (String(c.display_name || c.name || '').toLowerCase() === handle)
|
|
79
78
|
);
|
|
80
|
-
if (match) return { conversationId: match.id,
|
|
81
|
-
return { conversationId: null,
|
|
79
|
+
if (match) return { conversationId: match.id, threadRootMsgId: threadShort, error: null };
|
|
80
|
+
return { conversationId: null, threadRootMsgId: null, error: `unknown dm target: ${target}` };
|
|
82
81
|
}
|
|
83
82
|
if (base.startsWith('#')) {
|
|
84
83
|
const name = base.slice(1).toLowerCase();
|
|
85
84
|
const match = convs.find((c) =>
|
|
86
85
|
c.type === 'group' && (String(c.name || c.display_name || '').toLowerCase() === name)
|
|
87
86
|
);
|
|
88
|
-
if (match) return { conversationId: match.id,
|
|
89
|
-
|
|
90
|
-
if (info?.agent?.is_cos) {
|
|
91
|
-
const workstreams = await api.listWorkstreams({ actingAgentId });
|
|
92
|
-
const workstreamMatch = workstreams.find((c) =>
|
|
93
|
-
String(c.name || c.display_name || '').toLowerCase() === name
|
|
94
|
-
);
|
|
95
|
-
if (workstreamMatch) {
|
|
96
|
-
return { conversationId: workstreamMatch.id, replyToMessageId, error: null };
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return { conversationId: null, replyToMessageId: null, error: `unknown group target: ${target}` };
|
|
87
|
+
if (match) return { conversationId: match.id, threadRootMsgId: threadShort, error: null };
|
|
88
|
+
return { conversationId: null, threadRootMsgId: null, error: `unknown group target: ${target}` };
|
|
101
89
|
}
|
|
102
90
|
|
|
103
|
-
return { conversationId: null,
|
|
91
|
+
return { conversationId: null, threadRootMsgId: null, error: `invalid target syntax: ${target}` };
|
|
104
92
|
}
|
|
105
93
|
|
|
106
94
|
function getActingAgentId(req, body = {}) {
|
|
@@ -121,15 +109,6 @@ function getRuntimeHostId(req, body = {}) {
|
|
|
121
109
|
return null;
|
|
122
110
|
}
|
|
123
111
|
|
|
124
|
-
function getCurrentConversationId(req, body = {}) {
|
|
125
|
-
const fromHeader = req.headers['x-ticlawk-current-conversation-id'];
|
|
126
|
-
if (typeof fromHeader === 'string' && fromHeader.trim()) return fromHeader.trim();
|
|
127
|
-
if (typeof body?.current_conversation_id === 'string' && body.current_conversation_id.trim()) {
|
|
128
|
-
return body.current_conversation_id.trim();
|
|
129
|
-
}
|
|
130
|
-
return null;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
112
|
function validateActingAgent(actingAgentId, ctx) {
|
|
134
113
|
if (!actingAgentId) {
|
|
135
114
|
return { ok: false, status: 400, error: 'TICLAWK_RUNTIME_AGENT_ID required (passed via X-Ticlawk-Acting-Agent-Id or body.acting_as_agent_id)' };
|
|
@@ -144,16 +123,6 @@ function validateActingAgent(actingAgentId, ctx) {
|
|
|
144
123
|
return { ok: true };
|
|
145
124
|
}
|
|
146
125
|
|
|
147
|
-
function validateAgentResponsePhase(metadata) {
|
|
148
|
-
const phase = metadata?.agent_response_phase;
|
|
149
|
-
if (phase == null) return { ok: true, phase: null };
|
|
150
|
-
const normalized = String(phase).trim().toLowerCase();
|
|
151
|
-
if (normalized === 'progress' || normalized === 'final') {
|
|
152
|
-
return { ok: true, phase: normalized };
|
|
153
|
-
}
|
|
154
|
-
return { ok: false, error: 'metadata.agent_response_phase must be progress or final' };
|
|
155
|
-
}
|
|
156
|
-
|
|
157
126
|
export async function handleMessageSend(req, body, ctx) {
|
|
158
127
|
const actingAgentId = getActingAgentId(req, body);
|
|
159
128
|
const v = validateActingAgent(actingAgentId, ctx);
|
|
@@ -163,45 +132,22 @@ export async function handleMessageSend(req, body, ctx) {
|
|
|
163
132
|
if (!text) return { status: 400, body: { error: 'text is required' } };
|
|
164
133
|
|
|
165
134
|
let conversationId = body?.conversation_id || null;
|
|
166
|
-
let
|
|
135
|
+
let threadRootMsgId = null;
|
|
167
136
|
if (!conversationId && body?.target) {
|
|
168
137
|
const resolved = await resolveTarget(actingAgentId, String(body.target));
|
|
169
138
|
if (resolved.error) {
|
|
170
139
|
return { status: 404, body: { error: resolved.error } };
|
|
171
140
|
}
|
|
172
141
|
conversationId = resolved.conversationId;
|
|
173
|
-
|
|
142
|
+
threadRootMsgId = resolved.threadRootMsgId;
|
|
174
143
|
}
|
|
175
144
|
if (!conversationId) {
|
|
176
145
|
return { status: 400, body: { error: 'target or conversation_id is required' } };
|
|
177
146
|
}
|
|
178
147
|
|
|
179
|
-
const currentConversationId = getCurrentConversationId(req, body);
|
|
180
|
-
if (currentConversationId && currentConversationId !== conversationId && !body?.allow_cross_target) {
|
|
181
|
-
debugLog('agent-cli', 'send.blocked-cross-target', {
|
|
182
|
-
actingAgentId,
|
|
183
|
-
currentConversationId,
|
|
184
|
-
conversationId,
|
|
185
|
-
target: body?.target || null,
|
|
186
|
-
});
|
|
187
|
-
return {
|
|
188
|
-
status: 409,
|
|
189
|
-
body: {
|
|
190
|
-
error: 'refusing to send to a different conversation from the current runtime turn',
|
|
191
|
-
current_conversation_id: currentConversationId,
|
|
192
|
-
target_conversation_id: conversationId,
|
|
193
|
-
hint: 'Use --allow-cross-target only for an intentional cross-conversation send.',
|
|
194
|
-
},
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
|
|
198
148
|
const mediaAssetIds = Array.isArray(body?.media_asset_ids)
|
|
199
149
|
? body.media_asset_ids.map((v) => String(v).trim()).filter(Boolean)
|
|
200
150
|
: [];
|
|
201
|
-
const metadata = body?.metadata;
|
|
202
|
-
const phaseRes = validateAgentResponsePhase(metadata);
|
|
203
|
-
if (!phaseRes.ok) return { status: 400, body: { error: phaseRes.error } };
|
|
204
|
-
if (metadata && phaseRes.phase) metadata.agent_response_phase = phaseRes.phase;
|
|
205
151
|
|
|
206
152
|
try {
|
|
207
153
|
const data = await api.sendAgentMessage({
|
|
@@ -209,17 +155,15 @@ export async function handleMessageSend(req, body, ctx) {
|
|
|
209
155
|
conversationId,
|
|
210
156
|
text,
|
|
211
157
|
seenUpToSeq: body?.seen_up_to_seq,
|
|
212
|
-
replyToMessageId: body?.reply_to_message_id ||
|
|
158
|
+
replyToMessageId: body?.reply_to_message_id || threadRootMsgId || null,
|
|
213
159
|
runtimeHostId: getRuntimeHostId(req, body),
|
|
214
160
|
mediaAssetIds,
|
|
215
|
-
metadata,
|
|
216
161
|
});
|
|
217
162
|
debugLog('agent-cli', 'send.ok', {
|
|
218
163
|
actingAgentId,
|
|
219
164
|
conversationId,
|
|
220
165
|
messageId: data?.id,
|
|
221
166
|
seq: data?.seq,
|
|
222
|
-
agentResponsePhase: phaseRes.phase,
|
|
223
167
|
bodyChars: text.length,
|
|
224
168
|
});
|
|
225
169
|
return { status: 200, body: { ok: true, data } };
|
|
@@ -285,7 +229,6 @@ export async function handleTaskCreate(req, body, ctx) {
|
|
|
285
229
|
conversationId,
|
|
286
230
|
text,
|
|
287
231
|
title: body?.title ?? null,
|
|
288
|
-
assignAgentId: body?.assign_agent_id || body?.assignee_agent_id || null,
|
|
289
232
|
});
|
|
290
233
|
debugLog('agent-cli', 'task.create', {
|
|
291
234
|
actingAgentId,
|
|
@@ -387,127 +330,6 @@ export async function handleTaskUpdate(req, body, ctx) {
|
|
|
387
330
|
}
|
|
388
331
|
}
|
|
389
332
|
|
|
390
|
-
export async function handleGoalReport(req, body, ctx) {
|
|
391
|
-
const actingAgentId = getActingAgentId(req, body);
|
|
392
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
393
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
394
|
-
const conversationId = body?.conversation_id || getCurrentConversationId(req, body);
|
|
395
|
-
if (!conversationId) return { status: 400, body: { error: 'conversation_id is required' } };
|
|
396
|
-
if (!body?.transition_id) return { status: 400, body: { error: 'transition_id is required' } };
|
|
397
|
-
if (!body?.outcome) return { status: 400, body: { error: 'outcome is required' } };
|
|
398
|
-
try {
|
|
399
|
-
const data = await api.reportGoalTransition({
|
|
400
|
-
actingAgentId,
|
|
401
|
-
conversationId,
|
|
402
|
-
transitionId: body.transition_id,
|
|
403
|
-
outcome: body.outcome,
|
|
404
|
-
detail: body.detail || null,
|
|
405
|
-
currentTaskId: body.current_task_id || null,
|
|
406
|
-
});
|
|
407
|
-
debugLog('agent-cli', 'goal.report', {
|
|
408
|
-
actingAgentId,
|
|
409
|
-
conversationId,
|
|
410
|
-
outcome: body.outcome,
|
|
411
|
-
state: data?.state,
|
|
412
|
-
});
|
|
413
|
-
return { status: data?.ok ? 200 : 400, body: data };
|
|
414
|
-
} catch (err) {
|
|
415
|
-
return { status: err?.status || 500, body: { error: err?.message || 'goal report failed' } };
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
export async function handleGoalChanged(req, body, ctx) {
|
|
420
|
-
const actingAgentId = getActingAgentId(req, body);
|
|
421
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
422
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
423
|
-
const conversationId = body?.conversation_id || getCurrentConversationId(req, body);
|
|
424
|
-
if (!conversationId) return { status: 400, body: { error: 'conversation_id is required' } };
|
|
425
|
-
try {
|
|
426
|
-
const data = await api.noteGoalChanged({ actingAgentId, conversationId });
|
|
427
|
-
debugLog('agent-cli', 'goal.changed', {
|
|
428
|
-
actingAgentId,
|
|
429
|
-
conversationId,
|
|
430
|
-
goalVersion: data?.goal_version,
|
|
431
|
-
});
|
|
432
|
-
return { status: data?.ok ? 200 : 400, body: data };
|
|
433
|
-
} catch (err) {
|
|
434
|
-
return { status: err?.status || 500, body: { error: err?.message || 'goal changed failed' } };
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
export async function handleApprovalRequest(req, body, ctx) {
|
|
439
|
-
const actingAgentId = getActingAgentId(req, body);
|
|
440
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
441
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
442
|
-
const conversationId = body?.conversation_id || getCurrentConversationId(req, body);
|
|
443
|
-
if (!conversationId) return { status: 400, body: { error: 'conversation_id is required' } };
|
|
444
|
-
if (!body?.title) return { status: 400, body: { error: 'title is required' } };
|
|
445
|
-
try {
|
|
446
|
-
const data = await api.requestGoalApproval({
|
|
447
|
-
actingAgentId,
|
|
448
|
-
conversationId,
|
|
449
|
-
title: body.title,
|
|
450
|
-
detail: body.detail || null,
|
|
451
|
-
ttlSeconds: body.ttl_seconds || null,
|
|
452
|
-
});
|
|
453
|
-
debugLog('agent-cli', 'approval.request', {
|
|
454
|
-
actingAgentId,
|
|
455
|
-
conversationId,
|
|
456
|
-
requestId: data?.request_id,
|
|
457
|
-
});
|
|
458
|
-
return { status: data?.ok ? 200 : 400, body: data };
|
|
459
|
-
} catch (err) {
|
|
460
|
-
return { status: err?.status || 500, body: { error: err?.message || 'approval request failed' } };
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
export async function handleApprovalList(req, query, ctx) {
|
|
465
|
-
const actingAgentId = getActingAgentId(req, query);
|
|
466
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
467
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
468
|
-
let conversationId = query?.conversation_id || null;
|
|
469
|
-
if (!conversationId && query?.target) {
|
|
470
|
-
const resolved = await resolveTarget(actingAgentId, String(query.target));
|
|
471
|
-
if (!resolved.error) conversationId = resolved.conversationId;
|
|
472
|
-
}
|
|
473
|
-
if (!conversationId) return { status: 400, body: { error: 'conversation_id is required' } };
|
|
474
|
-
try {
|
|
475
|
-
const data = await api.listGoalApprovals({ actingAgentId, conversationId });
|
|
476
|
-
return { status: 200, body: { data } };
|
|
477
|
-
} catch (err) {
|
|
478
|
-
return { status: err?.status || 500, body: { error: err?.message || 'approval list failed' } };
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
export async function handleApprovalResolve(req, body, ctx) {
|
|
483
|
-
const actingAgentId = getActingAgentId(req, body);
|
|
484
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
485
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
486
|
-
if (!body?.request_id) return { status: 400, body: { error: 'request_id is required' } };
|
|
487
|
-
if (body?.decision !== 'granted' && body?.decision !== 'rejected') {
|
|
488
|
-
return { status: 400, body: { error: 'decision must be granted or rejected' } };
|
|
489
|
-
}
|
|
490
|
-
try {
|
|
491
|
-
const data = await api.resolveGoalApproval({
|
|
492
|
-
actingAgentId,
|
|
493
|
-
requestId: body.request_id,
|
|
494
|
-
decision: body.decision,
|
|
495
|
-
originalText: body.original_text || null,
|
|
496
|
-
confidence: body.confidence ?? null,
|
|
497
|
-
sourceMessageId: body.source_message_id || null,
|
|
498
|
-
});
|
|
499
|
-
debugLog('agent-cli', 'approval.resolve', {
|
|
500
|
-
actingAgentId,
|
|
501
|
-
requestId: body.request_id,
|
|
502
|
-
decision: body.decision,
|
|
503
|
-
resumed: data?.resumed,
|
|
504
|
-
});
|
|
505
|
-
return { status: data?.ok ? 200 : 400, body: data };
|
|
506
|
-
} catch (err) {
|
|
507
|
-
return { status: err?.status || 500, body: { error: err?.message || 'approval resolve failed' } };
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
|
|
511
333
|
export async function handleTaskList(req, query, ctx) {
|
|
512
334
|
const actingAgentId = getActingAgentId(req, query);
|
|
513
335
|
const v = validateActingAgent(actingAgentId, ctx);
|
|
@@ -894,366 +716,6 @@ export async function handleGroupMembersRemove(req, body, ctx) {
|
|
|
894
716
|
}
|
|
895
717
|
}
|
|
896
718
|
|
|
897
|
-
export async function handleWorkstreamCreate(req, body, ctx) {
|
|
898
|
-
const actingAgentId = getActingAgentId(req, body);
|
|
899
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
900
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
901
|
-
const name = String(body?.name || '').trim();
|
|
902
|
-
if (!name) return { status: 400, body: { error: 'name is required' } };
|
|
903
|
-
try {
|
|
904
|
-
const data = await api.createWorkstream({
|
|
905
|
-
actingAgentId,
|
|
906
|
-
name,
|
|
907
|
-
description: body?.description || null,
|
|
908
|
-
charter: body?.charter ?? null,
|
|
909
|
-
memberAgentIds: Array.isArray(body?.member_agent_ids) ? body.member_agent_ids : [],
|
|
910
|
-
});
|
|
911
|
-
invalidateServerInfoCache(actingAgentId);
|
|
912
|
-
debugLog('agent-cli', 'workstream.create', {
|
|
913
|
-
actingAgentId, conversationId: data?.conversation?.id,
|
|
914
|
-
});
|
|
915
|
-
return { status: 200, body: data };
|
|
916
|
-
} catch (err) {
|
|
917
|
-
return { status: err?.status || 500, body: { error: err?.message || 'workstream create failed' } };
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
export async function handleWorkstreamDelete(req, body, ctx) {
|
|
922
|
-
const actingAgentId = getActingAgentId(req, body);
|
|
923
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
924
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
925
|
-
let conversationId = body?.conversation_id || null;
|
|
926
|
-
if (!conversationId && body?.target) {
|
|
927
|
-
const resolved = await resolveTarget(actingAgentId, String(body.target));
|
|
928
|
-
if (resolved.error) return { status: 404, body: { error: resolved.error } };
|
|
929
|
-
conversationId = resolved.conversationId;
|
|
930
|
-
}
|
|
931
|
-
if (!conversationId) return { status: 400, body: { error: 'target or conversation_id is required' } };
|
|
932
|
-
try {
|
|
933
|
-
const data = await api.deleteWorkstream({ actingAgentId, conversationId });
|
|
934
|
-
invalidateServerInfoCache(actingAgentId);
|
|
935
|
-
return { status: 200, body: data };
|
|
936
|
-
} catch (err) {
|
|
937
|
-
return { status: err?.status || 500, body: { error: err?.message || 'workstream delete failed' } };
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
export async function handleWorkstreamList(req, query, ctx) {
|
|
942
|
-
const actingAgentId = getActingAgentId(req, query);
|
|
943
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
944
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
945
|
-
try {
|
|
946
|
-
const data = await api.listWorkstreams({ actingAgentId });
|
|
947
|
-
return { status: 200, body: { data } };
|
|
948
|
-
} catch (err) {
|
|
949
|
-
return { status: err?.status || 500, body: { error: err?.message || 'workstream list failed' } };
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
export async function handleAgentList(req, query, ctx) {
|
|
954
|
-
const actingAgentId = getActingAgentId(req, query);
|
|
955
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
956
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
957
|
-
try {
|
|
958
|
-
const data = await api.listAgentSlots({ actingAgentId });
|
|
959
|
-
return { status: 200, body: { data } };
|
|
960
|
-
} catch (err) {
|
|
961
|
-
return { status: err?.status || 500, body: { error: err?.message || 'agent list failed' } };
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
export async function handleAgentCreate(req, body, ctx) {
|
|
966
|
-
const actingAgentId = getActingAgentId(req, body);
|
|
967
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
968
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
969
|
-
const name = String(body?.name || '').trim();
|
|
970
|
-
const runtime = String(body?.runtime || '').trim();
|
|
971
|
-
if (!name) return { status: 400, body: { error: 'name is required' } };
|
|
972
|
-
if (!runtime) return { status: 400, body: { error: 'runtime is required' } };
|
|
973
|
-
try {
|
|
974
|
-
const data = await api.createAgentSlot({
|
|
975
|
-
actingAgentId,
|
|
976
|
-
name,
|
|
977
|
-
runtime,
|
|
978
|
-
description: body?.description || null,
|
|
979
|
-
displayName: body?.display_name || null,
|
|
980
|
-
model: body?.model || null,
|
|
981
|
-
});
|
|
982
|
-
return { status: 200, body: data };
|
|
983
|
-
} catch (err) {
|
|
984
|
-
return { status: err?.status || 500, body: { error: err?.message || 'agent create failed' } };
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
export async function handleAgentDelete(req, body, ctx) {
|
|
989
|
-
const actingAgentId = getActingAgentId(req, body);
|
|
990
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
991
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
992
|
-
const agentId = String(body?.agent_id || '').trim();
|
|
993
|
-
if (!agentId) return { status: 400, body: { error: 'agent_id is required' } };
|
|
994
|
-
try {
|
|
995
|
-
const data = await api.archiveAgentSlot({ actingAgentId, agentId });
|
|
996
|
-
return { status: 200, body: data };
|
|
997
|
-
} catch (err) {
|
|
998
|
-
return { status: err?.status || 500, body: { error: err?.message || 'agent delete failed' } };
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
export async function handleServiceCreate(req, body, ctx) {
|
|
1003
|
-
const actingAgentId = getActingAgentId(req, body);
|
|
1004
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
1005
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
1006
|
-
const name = String(body?.name || '').trim();
|
|
1007
|
-
if (!name) return { status: 400, body: { error: 'name is required' } };
|
|
1008
|
-
if (!body?.endpoint_config || typeof body.endpoint_config !== 'object') {
|
|
1009
|
-
return { status: 400, body: { error: 'endpoint_config is required' } };
|
|
1010
|
-
}
|
|
1011
|
-
try {
|
|
1012
|
-
const data = await api.createService({
|
|
1013
|
-
actingAgentId, name,
|
|
1014
|
-
description: body?.description || null,
|
|
1015
|
-
contractSchema: body?.contract_schema ?? null,
|
|
1016
|
-
endpointConfig: body.endpoint_config,
|
|
1017
|
-
});
|
|
1018
|
-
return { status: 200, body: data };
|
|
1019
|
-
} catch (err) {
|
|
1020
|
-
return { status: err?.status || 500, body: { error: err?.message || 'service create failed' } };
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
export async function handleServiceUpdate(req, body, ctx) {
|
|
1025
|
-
const actingAgentId = getActingAgentId(req, body);
|
|
1026
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
1027
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
1028
|
-
const serviceId = String(body?.service_id || '').trim();
|
|
1029
|
-
if (!serviceId) return { status: 400, body: { error: 'service_id is required' } };
|
|
1030
|
-
const patch = {};
|
|
1031
|
-
if ('description' in (body || {})) patch.description = body.description;
|
|
1032
|
-
if ('contract_schema' in (body || {})) patch.contract_schema = body.contract_schema;
|
|
1033
|
-
if ('endpoint_config' in (body || {})) patch.endpoint_config = body.endpoint_config;
|
|
1034
|
-
if ('status' in (body || {})) patch.status = body.status;
|
|
1035
|
-
try {
|
|
1036
|
-
const data = await api.updateService({ actingAgentId, serviceId, ...patch });
|
|
1037
|
-
return { status: 200, body: data };
|
|
1038
|
-
} catch (err) {
|
|
1039
|
-
return { status: err?.status || 500, body: { error: err?.message || 'service update failed' } };
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
export async function handleServiceDelete(req, body, ctx) {
|
|
1044
|
-
const actingAgentId = getActingAgentId(req, body);
|
|
1045
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
1046
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
1047
|
-
const serviceId = String(body?.service_id || '').trim();
|
|
1048
|
-
if (!serviceId) return { status: 400, body: { error: 'service_id is required' } };
|
|
1049
|
-
try {
|
|
1050
|
-
const data = await api.deleteService({ actingAgentId, serviceId });
|
|
1051
|
-
return { status: 200, body: data };
|
|
1052
|
-
} catch (err) {
|
|
1053
|
-
return { status: err?.status || 500, body: { error: err?.message || 'service delete failed' } };
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
export async function handleServiceList(req, query, ctx) {
|
|
1058
|
-
const actingAgentId = getActingAgentId(req, query);
|
|
1059
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
1060
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
1061
|
-
try {
|
|
1062
|
-
const data = await api.listServices({ actingAgentId });
|
|
1063
|
-
return { status: 200, body: { data } };
|
|
1064
|
-
} catch (err) {
|
|
1065
|
-
return { status: err?.status || 500, body: { error: err?.message || 'service list failed' } };
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
export async function handleServiceInfo(req, query, ctx) {
|
|
1070
|
-
const actingAgentId = getActingAgentId(req, query);
|
|
1071
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
1072
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
1073
|
-
const name = String(query?.name || '').trim();
|
|
1074
|
-
if (!name) return { status: 400, body: { error: 'name is required' } };
|
|
1075
|
-
try {
|
|
1076
|
-
const data = await api.getServiceInfo({ actingAgentId, name });
|
|
1077
|
-
return { status: 200, body: data };
|
|
1078
|
-
} catch (err) {
|
|
1079
|
-
return { status: err?.status || 500, body: { error: err?.message || 'service info failed' } };
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
export async function handleServiceCall(req, body, ctx) {
|
|
1084
|
-
const actingAgentId = getActingAgentId(req, body);
|
|
1085
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
1086
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
1087
|
-
const name = String(body?.name || '').trim();
|
|
1088
|
-
if (!name) return { status: 400, body: { error: 'name is required' } };
|
|
1089
|
-
try {
|
|
1090
|
-
const data = await api.callService({
|
|
1091
|
-
actingAgentId, name,
|
|
1092
|
-
input: body?.input ?? null,
|
|
1093
|
-
});
|
|
1094
|
-
return { status: 200, body: data };
|
|
1095
|
-
} catch (err) {
|
|
1096
|
-
return { status: err?.status || 500, body: { error: err?.message || 'service call failed' } };
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
export async function handleBriefingGet(req, query, ctx) {
|
|
1101
|
-
const actingAgentId = getActingAgentId(req, query);
|
|
1102
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
1103
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
1104
|
-
const briefingId = String(query?.id || '').trim();
|
|
1105
|
-
if (!briefingId) return { status: 400, body: { error: 'id is required' } };
|
|
1106
|
-
try {
|
|
1107
|
-
const data = await api.getBriefing({ actingAgentId, briefingId });
|
|
1108
|
-
return { status: 200, body: data };
|
|
1109
|
-
} catch (err) {
|
|
1110
|
-
return { status: err?.status || 500, body: { error: err?.message || 'briefing get failed' } };
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
export async function handleBriefingPublish(req, body, ctx) {
|
|
1115
|
-
const actingAgentId = getActingAgentId(req, body);
|
|
1116
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
1117
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
1118
|
-
const bodyText = typeof body?.body_text === 'string' && body.body_text.trim() ? body.body_text.trim() : null;
|
|
1119
|
-
const attachmentAssetId = typeof body?.attachment_asset_id === 'string' && body.attachment_asset_id.trim()
|
|
1120
|
-
? body.attachment_asset_id.trim()
|
|
1121
|
-
: null;
|
|
1122
|
-
const responseMode = typeof body?.response_mode === 'string' && body.response_mode.trim()
|
|
1123
|
-
? body.response_mode.trim().toLowerCase()
|
|
1124
|
-
: 'info';
|
|
1125
|
-
if (!bodyText) {
|
|
1126
|
-
return { status: 400, body: { error: 'body_text is required' } };
|
|
1127
|
-
}
|
|
1128
|
-
if (bodyText && bodyText.length > 140) {
|
|
1129
|
-
return { status: 400, body: { error: 'body_text must be ≤140 chars' } };
|
|
1130
|
-
}
|
|
1131
|
-
if (!['info', 'approval'].includes(responseMode)) {
|
|
1132
|
-
return { status: 400, body: { error: 'response_mode must be info or approval' } };
|
|
1133
|
-
}
|
|
1134
|
-
const currentConversationId = getCurrentConversationId(req, body);
|
|
1135
|
-
try {
|
|
1136
|
-
const data = await api.publishBriefing({
|
|
1137
|
-
actingAgentId,
|
|
1138
|
-
bodyText,
|
|
1139
|
-
attachmentAssetId,
|
|
1140
|
-
currentConversationId,
|
|
1141
|
-
responseMode,
|
|
1142
|
-
});
|
|
1143
|
-
return { status: 200, body: data };
|
|
1144
|
-
} catch (err) {
|
|
1145
|
-
return { status: err?.status || 500, body: { error: err?.message || 'briefing publish failed' } };
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
export async function handleCredentialRequest(req, body, ctx) {
|
|
1150
|
-
const actingAgentId = getActingAgentId(req, body);
|
|
1151
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
1152
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
1153
|
-
const name = String(body?.name || '').trim();
|
|
1154
|
-
if (!name) return { status: 400, body: { error: 'name is required' } };
|
|
1155
|
-
let workstreamId = body?.workstream_id || null;
|
|
1156
|
-
if (!workstreamId && body?.target) {
|
|
1157
|
-
const resolved = await resolveTarget(actingAgentId, String(body.target));
|
|
1158
|
-
if (resolved.error) return { status: 404, body: { error: resolved.error } };
|
|
1159
|
-
workstreamId = resolved.conversationId;
|
|
1160
|
-
}
|
|
1161
|
-
try {
|
|
1162
|
-
const data = await api.requestCredential({
|
|
1163
|
-
actingAgentId,
|
|
1164
|
-
name,
|
|
1165
|
-
description: body?.description || null,
|
|
1166
|
-
workstreamId,
|
|
1167
|
-
});
|
|
1168
|
-
return { status: 200, body: data };
|
|
1169
|
-
} catch (err) {
|
|
1170
|
-
return { status: err?.status || 500, body: { error: err?.message || 'credential request failed' } };
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
export async function handleWorkstreamDashboardSet(req, body, ctx) {
|
|
1175
|
-
const actingAgentId = getActingAgentId(req, body);
|
|
1176
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
1177
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
1178
|
-
let conversationId = body?.conversation_id || null;
|
|
1179
|
-
if (!conversationId && body?.target) {
|
|
1180
|
-
const resolved = await resolveTarget(actingAgentId, String(body.target));
|
|
1181
|
-
if (resolved.error) return { status: 404, body: { error: resolved.error } };
|
|
1182
|
-
conversationId = resolved.conversationId;
|
|
1183
|
-
}
|
|
1184
|
-
if (!conversationId) return { status: 400, body: { error: 'target or conversation_id is required' } };
|
|
1185
|
-
const payload = { actingAgentId, conversationId };
|
|
1186
|
-
if ('data_json' in (body || {})) payload.dataJson = body.data_json;
|
|
1187
|
-
if ('html_template' in (body || {})) payload.htmlTemplate = body.html_template;
|
|
1188
|
-
try {
|
|
1189
|
-
const data = await api.setWorkstreamDashboard(payload);
|
|
1190
|
-
return { status: 200, body: data };
|
|
1191
|
-
} catch (err) {
|
|
1192
|
-
return { status: err?.status || 500, body: { error: err?.message || 'dashboard set failed' } };
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
export async function handleWorkstreamDashboardGet(req, query, ctx) {
|
|
1197
|
-
const actingAgentId = getActingAgentId(req, query);
|
|
1198
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
1199
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
1200
|
-
let conversationId = query?.conversation_id || null;
|
|
1201
|
-
if (!conversationId && query?.target) {
|
|
1202
|
-
const resolved = await resolveTarget(actingAgentId, String(query.target));
|
|
1203
|
-
if (resolved.error) return { status: 404, body: { error: resolved.error } };
|
|
1204
|
-
conversationId = resolved.conversationId;
|
|
1205
|
-
}
|
|
1206
|
-
if (!conversationId) return { status: 400, body: { error: 'target or conversation_id is required' } };
|
|
1207
|
-
try {
|
|
1208
|
-
const data = await api.getWorkstreamDashboard({ actingAgentId, conversationId });
|
|
1209
|
-
return { status: 200, body: data };
|
|
1210
|
-
} catch (err) {
|
|
1211
|
-
return { status: err?.status || 500, body: { error: err?.message || 'dashboard get failed' } };
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
export async function handleWorkstreamCharterGet(req, query, ctx) {
|
|
1216
|
-
const actingAgentId = getActingAgentId(req, query);
|
|
1217
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
1218
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
1219
|
-
let conversationId = query?.conversation_id || null;
|
|
1220
|
-
if (!conversationId && query?.target) {
|
|
1221
|
-
const resolved = await resolveTarget(actingAgentId, String(query.target));
|
|
1222
|
-
if (resolved.error) return { status: 404, body: { error: resolved.error } };
|
|
1223
|
-
conversationId = resolved.conversationId;
|
|
1224
|
-
}
|
|
1225
|
-
if (!conversationId) return { status: 400, body: { error: 'target or conversation_id is required' } };
|
|
1226
|
-
try {
|
|
1227
|
-
const data = await api.getWorkstreamCharter({ actingAgentId, conversationId });
|
|
1228
|
-
return { status: 200, body: data };
|
|
1229
|
-
} catch (err) {
|
|
1230
|
-
return { status: err?.status || 500, body: { error: err?.message || 'charter get failed' } };
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
export async function handleWorkstreamCharterSet(req, body, ctx) {
|
|
1235
|
-
const actingAgentId = getActingAgentId(req, body);
|
|
1236
|
-
const v = validateActingAgent(actingAgentId, ctx);
|
|
1237
|
-
if (!v.ok) return { status: v.status, body: { error: v.error } };
|
|
1238
|
-
let conversationId = body?.conversation_id || null;
|
|
1239
|
-
if (!conversationId && body?.target) {
|
|
1240
|
-
const resolved = await resolveTarget(actingAgentId, String(body.target));
|
|
1241
|
-
if (resolved.error) return { status: 404, body: { error: resolved.error } };
|
|
1242
|
-
conversationId = resolved.conversationId;
|
|
1243
|
-
}
|
|
1244
|
-
if (!conversationId) return { status: 400, body: { error: 'target or conversation_id is required' } };
|
|
1245
|
-
const charter = typeof body?.charter === 'string' ? body.charter : null;
|
|
1246
|
-
try {
|
|
1247
|
-
const data = await api.setWorkstreamCharter({ actingAgentId, conversationId, charter });
|
|
1248
|
-
debugLog('agent-cli', 'workstream.charter.set', {
|
|
1249
|
-
actingAgentId, conversationId, len: charter?.length ?? 0,
|
|
1250
|
-
});
|
|
1251
|
-
return { status: 200, body: data };
|
|
1252
|
-
} catch (err) {
|
|
1253
|
-
return { status: err?.status || 500, body: { error: err?.message || 'charter set failed' } };
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
719
|
export async function handleServerInfo(req, query, ctx) {
|
|
1258
720
|
const actingAgentId = getActingAgentId(req, query);
|
|
1259
721
|
const v = validateActingAgent(actingAgentId, ctx);
|