// Normalize early so per-account config and manager lookup use the same id. // Falls back to DEFAULT_ACCOUNT_ID so accounts.default.threadBindings.* is // respected even when the requester omits accountId. const accountId = normalizeOptionalString(event.requester?.accountId) || DEFAULT_ACCOUNT_ID; const flags = resolveThreadBindingFlags(api, accountId);
if (!flags.enabled) { return {
status: "error",
error: "Matrix thread bindings are disabled (set channels.matrix.threadBindings.enabled=true to override for this account, or session.threadBindings.enabled=true globally).",
} satisfies SpawningResult;
} if (!flags.spawnSubagentSessions) { return {
status: "error",
error: "Matrix thread-bound subagent spawns are disabled for this account (set channels.matrix.threadBindings.spawnSubagentSessions=true to enable).",
};
}
// Resolve the raw Matrix room ID from the requester's `to` field // (e.g. "room:!abc123:example.org" → "!abc123:example.org"). const rawTo = normalizeOptionalString(event.requester?.to) ?? ""; const matrixTarget = rawTo ? resolveMatrixTargetIdentity(rawTo) : null; const roomId = matrixTarget?.kind === "room" ? matrixTarget.id : "";
if (!roomId) { return {
status: "error",
error: "Cannot create Matrix thread binding: no room target in spawn request (requester.to must be a Matrix room ID).",
};
}
// Verify the thread binding manager is running for this account. The manager // holds the captured Matrix client the SessionBindingAdapter needs to send // the intro message that bootstraps the thread. const manager = getMatrixThreadBindingManager(accountId); if (!manager) { return {
status: "error",
error: `No Matrix thread binding manager available for account "${accountId}". Is the Matrix channel running?`,
};
}
try { // placement="child" tells the Matrix SessionBindingAdapter to: // 1. Send an intro message to the room, creating a new thread root event // 2. Use the returned event ID as boundConversationId (the thread ID) // 3. Register the binding record in the in-memory store and persist it // // We do NOT call setBindingRecord here — the adapter's bind() handles // record creation, thread creation, and persistence atomically. const binding = await getSessionBindingService().bind({
targetSessionKey: event.childSessionKey,
targetKind: "subagent",
conversation: {
channel: "matrix",
accountId,
conversationId: roomId,
},
placement: "child",
metadata: {
agentId: event.agentId?.trim() || undefined,
label: normalizeOptionalString(event.label) || undefined,
boundBy: "system",
},
}); return {
status: "ok",
threadBindingReady: true,
deliveryOrigin: resolveMatrixBindingDeliveryOrigin(binding, accountId),
} satisfies SpawningResult;
} catch (err) { return {
status: "error",
error: `Matrix thread bind failed: ${summarizeError(err)}`,
};
}
}
export async function handleMatrixSubagentEnded(event: MatrixSubagentEndedEvent): Promise<void> { const accountId = normalizeOptionalString(event.accountId) || undefined; // Use the targeted account list when available; fall back to a full scan // so bindings are cleaned up even when accountId is absent. const candidates = accountId ? listBindingsForAccount(accountId) : listAllBindings(); const matching = candidates.filter(
(entry) => entry.targetSessionKey === event.targetSessionKey && entry.targetKind === "subagent",
); const removedBindingKeys = new Set<string>(); if (event.sendFarewell) { const bindingService = getSessionBindingService(); const reason = normalizeOptionalString(event.reason) || "subagent-ended"; for (const binding of matching) { const bindingId = resolveBindingKey(binding); const removed = await bindingService.unbind({ bindingId, reason }); if (removed.some((entry) => entry.bindingId === bindingId)) {
removedBindingKeys.add(bindingId);
}
}
}
const affectedAccountIds = new Set<string>(); for (const binding of matching) { if (removedBindingKeys.has(resolveBindingKey(binding))) { continue;
} if (removeBindingRecord(binding)) {
affectedAccountIds.add(binding.accountId);
}
} // Flush each affected account's manager so removals are persisted to disk. for (const acctId of affectedAccountIds) { const manager = getMatrixThreadBindingManager(acctId);
await manager?.persist();
}
}
export function handleMatrixSubagentDeliveryTarget(
event: MatrixSubagentDeliveryTargetEvent,
): DeliveryTargetResult | undefined { if (!event.expectsCompletionMessage) { return undefined;
} const requesterChannel = event.requesterOrigin?.channel?.trim().toLowerCase(); if (requesterChannel !== "matrix") { return undefined;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.