@love-moon/conductor-cli 0.2.40 → 0.2.42

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.
Files changed (2) hide show
  1. package/bin/conductor-fire.js +114 -16
  2. package/package.json +5 -5
@@ -848,6 +848,10 @@ async function main() {
848
848
  let nextInitialPrompt =
849
849
  taskContext.shouldProcessInitialPrompt ? cliArgs.initialPrompt : "";
850
850
  let pendingRefreshSessionRequest = null;
851
+ // Only the first iteration should deliver the configured pre_prompt; any
852
+ // subsequent refresh-session rebuild must skip it so users don't see the
853
+ // pre_prompt bubble duplicated.
854
+ let nextShouldProcessPrePrompt = Boolean(taskContext.shouldProcessPrePrompt);
851
855
 
852
856
  const sessionCommandLine = resolveAiSessionCommandLine(
853
857
  cliArgs.backend,
@@ -930,7 +934,6 @@ async function main() {
930
934
  ...(cliArgs.sessionOptions || {}),
931
935
  ...(sessionCommandLine ? { commandLine: sessionCommandLine } : {}),
932
936
  logger: { log },
933
- ...(resolvedPrePrompt ? { prePrompt: resolvedPrePrompt } : {}),
934
937
  sessionStoreKey: taskContext.taskId ? `task-${taskContext.taskId}` : undefined,
935
938
  resumePersistedSession: Boolean(!nextResumeSessionId && taskContext.taskId),
936
939
  });
@@ -947,6 +950,8 @@ async function main() {
947
950
  backendName: cliArgs.backend,
948
951
  resumeSessionId: nextResumeSessionId,
949
952
  daemonName: resolvedDaemonName,
953
+ prePrompt: resolvedPrePrompt || "",
954
+ shouldProcessPrePrompt: Boolean(resolvedPrePrompt) && nextShouldProcessPrePrompt,
950
955
  });
951
956
  reconnectRunner = runner;
952
957
  if (pendingRemoteStopEvent) {
@@ -993,6 +998,7 @@ async function main() {
993
998
  if (refreshedSessionId) {
994
999
  nextResumeSessionId = refreshedSessionId;
995
1000
  nextInitialPrompt = "";
1001
+ nextShouldProcessPrePrompt = false;
996
1002
  pendingRefreshSessionRequest = refreshSessionRequest;
997
1003
  continue;
998
1004
  }
@@ -1467,6 +1473,9 @@ async function ensureTaskContext(conductor, opts) {
1467
1473
  appUrl: null,
1468
1474
  shouldProcessInitialPrompt: Boolean(opts.initialPrompt),
1469
1475
  initialPromptDelivery: opts.initialPrompt ? "synthetic" : "none",
1476
+ // Daemon provided the task id, so this fire process is the first attach
1477
+ // for that task — it should inject the configured pre_prompt once.
1478
+ shouldProcessPrePrompt: true,
1470
1479
  };
1471
1480
  }
1472
1481
 
@@ -1493,6 +1502,9 @@ async function ensureTaskContext(conductor, opts) {
1493
1502
  appUrl: session.app_url || null,
1494
1503
  shouldProcessInitialPrompt: Boolean(opts.initialPrompt),
1495
1504
  initialPromptDelivery: opts.initialPrompt ? "queued" : "none",
1505
+ // Newly created task — pre_prompt should run exactly once right after
1506
+ // the backend session is announced, before any real user message.
1507
+ shouldProcessPrePrompt: true,
1496
1508
  };
1497
1509
  }
1498
1510
 
