ticlawk 0.1.16-dev.9 → 0.1.17-dev.1
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 +17 -3
- package/bin/ticlawk.mjs +255 -21
- package/package.json +1 -1
- package/src/adapters/ticlawk/api.mjs +350 -50
- package/src/adapters/ticlawk/credentials.mjs +41 -1
- package/src/adapters/ticlawk/index.mjs +248 -130
- package/src/adapters/ticlawk/wake-client.mjs +1 -1
- package/src/cli/agent-commands.mjs +715 -18
- package/src/core/agent-cli-handlers.mjs +556 -18
- package/src/core/agent-home.mjs +81 -1
- package/src/core/argv.mjs +11 -1
- package/src/core/events/worker-events.mjs +32 -36
- package/src/core/http.mjs +152 -0
- package/src/core/runtime-contract.mjs +0 -1
- package/src/core/runtime-env.mjs +7 -0
- package/src/core/runtime-support.mjs +130 -78
- package/src/runtimes/_shared/agent-handbook.mjs +45 -0
- package/src/runtimes/_shared/brand.mjs +2 -0
- package/src/runtimes/_shared/goal-step-prompt.mjs +98 -0
- package/src/runtimes/_shared/goal-task-protocol.mjs +50 -0
- package/src/runtimes/_shared/handbook/BASICS.md +27 -0
- package/src/runtimes/_shared/handbook/COLLABORATION.md +37 -0
- package/src/runtimes/_shared/handbook/COMMUNICATION.md +55 -0
- package/src/runtimes/_shared/handbook/DM_SCOPE.md +13 -0
- package/src/runtimes/_shared/handbook/GOAL_AUTHORITY.md +47 -0
- package/src/runtimes/_shared/handbook/GOAL_TASK_CORE.md +43 -0
- package/src/runtimes/_shared/handbook/GROUP_ADMIN_SCOPE.md +21 -0
- package/src/runtimes/_shared/handbook/GROUP_MEMBER_SCOPE.md +15 -0
- package/src/runtimes/_shared/handbook/SURFACES.md +41 -0
- package/src/runtimes/_shared/handbook/TASK_WORKER.md +14 -0
- package/src/runtimes/_shared/standing-prompt.mjs +124 -279
- package/src/runtimes/_shared/wake-prompt.mjs +268 -0
- package/src/runtimes/claude-code/index.mjs +19 -46
- package/src/runtimes/claude-code/session.mjs +2 -7
- package/src/runtimes/codex/index.mjs +115 -63
- package/src/runtimes/codex/session.mjs +2 -12
- package/src/runtimes/openclaw/index.mjs +11 -24
- package/src/runtimes/opencode/index.mjs +38 -60
- package/src/runtimes/opencode/session.mjs +12 -12
- package/src/runtimes/pi/index.mjs +38 -60
- package/src/runtimes/pi/session.mjs +9 -6
- package/ticlawk.mjs +0 -30
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-turn inbound wake prompt builder.
|
|
3
|
+
*
|
|
4
|
+
* Standing prompts define durable runtime behavior. This module owns the
|
|
5
|
+
* dynamic wrapper around each delivered Ticlawk message: who sent it, why it
|
|
6
|
+
* reached this agent, the exact reply target, and any attached goal/task/quote
|
|
7
|
+
* context for this turn.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
function promptBlock(text) {
|
|
11
|
+
return text.trim();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function buildEnvelopeTarget(msg) {
|
|
15
|
+
const convType = msg.conversation_type || 'dm';
|
|
16
|
+
const conversationId = msg.conversation_id || '';
|
|
17
|
+
const senderHandle = msg.sender_display_name || msg.sender_user_id || msg.sender_agent_id || '';
|
|
18
|
+
if (convType === 'dm') {
|
|
19
|
+
return senderHandle ? `dm:@${senderHandle}` : `dm:${conversationId}`;
|
|
20
|
+
}
|
|
21
|
+
if (convType === 'thread') {
|
|
22
|
+
const groupName = msg.conversation_name || conversationId;
|
|
23
|
+
const replyRoot = msg.thread_root_message_id || msg.message_id || '';
|
|
24
|
+
return `#${groupName}:${replyRoot}`;
|
|
25
|
+
}
|
|
26
|
+
// group
|
|
27
|
+
const groupName = msg.conversation_name || conversationId;
|
|
28
|
+
return `#${groupName}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function buildDebugTaskSuffix(msg) {
|
|
32
|
+
if (msg.task_number == null) return '';
|
|
33
|
+
const status = msg.task_status || 'todo';
|
|
34
|
+
const parts = [`task #${msg.task_number} status=${status}`];
|
|
35
|
+
if (msg.task_assignee_agent_id || msg.task_assignee_user_id) {
|
|
36
|
+
const t = msg.task_assignee_type || 'agent';
|
|
37
|
+
const id = msg.task_assignee_agent_id || msg.task_assignee_user_id;
|
|
38
|
+
parts.push(`assignee=${t}:${id}`);
|
|
39
|
+
}
|
|
40
|
+
if (msg.task_title) {
|
|
41
|
+
parts.push(`title=${JSON.stringify(msg.task_title)}`);
|
|
42
|
+
}
|
|
43
|
+
return ` [${parts.join(' ')}]`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function buildDebugReactionsSuffix(msg) {
|
|
47
|
+
const entries = Array.isArray(msg.reactions_summary) ? msg.reactions_summary : [];
|
|
48
|
+
if (entries.length === 0) return '';
|
|
49
|
+
return ` [reactions: ${entries.join('; ')}]`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function normalizeDeliveryReasonForPrompt(reason) {
|
|
53
|
+
return reason === 'thread_follow' ? 'reply_follow' : reason;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function buildDebugEnvelopeHeader(msg) {
|
|
57
|
+
const target = buildEnvelopeTarget(msg);
|
|
58
|
+
const msgId = msg.id || msg.message_id || '';
|
|
59
|
+
const seq = msg.seq != null ? msg.seq : '';
|
|
60
|
+
const time = msg.created_at || new Date().toISOString();
|
|
61
|
+
const type = msg.sender_type || 'human';
|
|
62
|
+
const sender = msg.sender_display_name || msg.sender_user_id || msg.sender_agent_id || 'unknown';
|
|
63
|
+
const convType = msg.conversation_type || 'dm';
|
|
64
|
+
const displayReason = normalizeDeliveryReasonForPrompt(msg.reason || '');
|
|
65
|
+
const reason = displayReason ? ` reason=${displayReason}` : '';
|
|
66
|
+
const recipientRole = String(msg.recipient_conversation_role || msg.recipient_role || '').trim().toLowerCase();
|
|
67
|
+
const role = convType === 'group' && recipientRole ? ` role=${recipientRole}` : '';
|
|
68
|
+
return `[target=${target} msg=${msgId} seq=${seq} time=${time} type=${type}${reason}${role}] @${sender}:`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const buildEnvelopeHeader = buildDebugEnvelopeHeader;
|
|
72
|
+
|
|
73
|
+
export function buildGroupContextBlock(msg) {
|
|
74
|
+
// Only useful in groups — DMs don't need group-purpose context.
|
|
75
|
+
if ((msg.conversation_type || 'dm') !== 'group') return '';
|
|
76
|
+
const lines = [];
|
|
77
|
+
const name = msg.conversation_name || msg.conversation_display_name || '';
|
|
78
|
+
if (name) lines.push(`Group: #${name}`);
|
|
79
|
+
const description = (msg.conversation_description || '').trim();
|
|
80
|
+
if (description) lines.push(`Purpose: ${description}`);
|
|
81
|
+
if (lines.length === 0) return '';
|
|
82
|
+
return lines.join('\n');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function buildCharterBlock(msg) {
|
|
86
|
+
const charter = (msg.conversation_charter || '').trim();
|
|
87
|
+
if (!charter) return '';
|
|
88
|
+
const conversationId = msg.conversation_id || '';
|
|
89
|
+
// The goal lane (transition deliveries) executes against the charter; its
|
|
90
|
+
// per-step instructions come from the goal-step prompt, so here the charter
|
|
91
|
+
// is just the goal/success spec. The chat lane must NOT run the loop — it
|
|
92
|
+
// only signals goal changes to wake the goal lane.
|
|
93
|
+
const authorityLine = msg.reason === 'transition'
|
|
94
|
+
? 'This is the goal and success spec for this conversation. Run the current step against it.'
|
|
95
|
+
: hasGoalAuthority(msg)
|
|
96
|
+
? `This is the goal the backend goal lane is already driving. Handle the incoming message and reply; do not run a goal loop or start gap/execution work yourself. If this message sets, clarifies, or changes the goal, update the charter and then run \`ticlawk goal changed --conversation ${conversationId}\` to wake the goal lane. See GOAL_AUTHORITY.md.`
|
|
97
|
+
: 'Use it as current group goal and role context. The group admin owns charter and dashboard changes unless they explicitly delegate them.';
|
|
98
|
+
return promptBlock(`
|
|
99
|
+
[conversation_goal]
|
|
100
|
+
Current charter for this conversation:
|
|
101
|
+
${charter}
|
|
102
|
+
|
|
103
|
+
${authorityLine}
|
|
104
|
+
[/conversation_goal]
|
|
105
|
+
`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function buildGoalStateBlock(msg) {
|
|
109
|
+
if ((msg.conversation_charter || '').trim()) return '';
|
|
110
|
+
const convType = msg.conversation_type || 'dm';
|
|
111
|
+
const subject = convType === 'group' ? 'This group' : 'This conversation';
|
|
112
|
+
const authorityLine = hasGoalAuthority(msg)
|
|
113
|
+
? 'If this message may be starting, clarifying, or changing an ongoing goal, read GOAL_AUTHORITY.md "Goal Setup When No Specific Goal Exists" and follow it to propose/confirm the goal before writing charter/dashboard state. If it is clearly one-off, answer normally.'
|
|
114
|
+
: 'The group admin owns goal setup; follow direct mentions or assigned tasks normally.';
|
|
115
|
+
return promptBlock(`
|
|
116
|
+
[conversation_goal]
|
|
117
|
+
${subject} does not have a chartered goal yet.
|
|
118
|
+
${authorityLine}
|
|
119
|
+
[/conversation_goal]
|
|
120
|
+
`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function hasGoalAuthority(msg) {
|
|
124
|
+
const convType = msg.conversation_type || 'dm';
|
|
125
|
+
if (convType !== 'group') return true;
|
|
126
|
+
if (msg.recipient_is_conversation_admin === true) return true;
|
|
127
|
+
const recipientRole = String(msg.recipient_conversation_role || msg.recipient_role || '').trim().toLowerCase();
|
|
128
|
+
return recipientRole === 'admin' || recipientRole === 'owner';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function buildQuoteBlock(msg, target = '') {
|
|
132
|
+
const meta = msg.message_metadata || msg.metadata || null;
|
|
133
|
+
const quote = meta && typeof meta === 'object' ? meta.quote : null;
|
|
134
|
+
if (!quote || typeof quote !== 'object') return '';
|
|
135
|
+
const kind = String(quote.kind || '').trim();
|
|
136
|
+
const ref = String(quote.ref || '').trim();
|
|
137
|
+
const snippet = String(quote.snippet || '').trim();
|
|
138
|
+
if (!kind || !ref) return '';
|
|
139
|
+
const fetchHint = kind === 'briefing'
|
|
140
|
+
? `ticlawk briefing get ${ref}`
|
|
141
|
+
: kind === 'dashboard'
|
|
142
|
+
? `ticlawk dashboard get --conversation-id ${ref}`
|
|
143
|
+
: kind === 'message'
|
|
144
|
+
? `ticlawk message read --target ${JSON.stringify(target)} --around ${ref}`
|
|
145
|
+
: '';
|
|
146
|
+
const lines = [
|
|
147
|
+
`The incoming message is a reply to a ${kind}.`,
|
|
148
|
+
`Referenced ${kind}: \`${ref}\``,
|
|
149
|
+
];
|
|
150
|
+
if (snippet) lines.push(`Quoted snippet: ${snippet}`);
|
|
151
|
+
if (fetchHint) lines.push(`Fetch it if needed: \`${fetchHint}\``);
|
|
152
|
+
return lines.join('\n');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function senderDescription(msg) {
|
|
156
|
+
const type = msg.sender_type === 'agent' ? 'an agent' : 'a human user';
|
|
157
|
+
const sender = msg.sender_display_name || msg.sender_user_id || msg.sender_agent_id || '';
|
|
158
|
+
return sender ? `@${sender}, ${type}` : type;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function senderFactDescription(msg) {
|
|
162
|
+
const type = msg.sender_type === 'agent' ? 'agent' : 'human user';
|
|
163
|
+
const sender = msg.sender_display_name || msg.sender_user_id || msg.sender_agent_id || '';
|
|
164
|
+
return sender ? `@${sender}, ${type}` : type;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function conversationLabel(msg, target) {
|
|
168
|
+
const convType = msg.conversation_type || 'dm';
|
|
169
|
+
if (convType === 'dm') return 'a one-on-one conversation';
|
|
170
|
+
return target || 'this group';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function buildMessageSummary(msg, target) {
|
|
174
|
+
const convType = msg.conversation_type || 'dm';
|
|
175
|
+
const reason = normalizeDeliveryReasonForPrompt(msg.reason || '');
|
|
176
|
+
const sender = senderDescription(msg);
|
|
177
|
+
const senderFact = senderFactDescription(msg);
|
|
178
|
+
const where = conversationLabel(msg, target);
|
|
179
|
+
|
|
180
|
+
if (convType === 'dm') {
|
|
181
|
+
return `This is a one-on-one message from ${sender}.`;
|
|
182
|
+
}
|
|
183
|
+
if (reason === 'assignment') {
|
|
184
|
+
return `This message assigns or routes work to you in ${where}.\nSender: ${senderFact}`;
|
|
185
|
+
}
|
|
186
|
+
if (reason === 'ambient') {
|
|
187
|
+
return `Sender: ${senderFact}`;
|
|
188
|
+
}
|
|
189
|
+
if (reason === 'mention') {
|
|
190
|
+
return `You were mentioned in ${where} by ${sender}.`;
|
|
191
|
+
}
|
|
192
|
+
if (reason === 'reply_follow') {
|
|
193
|
+
return `This is a reply in ${where} from ${sender}.`;
|
|
194
|
+
}
|
|
195
|
+
if (reason === 'manual') {
|
|
196
|
+
return `This is a manual wake-up or reminder for ${where}.\nSource: ${senderFact}`;
|
|
197
|
+
}
|
|
198
|
+
return `This is a message in ${where} from ${sender}.`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function buildTaskDetailsBlock(msg) {
|
|
202
|
+
if (msg.task_number == null) return '';
|
|
203
|
+
const lines = [`Task #${msg.task_number} is currently \`${msg.task_status || 'todo'}\`.`];
|
|
204
|
+
if (msg.task_title) lines.push(`Task title: ${msg.task_title}`);
|
|
205
|
+
if (msg.task_assignee_agent_id || msg.task_assignee_user_id) {
|
|
206
|
+
const t = msg.task_assignee_type || 'agent';
|
|
207
|
+
const id = msg.task_assignee_agent_id || msg.task_assignee_user_id;
|
|
208
|
+
lines.push(`Assignee: ${t} \`${id}\``);
|
|
209
|
+
}
|
|
210
|
+
return lines.join('\n');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function buildReactionsBlock(msg) {
|
|
214
|
+
const entries = Array.isArray(msg.reactions_summary) ? msg.reactions_summary : [];
|
|
215
|
+
if (entries.length === 0) return '';
|
|
216
|
+
return `Recent reactions: ${entries.join('; ')}`;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function buildContentBlock(rawText) {
|
|
220
|
+
const text = String(rawText || '').trim();
|
|
221
|
+
if (!text) return '';
|
|
222
|
+
return promptBlock(`
|
|
223
|
+
Content:
|
|
224
|
+
${text}
|
|
225
|
+
`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Wrap each per-turn message with the concrete dynamic context for this
|
|
229
|
+
// delivery. Durable communication rules live in COMMUNICATION.md; this block
|
|
230
|
+
// only carries the current message and its exact reply target.
|
|
231
|
+
export function buildWakePromptText({ messageSummary, target, rawText, groupContext, charterBlock, goalStateBlock, quoteBlock, taskDetails, reactionsBlock }) {
|
|
232
|
+
const contextPrefix = [charterBlock, goalStateBlock, quoteBlock].filter(Boolean).join('\n\n');
|
|
233
|
+
const prefix = contextPrefix ? `${contextPrefix}\n\n` : '';
|
|
234
|
+
const detailBlocks = [
|
|
235
|
+
messageSummary,
|
|
236
|
+
`Reply target: \`${target}\``,
|
|
237
|
+
groupContext,
|
|
238
|
+
taskDetails ? `Task details:\n${taskDetails}` : '',
|
|
239
|
+
reactionsBlock,
|
|
240
|
+
buildContentBlock(rawText),
|
|
241
|
+
].filter(Boolean).join('\n\n');
|
|
242
|
+
return promptBlock(`
|
|
243
|
+
${prefix}New message received:
|
|
244
|
+
|
|
245
|
+
${detailBlocks}
|
|
246
|
+
`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function buildInboundWakePrompt(msg) {
|
|
250
|
+
const rawText = msg.text || '';
|
|
251
|
+
const baseHeader = buildDebugEnvelopeHeader(msg);
|
|
252
|
+
const header = baseHeader + buildDebugTaskSuffix(msg) + buildDebugReactionsSuffix(msg);
|
|
253
|
+
const target = buildEnvelopeTarget(msg);
|
|
254
|
+
const groupContext = buildGroupContextBlock(msg);
|
|
255
|
+
const charterBlock = buildCharterBlock(msg);
|
|
256
|
+
const goalStateBlock = buildGoalStateBlock(msg);
|
|
257
|
+
const quoteBlock = buildQuoteBlock(msg, target);
|
|
258
|
+
const messageSummary = buildMessageSummary(msg, target);
|
|
259
|
+
const taskDetails = buildTaskDetailsBlock(msg);
|
|
260
|
+
const reactionsBlock = buildReactionsBlock(msg);
|
|
261
|
+
const text = buildWakePromptText({ messageSummary, target, rawText, groupContext, charterBlock, goalStateBlock, quoteBlock, taskDetails, reactionsBlock });
|
|
262
|
+
return {
|
|
263
|
+
header,
|
|
264
|
+
target,
|
|
265
|
+
text,
|
|
266
|
+
rawText,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
@@ -23,12 +23,14 @@ import {
|
|
|
23
23
|
} from './session.mjs';
|
|
24
24
|
import { discoverSessions } from './transcripts.mjs';
|
|
25
25
|
import { buildImageMessageFromInbound } from '../../core/media/inbound.mjs';
|
|
26
|
-
import {
|
|
26
|
+
import { emitWorkerEventBestEffort } from '../../core/events/worker-events.mjs';
|
|
27
27
|
import {
|
|
28
28
|
shouldStreamRuntime,
|
|
29
29
|
reportSubprocessFailure,
|
|
30
30
|
terminalRuntimeFailure,
|
|
31
31
|
updateBindingRuntimeMeta,
|
|
32
|
+
resolveRuntimeSessionScope,
|
|
33
|
+
buildRuntimeSessionMetaPatch,
|
|
32
34
|
} from '../../core/runtime-support.mjs';
|
|
33
35
|
|
|
34
36
|
export const claudeCodeRuntime = {
|
|
@@ -130,31 +132,35 @@ export const claudeCodeRuntime = {
|
|
|
130
132
|
? await buildImageMessageFromInbound(inbound, 'claude-code')
|
|
131
133
|
: inbound.text;
|
|
132
134
|
|
|
133
|
-
// shouldRotate=true means
|
|
135
|
+
// shouldRotate=true means this conversation has no runtime session
|
|
136
|
+
// yet, or the agent was reset and all scoped sessions are invalid.
|
|
134
137
|
// We pass sessionId=null so `claude` creates a fresh session; the new
|
|
135
138
|
// session_id is captured from stream events and persisted below.
|
|
136
139
|
// Unifying rotate + non-rotate into one path means the standing prompt
|
|
137
140
|
// is always attached, so the agent uses the CLI to reply on every
|
|
138
141
|
// turn — including the first.
|
|
139
|
-
const
|
|
140
|
-
const targetSessionId = shouldRotate ? null :
|
|
141
|
-
const errEventSessionId =
|
|
142
|
+
const sessionScope = resolveRuntimeSessionScope(meta, inbound);
|
|
143
|
+
const targetSessionId = sessionScope.shouldRotate ? null : sessionScope.sessionId;
|
|
144
|
+
const errEventSessionId = targetSessionId || binding.id;
|
|
142
145
|
|
|
143
146
|
try {
|
|
144
147
|
const claudePath = requireClaudePath(runtimeClaudePath);
|
|
145
148
|
const claudeVersion = getClaudeCodeRuntimeHealth(claudePath).version || meta.claudeVersion || null;
|
|
146
149
|
const agentEnv = buildAgentRuntimeEnv({
|
|
147
150
|
agentId: binding.id,
|
|
148
|
-
sessionId:
|
|
151
|
+
sessionId: targetSessionId,
|
|
149
152
|
hostId: binding.runtime_host_id,
|
|
153
|
+
conversationId: inbound.conversationId,
|
|
154
|
+
messageId: inbound.messageId,
|
|
155
|
+
target: inbound.envelopeTarget,
|
|
150
156
|
});
|
|
151
|
-
const appendSystemPrompt = buildStandingPrompt({ agentId: binding.id });
|
|
157
|
+
const appendSystemPrompt = buildStandingPrompt({ agentId: binding.id, inbound });
|
|
152
158
|
const result = shouldStreamRuntime(this.name, this)
|
|
153
159
|
? await this.runTurnStream({ sessionId: targetSessionId, projectDir, claudePath, agentEnv }, message, {
|
|
154
160
|
appendSystemPrompt,
|
|
155
161
|
onEvent: async (event) => {
|
|
156
162
|
if (event?.type === 'turn.started') {
|
|
157
|
-
|
|
163
|
+
emitWorkerEventBestEffort({
|
|
158
164
|
adapter,
|
|
159
165
|
binding,
|
|
160
166
|
agent: this.name,
|
|
@@ -167,34 +173,19 @@ export const claudeCodeRuntime = {
|
|
|
167
173
|
},
|
|
168
174
|
logger: ctx.logger,
|
|
169
175
|
});
|
|
170
|
-
} else if (event?.type === 'message.delta' && event.text) {
|
|
171
|
-
await emitWorkerEvent({
|
|
172
|
-
adapter,
|
|
173
|
-
binding,
|
|
174
|
-
agent: this.name,
|
|
175
|
-
sessionId: event.sessionId || targetSessionId || binding.id,
|
|
176
|
-
cwd: projectDir,
|
|
177
|
-
replyToMessageId: inbound.messageId || null,
|
|
178
|
-
event: {
|
|
179
|
-
hook_event_name: 'worker.message.delta',
|
|
180
|
-
worker_event_name: 'worker.message.delta',
|
|
181
|
-
delta: event.text,
|
|
182
|
-
},
|
|
183
|
-
logger: ctx.logger,
|
|
184
|
-
});
|
|
185
176
|
}
|
|
186
177
|
},
|
|
187
178
|
})
|
|
188
179
|
: await this.runTurn({ sessionId: targetSessionId, projectDir, claudePath, agentEnv }, message, { appendSystemPrompt });
|
|
189
180
|
const nextBinding = await updateBindingRuntimeMeta(ctx, binding, {
|
|
190
|
-
|
|
181
|
+
...buildRuntimeSessionMetaPatch(meta, sessionScope, {
|
|
182
|
+
sessionId: result?.sessionId,
|
|
183
|
+
}),
|
|
191
184
|
runtimePath: claudePath,
|
|
192
185
|
claudePath,
|
|
193
186
|
claudeVersion,
|
|
194
|
-
rotatePending: false,
|
|
195
|
-
lastRotatedAt: shouldRotate ? new Date().toISOString() : meta.lastRotatedAt,
|
|
196
187
|
}, { status: 'connected' });
|
|
197
|
-
|
|
188
|
+
emitWorkerEventBestEffort({
|
|
198
189
|
adapter,
|
|
199
190
|
binding: nextBinding,
|
|
200
191
|
agent: this.name,
|
|
@@ -209,7 +200,7 @@ export const claudeCodeRuntime = {
|
|
|
209
200
|
});
|
|
210
201
|
return true;
|
|
211
202
|
} catch (err) {
|
|
212
|
-
|
|
203
|
+
emitWorkerEventBestEffort({
|
|
213
204
|
adapter,
|
|
214
205
|
binding,
|
|
215
206
|
agent: this.name,
|
|
@@ -239,24 +230,6 @@ export const claudeCodeRuntime = {
|
|
|
239
230
|
}
|
|
240
231
|
},
|
|
241
232
|
|
|
242
|
-
async reconcileAfterRestart(binding, ctx) {
|
|
243
|
-
const meta = binding.runtimeMeta || {};
|
|
244
|
-
await emitWorkerEvent({
|
|
245
|
-
adapter: ctx.adapter,
|
|
246
|
-
binding,
|
|
247
|
-
agent: this.name,
|
|
248
|
-
sessionId: meta.sessionId || binding.id,
|
|
249
|
-
cwd: ensureAgentHome(binding.id) || '',
|
|
250
|
-
event: {
|
|
251
|
-
hook_event_name: 'Stop',
|
|
252
|
-
worker_event_name: 'worker.turn.complete',
|
|
253
|
-
reason: 'connector.restart.reconcile',
|
|
254
|
-
},
|
|
255
|
-
logger: ctx.logger,
|
|
256
|
-
});
|
|
257
|
-
return 1;
|
|
258
|
-
},
|
|
259
|
-
|
|
260
233
|
// Friendly display name for a CC session (encoded project path →
|
|
261
234
|
// last meaningful segment). Used when materializing a binding.
|
|
262
235
|
formatDisplayName(session) {
|
|
@@ -231,23 +231,19 @@ export function streamCCPrompt({
|
|
|
231
231
|
let seenTurnStart = false;
|
|
232
232
|
let activeSessionId = sessionId || null;
|
|
233
233
|
let finalText = '';
|
|
234
|
-
let eventChain = Promise.resolve();
|
|
235
234
|
|
|
236
235
|
const emit = (event) => {
|
|
237
236
|
if (typeof onEvent !== 'function') return;
|
|
238
|
-
|
|
237
|
+
void Promise.resolve()
|
|
239
238
|
.then(() => onEvent(event))
|
|
240
239
|
.catch(() => {});
|
|
241
|
-
return eventChain;
|
|
242
240
|
};
|
|
243
241
|
|
|
244
242
|
const settle = (fn, value) => {
|
|
245
243
|
if (settled) return;
|
|
246
244
|
settled = true;
|
|
247
245
|
if (timeout) clearTimeout(timeout);
|
|
248
|
-
|
|
249
|
-
.catch(() => {})
|
|
250
|
-
.finally(() => fn(value));
|
|
246
|
+
fn(value);
|
|
251
247
|
};
|
|
252
248
|
|
|
253
249
|
const parseLine = (line) => {
|
|
@@ -267,7 +263,6 @@ export function streamCCPrompt({
|
|
|
267
263
|
const deltaText = parsed.event?.delta?.text;
|
|
268
264
|
if (typeof deltaText === 'string' && deltaText) {
|
|
269
265
|
finalText += deltaText;
|
|
270
|
-
emit({ type: 'message.delta', sessionId: activeSessionId, text: deltaText });
|
|
271
266
|
}
|
|
272
267
|
return;
|
|
273
268
|
}
|