-
Notifications
You must be signed in to change notification settings - Fork 2
Automations overhaul #183
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Automations overhaul #183
Changes from all commits
8f653ca
fe2a62c
da925c1
f9168bc
d042cfc
8389790
ad82294
25d87d5
e37f497
a8c0410
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,13 @@ import { | |
| } from "../../desktop/src/main/services/computerUse/localComputerUse"; | ||
| import { loadAgentBrowserArtifactPayloadFromFile, parseAgentBrowserArtifactPayload } from "../../desktop/src/main/services/proof/agentBrowserArtifactAdapter"; | ||
| import { resolveAgentMemoryWritePolicy } from "../../desktop/src/main/services/memory/memoryService"; | ||
| import { | ||
| ADE_ACTION_ALLOWLIST, | ||
| type AdeActionDomain, | ||
| getAdeActionDomainServices, | ||
| isAllowedAdeAction, | ||
| listAllowedAdeActionNames, | ||
| } from "../../desktop/src/main/services/adeActions/registry"; | ||
| import { ReflectionValidationError } from "../../desktop/src/main/services/orchestrator/orchestratorService"; | ||
| import { getTeamMembersForRun, registerTeamMember, updateTeamMemberStatus } from "../../desktop/src/main/services/orchestrator/teamRuntimeState"; | ||
| import { launchPrIssueResolutionChat, previewPrIssueResolutionPrompt } from "../../desktop/src/main/services/prs/prIssueResolver"; | ||
|
|
@@ -232,6 +239,8 @@ const TOOL_SPECS: ToolSpec[] = [ | |
| "process", | ||
| "pty", | ||
| "computer_use_artifacts", | ||
| "automations", | ||
| "issue", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [🟠 High] [🔵 Bug] The new // apps/ade-cli/src/adeRpcServer.ts
"computer_use_artifacts",
"automations",
"issue", |
||
| ], | ||
| }, | ||
| action: { type: "string", minLength: 1 }, | ||
|
|
@@ -2992,217 +3001,6 @@ async function waitForTestRunCompletion(args: { | |
| }; | ||
| } | ||
|
|
||
| type AdeActionDomain = | ||
| | "lane" | ||
| | "git" | ||
| | "diff" | ||
| | "conflicts" | ||
| | "pr" | ||
| | "tests" | ||
| | "chat" | ||
| | "mission" | ||
| | "orchestrator" | ||
| | "orchestrator_core" | ||
| | "memory" | ||
| | "cto_state" | ||
| | "worker_agent" | ||
| | "session" | ||
| | "operation" | ||
| | "project_config" | ||
| | "issue_inventory" | ||
| | "flow_policy" | ||
| | "linear_dispatcher" | ||
| | "linear_issue_tracker" | ||
| | "linear_sync" | ||
| | "linear_ingress" | ||
| | "linear_routing" | ||
| | "file" | ||
| | "process" | ||
| | "pty" | ||
| | "computer_use_artifacts"; | ||
|
|
||
| const ADE_ACTION_ALLOWLIST: Partial<Record<AdeActionDomain, readonly string[]>> = { | ||
| lane: [ | ||
| "adoptAttached", | ||
| "attach", | ||
| "create", | ||
| "createFromUnstaged", | ||
| "delete", | ||
| "getChildren", | ||
| "getStackChain", | ||
| "importBranch", | ||
| "list", | ||
| "listUnregisteredWorktrees", | ||
| "refreshSnapshots", | ||
| "rename", | ||
| "reparent", | ||
| "updateAppearance", | ||
| ], | ||
| git: [ | ||
| "abortRebase", | ||
| "cherryPickCommit", | ||
| "commit", | ||
| "continueRebase", | ||
| "fetch", | ||
| "getCommitMessage", | ||
| "getConflictState", | ||
| "getFileHistory", | ||
| "getSyncStatus", | ||
| "listCommitFiles", | ||
| "mergeAbort", | ||
| "mergeContinue", | ||
| "pull", | ||
| "push", | ||
| "rebaseAbort", | ||
| "rebaseContinue", | ||
| "revertCommit", | ||
| "stash", | ||
| "stagePaths", | ||
| "unstagePaths", | ||
| ], | ||
| diff: ["getChanges", "getFileDiff"], | ||
| conflicts: ["getLaneStatus", "listOverlaps", "rebaseLane", "runPrediction"], | ||
| pr: [ | ||
| "addComment", | ||
| "aiReviewSummary", | ||
| "cleanupIntegrationWorkflow", | ||
| "createFromLane", | ||
| "createIntegrationLane", | ||
| "createIntegrationPr", | ||
| "createQueuePrs", | ||
| "dismissIntegrationCleanup", | ||
| "draftDescription", | ||
| "getActionRuns", | ||
| "getChecks", | ||
| "getComments", | ||
| "getDetail", | ||
| "getGithubSnapshot", | ||
| "getIntegrationResolutionState", | ||
| "getMobileSnapshot", | ||
| "getPrHealth", | ||
| "getQueueState", | ||
| "getReviewThreads", | ||
| "getReviews", | ||
| "landQueueNext", | ||
| "landStack", | ||
| "landStackEnhanced", | ||
| "linkToLane", | ||
| "listAll", | ||
| "listGroupPrs", | ||
| "listIntegrationProposals", | ||
| "listIntegrationWorkflows", | ||
| "listWithConflicts", | ||
| "postReviewComment", | ||
| "reactToComment", | ||
| "recheckIntegrationStep", | ||
| "refresh", | ||
| "reorderQueuePrs", | ||
| "requestReviewers", | ||
| "setLabels", | ||
| "setReviewThreadResolved", | ||
| "simulateIntegration", | ||
| "startIntegrationResolution", | ||
| "submitReview", | ||
| "updateDescription", | ||
| "updateIntegrationProposal", | ||
| "updateTitle", | ||
| ], | ||
| tests: ["getLogTail", "listRuns", "listSuites", "run", "stop"], | ||
| chat: [ | ||
| "createSession", | ||
| "deleteSession", | ||
| "getAvailableModels", | ||
| "getSessionSummary", | ||
| "getSlashCommands", | ||
| "interrupt", | ||
| "listSessions", | ||
| "resumeSession", | ||
| "sendMessage", | ||
| ], | ||
| memory: ["addSharedFact", "pinMemory", "searchMemories", "writeMemory"], | ||
| session: ["get", "readTranscriptTail"], | ||
| operation: ["finish", "list", "start"], | ||
| project_config: ["get", "save"], | ||
| issue_inventory: [ | ||
| "getConvergenceRuntime", | ||
| "getConvergenceStatus", | ||
| "getInventory", | ||
| "getNewItems", | ||
| "getPipelineSettings", | ||
| "markDismissed", | ||
| "markEscalated", | ||
| "markFixed", | ||
| "markSentToAgent", | ||
| "reconcileConvergenceSessionExit", | ||
| "savePipelineSettings", | ||
| "syncFromPrData", | ||
| ], | ||
| flow_policy: ["getPolicy", "savePolicy"], | ||
| linear_dispatcher: ["dispatchIssue", "getDashboard", "listEmployees", "listQueue"], | ||
| linear_issue_tracker: ["getStatus", "listIssues"], | ||
| linear_sync: ["getDashboard", "getRunDetail", "listQueue", "resolveQueueItem", "runSyncNow"], | ||
| linear_ingress: ["ensureRelayWebhook", "getStatus", "listRecentEvents"], | ||
| linear_routing: ["simulateRoute"], | ||
| file: [ | ||
| "createDirectory", | ||
| "createFile", | ||
| "deletePath", | ||
| "listTree", | ||
| "listWorkspaces", | ||
| "quickOpen", | ||
| "readFile", | ||
| "rename", | ||
| "searchText", | ||
| "writeWorkspaceText", | ||
| ], | ||
| process: ["getLogTail", "listDefinitions", "listRuntime", "startAll", "stopAll"], | ||
| pty: ["create", "dispose", "resize", "write"], | ||
| computer_use_artifacts: ["ingest", "listArtifacts"], | ||
| }; | ||
|
|
||
| function getAdeActionDomainServices(runtime: AdeRuntime): Partial<Record<AdeActionDomain, Record<string, unknown> | null | undefined>> { | ||
| return { | ||
| lane: runtime.laneService as unknown as Record<string, unknown>, | ||
| git: runtime.gitService as unknown as Record<string, unknown>, | ||
| diff: runtime.diffService as unknown as Record<string, unknown>, | ||
| conflicts: runtime.conflictService as unknown as Record<string, unknown>, | ||
| pr: (runtime.prService ?? null) as unknown as Record<string, unknown> | null, | ||
| tests: runtime.testService as unknown as Record<string, unknown>, | ||
| chat: (runtime.agentChatService ?? null) as unknown as Record<string, unknown> | null, | ||
| mission: runtime.missionService as unknown as Record<string, unknown>, | ||
| orchestrator: runtime.aiOrchestratorService as unknown as Record<string, unknown>, | ||
| orchestrator_core: runtime.orchestratorService as unknown as Record<string, unknown>, | ||
| memory: runtime.memoryService as unknown as Record<string, unknown>, | ||
| cto_state: runtime.ctoStateService as unknown as Record<string, unknown>, | ||
| worker_agent: runtime.workerAgentService as unknown as Record<string, unknown>, | ||
| session: runtime.sessionService as unknown as Record<string, unknown>, | ||
| operation: runtime.operationService as unknown as Record<string, unknown>, | ||
| project_config: runtime.projectConfigService as unknown as Record<string, unknown>, | ||
| issue_inventory: runtime.issueInventoryService as unknown as Record<string, unknown>, | ||
| flow_policy: (runtime.flowPolicyService ?? null) as unknown as Record<string, unknown> | null, | ||
| linear_dispatcher: (runtime.linearDispatcherService ?? null) as unknown as Record<string, unknown> | null, | ||
| linear_issue_tracker: (runtime.linearIssueTracker ?? null) as unknown as Record<string, unknown> | null, | ||
| linear_sync: (runtime.linearSyncService ?? null) as unknown as Record<string, unknown> | null, | ||
| linear_ingress: (runtime.linearIngressService ?? null) as unknown as Record<string, unknown> | null, | ||
| linear_routing: (runtime.linearRoutingService ?? null) as unknown as Record<string, unknown> | null, | ||
| file: (runtime.fileService ?? null) as unknown as Record<string, unknown> | null, | ||
| process: (runtime.processService ?? null) as unknown as Record<string, unknown> | null, | ||
| pty: runtime.ptyService as unknown as Record<string, unknown>, | ||
| computer_use_artifacts: runtime.computerUseArtifactBrokerService as unknown as Record<string, unknown>, | ||
| }; | ||
| } | ||
|
|
||
| function listAllowedAdeActionNames(domain: AdeActionDomain, service: Record<string, unknown>): string[] { | ||
| const allowed = ADE_ACTION_ALLOWLIST[domain] ?? []; | ||
| return allowed | ||
| .filter((key) => typeof service[key] === "function") | ||
| .sort((a, b) => a.localeCompare(b)); | ||
| } | ||
|
|
||
| function isAllowedAdeAction(domain: AdeActionDomain, action: string): boolean { | ||
| return (ADE_ACTION_ALLOWLIST[domain] ?? []).includes(action); | ||
| } | ||
|
|
||
| async function waitForSessionCompletion(args: { | ||
| runtime: AdeRuntime; | ||
| ptyId: string; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,6 +38,18 @@ import { | |
| } from "../../desktop/src/main/services/computerUse/computerUseArtifactBrokerService"; | ||
| import type { createFileService } from "../../desktop/src/main/services/files/fileService"; | ||
| import type { createProcessService } from "../../desktop/src/main/services/processes/processService"; | ||
| import type { createGithubService } from "../../desktop/src/main/services/github/githubService"; | ||
| import { | ||
| createAutomationService, | ||
| type AutomationAdeActionRegistry, | ||
| } from "../../desktop/src/main/services/automations/automationService"; | ||
| import { createAutomationPlannerService } from "../../desktop/src/main/services/automations/automationPlannerService"; | ||
| import { | ||
| ADE_ACTION_ALLOWLIST, | ||
| type AdeActionDomain, | ||
| getAdeActionDomainServices, | ||
| isAllowedAdeAction, | ||
| } from "../../desktop/src/main/services/adeActions/registry"; | ||
| import { createHeadlessLinearServices } from "./headlessLinearServices"; | ||
| import { createEventBuffer, type BufferedEvent, type EventBuffer } from "./eventBuffer"; | ||
|
|
||
|
|
@@ -93,6 +105,9 @@ export type AdeRuntime = { | |
| linearIngressService?: ReturnType<typeof createLinearIngressService> | null; | ||
| linearRoutingService?: ReturnType<typeof createLinearRoutingService> | null; | ||
| processService?: ReturnType<typeof createProcessService> | null; | ||
| githubService?: ReturnType<typeof createGithubService> | null; | ||
| automationService?: ReturnType<typeof createAutomationService> | null; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [🟠 High] [🔵 Bug] The diff extends // apps/ade-cli/src/bootstrap.ts
githubService?: ReturnType<typeof createGithubService> | null;
automationService?: ReturnType<typeof createAutomationService> | null;
automationPlannerService?: ReturnType<typeof createAutomationPlannerService> | null;I verified the execution path in @apps/ade-cli/src/cli.ts ( |
||
| automationPlannerService?: ReturnType<typeof createAutomationPlannerService> | null; | ||
| computerUseArtifactBrokerService: ComputerUseArtifactBrokerService; | ||
| orchestratorService: ReturnType<typeof createOrchestratorService>; | ||
| aiOrchestratorService: ReturnType<typeof createAiOrchestratorService>; | ||
|
|
@@ -361,7 +376,30 @@ export async function createAdeRuntime(args: { projectRoot: string; workspaceRoo | |
| openExternal: async () => {}, | ||
| }); | ||
|
|
||
| return { | ||
| const agentChatService = headlessLinearServices.agentChatService as unknown as ReturnType<typeof createAgentChatService> | null; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [🟡 Medium] [🔵 Bug] This new headless bootstrap path casts |
||
| const automationService = createAutomationService({ | ||
| db, | ||
| logger, | ||
| projectId, | ||
| projectRoot, | ||
| laneService, | ||
| projectConfigService, | ||
| conflictService, | ||
| testService, | ||
| agentChatService: agentChatService ?? undefined, | ||
| missionService, | ||
| aiOrchestratorService, | ||
| onEvent: (event) => pushEvent("runtime", { ...event, source: "automations" }), | ||
| }); | ||
| const automationPlannerService = createAutomationPlannerService({ | ||
| logger, | ||
| projectRoot, | ||
| projectConfigService, | ||
| laneService, | ||
| automationService, | ||
| }); | ||
|
|
||
| const runtime: AdeRuntime = { | ||
| projectRoot, | ||
| workspaceRoot, | ||
| projectId, | ||
|
|
@@ -379,11 +417,12 @@ export async function createAdeRuntime(args: { projectRoot: string; workspaceRoo | |
| missionService, | ||
| ptyService, | ||
| testService, | ||
| agentChatService: headlessLinearServices.agentChatService as unknown as ReturnType<typeof createAgentChatService> | null, | ||
| agentChatService, | ||
| issueInventoryService, | ||
| memoryService, | ||
| ctoStateService, | ||
| workerAgentService, | ||
| githubService: headlessLinearServices.githubService as never, | ||
| prService: headlessLinearServices.prService, | ||
| fileService: headlessLinearServices.fileService, | ||
| flowPolicyService: headlessLinearServices.flowPolicyService, | ||
|
|
@@ -393,12 +432,15 @@ export async function createAdeRuntime(args: { projectRoot: string; workspaceRoo | |
| linearIngressService: headlessLinearServices.linearIngressService, | ||
| linearRoutingService: headlessLinearServices.linearRoutingService, | ||
| processService: headlessLinearServices.processService, | ||
| automationService, | ||
| automationPlannerService, | ||
| computerUseArtifactBrokerService, | ||
| orchestratorService, | ||
| aiOrchestratorService, | ||
| eventBuffer, | ||
| dispose: () => { | ||
| const swallow = (fn: () => void) => { try { fn(); } catch { /* ignore */ } }; | ||
| swallow(() => automationService.dispose()); | ||
| swallow(() => headlessLinearServices.dispose()); | ||
| swallow(() => aiOrchestratorService.dispose()); | ||
| swallow(() => testService.disposeAll()); | ||
|
|
@@ -407,4 +449,23 @@ export async function createAdeRuntime(args: { projectRoot: string; workspaceRoo | |
| swallow(() => db.close()); | ||
| } | ||
| }; | ||
|
|
||
| const adeActionLookup: AutomationAdeActionRegistry = { | ||
| isAllowed(domain: string, action: string): boolean { | ||
| return isAllowedAdeAction(domain as AdeActionDomain, action); | ||
| }, | ||
| getService(domain: string): Record<string, unknown> | null { | ||
| const services = getAdeActionDomainServices(runtime); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [🟡 Medium] [🔵 Bug] @apps/ade-cli/src/bootstrap.ts binds the new automation ADE-action registry against // apps/ade-cli/src/bootstrap.ts
getService(domain: string): Record<string, unknown> | null {
const services = getAdeActionDomainServices(runtime);
return (services[domain as AdeActionDomain] ?? null) as Record<string, unknown> | null;
}In this PR the shared ADE-action registry adds the new |
||
| return (services[domain as AdeActionDomain] ?? null) as Record<string, unknown> | null; | ||
| }, | ||
| listDomains(): string[] { | ||
| return Object.keys(ADE_ACTION_ALLOWLIST); | ||
| }, | ||
| listActions(domain: string): string[] { | ||
| return [...(ADE_ACTION_ALLOWLIST[domain as AdeActionDomain] ?? [])]; | ||
| }, | ||
| }; | ||
| automationService.bindAdeActionRegistry(adeActionLookup); | ||
|
|
||
| return runtime; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[🟡 Medium] [🔵 Bug]
run_ade_actionnow exposes two new domains:but the headless runtime built by
createAdeRuntime()in @apps/ade-cli/src/bootstrap.ts still returns nogithubService,automationService, orautomationPlannerService. I verified thatgetAdeActionDomainServices(runtime)depends on those optional runtime fields in @apps/desktop/src/main/services/adeActions/registry.ts, so in headless mode both new domains resolve tonullandrun_ade_actionfails withDomain 'automations'/'issue' is unavailable in this runtime.This makes the new public RPC surface unusable for headless callers. Either construct those services in the headless bootstrap path or do not advertise these domains there until they exist.