feat(map): add persistent gateway and ontology package
This commit is contained in:
@@ -271,8 +271,9 @@ app.post("/api/ai-workspace/assistant/v1/actions", requireInternalApi, asyncRout
|
||||
|
||||
app.get("/api/ai-workspace/assistant/v1/executors", requireInternalApi, asyncRoute(async (req, res) => {
|
||||
const owner = getRequestOwner(req);
|
||||
const visibility = sanitizeExecutorVisibility(req.query.visibility || req.query.executorVisibility || req.query.executor_visibility || "interactive");
|
||||
const [executors, settings] = await Promise.all([
|
||||
listExecutors(owner),
|
||||
listExecutors(owner, { visibility }),
|
||||
getOwnerSettings(owner),
|
||||
]);
|
||||
res.json({ ok: true, owner: publicOwner(owner), selectedExecutorId: settings.selectedExecutorId, settings: publicOwnerSettings(settings), executors });
|
||||
@@ -692,7 +693,17 @@ httpServer.listen(config.port, "0.0.0.0", () => {
|
||||
process.on("SIGTERM", shutdown);
|
||||
process.on("SIGINT", shutdown);
|
||||
|
||||
async function listExecutors(owner) {
|
||||
function executorVisibility(executor) {
|
||||
const metadata = isPlainObject(executor?.metadata) ? executor.metadata : {};
|
||||
const explicitVisibility = normalizeKey(metadata.visibility || metadata.executorVisibility);
|
||||
if (explicitVisibility === "service" || explicitVisibility === "interactive") return explicitVisibility;
|
||||
const appId = normalizeKey(metadata.appId || metadata.app_id);
|
||||
const modeId = normalizeKey(metadata.modeId || metadata.mode_id);
|
||||
if (appId === "seo-mode" || modeId === "seo-model-task") return "service";
|
||||
return "interactive";
|
||||
}
|
||||
|
||||
async function listExecutors(owner, { visibility = "all" } = {}) {
|
||||
const result = await pool.query(
|
||||
`select *
|
||||
from ai_workspace_executors
|
||||
@@ -700,7 +711,10 @@ async function listExecutors(owner) {
|
||||
order by updated_at desc, created_at desc`,
|
||||
[owner.key]
|
||||
);
|
||||
return Promise.all(result.rows.map((row) => decorateExecutorLiveStatus(toExecutor(row))));
|
||||
const executors = result.rows.map(toExecutor).filter((executor) => (
|
||||
visibility === "all" || executorVisibility(executor) === visibility
|
||||
));
|
||||
return Promise.all(executors.map(decorateExecutorLiveStatus));
|
||||
}
|
||||
|
||||
async function decorateExecutorLiveStatus(executor) {
|
||||
@@ -1557,7 +1571,11 @@ async function readExecutorEvents(executor, options = {}) {
|
||||
10000
|
||||
);
|
||||
return {
|
||||
events: Array.isArray(payload.events) ? payload.events.map(cleanBridgeEventForUi) : [],
|
||||
events: Array.isArray(payload.events)
|
||||
? payload.events.map((event) => cleanBridgeEventForUi(event, {
|
||||
preserveLegacyCodexOutput: options.preserveLegacyCodexOutput === true,
|
||||
}))
|
||||
: [],
|
||||
latestEventId: Number(payload.latestEventId || 0),
|
||||
online: payload.online === true,
|
||||
};
|
||||
@@ -1898,6 +1916,7 @@ async function mirrorBridgeRunOnce(state) {
|
||||
const payload = await readExecutorEvents(state.executor, {
|
||||
since: state.latestEventId,
|
||||
limit: 500,
|
||||
preserveLegacyCodexOutput: true,
|
||||
});
|
||||
state.latestEventId = Math.max(state.latestEventId, Number(payload.latestEventId || 0));
|
||||
const nextEvents = (Array.isArray(payload.events) ? payload.events : [])
|
||||
@@ -3081,7 +3100,7 @@ function bridgeCompatibilityStatus(health, agent) {
|
||||
};
|
||||
}
|
||||
|
||||
function cleanBridgeEventForUi(event) {
|
||||
function cleanBridgeEventForUi(event, options = {}) {
|
||||
if (!isPlainObject(event)) return event;
|
||||
const kind = optionalString(event.kind) || "";
|
||||
const text = optionalString(event.text || event.message) || "";
|
||||
@@ -3093,7 +3112,9 @@ function cleanBridgeEventForUi(event) {
|
||||
};
|
||||
}
|
||||
if (kind !== "stdout" && kind !== "stderr") return event;
|
||||
const cleaned = summarizeLegacyCodexEventText(text);
|
||||
const cleaned = summarizeLegacyCodexEventText(text, {
|
||||
preserveCodexOutput: options.preserveLegacyCodexOutput === true,
|
||||
});
|
||||
if (!cleaned || cleaned === text) return event;
|
||||
return {
|
||||
...event,
|
||||
@@ -3103,12 +3124,14 @@ function cleanBridgeEventForUi(event) {
|
||||
};
|
||||
}
|
||||
|
||||
function summarizeLegacyCodexEventText(value) {
|
||||
function summarizeLegacyCodexEventText(value, options = {}) {
|
||||
const text = optionalString(value) || "";
|
||||
if (!text) return "";
|
||||
if (/^codex\s*\n/i.test(text)) {
|
||||
const answer = text.replace(/^codex\s*\n/i, "").trim();
|
||||
return answer ? `Codex emitted assistant output: ${answer.slice(0, 1200)}` : "Codex emitted assistant output.";
|
||||
if (!answer) return "Codex emitted assistant output.";
|
||||
const maxLength = options.preserveCodexOutput ? 250000 : 1200;
|
||||
return `Codex emitted assistant output: ${answer.slice(0, maxLength)}`;
|
||||
}
|
||||
if (/^tokens used/im.test(text)) return "Tokens reported.";
|
||||
return text.length > 1200 ? `${text.slice(0, 1200).trim()}...` : text;
|
||||
@@ -3139,8 +3162,21 @@ async function listThreads(owner, { surface, kind = "all", limit, includeArchive
|
||||
}
|
||||
if (kind === "shared") {
|
||||
clauses.push("coalesce(active_context->>'modeId', '') <> 'codex-remote'");
|
||||
clauses.push(`not (
|
||||
coalesce(metadata->>'visibility', '') = 'service'
|
||||
or origin_surface = 'seo-mode'
|
||||
or coalesce(active_context->>'modeId', '') = 'seo-model-task'
|
||||
or coalesce(metadata->>'threadKind', '') = 'model-task'
|
||||
)`);
|
||||
} else if (kind === "remote") {
|
||||
clauses.push("active_context->>'modeId' = 'codex-remote'");
|
||||
} else if (kind === "service") {
|
||||
clauses.push(`(
|
||||
coalesce(metadata->>'visibility', '') = 'service'
|
||||
or origin_surface = 'seo-mode'
|
||||
or coalesce(active_context->>'modeId', '') = 'seo-model-task'
|
||||
or coalesce(metadata->>'threadKind', '') = 'model-task'
|
||||
)`);
|
||||
}
|
||||
const result = await pool.query(
|
||||
`select *
|
||||
@@ -3653,10 +3689,16 @@ function sanitizeBridgeDispatchCommand(payload) {
|
||||
|
||||
function sanitizeThreadKind(value) {
|
||||
const text = normalizeKey(value || "all");
|
||||
if (text === "shared" || text === "remote" || text === "all") return text;
|
||||
if (text === "shared" || text === "remote" || text === "service" || text === "all") return text;
|
||||
return "all";
|
||||
}
|
||||
|
||||
function sanitizeExecutorVisibility(value) {
|
||||
const text = normalizeKey(value || "interactive");
|
||||
if (text === "interactive" || text === "service" || text === "all") return text;
|
||||
return "interactive";
|
||||
}
|
||||
|
||||
function sanitizeBridgeStopCommand(payload) {
|
||||
const source = isPlainObject(payload) ? payload : {};
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user