refactor: prune dead IPC surface and orphaned modules
Drop code that nothing consumed. Verified by full renderer+shared grep:
every removed channel had zero callers via window.agentCompany.* and
zero references from desktop-api types. No backend internal use either —
the database/API-server layers keep their methods, only the IPC exposure
goes.
Removed IPC handlers (26 total):
Thread CRUD surface (12) — writes happen inside the backend
createThread, listThreads, addThreadEvent,
setThreadWaitState, resolveThreadWaitState,
addParticipant, removeParticipant, listParticipants,
subscribeThread, unsubscribeThread,
listSubscriptions, listSubscribedThreads
(kept reads: getThread, listThreadEvents, listThreadWaitStates)
Half-implemented features (13)
saveSecret, listComments, getAgentMetrics, getApiPort,
listWakeupRequests, listAgentMessages, autoAssignTask,
listRunEvents, recoveryDismiss,
getHeartbeatPolicy, setHeartbeatPolicy,
getRuntimeState, setRuntimeState
Whole vertical slice: openConnectorAuthTerminal
IPC handler + channel + preload + DesktopApi type +
connector-auth-service function + useConnectorCallbacks
handleLoginConnector + OnboardingContext.onLoginConnector +
OnboardingWizard wiring. Pi-mono has no CLI auth flow;
the no-op that "returned false for legacy UI compatibility"
had no legacy UI left.
Orphaned modules (zero importers in repo):
src/main/runtime-env.ts
src/main/database-stale-recovery.ts
Dead schema files (only referenced by their own barrel re-export):
schemas-participants, schemas-subscriptions, schemas-heartbeat,
schemas-run-events, schemas-runtime-state, schemas-wakeup
Renderer leftovers:
.activity-row-enter CSS + its two unreferenced keyframes
5 orphan i18n keys (app.console, app.refresh, app.expand,
activity.timeline, auto.trigger) left over from earlier feature
deletions
Also update two unit tests that mocked openConnectorAuthTerminal in
the RegisterCoreHandlersDependencies pick.
This commit is contained in:
1 parent
1f7230fe87
commit
c71d033b0c
37 files changed
+17
-929
No files matched your search
@@ -5,7 +5,6 @@ import { join } from "node:path";
|
||||
import { randomBytes } from "node:crypto";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import log from "electron-log/main.js";
|
||||
import type { ConnectorId } from "@shared/types";
|
||||
|
||||
function quoteShell(value: string) {
|
||||
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
||||
@@ -88,25 +87,6 @@ function spawnTerminalWithScript(script: string, _title: string, _cwd: string):
|
||||
throw new Error("No supported terminal application was found.");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Connector auth terminal — deprecated under pi-mono */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* Pi-mono has no CLI auth flow. OAuth runs through `oauth-login-service`,
|
||||
* API keys are entered via the Settings form, and cloud providers (AWS,
|
||||
* Azure, GCP) use environment-driven credentials. The IPC handler still
|
||||
* calls this function for legacy UI compatibility, so we keep the signature
|
||||
* but treat it as a no-op that reports "not available".
|
||||
*/
|
||||
export async function openConnectorAuthTerminal(
|
||||
_connectorId: ConnectorId,
|
||||
_command: string,
|
||||
_cwd?: string,
|
||||
): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Generic terminal command (for install flows, etc.) */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import type { DatabaseSync } from "node:sqlite";
|
||||
import { STALE_LOCK_TIMEOUT_MS } from "./database-base";
|
||||
|
||||
function nowIso(): string {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stale-state recovery helpers extracted from `database-base.ts` to keep that
|
||||
* file under the 400-line cap. Both helpers are called by `AppDatabase.init`
|
||||
* (interruptStaleRuns) and the orchestration recovery loop (recoverStaleLocks).
|
||||
*/
|
||||
|
||||
interface InterruptDeps {
|
||||
db: DatabaseSync;
|
||||
transaction: <T>(op: () => T) => T;
|
||||
releaseAllWorkspaceLocks: () => void;
|
||||
}
|
||||
|
||||
export function interruptStaleRunsImpl(deps: InterruptDeps): number {
|
||||
const ts = nowIso();
|
||||
let interruptedCount = 0;
|
||||
deps.transaction(() => {
|
||||
const result = deps.db
|
||||
.prepare(
|
||||
"update runs set status = 'interrupted', error_message = 'AgentCompany restarted before the run could report completion.', finished_at = ?, updated_at = ? where status = 'running'",
|
||||
)
|
||||
.run(ts, ts);
|
||||
interruptedCount = Number(result.changes);
|
||||
if (interruptedCount > 0) {
|
||||
deps.db
|
||||
.prepare(
|
||||
"update tasks set status = 'blocked', updated_at = ? where id in (select task_id from runs where status = 'interrupted' and finished_at = ?) and status in ('in_progress', 'todo')",
|
||||
)
|
||||
.run(ts, ts);
|
||||
}
|
||||
deps.db
|
||||
.prepare(
|
||||
"update tasks set active_run_id = null where active_run_id in (select id from runs where status = 'interrupted')",
|
||||
)
|
||||
.run();
|
||||
deps.db.prepare("update agents set status = 'idle', updated_at = ? where status = 'running'").run(ts);
|
||||
deps.releaseAllWorkspaceLocks();
|
||||
});
|
||||
return interruptedCount;
|
||||
}
|
||||
|
||||
interface RecoverDeps {
|
||||
db: DatabaseSync;
|
||||
readAll: <T>(sql: string, ...params: import("node:sqlite").SQLInputValue[]) => T[];
|
||||
}
|
||||
|
||||
export function recoverStaleLocksImpl(
|
||||
deps: RecoverDeps,
|
||||
): Array<{ taskId: string; companyId: string; title: string }> {
|
||||
const cutoff = new Date(Date.now() - STALE_LOCK_TIMEOUT_MS).toISOString();
|
||||
const stale = deps.readAll<{ id: string; companyId: string; title: string }>(
|
||||
"SELECT id, company_id AS companyId, title FROM tasks WHERE execution_locked_at IS NOT NULL AND execution_locked_at < ? AND status = 'in_progress' AND checkout_run_id NOT IN (SELECT id FROM runs WHERE status = 'running')",
|
||||
cutoff,
|
||||
);
|
||||
const ts = nowIso();
|
||||
for (const task of stale) {
|
||||
deps.db
|
||||
.prepare(
|
||||
"UPDATE tasks SET checkout_run_id = NULL, execution_locked_at = NULL, status = 'todo', updated_at = ? WHERE id = ?",
|
||||
)
|
||||
.run(ts, task.id);
|
||||
}
|
||||
return stale.map((t) => ({ taskId: t.id, companyId: t.companyId, title: t.title }));
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
import {
|
||||
IPC_CHANNELS,
|
||||
agentMessageInputSchema,
|
||||
autoAssignSchema,
|
||||
markMessageReadSchema,
|
||||
messageQuerySchema,
|
||||
searchMessagesSchema,
|
||||
} from "@shared/contracts";
|
||||
import { fail, ok, type RegisterDomainHandlersDependencies } from "./ipc-domain-common";
|
||||
@@ -13,7 +11,6 @@ export function registerAgentCoordinationHandlers({
|
||||
registerHandle,
|
||||
db,
|
||||
ensureCompanyExists,
|
||||
findInvalidReference,
|
||||
publishDomainChanged,
|
||||
emitEvent,
|
||||
notify,
|
||||
@@ -24,7 +21,6 @@ export function registerAgentCoordinationHandlers({
|
||||
| "registerHandle"
|
||||
| "db"
|
||||
| "ensureCompanyExists"
|
||||
| "findInvalidReference"
|
||||
| "publishDomainChanged"
|
||||
| "emitEvent"
|
||||
| "notify"
|
||||
@@ -56,26 +52,6 @@ export function registerAgentCoordinationHandlers({
|
||||
});
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.listAgentMessages, async (_event, payload) => {
|
||||
const parsed = messageQuerySchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
const invalidReference = findInvalidReference(parsed.companyId, [
|
||||
{ id: parsed.agentId, table: "agents", label: "message reader" },
|
||||
{ id: parsed.channel === "project" ? parsed.channelTargetId : null, table: "projects", label: "project channel" },
|
||||
]);
|
||||
if (invalidReference) {
|
||||
return fail("INVALID_COMPANY_REFERENCE", `The selected ${invalidReference.label} does not belong to the current company.`);
|
||||
}
|
||||
return ok(db.listAgentMessages(parsed.companyId, {
|
||||
agentId: parsed.agentId,
|
||||
channel: parsed.channel,
|
||||
channelTargetId: parsed.channelTargetId,
|
||||
unreadOnly: parsed.unreadOnly,
|
||||
limit: parsed.limit,
|
||||
}));
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.markMessageRead, async (_event, payload) => {
|
||||
const parsed = markMessageReadSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
@@ -115,15 +91,4 @@ export function registerAgentCoordinationHandlers({
|
||||
}));
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.autoAssignTask, async (_event, payload) => {
|
||||
const parsed = autoAssignSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
const agentId = db.autoAssignTask(parsed.taskId, parsed.companyId);
|
||||
if (!agentId) {
|
||||
return fail("NO_AVAILABLE_AGENT", "No available agent could be found for this task.");
|
||||
}
|
||||
publishDomainChanged();
|
||||
return ok(agentId);
|
||||
});
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
connectorInputSchema,
|
||||
deleteEntitySchema,
|
||||
openTerminalCommandSchema,
|
||||
secretInputSchema,
|
||||
} from "@shared/contracts";
|
||||
import { validateConnectorConfigurationModel, validateConnectorModelSelection } from "./connector-model-policy";
|
||||
import { reconcileExecutionModeChange } from "./execution-mode-reconciler";
|
||||
@@ -40,11 +39,10 @@ export function registerCoreAdminHandlers({
|
||||
publishDomainChanged,
|
||||
handleApprovalResolved,
|
||||
checkConnector,
|
||||
openConnectorAuthTerminal,
|
||||
openGenericTerminal,
|
||||
}: Pick<
|
||||
RegisterCoreHandlersDependencies,
|
||||
"registerHandle" | "db" | "ensureCompanyExists" | "publishDomainChanged" | "handleApprovalResolved" | "checkConnector" | "openConnectorAuthTerminal" | "openGenericTerminal"
|
||||
"registerHandle" | "db" | "ensureCompanyExists" | "publishDomainChanged" | "handleApprovalResolved" | "checkConnector" | "openGenericTerminal"
|
||||
>) {
|
||||
registerHandle(IPC_CHANNELS.saveCompany, async (_event, payload) => {
|
||||
const parsed = companyInputSchema.parse(payload);
|
||||
@@ -123,12 +121,6 @@ export function registerCoreAdminHandlers({
|
||||
return ok(true);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.openConnectorAuthTerminal, async (_event, id) => {
|
||||
const parsed = connectorInputSchema.shape.id.parse(id);
|
||||
await openConnectorAuthTerminal(parsed);
|
||||
return ok(true);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.checkNodeInstalled, async () => {
|
||||
return ok(await probeShellNode());
|
||||
});
|
||||
@@ -146,23 +138,6 @@ export function registerCoreAdminHandlers({
|
||||
return ok(true);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.saveSecret, async (_event, payload) => {
|
||||
const parsed = secretInputSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
const id = db.saveSecret(parsed);
|
||||
db.addActivity({
|
||||
companyId: parsed.companyId,
|
||||
actor: "board",
|
||||
action: parsed.id ? "secret.updated" : "secret.created",
|
||||
entityType: "secret",
|
||||
entityId: id,
|
||||
detail: parsed.name,
|
||||
});
|
||||
publishDomainChanged();
|
||||
return ok(id);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.deleteSecret, async (_event, payload) => {
|
||||
const parsed = deleteEntitySchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
|
||||
@@ -26,12 +26,10 @@ export interface RegisterCoreHandlersDependencies {
|
||||
dispatchAutomation: (trigger: string, ctx: { companyId: string; [key: string]: unknown }) => void;
|
||||
queueTaskRun: (taskId: string, trigger: string, actor: string) => DesktopResult<string>;
|
||||
checkConnector: (id: ConnectorId) => Promise<void>;
|
||||
openConnectorAuthTerminal: (id: ConnectorId) => Promise<boolean>;
|
||||
openGenericTerminal: (input: { commandKey: string }) => Promise<boolean>;
|
||||
checkForUpdates: (options?: { manual?: boolean }) => Promise<boolean>;
|
||||
getWorkerProcess: () => { postMessage: (message: unknown) => void } | null;
|
||||
resetAfterRestore: () => void;
|
||||
getApiPort: () => number;
|
||||
listConnectorModels: (connectorId: string) => Promise<Array<{ id: string; label: string }>>;
|
||||
notify: (options: { title: string; body: string; urgency: "critical" | "informational"; navigation?: { section: SectionId; entityId?: string }; batchKey?: string }) => void;
|
||||
logger: {
|
||||
|
||||
@@ -4,15 +4,9 @@ import {
|
||||
approvalInputSchema,
|
||||
cancelRunSchema,
|
||||
commentInputSchema,
|
||||
heartbeatPolicySchema,
|
||||
heartbeatSettingsSchema,
|
||||
listCommentsSchema,
|
||||
listRunEventsSchema,
|
||||
runtimeStateSchema,
|
||||
runLogChunkSchema,
|
||||
runLogSchema,
|
||||
setHeartbeatPolicySchema,
|
||||
setRuntimeStateSchema,
|
||||
startRunSchema,
|
||||
triggerHeartbeatSchema,
|
||||
} from "@shared/contracts";
|
||||
@@ -209,16 +203,6 @@ export function registerCoreOperationHandlers({
|
||||
return ok(comment.id);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.listComments, async (_event, payload) => {
|
||||
const parsed = listCommentsSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
if (!db.belongsToCompany("tasks", parsed.taskId, parsed.companyId)) {
|
||||
return fail("TASK_NOT_FOUND", "Task not found in the current company.");
|
||||
}
|
||||
return ok(db.listComments(parsed.taskId));
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.setHeartbeat, async (_event, payload) => {
|
||||
const parsed = heartbeatSettingsSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
@@ -254,57 +238,4 @@ export function registerCoreOperationHandlers({
|
||||
return ok(runId);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.listRunEvents, async (_event, payload) => {
|
||||
const parsed = listRunEventsSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
if (!db.belongsToCompany("runs", parsed.runId, parsed.companyId)) {
|
||||
return fail("RUN_NOT_FOUND", "Run not found in the current company.");
|
||||
}
|
||||
return ok(db.listRunEvents(parsed.runId, parsed.companyId, { offset: parsed.offset, limit: parsed.limit }));
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.getHeartbeatPolicy, async (_event, payload) => {
|
||||
const parsed = heartbeatPolicySchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
if (!db.belongsToCompany("agents", parsed.agentId, parsed.companyId)) {
|
||||
return fail("AGENT_NOT_FOUND", "Agent not found in the current company.");
|
||||
}
|
||||
return ok(db.getHeartbeatPolicy(parsed.agentId));
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.setHeartbeatPolicy, async (_event, payload) => {
|
||||
const parsed = setHeartbeatPolicySchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
if (!db.belongsToCompany("agents", parsed.agentId, parsed.companyId)) {
|
||||
return fail("AGENT_NOT_FOUND", "Agent not found in the current company.");
|
||||
}
|
||||
const { companyId, agentId, ...policy } = parsed;
|
||||
db.setHeartbeatPolicy(agentId, companyId, policy);
|
||||
publishDomainChanged();
|
||||
return ok(null);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.getRuntimeState, async (_event, payload) => {
|
||||
const parsed = runtimeStateSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
if (!db.belongsToCompany("agents", parsed.agentId, parsed.companyId)) {
|
||||
return fail("AGENT_NOT_FOUND", "Agent not found in the current company.");
|
||||
}
|
||||
return ok(db.getRuntimeState(parsed.agentId));
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.setRuntimeState, async (_event, payload) => {
|
||||
const parsed = setRuntimeStateSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
if (!db.belongsToCompany("agents", parsed.agentId, parsed.companyId)) {
|
||||
return fail("AGENT_NOT_FOUND", "Agent not found in the current company.");
|
||||
}
|
||||
db.setRuntimeState(parsed.agentId, parsed.companyId, parsed.state);
|
||||
return ok(null);
|
||||
});
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
entityGetterSchema,
|
||||
metricsQuerySchema,
|
||||
settingsSchema,
|
||||
wakeupQuerySchema,
|
||||
} from "@shared/contracts";
|
||||
import { fail, ok, type RegisterCoreHandlersDependencies } from "./ipc-core-common";
|
||||
|
||||
@@ -18,7 +17,6 @@ export function registerCoreReadHandlers({
|
||||
publishDomainChanged,
|
||||
checkForUpdates,
|
||||
resetAfterRestore,
|
||||
getApiPort,
|
||||
listConnectorModels,
|
||||
logger,
|
||||
}: Pick<
|
||||
@@ -29,7 +27,6 @@ export function registerCoreReadHandlers({
|
||||
| "publishDomainChanged"
|
||||
| "checkForUpdates"
|
||||
| "resetAfterRestore"
|
||||
| "getApiPort"
|
||||
| "listConnectorModels"
|
||||
| "logger"
|
||||
>) {
|
||||
@@ -105,8 +102,6 @@ export function registerCoreReadHandlers({
|
||||
return ok(db.getSidebarBadges(companyId));
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.getApiPort, () => ok(getApiPort()));
|
||||
|
||||
registerHandle(IPC_CHANNELS.listConnectorModels, async (_event, connectorId) => {
|
||||
try {
|
||||
const parsed = connectorInputSchema.shape.id.parse(connectorId);
|
||||
@@ -141,21 +136,6 @@ export function registerCoreReadHandlers({
|
||||
return ok(db.getCompanyMetrics(input.companyId, input.days));
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.getAgentMetrics, (_event, payload) => {
|
||||
const input = metricsQuerySchema.parse(payload);
|
||||
const err = ensureCompanyExists(input.companyId);
|
||||
if (err) return err;
|
||||
if (!input.agentId) {
|
||||
return fail("MISSING_AGENT_ID", "agentId is required");
|
||||
}
|
||||
const metrics = db.getCompanyMetrics(input.companyId, input.days);
|
||||
const agentMetric = metrics.agentMetrics.find((metric: { agentId: string }) => metric.agentId === input.agentId);
|
||||
if (!agentMetric) {
|
||||
return fail("AGENT_NOT_FOUND", "Agent metrics not found");
|
||||
}
|
||||
return ok(agentMetric);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.getStandupReport, (_event, payload) => {
|
||||
const companyId = z.string().uuid().parse(payload);
|
||||
const err = ensureCompanyExists(companyId);
|
||||
@@ -163,17 +143,6 @@ export function registerCoreReadHandlers({
|
||||
return ok(db.getStandupReport(companyId));
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.listWakeupRequests, (_event, input) => {
|
||||
const parsed = wakeupQuerySchema.parse(input);
|
||||
const err = ensureCompanyExists(parsed.companyId);
|
||||
if (err) return err;
|
||||
return ok(db.listWakeupRequests(parsed.companyId, {
|
||||
status: parsed.status,
|
||||
agentId: parsed.agentId,
|
||||
limit: parsed.limit,
|
||||
}));
|
||||
});
|
||||
|
||||
// --- Entity getters for granular IPC updates ---
|
||||
|
||||
registerHandle(IPC_CHANNELS.getTask, (_event, payload) => {
|
||||
|
||||
@@ -46,18 +46,6 @@ export function registerRecoveryHandlers({
|
||||
return ok(null);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.recoveryDismiss, async (_event, payload) => {
|
||||
const parsed = recoveryTaskSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
if (!db.belongsToCompany("tasks", parsed.taskId, parsed.companyId)) {
|
||||
return fail("TASK_NOT_FOUND", "Task not found in the current company.");
|
||||
}
|
||||
db.dismissInterruptedTask(parsed.taskId);
|
||||
publishDomainChanged();
|
||||
return ok(null);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.recoveryFail, async (_event, payload) => {
|
||||
const parsed = recoveryTaskSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
|
||||
@@ -1,55 +1,13 @@
|
||||
import {
|
||||
IPC_CHANNELS,
|
||||
addThreadEventSchema,
|
||||
addParticipantSchema,
|
||||
createThreadSchema,
|
||||
listParticipantsSchema,
|
||||
listSubscriptionsSchema,
|
||||
listSubscribedThreadsSchema,
|
||||
listThreadEventsSchema,
|
||||
listThreadWaitStatesSchema,
|
||||
listThreadsSchema,
|
||||
removeParticipantSchema,
|
||||
resolveThreadWaitStateSchema,
|
||||
setThreadWaitStateSchema,
|
||||
subscribeThreadSchema,
|
||||
threadGetterSchema,
|
||||
unsubscribeThreadSchema,
|
||||
} from "@shared/contracts";
|
||||
import type { RegisterCoreHandlersDependencies } from "./ipc-core-common";
|
||||
import { fail, ok } from "./ipc-core-common";
|
||||
|
||||
export function registerThreadHandlers({ registerHandle, db, ensureCompanyExists, publishDomainChanged, emitEvent }: RegisterCoreHandlersDependencies) {
|
||||
registerHandle(IPC_CHANNELS.createThread, async (_event, payload) => {
|
||||
const input = createThreadSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
const thread = db.createThread({
|
||||
companyId: input.companyId,
|
||||
title: input.title,
|
||||
type: input.type,
|
||||
relatedEntityType: input.relatedEntityType ?? null,
|
||||
relatedEntityId: input.relatedEntityId ?? null,
|
||||
});
|
||||
publishDomainChanged();
|
||||
return ok(thread);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.listThreads, async (_event, payload) => {
|
||||
const input = listThreadsSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
const threads = db.listThreads({
|
||||
companyId: input.companyId,
|
||||
type: input.type,
|
||||
relatedEntityType: input.relatedEntityType,
|
||||
relatedEntityId: input.relatedEntityId,
|
||||
limit: input.limit,
|
||||
offset: input.offset,
|
||||
});
|
||||
return ok(threads);
|
||||
});
|
||||
|
||||
export function registerThreadHandlers({ registerHandle, db, ensureCompanyExists }: RegisterCoreHandlersDependencies) {
|
||||
registerHandle(IPC_CHANNELS.getThread, async (_event, payload) => {
|
||||
const input = threadGetterSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
@@ -59,22 +17,6 @@ export function registerThreadHandlers({ registerHandle, db, ensureCompanyExists
|
||||
return ok(thread);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.addThreadEvent, async (_event, payload) => {
|
||||
const input = addThreadEventSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
const event = db.addThreadEvent({
|
||||
threadId: input.threadId,
|
||||
actorId: input.actorId,
|
||||
actorType: input.actorType,
|
||||
eventType: input.eventType,
|
||||
body: input.body,
|
||||
metadataJson: input.metadataJson,
|
||||
});
|
||||
publishDomainChanged();
|
||||
return ok(event);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.listThreadEvents, async (_event, payload) => {
|
||||
const input = listThreadEventsSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
@@ -86,50 +28,6 @@ export function registerThreadHandlers({ registerHandle, db, ensureCompanyExists
|
||||
return ok(events);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.setThreadWaitState, async (_event, payload) => {
|
||||
const input = setThreadWaitStateSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
const waitState = db.setThreadWaitState({
|
||||
threadId: input.threadId,
|
||||
companyId: input.companyId,
|
||||
waitingFor: input.waitingFor,
|
||||
relatedApprovalId: input.relatedApprovalId ?? null,
|
||||
reason: input.reason,
|
||||
});
|
||||
publishDomainChanged();
|
||||
emitEvent({
|
||||
type: "thread-wait-state-updated",
|
||||
threadId: input.threadId,
|
||||
companyId: input.companyId,
|
||||
waitStateId: waitState.id,
|
||||
action: "created",
|
||||
});
|
||||
return ok(waitState);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.resolveThreadWaitState, async (_event, payload) => {
|
||||
const input = resolveThreadWaitStateSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
if (companyError) return companyError;
|
||||
|
||||
// Query the wait state before resolving to capture its threadId for the event
|
||||
const waitStates = db.listThreadWaitStates(input.companyId);
|
||||
const target = waitStates.find((ws) => ws.id === input.waitStateId);
|
||||
if (!target) return fail("THREAD_WAIT_STATE_NOT_FOUND", "Wait state not found");
|
||||
|
||||
db.resolveThreadWaitState(input.waitStateId, input.companyId);
|
||||
publishDomainChanged();
|
||||
emitEvent({
|
||||
type: "thread-wait-state-updated",
|
||||
threadId: target.threadId,
|
||||
companyId: input.companyId,
|
||||
waitStateId: input.waitStateId,
|
||||
action: "resolved",
|
||||
});
|
||||
return ok(true);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.listThreadWaitStates, async (_event, payload) => {
|
||||
const input = listThreadWaitStatesSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(input.companyId);
|
||||
@@ -137,72 +35,4 @@ export function registerThreadHandlers({ registerHandle, db, ensureCompanyExists
|
||||
const waitStates = db.listThreadWaitStates(input.companyId, { resolvedOnly: input.resolvedOnly });
|
||||
return ok(waitStates);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.listParticipants, async (_event, payload) => {
|
||||
const input = listParticipantsSchema.parse(payload);
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found in this company");
|
||||
const participants = db.listParticipants(input.threadId);
|
||||
return ok(participants);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.addParticipant, async (_event, payload) => {
|
||||
const input = addParticipantSchema.parse(payload);
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found in this company");
|
||||
const participant = db.addParticipant({
|
||||
threadId: input.threadId,
|
||||
participantId: input.participantId,
|
||||
participantType: input.participantType,
|
||||
role: input.role,
|
||||
});
|
||||
publishDomainChanged();
|
||||
return ok(participant);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.removeParticipant, async (_event, payload) => {
|
||||
const input = removeParticipantSchema.parse(payload);
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found in this company");
|
||||
db.removeParticipant(input.threadId, input.participantId);
|
||||
publishDomainChanged();
|
||||
return ok(true);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.subscribeThread, async (_event, payload) => {
|
||||
const input = subscribeThreadSchema.parse(payload);
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found in this company");
|
||||
const subscription = db.subscribe({
|
||||
threadId: input.threadId,
|
||||
subscriberId: input.subscriberId,
|
||||
subscriberType: input.subscriberType,
|
||||
notifyOn: input.notifyOn,
|
||||
});
|
||||
publishDomainChanged();
|
||||
return ok(subscription);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.unsubscribeThread, async (_event, payload) => {
|
||||
const input = unsubscribeThreadSchema.parse(payload);
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found in this company");
|
||||
db.unsubscribe(input.threadId, input.subscriberId);
|
||||
publishDomainChanged();
|
||||
return ok(true);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.listSubscriptions, async (_event, payload) => {
|
||||
const input = listSubscriptionsSchema.parse(payload);
|
||||
const thread = db.getThread(input.threadId, input.companyId);
|
||||
if (!thread) return fail("THREAD_NOT_FOUND", "Thread not found in this company");
|
||||
const subscriptions = db.listSubscriptions(input.threadId);
|
||||
return ok(subscriptions);
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.listSubscribedThreads, async (_event, payload) => {
|
||||
const input = listSubscribedThreadsSchema.parse(payload);
|
||||
const threads = db.listSubscribedThreads(input.subscriberId, input.companyId);
|
||||
return ok(threads);
|
||||
});
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import log from "electron-log/main.js";
|
||||
import type { AgentApiServer } from "./api-server";
|
||||
import { initializeAgentTokenSigningKey } from "./agent-auth";
|
||||
import { bootstrapApplication } from "./bootstrap-application";
|
||||
import { openConnectorAuthTerminal as openConnectorAuthTerminalImpl, openGenericTerminal as openGenericTerminalImpl } from "./connector-auth-service";
|
||||
import { openGenericTerminal as openGenericTerminalImpl } from "./connector-auth-service";
|
||||
import { getConnectorModelPolicy } from "./connector-model-policy";
|
||||
import type { ConnectorId } from "@shared/types";
|
||||
import type { AppDatabase } from "./database";
|
||||
@@ -145,10 +145,6 @@ export function createMainRuntimeSupport(state: MainRuntimeState) {
|
||||
queueTaskRun: (taskId, trigger, actor) =>
|
||||
requireOrchestrationService().queueTaskRun(taskId, trigger, actor),
|
||||
checkConnector: async (id) => requireConnectorHealthService().checkConnector(id),
|
||||
openConnectorAuthTerminal: async (id) => {
|
||||
const connector = requireDb().getConnector(id);
|
||||
return openConnectorAuthTerminalImpl(id, connector.command);
|
||||
},
|
||||
openGenericTerminal: (input) => openGenericTerminalImpl(input),
|
||||
checkForUpdates: () => requireUpdaterService().checkForUpdates(),
|
||||
getWorkerProcess: () => requireServiceGraph().getWorkerService()?.getWorker() ?? null,
|
||||
@@ -156,7 +152,6 @@ export function createMainRuntimeSupport(state: MainRuntimeState) {
|
||||
initializeAgentTokenSigningKey(requireDb().profileDir);
|
||||
void requireBrowserActionService().resetBrowserManager();
|
||||
},
|
||||
getApiPort: () => state.apiServer?.port ?? 0,
|
||||
notify: (options) => requireIpcSupport().notify(options),
|
||||
listConnectorModels: async (connectorId) => {
|
||||
const policy = getConnectorModelPolicy(requireDb(), connectorId as ConnectorId);
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
const ENV_ALLOWLIST: ReadonlySet<string> = new Set([
|
||||
"PATH",
|
||||
"HOME",
|
||||
"USER",
|
||||
"SHELL",
|
||||
"LANG",
|
||||
"LC_ALL",
|
||||
"LC_CTYPE",
|
||||
"TERM",
|
||||
"TMPDIR",
|
||||
"DEVELOPER_DIR",
|
||||
"SDKROOT",
|
||||
"NODE_ENV",
|
||||
"GIT_AUTHOR_NAME",
|
||||
"GIT_AUTHOR_EMAIL",
|
||||
"GIT_COMMITTER_NAME",
|
||||
"GIT_COMMITTER_EMAIL",
|
||||
"GIT_SSH_COMMAND",
|
||||
"GIT_ASKPASS",
|
||||
"GIT_TERMINAL_PROMPT",
|
||||
"XDG_CONFIG_HOME",
|
||||
"XDG_DATA_HOME",
|
||||
"XDG_CACHE_HOME",
|
||||
"XDG_RUNTIME_DIR",
|
||||
"DISPLAY",
|
||||
"WAYLAND_DISPLAY",
|
||||
"DBUS_SESSION_BUS_ADDRESS",
|
||||
"APPDATA",
|
||||
"LOCALAPPDATA",
|
||||
"USERPROFILE",
|
||||
"SYSTEMROOT",
|
||||
"COMSPEC",
|
||||
"PROGRAMFILES",
|
||||
"PROGRAMFILES(X86)",
|
||||
"PROGRAMDATA",
|
||||
"PATHEXT",
|
||||
"NODE_EXTRA_CA_CERTS",
|
||||
"SSL_CERT_FILE",
|
||||
"SSL_CERT_DIR",
|
||||
"REQUESTS_CA_BUNDLE",
|
||||
"CURL_CA_BUNDLE",
|
||||
"HTTP_PROXY",
|
||||
"HTTPS_PROXY",
|
||||
"http_proxy",
|
||||
"https_proxy",
|
||||
"ALL_PROXY",
|
||||
"all_proxy",
|
||||
"NO_PROXY",
|
||||
"no_proxy",
|
||||
"DENO_CERT",
|
||||
"CARGO_HTTP_CAINFO",
|
||||
"CARGO_HTTP_PROXY_CAINFO",
|
||||
"CARGO_HTTP_CHECK_REVOKE",
|
||||
"AGENT_COMPANY_AGENT_ID",
|
||||
"AGENT_COMPANY_COMPANY_ID",
|
||||
"AGENT_COMPANY_API_URL",
|
||||
"AGENT_COMPANY_API_KEY",
|
||||
"AGENT_COMPANY_RUN_ID",
|
||||
"AGENT_COMPANY_TASK_ID",
|
||||
"AGENT_COMPANY_WAKE_REASON",
|
||||
"AGENT_COMPANY_WAKE_COMMENT_ID",
|
||||
"AGENT_COMPANY_APPROVAL_ID",
|
||||
"AGENT_COMPANY_APPROVAL_STATUS",
|
||||
"AGENT_COMPANY_WORKSPACE_CWD",
|
||||
"AGENT_COMPANY_WORKSPACE_SOURCE",
|
||||
"AGENT_COMPANY_WORKSPACE_STRATEGY",
|
||||
"AGENT_COMPANY_WORKSPACE_ID",
|
||||
"AGENT_COMPANY_WORKSPACE_REPO_URL",
|
||||
"AGENT_COMPANY_WORKSPACE_REPO_REF",
|
||||
"AGENT_COMPANY_WORKSPACE_BRANCH",
|
||||
"AGENT_COMPANY_WORKSPACE_WORKTREE_PATH",
|
||||
"AGENT_COMPANY_WORKSPACES_JSON",
|
||||
"AGENT_COMPANY_RUNTIME_SERVICE_INTENTS_JSON",
|
||||
"AGENT_COMPANY_RUNTIME_SERVICES_JSON",
|
||||
"AGENT_COMPANY_RUNTIME_PRIMARY_URL",
|
||||
"AGENT_COMPANY_LINKED_ISSUE_IDS",
|
||||
]);
|
||||
|
||||
const FORBIDDEN_RUNTIME_OVERRIDE_KEYS: ReadonlySet<string> = new Set([
|
||||
"PATH",
|
||||
"HOME",
|
||||
"USER",
|
||||
"SHELL",
|
||||
"NODE_OPTIONS",
|
||||
"ELECTRON_RUN_AS_NODE",
|
||||
"DYLD_INSERT_LIBRARIES",
|
||||
"LD_PRELOAD",
|
||||
"PWD",
|
||||
]);
|
||||
|
||||
function normalizeStringEnv(input: NodeJS.ProcessEnv | Record<string, string>): Record<string, string> {
|
||||
return Object.fromEntries(
|
||||
Object.entries(input).filter((entry): entry is [string, string] => typeof entry[1] === "string"),
|
||||
);
|
||||
}
|
||||
|
||||
export function stripForbiddenRuntimeOverrides(env: Record<string, string>): Record<string, string> {
|
||||
const sanitized: Record<string, string> = {};
|
||||
for (const [key, value] of Object.entries(env)) {
|
||||
if (FORBIDDEN_RUNTIME_OVERRIDE_KEYS.has(key)) {
|
||||
continue;
|
||||
}
|
||||
sanitized[key] = value;
|
||||
}
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
export function buildSanitizedRuntimeEnv(
|
||||
hostEnv: NodeJS.ProcessEnv | Record<string, string>,
|
||||
runtimeOverrides: Record<string, string>,
|
||||
): Record<string, string> {
|
||||
const normalizedHostEnv = normalizeStringEnv(hostEnv);
|
||||
const sanitized: Record<string, string> = {};
|
||||
|
||||
if (process.platform === "win32") {
|
||||
// On Windows, env var keys are case-insensitive but plain JS objects are not.
|
||||
// Build a case-insensitive lookup so allowlist entries like "PATH" match "Path".
|
||||
const hostKeyMap = new Map<string, string>();
|
||||
for (const key of Object.keys(normalizedHostEnv)) {
|
||||
hostKeyMap.set(key.toUpperCase(), key);
|
||||
}
|
||||
for (const allowedKey of ENV_ALLOWLIST) {
|
||||
const actualKey = hostKeyMap.get(allowedKey.toUpperCase());
|
||||
if (actualKey && normalizedHostEnv[actualKey]) {
|
||||
sanitized[allowedKey] = normalizedHostEnv[actualKey]!;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const key of ENV_ALLOWLIST) {
|
||||
if (key in normalizedHostEnv) {
|
||||
sanitized[key] = normalizedHostEnv[key]!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// On Windows, many CLI tools (Claude Code, etc.) use HOME to find config directories.
|
||||
// Windows doesn't set HOME by default — synthesize it from USERPROFILE if missing.
|
||||
if (process.platform === "win32" && !sanitized.HOME && sanitized.USERPROFILE) {
|
||||
sanitized.HOME = sanitized.USERPROFILE;
|
||||
}
|
||||
|
||||
return {
|
||||
...sanitized,
|
||||
...stripForbiddenRuntimeOverrides(runtimeOverrides),
|
||||
};
|
||||
}
|
||||
+1
-33
@@ -21,7 +21,6 @@ const api: DesktopApi = {
|
||||
saveTask: (input) => ipcRenderer.invoke(IPC_CHANNELS.saveTask, input),
|
||||
requestApproval: (input) => ipcRenderer.invoke(IPC_CHANNELS.requestApproval, input),
|
||||
decideApproval: (input) => ipcRenderer.invoke(IPC_CHANNELS.decideApproval, input),
|
||||
saveSecret: (input) => ipcRenderer.invoke(IPC_CHANNELS.saveSecret, input),
|
||||
startTaskRun: (input) => ipcRenderer.invoke(IPC_CHANNELS.startTaskRun, input),
|
||||
cancelRun: (input) => ipcRenderer.invoke(IPC_CHANNELS.cancelRun, input),
|
||||
getRunLog: (input) => ipcRenderer.invoke(IPC_CHANNELS.getRunLog, input),
|
||||
@@ -42,15 +41,11 @@ const api: DesktopApi = {
|
||||
listConnectorModels: (connectorId) => ipcRenderer.invoke(IPC_CHANNELS.listConnectorModels, connectorId),
|
||||
getConnectorDisabledModels: (connectorId) => ipcRenderer.invoke(IPC_CHANNELS.getConnectorDisabledModels, connectorId),
|
||||
setConnectorDisabledModels: (input) => ipcRenderer.invoke(IPC_CHANNELS.setConnectorDisabledModels, input),
|
||||
openConnectorAuthTerminal: (connectorId) => ipcRenderer.invoke(IPC_CHANNELS.openConnectorAuthTerminal, connectorId),
|
||||
addComment: (input) => ipcRenderer.invoke(IPC_CHANNELS.addComment, input),
|
||||
listComments: (input) => ipcRenderer.invoke(IPC_CHANNELS.listComments, input),
|
||||
setHeartbeat: (input) => ipcRenderer.invoke(IPC_CHANNELS.setHeartbeat, input),
|
||||
triggerHeartbeat: (input) => ipcRenderer.invoke(IPC_CHANNELS.triggerHeartbeat, input),
|
||||
getApiPort: () => ipcRenderer.invoke(IPC_CHANNELS.getApiPort),
|
||||
getInbox: (companyId) => ipcRenderer.invoke(IPC_CHANNELS.getInbox, companyId),
|
||||
getCompanyMetrics: (input) => ipcRenderer.invoke(IPC_CHANNELS.getCompanyMetrics, input),
|
||||
getAgentMetrics: (input) => ipcRenderer.invoke(IPC_CHANNELS.getAgentMetrics, input),
|
||||
getStandupReport: (companyId) => ipcRenderer.invoke(IPC_CHANNELS.getStandupReport, companyId),
|
||||
bootstrapOnboarding: (input) => ipcRenderer.invoke(IPC_CHANNELS.bootstrapOnboarding, input),
|
||||
checkNodeInstalled: () => ipcRenderer.invoke(IPC_CHANNELS.checkNodeInstalled),
|
||||
@@ -73,13 +68,11 @@ const api: DesktopApi = {
|
||||
toggleAutomationRule: (input) => ipcRenderer.invoke(IPC_CHANNELS.toggleAutomationRule, input),
|
||||
getAutomationLog: (companyId) => ipcRenderer.invoke(IPC_CHANNELS.getAutomationLog, companyId),
|
||||
sendAgentMessage: (input) => ipcRenderer.invoke(IPC_CHANNELS.sendAgentMessage, input),
|
||||
listAgentMessages: (input) => ipcRenderer.invoke(IPC_CHANNELS.listAgentMessages, input),
|
||||
searchMessages: (input) => ipcRenderer.invoke(IPC_CHANNELS.searchMessages, input),
|
||||
markMessageRead: (input) => ipcRenderer.invoke(IPC_CHANNELS.markMessageRead, input),
|
||||
saveWorkflow: (input) => ipcRenderer.invoke(IPC_CHANNELS.saveWorkflow, input),
|
||||
deleteWorkflow: (input) => ipcRenderer.invoke(IPC_CHANNELS.deleteWorkflow, input),
|
||||
runWorkflow: (input) => ipcRenderer.invoke(IPC_CHANNELS.runWorkflow, input),
|
||||
autoAssignTask: (input) => ipcRenderer.invoke(IPC_CHANNELS.autoAssignTask, input),
|
||||
importSkill: (companyId, source) =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.importSkill, { companyId, source }),
|
||||
listSkills: (companyId) =>
|
||||
@@ -100,7 +93,6 @@ const api: DesktopApi = {
|
||||
ipcRenderer.invoke(IPC_CHANNELS.listSkillAgents, { companyId, skillId }),
|
||||
listAgentSkills: (companyId, agentId) =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.listAgentSkills, { companyId, agentId }),
|
||||
listWakeupRequests: (input) => ipcRenderer.invoke(IPC_CHANNELS.listWakeupRequests, input),
|
||||
saveBudgetPolicy: (input) => ipcRenderer.invoke(IPC_CHANNELS.saveBudgetPolicy, input),
|
||||
deleteBudgetPolicy: (input) => ipcRenderer.invoke(IPC_CHANNELS.deleteBudgetPolicy, input),
|
||||
listBudgetPolicies: (companyId) => ipcRenderer.invoke(IPC_CHANNELS.listBudgetPolicies, { companyId }),
|
||||
@@ -109,40 +101,16 @@ const api: DesktopApi = {
|
||||
getBudgetStatus: (companyId) => ipcRenderer.invoke(IPC_CHANNELS.getBudgetStatus, { companyId }),
|
||||
recoveryRetry: (taskId: string, agentId: string, companyId: string) =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.recoveryRetry, { taskId, agentId, companyId }),
|
||||
recoveryDismiss: (taskId: string, companyId: string) =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.recoveryDismiss, { taskId, companyId }),
|
||||
recoveryFail: (taskId: string, companyId: string) =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.recoveryFail, { taskId, companyId }),
|
||||
getTask: (input) => ipcRenderer.invoke(IPC_CHANNELS.getTask, input),
|
||||
getAgent: (input) => ipcRenderer.invoke(IPC_CHANNELS.getAgent, input),
|
||||
getRun: (input) => ipcRenderer.invoke(IPC_CHANNELS.getRun, input),
|
||||
getApproval: (input) => ipcRenderer.invoke(IPC_CHANNELS.getApproval, input),
|
||||
listRunEvents: (companyId: string, runId: string, opts?: { offset?: number; limit?: number }) =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.listRunEvents, { companyId, runId, ...opts }),
|
||||
getHeartbeatPolicy: (agentId: string, companyId: string) =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.getHeartbeatPolicy, { companyId, agentId }),
|
||||
setHeartbeatPolicy: (companyId, agentId, policy) =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.setHeartbeatPolicy, { companyId, agentId, ...policy }),
|
||||
getRuntimeState: (agentId: string, companyId: string) =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.getRuntimeState, { companyId, agentId }),
|
||||
setRuntimeState: (companyId: string, agentId: string, state: Record<string, unknown>) =>
|
||||
ipcRenderer.invoke(IPC_CHANNELS.setRuntimeState, { companyId, agentId, state }),
|
||||
// Thread runtime
|
||||
createThread: (input) => ipcRenderer.invoke(IPC_CHANNELS.createThread, input),
|
||||
listThreads: (input) => ipcRenderer.invoke(IPC_CHANNELS.listThreads, input),
|
||||
// Thread runtime (read-only)
|
||||
getThread: (input) => ipcRenderer.invoke(IPC_CHANNELS.getThread, input),
|
||||
addThreadEvent: (input) => ipcRenderer.invoke(IPC_CHANNELS.addThreadEvent, input),
|
||||
listThreadEvents: (input) => ipcRenderer.invoke(IPC_CHANNELS.listThreadEvents, input),
|
||||
setThreadWaitState: (input) => ipcRenderer.invoke(IPC_CHANNELS.setThreadWaitState, input),
|
||||
resolveThreadWaitState: (input) => ipcRenderer.invoke(IPC_CHANNELS.resolveThreadWaitState, input),
|
||||
listThreadWaitStates: (input) => ipcRenderer.invoke(IPC_CHANNELS.listThreadWaitStates, input),
|
||||
listParticipants: (input) => ipcRenderer.invoke(IPC_CHANNELS.listParticipants, input),
|
||||
addParticipant: (input) => ipcRenderer.invoke(IPC_CHANNELS.addParticipant, input),
|
||||
removeParticipant: (input) => ipcRenderer.invoke(IPC_CHANNELS.removeParticipant, input),
|
||||
subscribeThread: (input) => ipcRenderer.invoke(IPC_CHANNELS.subscribeThread, input),
|
||||
unsubscribeThread: (input) => ipcRenderer.invoke(IPC_CHANNELS.unsubscribeThread, input),
|
||||
listSubscriptions: (input) => ipcRenderer.invoke(IPC_CHANNELS.listSubscriptions, input),
|
||||
listSubscribedThreads: (input) => ipcRenderer.invoke(IPC_CHANNELS.listSubscribedThreads, input),
|
||||
subscribe: (callback: (event: DesktopEvent) => void) => {
|
||||
const listener = (_event: Electron.IpcRendererEvent, payload: DesktopEvent) => callback(payload);
|
||||
ipcRenderer.on(IPC_CHANNELS.rendererEvent, listener);
|
||||
|
||||
@@ -59,7 +59,6 @@ export interface OnboardingConnectorsContextValue {
|
||||
model: string | null,
|
||||
notes?: string,
|
||||
) => void;
|
||||
onLoginConnector: (connectorId: string) => void;
|
||||
|
||||
selectedConnectorId: ConnectorId | "";
|
||||
setSelectedConnectorId: (value: ConnectorId | "") => void;
|
||||
|
||||
@@ -83,7 +83,7 @@ export function CompanyOnboarding({
|
||||
}
|
||||
}, [step, nodeStatus, checkNode]);
|
||||
|
||||
const { handleTestConnector, handleSaveConnector, handleLoginConnector, handleInstallCommand } =
|
||||
const { handleTestConnector, handleSaveConnector, handleInstallCommand } =
|
||||
useConnectorCallbacks(connectors);
|
||||
|
||||
const {
|
||||
@@ -208,7 +208,6 @@ export function CompanyOnboarding({
|
||||
hasAtLeastOneReady,
|
||||
onTestConnector: (id) => void handleTestConnector(id),
|
||||
onSaveConnector: (id, env, model, notes) => void handleSaveConnector(id, env, model, notes),
|
||||
onLoginConnector: (id) => void handleLoginConnector(id),
|
||||
selectedConnectorId,
|
||||
setSelectedConnectorId,
|
||||
selectedModel,
|
||||
@@ -228,7 +227,6 @@ export function CompanyOnboarding({
|
||||
hasAtLeastOneReady,
|
||||
handleTestConnector,
|
||||
handleSaveConnector,
|
||||
handleLoginConnector,
|
||||
selectedConnectorId,
|
||||
setSelectedConnectorId,
|
||||
selectedModel,
|
||||
|
||||
@@ -36,30 +36,6 @@ export function useConnectorCallbacks(connectors: ProfileSnapshot["connectors"])
|
||||
}
|
||||
}, [connectors]);
|
||||
|
||||
const handleLoginConnector = useCallback(async (connectorId: string) => {
|
||||
try {
|
||||
unwrap(await window.agentCompany.openConnectorAuthTerminal(connectorId as ConnectorId));
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`[onboarding/useConnectorCallbacks] openConnectorAuthTerminal(${connectorId}) failed: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Poll connector health after login terminal opens (same pattern as ConnectorEditor)
|
||||
let attempts = 0;
|
||||
const poll = setInterval(async () => {
|
||||
attempts++;
|
||||
if (attempts > 12) { clearInterval(poll); return; }
|
||||
try {
|
||||
unwrap(await window.agentCompany.testConnector(connectorId as ConnectorId));
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`[onboarding/useConnectorCallbacks] post-login testConnector(${connectorId}) failed (attempt ${attempts}): ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
}
|
||||
}, 5000);
|
||||
}, []);
|
||||
|
||||
const handleInstallCommand = useCallback(async (commandKey: z.infer<typeof openTerminalCommandSchema>["commandKey"]) => {
|
||||
try {
|
||||
unwrap(await window.agentCompany.openTerminalCommand({ commandKey }));
|
||||
@@ -88,5 +64,5 @@ export function useConnectorCallbacks(connectors: ProfileSnapshot["connectors"])
|
||||
}, 5000);
|
||||
}, [connectors]);
|
||||
|
||||
return { handleTestConnector, handleSaveConnector, handleLoginConnector, handleInstallCommand };
|
||||
return { handleTestConnector, handleSaveConnector, handleInstallCommand };
|
||||
}
|
||||
@@ -39,10 +39,7 @@ export const en: Record<string, string> = {
|
||||
"app.selectCompany": "Select company",
|
||||
"app.newCompany": "New company",
|
||||
"app.commandPalette": "Command palette",
|
||||
"app.refresh": "Refresh",
|
||||
"app.console": "Console",
|
||||
"app.collapse": "Collapse",
|
||||
"app.expand": "Expand",
|
||||
"app.noResults": "No results for",
|
||||
"app.clear": "Clear",
|
||||
"app.search": "Search sections, agents, tasks, goals, projects...",
|
||||
@@ -358,9 +355,6 @@ export const en: Record<string, string> = {
|
||||
"costs.recentEntries": "Recent cost entries",
|
||||
"costs.budgetPosture": "Budget posture",
|
||||
|
||||
// ── Activity page ──
|
||||
"activity.timeline": "Activity timeline",
|
||||
|
||||
// ── Workspaces page ──
|
||||
"workspaces.registry": "Workspace registry",
|
||||
"workspaces.controlCenter": "Workspace control center",
|
||||
@@ -815,7 +809,6 @@ export const en: Record<string, string> = {
|
||||
"auto.newRuleTitle": "New Automation Rule",
|
||||
"auto.ruleName": "Rule Name",
|
||||
"auto.ruleDescription": "Description",
|
||||
"auto.trigger": "Trigger",
|
||||
"auto.conditionsJson": "Conditions (JSON)",
|
||||
"auto.action": "Action",
|
||||
"auto.actionConfigJson": "Action Configuration (JSON)",
|
||||
|
||||
@@ -39,10 +39,7 @@ export const zh: Record<string, string> = {
|
||||
"app.selectCompany": "选择公司",
|
||||
"app.newCompany": "新建公司",
|
||||
"app.commandPalette": "命令面板",
|
||||
"app.refresh": "刷新",
|
||||
"app.console": "控制台",
|
||||
"app.collapse": "收起",
|
||||
"app.expand": "展开",
|
||||
"app.noResults": "未找到匹配结果",
|
||||
"app.clear": "清除",
|
||||
"app.search": "搜索版块、智能体、任务、目标、项目...",
|
||||
@@ -358,9 +355,6 @@ export const zh: Record<string, string> = {
|
||||
"costs.recentEntries": "近期费用",
|
||||
"costs.budgetPosture": "预算概况",
|
||||
|
||||
// ── 活动页面 ──
|
||||
"activity.timeline": "活动时间线",
|
||||
|
||||
// ── 工作区页面 ──
|
||||
"workspaces.registry": "工作区注册表",
|
||||
"workspaces.controlCenter": "工作区控制中心",
|
||||
@@ -815,7 +809,6 @@ export const zh: Record<string, string> = {
|
||||
"auto.newRuleTitle": "新建自动化规则",
|
||||
"auto.ruleName": "规则名称",
|
||||
"auto.ruleDescription": "描述",
|
||||
"auto.trigger": "触发器",
|
||||
"auto.conditionsJson": "条件(JSON)",
|
||||
"auto.action": "动作",
|
||||
"auto.actionConfigJson": "动作配置(JSON)",
|
||||
|
||||
@@ -207,48 +207,6 @@
|
||||
transition: max-width 200ms cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
/* Dashboard activity row entry motion */
|
||||
@keyframes dashboard-activity-enter {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(-14px) scale(0.985);
|
||||
filter: blur(4px);
|
||||
}
|
||||
62% {
|
||||
opacity: 1;
|
||||
transform: translateY(2px) scale(1.002);
|
||||
filter: blur(0);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
filter: blur(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes dashboard-activity-highlight {
|
||||
0% {
|
||||
box-shadow: inset 2px 0 0 var(--primary);
|
||||
background-color: color-mix(in oklab, var(--accent) 55%, transparent);
|
||||
}
|
||||
100% {
|
||||
box-shadow: inset 0 0 0 transparent;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.activity-row-enter {
|
||||
animation:
|
||||
dashboard-activity-enter 520ms cubic-bezier(0.16, 1, 0.3, 1),
|
||||
dashboard-activity-highlight 920ms cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.activity-row-enter {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* MDXEditor theme integration */
|
||||
.paperclip-mdxeditor-scope,
|
||||
.paperclip-mdxeditor {
|
||||
|
||||
@@ -14,7 +14,6 @@ export const IPC_CHANNELS = {
|
||||
saveTask: "agent-company:save-task",
|
||||
requestApproval: "agent-company:request-approval",
|
||||
decideApproval: "agent-company:decide-approval",
|
||||
saveSecret: "agent-company:save-secret",
|
||||
startTaskRun: "agent-company:start-task-run",
|
||||
cancelRun: "agent-company:cancel-run",
|
||||
getRunLog: "agent-company:get-run-log",
|
||||
@@ -33,16 +32,12 @@ export const IPC_CHANNELS = {
|
||||
deleteSecret: "agent-company:delete-secret",
|
||||
getSidebarBadges: "agent-company:get-sidebar-badges",
|
||||
listConnectorModels: "agent-company:list-connector-models",
|
||||
openConnectorAuthTerminal: "agent-company:open-connector-auth-terminal",
|
||||
addComment: "agent-company:add-comment",
|
||||
listComments: "agent-company:list-comments",
|
||||
setHeartbeat: "agent-company:set-heartbeat",
|
||||
triggerHeartbeat: "agent-company:trigger-heartbeat",
|
||||
getApiPort: "agent-company:get-api-port",
|
||||
rendererEvent: "agent-company:event",
|
||||
getInbox: "agent-company:get-inbox",
|
||||
getCompanyMetrics: "agent-company:get-company-metrics",
|
||||
getAgentMetrics: "agent-company:get-agent-metrics",
|
||||
getStandupReport: "agent-company:get-standup-report",
|
||||
bootstrapOnboarding: "agent-company:bootstrap-onboarding",
|
||||
checkNodeInstalled: "agent-company:check-node-installed",
|
||||
@@ -65,12 +60,10 @@ export const IPC_CHANNELS = {
|
||||
toggleAutomationRule: "agent-company:toggle-automation-rule",
|
||||
getAutomationLog: "agent-company:get-automation-log",
|
||||
sendAgentMessage: "agent-company:send-agent-message",
|
||||
listAgentMessages: "agent-company:list-agent-messages",
|
||||
markMessageRead: "agent-company:mark-message-read",
|
||||
saveWorkflow: "agent-company:save-workflow",
|
||||
deleteWorkflow: "agent-company:delete-workflow",
|
||||
runWorkflow: "agent-company:run-workflow",
|
||||
autoAssignTask: "agent-company:auto-assign-task",
|
||||
searchMessages: "agent-company:search-messages",
|
||||
importSkill: "agent-company:import-skill",
|
||||
listSkills: "agent-company:list-skills",
|
||||
@@ -82,7 +75,6 @@ export const IPC_CHANNELS = {
|
||||
checkSkillUpdate: "agent-company:check-skill-update",
|
||||
listSkillAgents: "agent-company:list-skill-agents",
|
||||
listAgentSkills: "agent-company:list-agent-skills",
|
||||
listWakeupRequests: "agent-company:list-wakeup-requests",
|
||||
saveBudgetPolicy: "agent-company:save-budget-policy",
|
||||
deleteBudgetPolicy: "agent-company:delete-budget-policy",
|
||||
listBudgetPolicies: "agent-company:list-budget-policies",
|
||||
@@ -97,32 +89,11 @@ export const IPC_CHANNELS = {
|
||||
setConnectorDisabledModels: "agent-company:set-connector-disabled-models",
|
||||
// Recovery
|
||||
recoveryRetry: "recovery:retry",
|
||||
recoveryDismiss: "recovery:dismiss",
|
||||
recoveryFail: "recovery:fail",
|
||||
// Run events
|
||||
listRunEvents: "agent-company:list-run-events",
|
||||
// Heartbeat policy
|
||||
getHeartbeatPolicy: "agent-company:get-heartbeat-policy",
|
||||
setHeartbeatPolicy: "agent-company:set-heartbeat-policy",
|
||||
// Runtime state
|
||||
getRuntimeState: "agent-company:get-runtime-state",
|
||||
setRuntimeState: "agent-company:set-runtime-state",
|
||||
// Thread runtime
|
||||
createThread: "agent-company:create-thread",
|
||||
listThreads: "agent-company:list-threads",
|
||||
// Thread runtime (read-only — writes happen inside the backend)
|
||||
getThread: "agent-company:get-thread",
|
||||
addThreadEvent: "agent-company:add-thread-event",
|
||||
listThreadEvents: "agent-company:list-thread-events",
|
||||
setThreadWaitState: "agent-company:set-thread-wait-state",
|
||||
resolveThreadWaitState: "agent-company:resolve-thread-wait-state",
|
||||
listThreadWaitStates: "agent-company:list-thread-wait-states",
|
||||
listParticipants: "agent-company:list-participants",
|
||||
addParticipant: "agent-company:add-participant",
|
||||
removeParticipant: "agent-company:remove-participant",
|
||||
subscribeThread: "agent-company:subscribe-thread",
|
||||
unsubscribeThread: "agent-company:unsubscribe-thread",
|
||||
listSubscriptions: "agent-company:list-subscriptions",
|
||||
listSubscribedThreads: "agent-company:list-subscribed-threads",
|
||||
windowMinimize: "window:minimize",
|
||||
windowMaximize: "window:maximize",
|
||||
windowClose: "window:close",
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
import type { z } from "zod";
|
||||
import type { AgentMetrics, AgentRecord } from "../../types";
|
||||
import type { AgentRecord } from "../../types";
|
||||
import type { agentInputSchema, heartbeatSettingsSchema, hireRequestSchema, triggerHeartbeatSchema } from "../schemas-agents";
|
||||
import type { entityGetterSchema, metricsQuerySchema } from "../schemas-badges";
|
||||
import type { entityGetterSchema } from "../schemas-badges";
|
||||
import type { deleteEntitySchema } from "../schemas-badges";
|
||||
import type { DesktopResult, HeartbeatPolicy } from "./common";
|
||||
import type { DesktopResult } from "./common";
|
||||
|
||||
export type AgentsApi = {
|
||||
saveAgent: (input: z.infer<typeof agentInputSchema>) => Promise<DesktopResult<string>>;
|
||||
requestHire: (input: z.infer<typeof hireRequestSchema>) => Promise<DesktopResult<{ agentId: string; approvalId: string; autoApproved: boolean }>>;
|
||||
deleteAgent: (input: z.infer<typeof deleteEntitySchema>) => Promise<DesktopResult<boolean>>;
|
||||
getAgent: (input: z.infer<typeof entityGetterSchema>) => Promise<DesktopResult<AgentRecord>>;
|
||||
getAgentMetrics: (input: z.infer<typeof metricsQuerySchema>) => Promise<DesktopResult<AgentMetrics>>;
|
||||
setHeartbeat: (input: z.infer<typeof heartbeatSettingsSchema>) => Promise<DesktopResult<boolean>>;
|
||||
triggerHeartbeat: (input: z.infer<typeof triggerHeartbeatSchema>) => Promise<DesktopResult<string>>;
|
||||
getHeartbeatPolicy: (agentId: string, companyId: string) => Promise<DesktopResult<HeartbeatPolicy>>;
|
||||
setHeartbeatPolicy: (companyId: string, agentId: string, policy: Partial<HeartbeatPolicy>) => Promise<DesktopResult<boolean>>;
|
||||
getRuntimeState: (agentId: string, companyId: string) => Promise<DesktopResult<Record<string, unknown>>>;
|
||||
setRuntimeState: (companyId: string, agentId: string, state: Record<string, unknown>) => Promise<DesktopResult<boolean>>;
|
||||
};
|
||||
@@ -4,7 +4,6 @@ import type { automationRuleInputSchema, toggleAutomationRuleSchema } from "../s
|
||||
import type { browserActionQuerySchema, cancelBrowserActionSchema, openTerminalCommandSchema, runWorkflowSchema, triggerBrowserLoginSchema, workflowInputSchema } from "../schemas-execution";
|
||||
import type { socialAccountInputSchema } from "../schemas-company";
|
||||
import type { deleteEntitySchema } from "../schemas-badges";
|
||||
import type { wakeupQuerySchema, WakeupRequestRecord } from "../schemas-wakeup";
|
||||
import type { DesktopResult } from "./common";
|
||||
|
||||
export type AutomationApi = {
|
||||
@@ -15,7 +14,6 @@ export type AutomationApi = {
|
||||
saveWorkflow: (input: z.infer<typeof workflowInputSchema>) => Promise<DesktopResult<string>>;
|
||||
deleteWorkflow: (input: z.infer<typeof deleteEntitySchema>) => Promise<DesktopResult<boolean>>;
|
||||
runWorkflow: (input: z.infer<typeof runWorkflowSchema>) => Promise<DesktopResult<boolean>>;
|
||||
listWakeupRequests: (input: z.infer<typeof wakeupQuerySchema>) => Promise<DesktopResult<WakeupRequestRecord[]>>;
|
||||
saveSocialAccount: (input: z.infer<typeof socialAccountInputSchema>) => Promise<DesktopResult<string>>;
|
||||
deleteSocialAccount: (input: z.infer<typeof deleteEntitySchema>) => Promise<DesktopResult<boolean>>;
|
||||
listBrowserActions: (input: z.infer<typeof browserActionQuerySchema>) => Promise<DesktopResult<BrowserActionRecord[]>>;
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import type { z } from "zod";
|
||||
import type { AgentMessageRecord, CommentRecord, DocumentRecord, KnowledgeEntryRecord } from "../../types";
|
||||
import type { agentMessageInputSchema, commentInputSchema, documentInputSchema, knowledgeEntryInputSchema, listCommentsSchema, markMessageReadSchema, meetingInputSchema, messageQuerySchema, searchMessagesSchema } from "../schemas-messages";
|
||||
import type { AgentMessageRecord, DocumentRecord, KnowledgeEntryRecord } from "../../types";
|
||||
import type { agentMessageInputSchema, commentInputSchema, documentInputSchema, knowledgeEntryInputSchema, markMessageReadSchema, meetingInputSchema, searchMessagesSchema } from "../schemas-messages";
|
||||
import type { deleteEntitySchema } from "../schemas-badges";
|
||||
import type { DesktopResult } from "./common";
|
||||
|
||||
export type CommunicationApi = {
|
||||
addComment: (input: z.infer<typeof commentInputSchema>) => Promise<DesktopResult<string>>;
|
||||
listComments: (input: z.infer<typeof listCommentsSchema>) => Promise<DesktopResult<CommentRecord[]>>;
|
||||
saveMeeting: (input: z.infer<typeof meetingInputSchema>) => Promise<DesktopResult<string>>;
|
||||
deleteMeeting: (input: z.infer<typeof deleteEntitySchema>) => Promise<DesktopResult<boolean>>;
|
||||
saveDocument: (input: z.infer<typeof documentInputSchema>) => Promise<DesktopResult<string>>;
|
||||
@@ -14,7 +13,6 @@ export type CommunicationApi = {
|
||||
saveKnowledgeEntry: (input: z.infer<typeof knowledgeEntryInputSchema>) => Promise<DesktopResult<string>>;
|
||||
deleteKnowledgeEntry: (input: z.infer<typeof deleteEntitySchema>) => Promise<DesktopResult<boolean>>;
|
||||
sendAgentMessage: (input: z.infer<typeof agentMessageInputSchema>) => Promise<DesktopResult<import("@shared/types").SendMessageResult>>;
|
||||
listAgentMessages: (input: z.infer<typeof messageQuerySchema>) => Promise<DesktopResult<AgentMessageRecord[]>>;
|
||||
markMessageRead: (input: z.infer<typeof markMessageReadSchema>) => Promise<DesktopResult<boolean>>;
|
||||
searchMessages: (input: z.infer<typeof searchMessagesSchema>) => Promise<DesktopResult<AgentMessageRecord[]>>;
|
||||
// Keep reference to silence unused-import checker for DocumentRecord/KnowledgeEntryRecord — consumers infer via schemas.
|
||||
|
||||
@@ -19,8 +19,6 @@ export type CompaniesApi = {
|
||||
listConnectorModels: (connectorId: string) => Promise<DesktopResult<Array<{ id: string; label: string }>>>;
|
||||
getConnectorDisabledModels: (connectorId: string) => Promise<DesktopResult<Array<{ id: string; label: string }>>>;
|
||||
setConnectorDisabledModels: (input: z.infer<typeof connectorDisabledModelsSchema>) => Promise<DesktopResult<boolean>>;
|
||||
openConnectorAuthTerminal: (connectorId: z.infer<typeof connectorInputSchema.shape.id>) => Promise<DesktopResult<boolean>>;
|
||||
getApiPort: () => Promise<DesktopResult<number>>;
|
||||
getInbox: (companyId: string) => Promise<DesktopResult<InboxItem[]>>;
|
||||
getCompanyMetrics: (input: z.infer<typeof metricsQuerySchema>) => Promise<DesktopResult<CompanyMetrics>>;
|
||||
getStandupReport: (companyId: string) => Promise<DesktopResult<StandupReport>>;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { z } from "zod";
|
||||
import type { secretInputSchema } from "../schemas-company";
|
||||
import type { deleteEntitySchema } from "../schemas-badges";
|
||||
import type { z } from "zod";
|
||||
import type {
|
||||
CredentialSummary,
|
||||
SaveCredentialInput,
|
||||
@@ -13,7 +12,6 @@ import type {
|
||||
import type { DesktopResult } from "./common";
|
||||
|
||||
export type CredentialsApi = {
|
||||
saveSecret: (input: z.infer<typeof secretInputSchema>) => Promise<DesktopResult<string>>;
|
||||
deleteSecret: (input: z.infer<typeof deleteEntitySchema>) => Promise<DesktopResult<boolean>>;
|
||||
// pi-mono credentials
|
||||
listCredentials: () => Promise<DesktopResult<CredentialSummary[]>>;
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { z } from "zod";
|
||||
import type { RunLogChunk, RunRecord } from "../../types";
|
||||
import type { cancelRunSchema, runLogChunkSchema, runLogSchema, startRunSchema } from "../schemas-execution";
|
||||
import type { entityGetterSchema } from "../schemas-badges";
|
||||
import type { DesktopResult, RunEventRecord } from "./common";
|
||||
import type { DesktopResult } from "./common";
|
||||
|
||||
export type RunsApi = {
|
||||
startTaskRun: (input: z.infer<typeof startRunSchema>) => Promise<DesktopResult<string>>;
|
||||
@@ -10,8 +10,6 @@ export type RunsApi = {
|
||||
getRunLog: (input: z.infer<typeof runLogSchema>) => Promise<DesktopResult<string>>;
|
||||
getRunLogChunk: (input: z.infer<typeof runLogChunkSchema>) => Promise<DesktopResult<RunLogChunk>>;
|
||||
getRun: (input: z.infer<typeof entityGetterSchema>) => Promise<DesktopResult<RunRecord>>;
|
||||
listRunEvents: (companyId: string, runId: string, opts?: { offset?: number; limit?: number }) => Promise<DesktopResult<RunEventRecord[]>>;
|
||||
recoveryRetry: (taskId: string, agentId: string, companyId: string) => Promise<DesktopResult<null>>;
|
||||
recoveryDismiss: (taskId: string, companyId: string) => Promise<DesktopResult<null>>;
|
||||
recoveryFail: (taskId: string, companyId: string) => Promise<DesktopResult<null>>;
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { z } from "zod";
|
||||
import type { TaskRecord } from "../../types";
|
||||
import type { autoAssignSchema, goalInputSchema, projectInputSchema, sprintInputSchema, taskInputSchema } from "../schemas-tasks";
|
||||
import type { goalInputSchema, projectInputSchema, sprintInputSchema, taskInputSchema } from "../schemas-tasks";
|
||||
import type { workspaceInputSchema } from "../schemas-company";
|
||||
import type { deleteEntitySchema, entityGetterSchema } from "../schemas-badges";
|
||||
import type { DesktopResult, SaveTaskResult } from "./common";
|
||||
@@ -17,5 +17,4 @@ export type TasksApi = {
|
||||
deleteWorkspace: (input: z.infer<typeof deleteEntitySchema>) => Promise<DesktopResult<boolean>>;
|
||||
deleteSprint: (input: z.infer<typeof deleteEntitySchema>) => Promise<DesktopResult<boolean>>;
|
||||
getTask: (input: z.infer<typeof entityGetterSchema>) => Promise<DesktopResult<TaskRecord>>;
|
||||
autoAssignTask: (input: z.infer<typeof autoAssignSchema>) => Promise<DesktopResult<string>>;
|
||||
};
|
||||
@@ -1,24 +1,10 @@
|
||||
import type { z } from "zod";
|
||||
import type { ConversationEventRecord, ConversationParticipantRecord, ConversationThreadRecord, ThreadSubscriptionRecord, ThreadWaitStateRecord } from "../../types";
|
||||
import type { addThreadEventSchema, createThreadSchema, listThreadEventsSchema, listThreadsSchema, listThreadWaitStatesSchema, resolveThreadWaitStateSchema, setThreadWaitStateSchema, threadGetterSchema } from "../schemas-threads";
|
||||
import type { addParticipantSchema, listParticipantsSchema, removeParticipantSchema } from "../schemas-participants";
|
||||
import type { listSubscribedThreadsSchema, listSubscriptionsSchema, subscribeThreadSchema, unsubscribeThreadSchema } from "../schemas-subscriptions";
|
||||
import type { ConversationEventRecord, ConversationThreadRecord, ThreadWaitStateRecord } from "../../types";
|
||||
import type { listThreadEventsSchema, listThreadWaitStatesSchema, threadGetterSchema } from "../schemas-threads";
|
||||
import type { DesktopResult } from "./common";
|
||||
|
||||
export type ThreadsApi = {
|
||||
createThread: (input: z.infer<typeof createThreadSchema>) => Promise<DesktopResult<ConversationThreadRecord>>;
|
||||
listThreads: (input: z.infer<typeof listThreadsSchema>) => Promise<DesktopResult<ConversationThreadRecord[]>>;
|
||||
getThread: (input: z.infer<typeof threadGetterSchema>) => Promise<DesktopResult<ConversationThreadRecord>>;
|
||||
addThreadEvent: (input: z.infer<typeof addThreadEventSchema>) => Promise<DesktopResult<ConversationEventRecord>>;
|
||||
listThreadEvents: (input: z.infer<typeof listThreadEventsSchema>) => Promise<DesktopResult<ConversationEventRecord[]>>;
|
||||
setThreadWaitState: (input: z.infer<typeof setThreadWaitStateSchema>) => Promise<DesktopResult<ThreadWaitStateRecord>>;
|
||||
resolveThreadWaitState: (input: z.infer<typeof resolveThreadWaitStateSchema>) => Promise<DesktopResult<boolean>>;
|
||||
listThreadWaitStates: (input: z.infer<typeof listThreadWaitStatesSchema>) => Promise<DesktopResult<ThreadWaitStateRecord[]>>;
|
||||
listParticipants: (input: z.infer<typeof listParticipantsSchema>) => Promise<DesktopResult<ConversationParticipantRecord[]>>;
|
||||
addParticipant: (input: z.infer<typeof addParticipantSchema>) => Promise<DesktopResult<ConversationParticipantRecord>>;
|
||||
removeParticipant: (input: z.infer<typeof removeParticipantSchema>) => Promise<DesktopResult<boolean>>;
|
||||
subscribeThread: (input: z.infer<typeof subscribeThreadSchema>) => Promise<DesktopResult<ThreadSubscriptionRecord>>;
|
||||
unsubscribeThread: (input: z.infer<typeof unsubscribeThreadSchema>) => Promise<DesktopResult<boolean>>;
|
||||
listSubscriptions: (input: z.infer<typeof listSubscriptionsSchema>) => Promise<DesktopResult<ThreadSubscriptionRecord[]>>;
|
||||
listSubscribedThreads: (input: z.infer<typeof listSubscribedThreadsSchema>) => Promise<DesktopResult<ConversationThreadRecord[]>>;
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const heartbeatPolicySchema = z.object({
|
||||
companyId: z.string().min(1),
|
||||
agentId: z.string().min(1),
|
||||
});
|
||||
|
||||
export const setHeartbeatPolicySchema = z.object({
|
||||
companyId: z.string().min(1),
|
||||
agentId: z.string().min(1),
|
||||
enabled: z.boolean().optional(),
|
||||
intervalSec: z.number().optional(),
|
||||
cooldownSec: z.number().optional(),
|
||||
maxConcurrentRuns: z.number().optional(),
|
||||
wakeOnDemand: z.boolean().optional(),
|
||||
});
|
||||
@@ -3,10 +3,4 @@
|
||||
export * from "./schemas-badges";
|
||||
export * from "./schemas-automation";
|
||||
export * from "./schemas-budget";
|
||||
export * from "./schemas-wakeup";
|
||||
export * from "./schemas-run-events";
|
||||
export * from "./schemas-heartbeat";
|
||||
export * from "./schemas-runtime-state";
|
||||
export * from "./schemas-threads";
|
||||
export * from "./schemas-participants";
|
||||
export * from "./schemas-subscriptions";
|
||||
@@ -1,20 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const listParticipantsSchema = z.object({
|
||||
threadId: z.string().uuid(),
|
||||
companyId: z.string().uuid(),
|
||||
});
|
||||
|
||||
export const addParticipantSchema = z.object({
|
||||
threadId: z.string().uuid(),
|
||||
companyId: z.string().uuid(),
|
||||
participantId: z.string(),
|
||||
participantType: z.enum(["agent", "user", "system"]),
|
||||
role: z.enum(["owner", "member", "observer"]).default("member"),
|
||||
});
|
||||
|
||||
export const removeParticipantSchema = z.object({
|
||||
threadId: z.string().uuid(),
|
||||
companyId: z.string().uuid(),
|
||||
participantId: z.string(),
|
||||
});
|
||||
@@ -1,8 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const listRunEventsSchema = z.object({
|
||||
companyId: z.string().min(1),
|
||||
runId: z.string().min(1),
|
||||
offset: z.number().optional(),
|
||||
limit: z.number().optional(),
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const runtimeStateSchema = z.object({
|
||||
companyId: z.string().min(1),
|
||||
agentId: z.string().min(1),
|
||||
});
|
||||
|
||||
export const setRuntimeStateSchema = z.object({
|
||||
companyId: z.string().min(1),
|
||||
agentId: z.string().min(1),
|
||||
state: z.record(z.string(), z.unknown()),
|
||||
});
|
||||
@@ -1,25 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const subscribeThreadSchema = z.object({
|
||||
threadId: z.string().uuid(),
|
||||
companyId: z.string().uuid(),
|
||||
subscriberId: z.string(),
|
||||
subscriberType: z.enum(["agent", "user", "system"]),
|
||||
notifyOn: z.enum(["all", "mentions", "none"]).default("all"),
|
||||
});
|
||||
|
||||
export const unsubscribeThreadSchema = z.object({
|
||||
threadId: z.string().uuid(),
|
||||
companyId: z.string().uuid(),
|
||||
subscriberId: z.string(),
|
||||
});
|
||||
|
||||
export const listSubscriptionsSchema = z.object({
|
||||
threadId: z.string().uuid(),
|
||||
companyId: z.string().uuid(),
|
||||
});
|
||||
|
||||
export const listSubscribedThreadsSchema = z.object({
|
||||
subscriberId: z.string(),
|
||||
companyId: z.string().uuid(),
|
||||
});
|
||||
@@ -1,37 +1,10 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const createThreadSchema = z.object({
|
||||
companyId: z.string().uuid(),
|
||||
title: z.string().min(1).max(200),
|
||||
type: z.enum(["task", "approval", "general", "announcement"]),
|
||||
relatedEntityType: z.string().nullable().optional(),
|
||||
relatedEntityId: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export const listThreadsSchema = z.object({
|
||||
companyId: z.string().uuid(),
|
||||
type: z.enum(["task", "approval", "general", "announcement"]).optional(),
|
||||
relatedEntityType: z.string().optional(),
|
||||
relatedEntityId: z.string().optional(),
|
||||
limit: z.number().int().min(1).max(100).default(50),
|
||||
offset: z.number().int().min(0).default(0),
|
||||
});
|
||||
|
||||
export const threadGetterSchema = z.object({
|
||||
companyId: z.string().uuid(),
|
||||
threadId: z.string().uuid(),
|
||||
});
|
||||
|
||||
export const addThreadEventSchema = z.object({
|
||||
companyId: z.string().uuid(),
|
||||
threadId: z.string().uuid(),
|
||||
actorId: z.string().min(1),
|
||||
actorType: z.enum(["agent", "user", "system"]),
|
||||
eventType: z.enum(["message", "status_change", "approval_request", "approval_decision", "file_attachment", "system_note"]),
|
||||
body: z.string().default(""),
|
||||
metadataJson: z.string().default("{}"),
|
||||
});
|
||||
|
||||
export const listThreadEventsSchema = z.object({
|
||||
companyId: z.string().uuid(),
|
||||
threadId: z.string().uuid(),
|
||||
@@ -39,19 +12,6 @@ export const listThreadEventsSchema = z.object({
|
||||
offset: z.number().int().min(0).default(0),
|
||||
});
|
||||
|
||||
export const setThreadWaitStateSchema = z.object({
|
||||
companyId: z.string().uuid(),
|
||||
threadId: z.string().uuid(),
|
||||
waitingFor: z.enum(["user_review", "user_approval", "user_reply"]),
|
||||
relatedApprovalId: z.string().nullable().optional(),
|
||||
reason: z.string().min(1).max(500),
|
||||
});
|
||||
|
||||
export const resolveThreadWaitStateSchema = z.object({
|
||||
companyId: z.string().uuid(),
|
||||
waitStateId: z.string().uuid(),
|
||||
});
|
||||
|
||||
export const listThreadWaitStatesSchema = z.object({
|
||||
companyId: z.string().uuid(),
|
||||
resolvedOnly: z.boolean().default(false),
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const wakeupQuerySchema = z.object({
|
||||
companyId: z.string().uuid(),
|
||||
status: z.enum(["queued", "claimed", "coalesced", "deferred", "completed"]).optional(),
|
||||
agentId: z.string().uuid().optional(),
|
||||
limit: z.number().int().min(1).max(100).default(50),
|
||||
});
|
||||
|
||||
export interface WakeupRequestRecord {
|
||||
id: string;
|
||||
companyId: string;
|
||||
agentId: string;
|
||||
source: string;
|
||||
triggerDetail: string | null;
|
||||
status: string;
|
||||
coalescedCount: number;
|
||||
runId: string | null;
|
||||
requestedAt: string;
|
||||
claimedAt: string | null;
|
||||
finishedAt: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
@@ -128,7 +128,6 @@ describe("company mode switching", () => {
|
||||
threadIntegration.onApprovalResolved(resolvedApprovalId, resolvedCompanyId, decision);
|
||||
},
|
||||
checkConnector: vi.fn(),
|
||||
openConnectorAuthTerminal: vi.fn(),
|
||||
openGenericTerminal: vi.fn(),
|
||||
});
|
||||
|
||||
|
||||
@@ -47,7 +47,6 @@ describe("ipc-core-admin-handlers connector saves", () => {
|
||||
publishDomainChanged: vi.fn(),
|
||||
handleApprovalResolved: vi.fn(),
|
||||
checkConnector: vi.fn(),
|
||||
openConnectorAuthTerminal: vi.fn(),
|
||||
openGenericTerminal: vi.fn(),
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user