(
workspaceId: string,
message: string,
options: SendMessageOptions & {
fileParts?: FilePart[];
},
internal?: {
allowQueuedAgentTask?: boolean;
skipAutoResumeReset?: boolean;
synthetic?: boolean;
/** Marks a synthetic send as an active-goal continuation turn. */
goalContinuation?: boolean;
/** Specific active-goal synthetic turn kind to persist on the user message. */
goalKind?: GoalSyntheticMessageKind;
/** Force Copilot billing classification to "agent" for internal sends. */
agentInitiated?: boolean;
onAccepted?: () => Promise<void> | void;
onCanceled?: (reason: string) => Promise<void> | void;
onAcceptedPreStreamFailure?: (error: SendMessageError) => Promise<void> | void;
/** Return once the user message is accepted; stream startup continues asynchronously. */
startStreamInBackground?: boolean;
/** When true, reject instead of queueing if the workspace is busy. */
requireIdle?: boolean;
}
)
| 6982 | } |
| 6983 | |
| 6984 | async sendMessage( |
| 6985 | workspaceId: string, |
| 6986 | message: string, |
| 6987 | options: SendMessageOptions & { |
| 6988 | fileParts?: FilePart[]; |
| 6989 | }, |
| 6990 | internal?: { |
| 6991 | allowQueuedAgentTask?: boolean; |
| 6992 | skipAutoResumeReset?: boolean; |
| 6993 | synthetic?: boolean; |
| 6994 | /** Marks a synthetic send as an active-goal continuation turn. */ |
| 6995 | goalContinuation?: boolean; |
| 6996 | /** Specific active-goal synthetic turn kind to persist on the user message. */ |
| 6997 | goalKind?: GoalSyntheticMessageKind; |
| 6998 | /** Force Copilot billing classification to "agent" for internal sends. */ |
| 6999 | agentInitiated?: boolean; |
| 7000 | onAccepted?: () => Promise<void> | void; |
| 7001 | onCanceled?: (reason: string) => Promise<void> | void; |
| 7002 | onAcceptedPreStreamFailure?: (error: SendMessageError) => Promise<void> | void; |
| 7003 | /** Return once the user message is accepted; stream startup continues asynchronously. */ |
| 7004 | startStreamInBackground?: boolean; |
| 7005 | /** When true, reject instead of queueing if the workspace is busy. */ |
| 7006 | requireIdle?: boolean; |
| 7007 | } |
| 7008 | ): Promise<Result<void, SendMessageError>> { |
| 7009 | log.debug("sendMessage handler: Received", { |
| 7010 | workspaceId, |
| 7011 | messagePreview: message.substring(0, 50), |
| 7012 | agentId: options?.agentId, |
| 7013 | options, |
| 7014 | }); |
| 7015 | |
| 7016 | let resumedInterruptedTask = false; |
| 7017 | let claimedAutoTitle = false; |
| 7018 | try { |
| 7019 | // Block streaming while workspace is being renamed to prevent path conflicts |
| 7020 | if (this.renamingWorkspaces.has(workspaceId)) { |
| 7021 | log.debug("sendMessage blocked: workspace is being renamed", { workspaceId }); |
| 7022 | return Err({ |
| 7023 | type: "unknown", |
| 7024 | raw: "Workspace is being renamed. Please wait and try again.", |
| 7025 | }); |
| 7026 | } |
| 7027 | |
| 7028 | // Block streaming while workspace is being removed to prevent races with config/session deletion. |
| 7029 | if (this.removingWorkspaces.has(workspaceId)) { |
| 7030 | log.debug("sendMessage blocked: workspace is being removed", { workspaceId }); |
| 7031 | return Err({ |
| 7032 | type: "unknown", |
| 7033 | raw: "Workspace is being deleted. Please wait and try again.", |
| 7034 | }); |
| 7035 | } |
| 7036 | |
| 7037 | if (this.resettingContextWorkspaces.has(workspaceId)) { |
| 7038 | log.debug("sendMessage blocked: context reset is in progress", { workspaceId }); |
| 7039 | return Err({ |
| 7040 | type: "unknown", |
| 7041 | raw: "Workspace context is resetting. Please wait and try again.", |
no test coverage detected