openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
584 lines (583 loc) • 26.5 kB
JavaScript
import "./src-vebZIeLe.js";
import { t as expectDefined } from "./expect-CyE8FADM.js";
import { t as stableStringify } from "./stable-stringify-C8X7niaI.js";
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { n as normalizeAgentId } from "./agent-id-CeT3w4ap.js";
import { m as resolveAgentWorkspaceDir } from "./agent-scope-config-DcbEhP0R.js";
import { O as parseAgentSessionKey } from "./session-key-BnWWjqNc.js";
import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js";
import "./agent-scope-DbtJyKUL.js";
import { t as ADMIN_SCOPE } from "./operator-scopes-Dw7Gu2cA.js";
import { t as ErrorCodes } from "./gateway-error-details-w0nAGBBp.js";
import { ei as validateSessionsCreateParams, vm as SESSION_CREATE_IDEMPOTENCY_RETENTION_MS } from "./src-BiL5aQto.js";
import { r as authorizeOperatorScopesForRequiredScope } from "./method-scopes-K6J_UQGL.js";
import { d as errorShape, f as missingScopeErrorShape } from "./error-codes-Bo8q2D1o.js";
import { O as sessionEntryForkedFromParent } from "./session-accessor.sqlite-entry-CWk3jL7s.js";
import "./server-constants-BrVEC7RW.js";
import { r as resolveAgentMainSessionKey } from "./main-session-Br0F9dzh.js";
import { t as assertPreparedSkillLibrarySelection } from "./selection-NYtFFG08.js";
import { f as resolveOperatorSessionCreation } from "./operator-role-policy-wsr1DeJv.js";
import { a as insideGitCheckout } from "./git-BuLvhVpD.js";
import { n as resolveRequestedSessionAgentId } from "./session-request-agent-CCRSEGCB.js";
import { d as resolveGatewaySessionStoreTarget, i as loadGatewaySessionEntryReadOnly } from "./session-utils-store-CInT2loy.js";
import "./session-utils-Cai0_C6U.js";
import { n as resolveWorkspacePathContainment } from "./workspace-path-containment-Sb6Du35U.js";
import { y as ensureSessionGroupRegistered } from "./session-sharing-B7MI8hNo.js";
import { a as normalizeChatSendRequest, c as prepareSessionCreateFilesystemRoot, l as prepareSessionWorktree, o as normalizeSessionProjectGitUrl, s as validateSessionProjectPreparation, u as resolveSpawnParentWorktreeSource } from "./chat-send-handler-Dpw17Gmy.js";
import { t as chatHandlers } from "./chat-IsrqkYID.js";
import { t as ModelAccountConnectAuthorityError } from "./model-account-connect-BqlZBB8r.js";
import { r as prepareSessionModelAccountAccess } from "./users-model-account-access-B9iXjlLs.js";
import { t as assertValidParams } from "./validation-pzrlzFvo.js";
import { t as prepareSkillLibrarySessionCreation } from "./skill-library-session-B_Xf-cjX.js";
import { c as resolveExplicitSessionName, n as generateWorktreeSessionTitle, t as buildDashboardSessionTitleSource } from "./dashboard-session-title-DoHAIULX.js";
import { n as emitSessionsChanged } from "./session-change-event-DzmH4zlz.js";
import { c as resolveProjectDirectory, l as resolveProjectRegistry, s as resolveProjectCheckout, t as ProjectCheckoutError } from "./project-registry-Cm-dqnhv.js";
import { n as createAgentRuntimeAuthorityGuard } from "./agent-runtime-authority-BkcmfdKo.js";
import { t as normalizeRpcAttachmentsToChatAttachments } from "./attachment-normalize-BA7mKleS.js";
import { d as sessionLog } from "./sessions-shared-D_lV07q2.js";
import { t as resolveRegisteredCatalogCreateTarget } from "./session-catalog-D-2VZMkn.js";
import { n as createGatewaySession, t as buildDashboardSessionKey } from "./session-create-service-CM4MxLMO.js";
import path from "node:path";
import { createHash, randomUUID } from "node:crypto";
//#region src/gateway/server-methods/session-create-category.ts
function registerCreatedSessionCategory(category, context) {
if (!category) return;
try {
if (ensureSessionGroupRegistered(category)) emitSessionsChanged(context, { reason: "groups" });
} catch (error) {
sessionLog.warn(`failed to register created session category: ${formatErrorMessage(error)}`);
}
}
//#endregion
//#region src/gateway/server-methods/session-create-idempotency.ts
const sessionCreatesByContext = /* @__PURE__ */ new WeakMap();
function idempotentSessionCreate(handler) {
return async (request) => {
const idempotencyKey = request.params.idempotencyKey;
if (typeof idempotencyKey !== "string" || !idempotencyKey) {
await handler(request);
return;
}
const principal = request.client?.authenticatedUserProfile?.profileId ?? request.client?.authenticatedUserId;
const deviceId = request.client?.connect.device?.id?.trim();
if (!principal && !deviceId) {
request.respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "idempotent session creation requires an authenticated principal or device identity"));
return;
}
const owner = principal ? `principal:${principal}` : `device:${deviceId}`;
let entriesByOwner = sessionCreatesByContext.get(request.context);
if (!entriesByOwner) {
entriesByOwner = /* @__PURE__ */ new Map();
sessionCreatesByContext.set(request.context, entriesByOwner);
}
const now = Date.now();
let retainedEntryCount = 0;
for (const [entryOwner, ownerEntries] of entriesByOwner) {
for (const [key, entry] of ownerEntries) if (entry.state.kind === "completed" && entry.expiresAt <= now) ownerEntries.delete(key);
if (ownerEntries.size === 0) entriesByOwner.delete(entryOwner);
else retainedEntryCount += ownerEntries.size;
}
let entries = entriesByOwner.get(owner);
const requestIdentity = createHash("sha256").update(stableStringify(request.params)).digest("hex");
const authorization = {
role: request.client?.connect.role ?? null,
scopes: request.client?.connect.scopes?.toSorted() ?? []
};
const existing = entries?.get(idempotencyKey);
if (existing) {
if (existing.requestIdentity !== requestIdentity) {
request.respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "session creation idempotency key was reused with different parameters"));
return;
}
if (existing.authorization.role !== authorization.role) {
request.respond(false, void 0, errorShape(ErrorCodes.FORBIDDEN, "session creation authorization changed; start again"));
return;
}
const missingScope = existing.authorization.scopes.find((scope) => !authorization.scopes.includes(scope));
if (missingScope) {
request.respond(false, void 0, missingScopeErrorShape({
missingScope,
requiredScopes: existing.authorization.scopes
}));
return;
}
const result = existing.state.kind === "completed" ? existing.state.result : await existing.state.work;
request.respond(result.ok, result.payload, result.error, {
...result.meta,
cached: true
});
return;
}
if ((entries?.size ?? 0) >= 1e3 || retainedEntryCount >= 2e3) {
request.respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, "session creation capacity is full; retry later"));
return;
}
if (!entries) {
entries = /* @__PURE__ */ new Map();
entriesByOwner.set(owner, entries);
}
const releaseEntry = () => {
entries.delete(idempotencyKey);
if (entries.size === 0) entriesByOwner.delete(owner);
};
const work = Promise.resolve().then(async () => {
try {
let result;
await handler({
...request,
respond: (ok, payload, error, meta) => {
result = {
ok,
payload,
error,
meta
};
}
});
result ??= {
ok: false,
error: errorShape(ErrorCodes.UNAVAILABLE, "session creation was interrupted")
};
if (result.ok) {
entry.expiresAt = Date.now() + SESSION_CREATE_IDEMPOTENCY_RETENTION_MS;
entry.state = {
kind: "completed",
result
};
} else releaseEntry();
return result;
} catch (error) {
releaseEntry();
throw error;
}
});
const entry = {
requestIdentity,
authorization,
expiresAt: now + SESSION_CREATE_IDEMPOTENCY_RETENTION_MS,
state: {
kind: "inflight",
work
}
};
entries.set(idempotencyKey, entry);
const result = await work;
request.respond(result.ok, result.payload, result.error, result.meta);
};
}
//#endregion
//#region src/gateway/server-methods/session-create-initial-turn.ts
function resolveOptionalInitialSessionMessage(params) {
if (typeof params.task === "string" && params.task.trim()) return params.task;
if (typeof params.message === "string" && params.message.trim()) return params.message;
}
function resolveSessionCreateInitialTurn(params) {
const message = resolveOptionalInitialSessionMessage(params);
const normalizedAttachments = normalizeRpcAttachmentsToChatAttachments(params.attachments);
if (params.attachments?.length && !message && normalizedAttachments.length === 0) return null;
const attachments = normalizedAttachments.length ? normalizedAttachments : void 0;
return {
attachments,
hasInitialTurn: message !== void 0 || attachments !== void 0,
message
};
}
function isFreshChatSendStarted(params) {
if (params.cached) return false;
return (params.payload && typeof params.payload === "object" ? params.payload.status : void 0) === "started";
}
//#endregion
//#region src/gateway/server-methods/sessions-create.ts
const sessionCreateHandlers = { "sessions.create": async ({ req, params, respond, context, client, isWebchatConnect, sessionMutationCommitGuard, sessionMutationAuthorization, signal }) => {
if (!assertValidParams(params, validateSessionsCreateParams, "sessions.create", respond)) return;
const p = params;
const parentSessionKey = normalizeOptionalString(p.parentSessionKey);
const sessionCreation = prepareSkillLibrarySessionCreation(client, context.getRuntimeConfig, resolveOperatorSessionCreation(client, { allowTrustedHint: true }));
const spawnRequesterSessionKey = sessionCreation.via === "spawn" ? normalizeOptionalString(sessionCreation.requesterSessionKey) : void 0;
if (sessionCreation.inheritedToolPolicy && parentSessionKey !== spawnRequesterSessionKey) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "spawn parent must match the trusted agent caller"));
return;
}
const requestedModel = normalizeOptionalString(p.model);
let personalAccounts;
try {
personalAccounts = prepareSessionModelAccountAccess({
client,
context,
signal
}, requestedModel);
} catch (error) {
if (!(error instanceof ModelAccountConnectAuthorityError)) throw error;
respond(false, void 0, errorShape(ErrorCodes.FORBIDDEN, error.message));
return;
}
const { personalModelSelection, personalAccountDefaults } = personalAccounts;
const cfg = context.getRuntimeConfig();
const authority = createAgentRuntimeAuthorityGuard(client, context, respond);
let commitGuard = () => {
sessionMutationCommitGuard?.();
authority.commitGuard?.();
sessionMutationAuthorization?.assertCurrent();
assertPreparedSkillLibrarySelection(sessionCreation.skillLibrarySelections);
personalModelSelection?.assertCurrent();
personalAccountDefaults?.assertCurrent();
};
const catalogId = normalizeOptionalString(p.catalogId);
const catalogConflict = p.model ? "model" : p.key ? "key" : void 0;
if (catalogId && catalogConflict) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `sessions.create catalogId cannot include ${catalogConflict}`));
return;
}
const explicitlyRequestedKey = normalizeOptionalString(p.key);
const explicitlyRequestedAgent = resolveRequestedSessionAgentId(cfg, explicitlyRequestedKey ?? (p.agentId === void 0 ? "main" : void 0), p.agentId ?? parseAgentSessionKey(explicitlyRequestedKey)?.agentId);
if (!explicitlyRequestedAgent.ok) {
respond(false, void 0, explicitlyRequestedAgent.error);
return;
}
const catalogRequestedKey = normalizeOptionalString(p.key) ?? "global";
const catalogAgentId = catalogId ? normalizeAgentId(parseAgentSessionKey(catalogRequestedKey)?.agentId ?? explicitlyRequestedAgent.agentId) : void 0;
const catalogTarget = catalogId && catalogAgentId ? resolveRegisteredCatalogCreateTarget(catalogId, catalogAgentId, cfg) : void 0;
if (catalogTarget && !catalogTarget.ok) {
respond(false, void 0, errorShape(catalogTarget.unknownCatalog ? ErrorCodes.INVALID_REQUEST : ErrorCodes.UNAVAILABLE, catalogTarget.message));
return;
}
const initialTurn = resolveSessionCreateInitialTurn(p);
if (!initialTurn) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "sessions.create attachments require usable content"));
return;
}
const { attachments: initialAttachments, hasInitialTurn, message: initialMessage } = initialTurn;
let sessionKey = explicitlyRequestedKey;
const initialRunId = randomUUID();
if (p.mentions?.length) {
if (catalogId || p.incognito) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "Human mentions are unavailable for this session mode. Remove the selected mentions to continue."));
return;
}
sessionKey ??= buildDashboardSessionKey(explicitlyRequestedAgent.agentId);
const normalized = normalizeChatSendRequest({
params: {
sessionKey,
message: initialMessage ?? "",
mentions: p.mentions,
idempotencyKey: initialRunId
},
client
});
if (!normalized.ok) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, normalized.error));
return;
}
const eligible = context.mentionInbox?.validateRecipients(client, {
agentId: explicitlyRequestedAgent.agentId,
...p.visibility ? { visibility: p.visibility } : {}
}, p.mentions.map((mention) => mention.profileId));
if (!eligible?.ok) {
respond(false, void 0, eligible?.error ?? errorShape(ErrorCodes.UNAVAILABLE, "Human mentions are unavailable; reconnect and retry."));
return;
}
}
let requestedCwd = normalizeOptionalString(p.cwd);
const requestedExecNode = normalizeOptionalString(p.execNode);
const requestedProjectId = normalizeOptionalString(p.projectId);
const requestedProjectGitUrl = p.projectGitUrl;
const projectPreparationError = validateSessionProjectPreparation({
cwd: requestedCwd,
execNode: requestedExecNode,
gitUrl: requestedProjectGitUrl,
hasInitialTurn,
projectId: requestedProjectId
});
if (projectPreparationError) {
respond(false, void 0, projectPreparationError);
return;
}
if (!(!requestedCwd || (requestedExecNode ? path.isAbsolute(requestedCwd) || path.win32.isAbsolute(requestedCwd) : path.isAbsolute(requestedCwd)))) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "sessions.create cwd must be absolute"));
return;
}
const clientScopes = Array.isArray(client?.connect?.scopes) ? client.connect.scopes : [];
if (p.permissionMode === "full" && client !== null && !clientScopes.includes("operator.admin")) {
respond(false, void 0, missingScopeErrorShape({
missingScope: ADMIN_SCOPE,
requiredScopes: [ADMIN_SCOPE]
}));
return;
}
if (requestedCwd && !requestedExecNode && !clientScopes.includes("operator.admin")) {
const containment = await resolveWorkspacePathContainment(requestedCwd, cfg);
if (!containment) {
respond(false, void 0, missingScopeErrorShape({
missingScope: ADMIN_SCOPE,
requiredScopes: [ADMIN_SCOPE]
}));
return;
}
requestedCwd = containment.path;
}
if (requestedExecNode && p.worktree === true) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "sessions.create worktree cannot target execNode"));
return;
}
const requestedWorktreeBaseRef = normalizeOptionalString(p.worktreeBaseRef);
const requestedWorktreeName = normalizeOptionalString(p.worktreeName);
if ((requestedWorktreeBaseRef || requestedWorktreeName) && p.worktree !== true) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "sessions.create worktreeBaseRef/worktreeName require worktree=true"));
return;
}
const explicitSessionLabel = normalizeOptionalString(p.label);
const preparedDisplayName = normalizeOptionalString(p.displayName);
const titleAgentId = explicitlyRequestedAgent.agentId;
const existingWorktreeTarget = p.worktree === true && explicitlyRequestedKey ? loadGatewaySessionEntryReadOnly(explicitlyRequestedKey, { agentId: titleAgentId }).entry : void 0;
const deferWorktree = p.worktree === true && hasInitialTurn && !existingWorktreeTarget;
let projectRoot;
if (requestedProjectId) {
const project = resolveProjectRegistry(cfg, requestedProjectId);
if (!project) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `unknown project id: ${requestedProjectId}`));
return;
}
try {
const checkout = p.worktree === true ? await resolveProjectCheckout(project.repoRoot) : void 0;
projectRoot = checkout?.path ?? await resolveProjectDirectory(project.repoRoot);
if (checkout && project.source !== "workspace" && checkout.path !== checkout.repoRoot) throw new ProjectCheckoutError(`project root is no longer a git checkout`);
} catch (error) {
const detail = error instanceof ProjectCheckoutError ? error.message : formatErrorMessage(error);
respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, `project ${requestedProjectId} is unavailable (${detail}); update the agent workspace path or re-register the project`));
return;
}
}
let sessionAgentId = catalogAgentId ?? explicitlyRequestedAgent.agentId;
let preparedWorktree;
let pendingWorktree;
const sessionExecCwd = requestedExecNode ? requestedCwd : void 0;
let sessionCwd = requestedExecNode ? void 0 : projectRoot ?? requestedCwd;
let prepareLifecycle;
const preparedRoot = prepareSessionCreateFilesystemRoot({
cfg,
enforceSandboxContainment: Boolean(sessionCwd && !requestedExecNode && (requestedProjectId || p.worktree !== true)),
requestedExecNode,
requestedProjectId,
sessionCwd,
sessionKey,
targetAgentId: sessionAgentId
});
if (!preparedRoot.ok) {
respond(false, void 0, preparedRoot.error);
return;
}
sessionCwd = preparedRoot.value.sessionCwd;
const sessionRoot = preparedRoot.value.sessionRoot;
if (p.worktree === true) {
const agentId = explicitlyRequestedAgent.agentId;
let targetKey = sessionKey;
let preservesUnspecifiedKey = false;
if (!targetKey && parentSessionKey && p.emitCommandHooks === true && !hasInitialTurn && cfg.session?.dmScope === "main") {
const parentRequestedAgent = resolveRequestedSessionAgentId(cfg, parentSessionKey, agentId);
if (!parentRequestedAgent.ok) {
respond(false, void 0, parentRequestedAgent.error);
return;
}
const parent = loadGatewaySessionEntryReadOnly(parentSessionKey, { agentId: parentRequestedAgent.agentId });
const parentAgentId = parentRequestedAgent.agentId;
if (parent.entry?.sessionId && parent.canonicalKey === resolveAgentMainSessionKey({
cfg,
agentId: parentAgentId
})) {
targetKey = parent.canonicalKey;
preservesUnspecifiedKey = true;
}
}
targetKey ??= buildDashboardSessionKey(agentId);
const target = resolveGatewaySessionStoreTarget({
cfg,
key: targetKey,
agentId
});
sessionKey = preservesUnspecifiedKey ? void 0 : targetKey;
sessionAgentId = target.agentId;
const inheritedSource = !projectRoot && !requestedCwd && !requestedProjectGitUrl && spawnRequesterSessionKey && spawnRequesterSessionKey === parentSessionKey && sessionCreation.actor?.type === "agent" && normalizeAgentId(sessionCreation.actor.id) === target.agentId ? resolveSpawnParentWorktreeSource(spawnRequesterSessionKey, target.agentId, commitGuard) : void 0;
commitGuard = inheritedSource?.assertCurrent ?? commitGuard;
const workspace = projectRoot ?? requestedCwd ?? inheritedSource?.workspace ?? resolveAgentWorkspaceDir(cfg, target.agentId);
if (!requestedProjectGitUrl && !insideGitCheckout(workspace)) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "agent workspace is not a git checkout"));
return;
}
if (deferWorktree) pendingWorktree = {
...requestedProjectGitUrl ? {} : { workspace },
name: requestedWorktreeName,
baseRef: requestedWorktreeBaseRef,
titleSource: buildDashboardSessionTitleSource({
message: initialMessage ?? "",
attachments: initialAttachments
})
};
else prepareLifecycle = async (lifecycleTarget) => {
const source = buildDashboardSessionTitleSource({
message: initialMessage ?? "",
attachments: initialAttachments
});
const title = !requestedWorktreeName && !explicitSessionLabel && !preparedDisplayName && lifecycleTarget.entry && lifecycleTarget.titleModelSelection !== null ? await generateWorktreeSessionTitle({
cfg,
agentId: lifecycleTarget.agentId,
entry: requestedModel && !personalModelSelection ? {
...lifecycleTarget.entry,
...lifecycleTarget.titleModelSelection
} : lifecycleTarget.entry,
sessionId: lifecycleTarget.entry.sessionId,
sessionKey: lifecycleTarget.key,
storePath: lifecycleTarget.storePath,
currentUserMessage: initialMessage,
userMessage: source,
commitGuard,
onError: (error) => sessionLog.warn(`worktree title failed: ${formatErrorMessage(error)}`),
onPersisted: () => emitSessionsChanged(context, {
sessionKey: lifecycleTarget.key,
agentId: lifecycleTarget.agentId,
reason: "chat.title"
})
}) : void 0;
const prepared = await prepareSessionWorktree({
target: lifecycleTarget,
workspace,
name: requestedWorktreeName,
baseRef: requestedWorktreeBaseRef,
label: explicitSessionLabel ?? preparedDisplayName ?? title ?? resolveExplicitSessionName(lifecycleTarget.entry) ?? source,
runSetupScript: clientScopes.includes(ADMIN_SCOPE),
commitGuard
});
if (prepared.ok) preparedWorktree = prepared.value;
return prepared;
};
}
let runPayload;
let runError;
let runMeta;
const allowExistingModelSelection = authorizeOperatorScopesForRequiredScope(ADMIN_SCOPE, clientScopes).allowed;
const modelCatalogAgentId = sessionAgentId;
if (!authority.ensureActive()) return;
const created = await createGatewaySession({
cfg,
key: sessionKey,
agentId: sessionAgentId,
label: p.label,
displayName: preparedDisplayName,
category: p.category,
...catalogTarget ? { catalogTarget: catalogTarget.target } : { model: requestedModel },
personalModelSelection,
personalAccountDefaults,
contextWindow: p.contextWindow,
thinkingLevel: p.thinkingLevel,
fastMode: p.fastMode,
projectId: requestedProjectId,
pendingProjectGitUrl: normalizeSessionProjectGitUrl(requestedProjectGitUrl),
pendingWorktree,
incognito: p.incognito,
...client?.connect ? { requestingOperatorScopes: clientScopes } : {},
...client?.authenticatedUserProfile ? { requestingOperatorProfileId: client.authenticatedUserProfile.profileId } : {},
...client?.internal?.operatorRoleActor ? { operatorRoleActor: client.internal.operatorRoleActor } : {},
visibility: p.visibility,
allowExistingModelSelection,
parentSessionKey,
spawnDepth: p.spawnDepth,
spawnToolPolicy: sessionCreation.via === "spawn" && sessionCreation.inheritedToolPolicy ? {
...sessionCreation.inheritedToolPolicy,
...sessionCreation.completionOwnerSessionKey ? { completionOwnerSessionKey: sessionCreation.completionOwnerSessionKey } : {}
} : void 0,
spawnedCwd: p.worktree === true ? void 0 : sessionCwd,
sessionRoot: p.worktree === true ? void 0 : sessionRoot,
permissionMode: p.permissionMode,
...p.toolOverrides !== void 0 ? { toolOverrides: p.toolOverrides } : {},
prepareLifecycle,
onLifecycleCleanupError: (error) => {
sessionLog.warn(`failed to finalize session worktree lifecycle: ${formatErrorMessage(error)}`);
},
execNode: requestedExecNode,
execCwd: sessionExecCwd,
clearExecBinding: !requestedExecNode,
clearSpawnedCwd: p.worktree !== true && !sessionCwd,
fork: p.fork,
forkFrom: p.forkFrom,
succeedsParent: p.succeedsParent,
emitCommandHooks: p.emitCommandHooks,
resetMainWhenUnspecified: !hasInitialTurn,
commandSource: "webchat",
creation: sessionCreation,
authorizedPluginId: normalizeOptionalString(client?.internal?.pluginRuntimeOwnerId),
armSessionDiffBaselineCapture: true,
loadGatewayModelCatalog: () => context.loadGatewayModelCatalog({ agentId: modelCatalogAgentId }),
commitGuard,
afterCreate: async ({ key, agentId }) => {
if (!authority.hasActive()) return;
if (hasInitialTurn) await expectDefined(chatHandlers["chat.send"], "chat.send handler")({
req,
params: {
sessionKey: key,
agentId,
message: initialMessage ?? "",
idempotencyKey: initialRunId,
...p.mentions ? { mentions: p.mentions } : {},
...initialAttachments ? { attachments: initialAttachments } : {}
},
respond: (ok, payload, error, meta) => {
if (ok && payload && typeof payload === "object") runPayload = payload;
else runError = error;
runMeta = meta;
},
context,
client,
isWebchatConnect
});
}
}).catch((error) => {
if (error instanceof ModelAccountConnectAuthorityError) {
respond(false, void 0, errorShape(ErrorCodes.FORBIDDEN, error.message));
return;
}
return authority.handleClosedError(error);
});
if (!created) return;
if (!created.ok) {
respond(false, void 0, created.error);
return;
}
if (created.postCommit.status === "failed") runError = errorShape(ErrorCodes.UNAVAILABLE, formatErrorMessage(created.postCommit.error));
registerCreatedSessionCategory(normalizeOptionalString(p.category), context);
const createdWorktree = preparedWorktree?.worktree ? {
id: preparedWorktree.worktree.id,
path: preparedWorktree.sessionRoot,
branch: preparedWorktree.worktree.branch
} : void 0;
const responseEntry = sessionEntryForkedFromParent(created.entry) ? {
...created.entry,
forkedFromParent: true
} : created.entry;
const runStarted = !created.resetExisting && runPayload !== void 0 && isFreshChatSendStarted({
payload: runPayload,
cached: runMeta?.cached === true
});
respond(true, {
ok: true,
key: created.key,
sessionId: created.entry.sessionId,
entry: responseEntry,
runStarted,
...!created.resetExisting && runPayload ? runPayload : {},
...!created.resetExisting && runError ? { runError } : {},
resolved: created.resolved,
...createdWorktree ? { worktree: createdWorktree } : {}
}, void 0);
emitSessionsChanged(context, {
sessionKey: created.key,
agentId: created.agentId,
reason: created.resetExisting ? "new" : "create"
});
if (runStarted) emitSessionsChanged(context, {
sessionKey: created.key,
agentId: created.agentId,
reason: "send"
});
} };
sessionCreateHandlers["sessions.create"] = idempotentSessionCreate(expectDefined(sessionCreateHandlers["sessions.create"], "sessions.create handler"));
//#endregion
export { isFreshChatSendStarted as n, sessionCreateHandlers as t };