@@ -1789,6 +1801,8 @@ export class BridgeRunner {
1789
1801
  backendName,
1790
1802
  resumeSessionId,
1791
1803
  daemonName,
1804
+ prePrompt,
1805
+ shouldProcessPrePrompt,
1792
1806
  }) {
1793
1807
  this.backendSession = backendSession;
1794
1808
  this.conductor = conductor;
@@ -1818,6 +1832,9 @@ export class BridgeRunner {
1818
1832
  this.initialPromptDelivery === "queued" && typeof initialPrompt === "string" && initialPrompt.trim()
1819
1833
  ? initialPrompt.trim()
1820
1834
  : "";
1835
+ this.prePrompt =
1836
+ typeof prePrompt === "string" && prePrompt.trim() ? prePrompt.trim() : "";
1837
+ this.shouldProcessPrePrompt = Boolean(shouldProcessPrePrompt) && Boolean(this.prePrompt);
1821
1838
  this.sessionStreamReplyCounts = new Map();
1822
1839
  this.lastRuntimeStatusSignature = null;
1823
1840
  this.lastRuntimeStatusPayload = null;
@@ -2027,6 +2044,17 @@ export class BridgeRunner {
2027
2044
  return;
2028
2045
  }
2029
2046
 
2047
+ if (this.shouldProcessPrePrompt) {
2048
+ this.copilotLog(
2049
+ `processing pre_prompt via synthetic attach flow contentLen=${this.prePrompt.length}`,
2050
+ );
2051
+ this.shouldProcessPrePrompt = false;
2052
+ await this.handlePrePromptMessage(this.prePrompt);
2053
+ if (this.stopped) {
2054
+ return;
2055
+ }
2056
+ }
2057
+
2030
2058
  if (this.initialPrompt && this.initialPromptDelivery === "synthetic") {
2031
2059
  this.copilotLog("processing initial prompt via synthetic attach flow");
2032
2060
  await this.handleSyntheticMessage(this.initialPrompt, {
@@ -3049,6 +3077,51 @@ export class BridgeRunner {
3049
3077
  }
3050
3078
 
3051
3079
  async handleSyntheticMessage(content, { includeImages }) {
3080
+ return this.runSyntheticTurn({
3081
+ content,
3082
+ replyTarget: "initial",
3083
+ includeImages: Boolean(includeImages),
3084
+ logTag: "synthetic",
3085
+ introLabel: "初始提示",
3086
+ errorLabel: "初始提示",
3087
+ });
3088
+ }
3089
+
3090
+ async handlePrePromptMessage(content) {
3091
+ const text = typeof content === "string" ? content.trim() : "";
3092
+ if (!text) {
3093
+ return;
3094
+ }
3095
+ return this.runSyntheticTurn({
3096
+ content: text,
3097
+ replyTarget: "pre_prompt",
3098
+ includeImages: false,
3099
+ logTag: "pre_prompt",
3100
+ introLabel: "pre_prompt",
3101
+ errorLabel: "pre_prompt",
3102
+ surfaceUserMessage: {
3103
+ content: text,
3104
+ metadata: {
3105
+ pre_prompt: true,
3106
+ role: "user",
3107
+ visible_as: "user",
3108
+ origin: "pre_prompt",
3109
+ },
3110
+ },
3111
+ replyMetadata: { pre_prompt_response: true },
3112
+ });
3113
+ }
3114
+
3115
+ async runSyntheticTurn({
3116
+ content,
3117
+ replyTarget,
3118
+ includeImages,
3119
+ logTag,
3120
+ introLabel,
3121
+ errorLabel,
3122
+ surfaceUserMessage,
3123
+ replyMetadata,
3124
+ }) {
3052
3125
  this.lastRuntimeStatusSignature = null;
3053
3126
  this.runningTurn = true;
3054
3127
  const startedAt = Date.now();
@@ -3056,28 +3129,48 @@ export class BridgeRunner {
3056
3129
  this.useSessionFileReplyStream &&
3057
3130
  typeof this.backendSession?.setSessionReplyTarget === "function"
3058
3131
  ) {
3059
- this.backendSession.setSessionReplyTarget("initial");
3132
+ this.backendSession.setSessionReplyTarget(replyTarget);
3133
+ }
3134
+ this.copilotLog(
3135
+ `${logTag} turn start includeImages=${Boolean(includeImages)} contentLen=${String(content || "").length}`,
3136
+ );
3137
+ if (surfaceUserMessage) {
3138
+ try {
3139
+ await this.conductor.sendMessage(this.taskId, surfaceUserMessage.content, {
3140
+ backend: this.backendName,
3141
+ thread_id: this.backendSession?.threadId,
3142
+ cli_args: this.cliArgs,
3143
+ synthetic: true,
3144
+ ...(surfaceUserMessage.metadata || {}),
3145
+ });
3146
+ this.copilotLog(
3147
+ `${logTag} message surfaced to task len=${surfaceUserMessage.content.length}`,
3148
+ );
3149
+ } catch (error) {
3150
+ log(`Failed to surface ${logTag} message: ${error?.message || error}`);
3151
+ }
3060
3152
  }
3061
- this.copilotLog(`synthetic turn start includeImages=${Boolean(includeImages)} contentLen=${String(content || "").length}`);
3062
3153
  try {
3063
3154
  const result = await this.backendSession.runTurn(content, {
3064
- useInitialImages: includeImages,
3155
+ useInitialImages: Boolean(includeImages),
3065
3156
  onProgress: (payload) => {
3066
- void this.reportRuntimeStatus(payload, "initial");
3157
+ void this.reportRuntimeStatus(payload, replyTarget);
3067
3158
  },
3068
3159
  });
3069
3160
  this.copilotLog(
3070
- `synthetic runTurn completed elapsedMs=${Date.now() - startedAt} answerLen=${String(result.text || "").trim().length}`,
3161
+ `${logTag} runTurn completed elapsedMs=${Date.now() - startedAt} answerLen=${String(result.text || "").trim().length}`,
3071
3162
  );
3072
3163
  if (!this.useSessionFileReplyStream) {
3073
3164
  const backendLabel = this.backendName.charAt(0).toUpperCase() + this.backendName.slice(1);
3074
- const intro = `${backendLabel} 已根据初始提示给出回复:`;
3165
+ const intro = `${backendLabel} 已根据${introLabel}给出回复:`;
3075
3166
  const replyText =
3076
- result.text || extractAgentTextFromItems(result.items) || extractAgentTextFromMetadata(result.metadata);
3167
+ result.text ||
3168
+ extractAgentTextFromItems(result.items) ||
3169
+ extractAgentTextFromMetadata(result.metadata);
3077
3170
  const text = replyText ? `${intro}\n\n${replyText}` : intro;
3078
3171
  logBackendReply(this.backendName, replyText || "(无文本输出)", {
3079
3172
  usage: result.usage,
3080
- replyTo: "initial",
3173
+ replyTo: replyTarget,
3081
3174
  });
3082
3175
  await this.conductor.sendMessage(this.taskId, text, {
3083
3176
  model: this.backendSession.threadOptions?.model || this.backendName,
@@ -3086,27 +3179,32 @@ export class BridgeRunner {
3086
3179
  thread_id: this.backendSession.threadId,
3087
3180
  cli_args: this.cliArgs,
3088
3181
  synthetic: true,
3182
+ ...(replyMetadata || {}),
3089
3183
  });
3090
- this.copilotLog(`synthetic sdk_message sent responseLen=${text.length}`);
3184
+ this.copilotLog(`${logTag} sdk_message sent responseLen=${text.length}`);
3091
3185
  } else {
3092
- this.copilotLog("synthetic session_file turn settled");
3186
+ this.copilotLog(`${logTag} session_file turn settled`);
3093
3187
  }
3094
3188
  await this.syncBackendSessionBinding();
3095
3189
  } catch (error) {
3096
3190
  const errorMessage = error instanceof Error ? error.message : String(error);
3097
3191
  if (this.stopped && (this.remoteStopInfo || isSessionClosedError(error))) {
3098
- this.copilotLog(`synthetic turn interrupted by stop_task elapsedMs=${Date.now() - startedAt}`);
3192
+ this.copilotLog(`${logTag} turn interrupted by stop_task elapsedMs=${Date.now() - startedAt}`);
3099
3193
  return;
3100
3194
  }
3101
- if (await this.settleCodexCheckpointUnavailableAfterStream("initial", errorMessage, { markProcessed: false })) {
3195
+ if (
3196
+ await this.settleCodexCheckpointUnavailableAfterStream(replyTarget, errorMessage, {
3197
+ markProcessed: false,
3198
+ })
3199
+ ) {
3102
3200
  return;
3103
3201
  }
3104
3202
  this.copilotLog(
3105
- `synthetic turn failed elapsedMs=${Date.now() - startedAt} error="${sanitizeForLog(errorMessage, 200)}"`,
3203
+ `${logTag} turn failed elapsedMs=${Date.now() - startedAt} error="${sanitizeForLog(errorMessage, 200)}"`,
3106
3204
  );
3107
- await this.reportError(`初始提示执行失败: ${errorMessage}`);
3205
+ await this.reportError(`${errorLabel}执行失败: ${errorMessage}`);
3108
3206
  } finally {
3109
- this.copilotLog(`synthetic turn end elapsedMs=${Date.now() - startedAt}`);
3207
+ this.copilotLog(`${logTag} turn end elapsedMs=${Date.now() - startedAt}`);
3110
3208
  this.runningTurn = false;
3111
3209
  }
3112
3210
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@love-moon/conductor-cli",
3
- "version": "0.2.40",
4
- "gitCommitId": "e08f8b6",
3
+ "version": "0.2.42",
4
+ "gitCommitId": "f79f36f",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "conductor": "bin/conductor.js"
@@ -17,9 +17,9 @@
17
17
  "test": "node --test test/*.test.js"
18
18
  },
19
19
  "dependencies": {
20
- "@love-moon/ai-manager": "0.2.40",
21
- "@love-moon/ai-sdk": "0.2.40",
22
- "@love-moon/conductor-sdk": "0.2.40",
20
+ "@love-moon/ai-manager": "0.2.42",
21
+ "@love-moon/ai-sdk": "0.2.42",
22
+ "@love-moon/conductor-sdk": "0.2.42",
23
23
  "@github/copilot-sdk": "^0.2.2",
24
24
  "chrome-launcher": "^1.2.1",
25
25
  "chrome-remote-interface": "^0.33.0",