whipped 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +197 -124
- package/dist/mcp-server.js +24 -6
- package/dist/migrations/013_agent_id_add_mimo.sql +93 -0
- package/dist/web-ui/assets/{index-Dgu7Q26n.css → index-C-IzbCaE.css} +16 -0
- package/dist/web-ui/assets/{index-Ce4HDOh7.js → index-C-dcfzmG.js} +28 -9
- package/dist/web-ui/index.html +2 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3529,11 +3529,12 @@ function isResumableSessionState(state) {
|
|
|
3529
3529
|
function normalizeTag(raw2) {
|
|
3530
3530
|
return raw2.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
3531
3531
|
}
|
|
3532
|
-
var runtimeAgentIdSchema, effortLevelSchema, agentModelChoiceSchema, workflowSlotTypeSchema, tierLevelSchema, LEVEL_ORDER, modelPairSchema, pairSelectionModeSchema, SLOT_TOOL_IDS, slotToolSchema, slotModelConfigSchema, cardModelConfigSchema, promptValueSchema, EMPTY_INLINE_PROMPT, workflowSlotSchema, DEFAULT_MODEL_PAIR, DEFAULT_SLOT_MODEL_FIELDS, workflowSchema, DEFAULT_WORKFLOW, DEFAULT_STORY_WORKFLOW, DEFAULT_GIT_INSTRUCTIONS, runtimeBoardColumnIdSchema, BOARD_COLUMNS, reviewActorSchema, reviewIssueSchema, reviewAttachmentSchema, runtimeReviewCommentSchema, runtimeActivityEntrySchema, runtimeTaskSessionStateSchema, runtimeTerminalSessionEntrySchema, runtimeCardPrioritySchema, cardTypeSchema, runtimePrMetaSchema, runtimeBoardCardSchema, runtimeBoardColumnSchema, runtimeBoardDataSchema, runtimeGlobalConfigSchema, runtimeGithubConfigSchema, runtimeWorktreeSetupSchema, runtimeProjectSecretSchema, runtimeProjectConfigSchema, runtimeWorkspaceStateResponseSchema, runtimeWorkspaceStateSaveRequestSchema, runtimeVisualElementSchema, runtimeVisualCommentSchema, runtimeCardCreateRequestSchema, runtimeCardMoveRequestSchema, runtimeCardUpdateRequestSchema, memoryScopeSchema, memoryTypeSchema, memorySourceTypeSchema, memoryStatusSchema, runtimeMemoryOriginAgentSchema, runtimeMemorySchema, recurringScheduleKindSchema, recurringScheduleSchema, recurringRunStatusSchema, recurringRunTriggerSchema, recurringAgentRunSchema, recurringAgentSchema, recurringAgentCreateRequestSchema, recurringAgentUpdateRequestSchema, projectFolderSchema, topLevelItemSchema, projectsLayoutSchema, runtimeProjectSchema;
|
|
3532
|
+
var ASSISTANT_AGENT_PREFIX, runtimeAgentIdSchema, effortLevelSchema, agentModelChoiceSchema, workflowSlotTypeSchema, tierLevelSchema, LEVEL_ORDER, modelPairSchema, pairSelectionModeSchema, SLOT_TOOL_IDS, slotToolSchema, slotModelConfigSchema, cardModelConfigSchema, promptValueSchema, EMPTY_INLINE_PROMPT, workflowSlotSchema, DEFAULT_MODEL_PAIR, DEFAULT_SLOT_MODEL_FIELDS, workflowSchema, DEFAULT_WORKFLOW, DEFAULT_STORY_WORKFLOW, DEFAULT_GIT_INSTRUCTIONS, runtimeBoardColumnIdSchema, BOARD_COLUMNS, reviewActorSchema, reviewIssueSchema, reviewAttachmentSchema, runtimeReviewCommentSchema, runtimeActivityEntrySchema, runtimeTaskSessionStateSchema, runtimeTerminalSessionEntrySchema, runtimeCardPrioritySchema, cardTypeSchema, runtimePrMetaSchema, runtimeBoardCardSchema, runtimeBoardColumnSchema, runtimeBoardDataSchema, runtimeGlobalConfigSchema, runtimeGithubConfigSchema, runtimeWorktreeSetupSchema, runtimeProjectSecretSchema, runtimeProjectConfigSchema, runtimeWorkspaceStateResponseSchema, runtimeWorkspaceStateSaveRequestSchema, runtimeVisualElementSchema, runtimeVisualCommentSchema, runtimeCardCreateRequestSchema, runtimeCardMoveRequestSchema, runtimeCardUpdateRequestSchema, memoryScopeSchema, memoryTypeSchema, memorySourceTypeSchema, memoryStatusSchema, runtimeMemoryOriginAgentSchema, runtimeMemorySchema, recurringScheduleKindSchema, recurringScheduleSchema, recurringRunStatusSchema, recurringRunTriggerSchema, recurringAgentRunSchema, recurringAgentSchema, recurringAgentCreateRequestSchema, recurringAgentUpdateRequestSchema, projectFolderSchema, topLevelItemSchema, projectsLayoutSchema, runtimeProjectSchema;
|
|
3533
3533
|
var init_api_contract = __esm({
|
|
3534
3534
|
"src/core/api-contract.ts"() {
|
|
3535
3535
|
"use strict";
|
|
3536
|
-
|
|
3536
|
+
ASSISTANT_AGENT_PREFIX = "__assistant__:";
|
|
3537
|
+
runtimeAgentIdSchema = z.enum(["claude", "codex", "opencode", "cursor", "mimo"]);
|
|
3537
3538
|
effortLevelSchema = z.enum(["low", "medium", "high", "xhigh", "max"]);
|
|
3538
3539
|
agentModelChoiceSchema = z.object({
|
|
3539
3540
|
agentId: runtimeAgentIdSchema.default("claude"),
|
|
@@ -4266,21 +4267,26 @@ function getDb() {
|
|
|
4266
4267
|
function runMigrations(db) {
|
|
4267
4268
|
const currentVersion = db.pragma("user_version", { simple: true });
|
|
4268
4269
|
const files = readdirSync(MIGRATIONS_DIR).filter((f2) => /^\d+_.+\.sql$/.test(f2)).sort();
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
db.
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4270
|
+
db.pragma("foreign_keys = OFF");
|
|
4271
|
+
try {
|
|
4272
|
+
for (const file of files) {
|
|
4273
|
+
const version = Number.parseInt(file.split("_")[0] ?? "0", 10);
|
|
4274
|
+
if (!Number.isFinite(version) || version <= currentVersion) continue;
|
|
4275
|
+
const sql = readFileSync2(join4(MIGRATIONS_DIR, file), "utf-8");
|
|
4276
|
+
logger.info({ file, version }, "Running migration");
|
|
4277
|
+
const tx = db.transaction(() => {
|
|
4278
|
+
db.exec(sql);
|
|
4279
|
+
db.pragma(`user_version = ${version}`);
|
|
4280
|
+
});
|
|
4281
|
+
try {
|
|
4282
|
+
tx();
|
|
4283
|
+
} catch (err) {
|
|
4284
|
+
logger.error({ err, file }, "Migration failed; rolled back");
|
|
4285
|
+
throw new Error(`Migration ${file} failed: ${err.message}`);
|
|
4286
|
+
}
|
|
4283
4287
|
}
|
|
4288
|
+
} finally {
|
|
4289
|
+
db.pragma("foreign_keys = ON");
|
|
4284
4290
|
}
|
|
4285
4291
|
}
|
|
4286
4292
|
var MIGRATIONS_DIR, DEFAULT_DB_PATH, cachedDb;
|
|
@@ -13639,13 +13645,40 @@ function buildTaskHookEnv(taskId, workspaceId) {
|
|
|
13639
13645
|
[MACHINE_TOKEN_ENV]: getMachineToken() ?? ""
|
|
13640
13646
|
};
|
|
13641
13647
|
}
|
|
13642
|
-
var
|
|
13643
|
-
|
|
13648
|
+
var PLUGIN_AGENT_SPECS = {
|
|
13649
|
+
opencode: {
|
|
13650
|
+
configDirEnv: "OPENCODE_CONFIG_DIR",
|
|
13651
|
+
configFile: "opencode.json",
|
|
13652
|
+
dirPrefix: "opencode",
|
|
13653
|
+
pluginHeader: 'import { tool } from "@opencode-ai/plugin"\nimport type { Plugin } from "@opencode-ai/plugin"\n\n',
|
|
13654
|
+
pluginExportType: ": Plugin",
|
|
13655
|
+
toolWrapperOpen: "tool(",
|
|
13656
|
+
toolWrapperClose: ")"
|
|
13657
|
+
},
|
|
13658
|
+
mimo: {
|
|
13659
|
+
configDirEnv: "MIMOCODE_CONFIG_DIR",
|
|
13660
|
+
configFile: "mimocode.json",
|
|
13661
|
+
dirPrefix: "mimo",
|
|
13662
|
+
pluginHeader: "",
|
|
13663
|
+
pluginExportType: "",
|
|
13664
|
+
toolWrapperOpen: "",
|
|
13665
|
+
toolWrapperClose: ""
|
|
13666
|
+
}
|
|
13667
|
+
};
|
|
13668
|
+
function isPluginConfigAgent(agentId) {
|
|
13669
|
+
return agentId in PLUGIN_AGENT_SPECS;
|
|
13670
|
+
}
|
|
13671
|
+
function getPluginAgentConfigDir(agentId, id) {
|
|
13644
13672
|
const safe = id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
13645
|
-
return join9(HOOKS_DIR,
|
|
13673
|
+
return join9(HOOKS_DIR, `${PLUGIN_AGENT_SPECS[agentId].dirPrefix}-${safe}`);
|
|
13674
|
+
}
|
|
13675
|
+
function pluginAgentConfigDirEnv(agentId, id) {
|
|
13676
|
+
if (!isPluginConfigAgent(agentId)) return {};
|
|
13677
|
+
return { [PLUGIN_AGENT_SPECS[agentId].configDirEnv]: getPluginAgentConfigDir(agentId, id) };
|
|
13646
13678
|
}
|
|
13647
|
-
async function
|
|
13648
|
-
const
|
|
13679
|
+
async function writePluginAgentFiles(agentId, id, serverPort, mcpServer, opts = {}) {
|
|
13680
|
+
const spec = PLUGIN_AGENT_SPECS[agentId];
|
|
13681
|
+
const dir = getPluginAgentConfigDir(agentId, id);
|
|
13649
13682
|
await mkdir(join9(dir, "plugin"), { recursive: true });
|
|
13650
13683
|
const systemParts = [];
|
|
13651
13684
|
if (opts.appendSystemPrompt) systemParts.push(opts.appendSystemPrompt);
|
|
@@ -13656,15 +13689,12 @@ async function writeOpencodeFiles(id, serverPort, mcpServer, opts = {}) {
|
|
|
13656
13689
|
"experimental.chat.system.transform": async (_input, output) => {
|
|
13657
13690
|
${systemParts.map((p2) => `output.system.push(${JSON.stringify(p2)})`).join("\n ")}
|
|
13658
13691
|
},`;
|
|
13659
|
-
const plugin =
|
|
13660
|
-
import type { Plugin } from "@opencode-ai/plugin"
|
|
13661
|
-
|
|
13662
|
-
export const WhippedPlugin: Plugin = async () => {
|
|
13692
|
+
const plugin = `${spec.pluginHeader}export const WhippedPlugin${spec.pluginExportType} = async () => {
|
|
13663
13693
|
const port = ${serverPort}
|
|
13664
13694
|
|
|
13665
13695
|
return {${systemTransformHook}
|
|
13666
13696
|
tool: {
|
|
13667
|
-
task_complete:
|
|
13697
|
+
task_complete: ${spec.toolWrapperOpen}{
|
|
13668
13698
|
description: "Signal that you have finished all work on this task. Call this after completing all code changes, setting PR metadata with kanban_set_pr_meta, and adding your summary with kanban_add_comment.",
|
|
13669
13699
|
args: {},
|
|
13670
13700
|
execute: async (_args, _ctx) => {
|
|
@@ -13677,15 +13707,15 @@ export const WhippedPlugin: Plugin = async () => {
|
|
|
13677
13707
|
}
|
|
13678
13708
|
return "Task marked as complete."
|
|
13679
13709
|
}
|
|
13680
|
-
}
|
|
13710
|
+
}${spec.toolWrapperClose}
|
|
13681
13711
|
},
|
|
13682
13712
|
}
|
|
13683
13713
|
}
|
|
13684
13714
|
`;
|
|
13685
13715
|
const extraMcp = Object.fromEntries(
|
|
13686
|
-
Object.entries(opts.extraMcp ?? {}).map(([name,
|
|
13716
|
+
Object.entries(opts.extraMcp ?? {}).map(([name, spec2]) => [
|
|
13687
13717
|
name,
|
|
13688
|
-
{ type: "local", command: [
|
|
13718
|
+
{ type: "local", command: [spec2.command, ...spec2.args] }
|
|
13689
13719
|
])
|
|
13690
13720
|
);
|
|
13691
13721
|
const config = {
|
|
@@ -13706,11 +13736,11 @@ export const WhippedPlugin: Plugin = async () => {
|
|
|
13706
13736
|
};
|
|
13707
13737
|
await Promise.all([
|
|
13708
13738
|
writeFile(join9(dir, "plugin", "whipped.ts"), plugin),
|
|
13709
|
-
writeFile(join9(dir,
|
|
13739
|
+
writeFile(join9(dir, spec.configFile), JSON.stringify(config, null, 2))
|
|
13710
13740
|
]);
|
|
13711
13741
|
}
|
|
13712
|
-
async function
|
|
13713
|
-
await rm(
|
|
13742
|
+
async function cleanupPluginAgentFiles(agentId, id) {
|
|
13743
|
+
await rm(getPluginAgentConfigDir(agentId, id), { recursive: true, force: true });
|
|
13714
13744
|
}
|
|
13715
13745
|
var CURSOR_CONFIG_DIR_ENV = "CURSOR_CONFIG_DIR";
|
|
13716
13746
|
function getCursorConfigDir(id) {
|
|
@@ -14278,6 +14308,7 @@ init_logger();
|
|
|
14278
14308
|
init_task_id();
|
|
14279
14309
|
|
|
14280
14310
|
// src/daemon/poller.ts
|
|
14311
|
+
init_runtime_config();
|
|
14281
14312
|
init_logger();
|
|
14282
14313
|
init_task_id();
|
|
14283
14314
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
@@ -18626,6 +18657,8 @@ var BoardPoller = class {
|
|
|
18626
18657
|
async poll() {
|
|
18627
18658
|
const { workspaceId, repoPath, scheduler, stateHub, onCardReadyForReview } = this.options;
|
|
18628
18659
|
const state = await loadWorkspaceState(workspaceId, repoPath);
|
|
18660
|
+
const effectiveLimit = state.projectConfig.maxParallelTasks ?? (await loadGlobalConfig()).maxParallelTasks;
|
|
18661
|
+
scheduler.setMaxParallelTasks(effectiveLimit);
|
|
18629
18662
|
const board = state.board;
|
|
18630
18663
|
const pendingCards = [];
|
|
18631
18664
|
const todoColumn = board.columns.find((c) => c.id === "todo");
|
|
@@ -18902,9 +18935,9 @@ function buildBrowserMcpServer(cardId) {
|
|
|
18902
18935
|
}
|
|
18903
18936
|
|
|
18904
18937
|
// src/agents/agent-registry.ts
|
|
18905
|
-
function
|
|
18938
|
+
function getProviderModels(binary) {
|
|
18906
18939
|
try {
|
|
18907
|
-
const result = spawnSync4(
|
|
18940
|
+
const result = spawnSync4(binary, ["models"], {
|
|
18908
18941
|
stdio: ["ignore", "pipe", "ignore"],
|
|
18909
18942
|
timeout: 1e4,
|
|
18910
18943
|
encoding: "utf-8"
|
|
@@ -18916,6 +18949,12 @@ function getOpencodeModels() {
|
|
|
18916
18949
|
}
|
|
18917
18950
|
return [];
|
|
18918
18951
|
}
|
|
18952
|
+
function getOpencodeModels() {
|
|
18953
|
+
return getProviderModels("opencode");
|
|
18954
|
+
}
|
|
18955
|
+
function getMimoModels() {
|
|
18956
|
+
return getProviderModels("mimo");
|
|
18957
|
+
}
|
|
18919
18958
|
function getCursorModels() {
|
|
18920
18959
|
try {
|
|
18921
18960
|
const result = spawnSync4("agent", ["models"], {
|
|
@@ -18959,6 +18998,12 @@ var AGENT_DEFINITIONS = [
|
|
|
18959
18998
|
label: "Cursor Agent",
|
|
18960
18999
|
command: "agent",
|
|
18961
19000
|
checkCommand: ["agent", "--version"]
|
|
19001
|
+
},
|
|
19002
|
+
{
|
|
19003
|
+
id: "mimo",
|
|
19004
|
+
label: "MiMo Code",
|
|
19005
|
+
command: "mimo",
|
|
19006
|
+
checkCommand: ["mimo", "--version"]
|
|
18962
19007
|
}
|
|
18963
19008
|
];
|
|
18964
19009
|
function isCommandAvailable(args) {
|
|
@@ -19027,24 +19072,29 @@ function buildAgentArgs(agentId, prompt, ctx = {}) {
|
|
|
19027
19072
|
}
|
|
19028
19073
|
return args;
|
|
19029
19074
|
}
|
|
19030
|
-
|
|
19075
|
+
// mimo (mimocode) is an opencode fork with an identical CLI surface
|
|
19076
|
+
// (`run`/`--agent build`/`-m`/`--prompt`/`--variant`), so it shares this branch.
|
|
19077
|
+
case "opencode":
|
|
19078
|
+
case "mimo": {
|
|
19031
19079
|
if (mode === "print") {
|
|
19032
19080
|
const args2 = ["run", "--agent", "build"];
|
|
19081
|
+
if (agentId === "mimo") args2.push("--never-ask", "--trust");
|
|
19033
19082
|
if (ctx.model) args2.push("-m", ctx.model);
|
|
19034
19083
|
if (ctx.effort) {
|
|
19035
|
-
const
|
|
19084
|
+
const OPENCODE_EFFORT_VARIANT = {
|
|
19036
19085
|
low: "minimal",
|
|
19037
19086
|
medium: "low",
|
|
19038
19087
|
high: "medium",
|
|
19039
19088
|
xhigh: "high",
|
|
19040
19089
|
max: "max"
|
|
19041
19090
|
};
|
|
19042
|
-
args2.push("--variant",
|
|
19091
|
+
args2.push("--variant", agentId === "mimo" ? ctx.effort : OPENCODE_EFFORT_VARIANT[ctx.effort]);
|
|
19043
19092
|
}
|
|
19044
19093
|
if (prompt.trim()) args2.push(prompt);
|
|
19045
19094
|
return args2;
|
|
19046
19095
|
}
|
|
19047
19096
|
const args = ["--agent", "build"];
|
|
19097
|
+
if (agentId === "mimo") args.push("--never-ask", "--trust");
|
|
19048
19098
|
if (ctx.model) args.push("-m", ctx.model);
|
|
19049
19099
|
if (prompt.trim()) args.push("--prompt", prompt);
|
|
19050
19100
|
return args;
|
|
@@ -19966,38 +20016,6 @@ function markRecurringRan(recurringAgentId) {
|
|
|
19966
20016
|
getDb().prepare("UPDATE recurring_agents SET last_run_at = ?, next_run_at = ? WHERE id = ?").run(now, nextRunAt, recurringAgentId);
|
|
19967
20017
|
}
|
|
19968
20018
|
|
|
19969
|
-
// src/daemon/recurring-agent-scheduler.ts
|
|
19970
|
-
init_workspace_state();
|
|
19971
|
-
init_workspace_state();
|
|
19972
|
-
|
|
19973
|
-
// src/daemon/scheduler.ts
|
|
19974
|
-
import { spawn as spawn5 } from "node:child_process";
|
|
19975
|
-
import { cpSync, existsSync as existsSync10, mkdirSync as mkdirSync9 } from "node:fs";
|
|
19976
|
-
import { unlink as unlink2 } from "node:fs/promises";
|
|
19977
|
-
import { dirname as dirname5, join as join16, resolve as resolve2 } from "node:path";
|
|
19978
|
-
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
19979
|
-
init_api_contract();
|
|
19980
|
-
init_logger();
|
|
19981
|
-
|
|
19982
|
-
// src/core/prompt-resolver.ts
|
|
19983
|
-
init_logger();
|
|
19984
|
-
import { readFileSync as readFileSync5 } from "node:fs";
|
|
19985
|
-
import { isAbsolute, join as join14 } from "node:path";
|
|
19986
|
-
function resolvePromptText(prompt, repoPath) {
|
|
19987
|
-
if (!prompt) return "";
|
|
19988
|
-
if (prompt.source === "inline") return prompt.text;
|
|
19989
|
-
const path2 = isAbsolute(prompt.path) ? prompt.path : join14(repoPath, prompt.path);
|
|
19990
|
-
try {
|
|
19991
|
-
return readFileSync5(path2, "utf-8");
|
|
19992
|
-
} catch (err) {
|
|
19993
|
-
logger.warn({ err: err.message, path: path2 }, "Slot prompt file unreadable \u2014 falling back to empty prompt");
|
|
19994
|
-
return "";
|
|
19995
|
-
}
|
|
19996
|
-
}
|
|
19997
|
-
|
|
19998
|
-
// src/daemon/scheduler.ts
|
|
19999
|
-
init_task_id();
|
|
20000
|
-
|
|
20001
20019
|
// src/state/memory-store.ts
|
|
20002
20020
|
init_api_contract();
|
|
20003
20021
|
init_task_id();
|
|
@@ -20115,6 +20133,7 @@ function createMemory(input) {
|
|
|
20115
20133
|
throw new Error("global-scoped memory requires at least one tag");
|
|
20116
20134
|
}
|
|
20117
20135
|
const originWorkspaceId = input.originWorkspaceId ?? workspaceId;
|
|
20136
|
+
const originCardId = input.originCardId && db.prepare("SELECT 1 FROM cards WHERE id = ?").get(input.originCardId) ? input.originCardId : null;
|
|
20118
20137
|
const tx = db.transaction(() => {
|
|
20119
20138
|
db.prepare(
|
|
20120
20139
|
`INSERT INTO memories (
|
|
@@ -20131,7 +20150,7 @@ function createMemory(input) {
|
|
|
20131
20150
|
input.content,
|
|
20132
20151
|
input.sourceType,
|
|
20133
20152
|
input.importance ?? 1,
|
|
20134
|
-
|
|
20153
|
+
originCardId,
|
|
20135
20154
|
input.originAgent ? JSON.stringify(input.originAgent) : null,
|
|
20136
20155
|
input.status ?? "approved",
|
|
20137
20156
|
now,
|
|
@@ -20255,7 +20274,7 @@ function searchMemories(query, workspaceId, limit = 20) {
|
|
|
20255
20274
|
).all({ q: ftsQuery, ws: workspaceId, limit });
|
|
20256
20275
|
return hydrate(rows.map(rowToMemory));
|
|
20257
20276
|
}
|
|
20258
|
-
function buildMemoryContext(workspaceId, memoryLimit = 40) {
|
|
20277
|
+
function buildMemoryContext(workspaceId, memoryLimit = 40, opts) {
|
|
20259
20278
|
const sections = [];
|
|
20260
20279
|
const fmt = (m2) => {
|
|
20261
20280
|
const tagSuffix = m2.tags.length > 0 ? ` _(tags: ${m2.tags.join(", ")})_` : "";
|
|
@@ -20273,18 +20292,50 @@ ${projectMem.map(fmt).join("\n")}`);
|
|
|
20273
20292
|
${globalMem.map(fmt).join("\n")}`);
|
|
20274
20293
|
}
|
|
20275
20294
|
if (sections.length === 0) return "";
|
|
20295
|
+
const readOnly = opts?.readOnly ?? false;
|
|
20276
20296
|
const knownTags = listTags();
|
|
20277
|
-
const tagLine = knownTags.length > 0 ? `
|
|
20297
|
+
const tagLine = !readOnly && knownTags.length > 0 ? `
|
|
20278
20298
|
|
|
20279
20299
|
Existing tags (reuse before inventing new ones): ${knownTags.join(", ")}.` : "";
|
|
20300
|
+
const recallLine = readOnly ? "Use `whipped_search_memory` / `whipped_get_memory` to recall more." : "Use `whipped_search_memory` / `whipped_get_memory` to recall more, and `whipped_update_memory` to correct an entry that's now wrong.";
|
|
20280
20301
|
return [
|
|
20281
20302
|
"## Memory",
|
|
20282
|
-
`This is whipped's persistent project memory \u2014 durable knowledge from past work. Each entry is prefixed with its id.
|
|
20303
|
+
`This is whipped's persistent project memory \u2014 durable knowledge from past work. Each entry is prefixed with its id. ${recallLine} Treat these as hints, not gospel: if a memory references a file, symbol, or rule, verify it still holds before relying on it.${tagLine}`,
|
|
20283
20304
|
...sections
|
|
20284
20305
|
].join("\n\n");
|
|
20285
20306
|
}
|
|
20286
20307
|
|
|
20308
|
+
// src/daemon/recurring-agent-scheduler.ts
|
|
20309
|
+
init_workspace_state();
|
|
20310
|
+
init_workspace_state();
|
|
20311
|
+
|
|
20287
20312
|
// src/daemon/scheduler.ts
|
|
20313
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
20314
|
+
import { cpSync, existsSync as existsSync10, mkdirSync as mkdirSync9 } from "node:fs";
|
|
20315
|
+
import { unlink as unlink2 } from "node:fs/promises";
|
|
20316
|
+
import { dirname as dirname5, join as join16, resolve as resolve2 } from "node:path";
|
|
20317
|
+
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
20318
|
+
init_api_contract();
|
|
20319
|
+
init_logger();
|
|
20320
|
+
|
|
20321
|
+
// src/core/prompt-resolver.ts
|
|
20322
|
+
init_logger();
|
|
20323
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
20324
|
+
import { isAbsolute, join as join14 } from "node:path";
|
|
20325
|
+
function resolvePromptText(prompt, repoPath) {
|
|
20326
|
+
if (!prompt) return "";
|
|
20327
|
+
if (prompt.source === "inline") return prompt.text;
|
|
20328
|
+
const path2 = isAbsolute(prompt.path) ? prompt.path : join14(repoPath, prompt.path);
|
|
20329
|
+
try {
|
|
20330
|
+
return readFileSync5(path2, "utf-8");
|
|
20331
|
+
} catch (err) {
|
|
20332
|
+
logger.warn({ err: err.message, path: path2 }, "Slot prompt file unreadable \u2014 falling back to empty prompt");
|
|
20333
|
+
return "";
|
|
20334
|
+
}
|
|
20335
|
+
}
|
|
20336
|
+
|
|
20337
|
+
// src/daemon/scheduler.ts
|
|
20338
|
+
init_task_id();
|
|
20288
20339
|
init_workspace_state();
|
|
20289
20340
|
|
|
20290
20341
|
// src/daemon/review-pipeline.ts
|
|
@@ -20478,9 +20529,9 @@ ${rawSystemPrompt}` : rawSystemPrompt;
|
|
|
20478
20529
|
|
|
20479
20530
|
After calling \`kanban_add_comment\`, call the \`task_complete\` MCP tool to signal that you are done.` : withMemory;
|
|
20480
20531
|
const triggerWord = getSlotTriggerWord(slot.type);
|
|
20481
|
-
const mcpConfigPath = agentBinary
|
|
20482
|
-
const hookServerPort = agentBinary === "codex" || agentBinary
|
|
20483
|
-
const mcpServer = agentBinary === "codex" || agentBinary
|
|
20532
|
+
const mcpConfigPath = !isPluginConfigAgent(agentBinary) && agentBinary !== "cursor" ? getMcpConfigPath(streamId) : void 0;
|
|
20533
|
+
const hookServerPort = agentBinary === "codex" || isPluginConfigAgent(agentBinary) || agentBinary === "cursor" ? getServerPort(options.serverUrl) : void 0;
|
|
20534
|
+
const mcpServer = agentBinary === "codex" || isPluginConfigAgent(agentBinary) || agentBinary === "cursor" ? buildWhippedMcpServerSpec(options.mcpBinary, options.serverUrl, workspaceId, agentBinary) : void 0;
|
|
20484
20535
|
const browserMcp = browserEnabled ? buildBrowserMcpServer(card.id) : void 0;
|
|
20485
20536
|
const browserMcpSpec = browserMcp ? { command: browserMcp.command, args: browserMcp.args } : void 0;
|
|
20486
20537
|
const extraMcpServers = browserMcpSpec ? { [PLAYWRIGHT_MCP_SERVER_NAME]: browserMcpSpec } : void 0;
|
|
@@ -20789,9 +20840,11 @@ async function handleReviewSuccess(card, options) {
|
|
|
20789
20840
|
}
|
|
20790
20841
|
function runAgentOnce(agentId, prompt, cwd, workspaceId, streamId, stateHub, registerStopCallback, registerLiveProcess, mcpConfigPath, appendSystemPrompt, files, secretsEnv, effort, hookServerPort, mcpServer, model, slotType, browserMcpServer) {
|
|
20791
20842
|
const extraMcp = browserMcpServer ? { [PLAYWRIGHT_MCP_SERVER_NAME]: browserMcpServer } : void 0;
|
|
20792
|
-
if (agentId
|
|
20793
|
-
void
|
|
20794
|
-
|
|
20843
|
+
if (isPluginConfigAgent(agentId) && hookServerPort != null && mcpServer) {
|
|
20844
|
+
void writePluginAgentFiles(agentId, streamId, hookServerPort, mcpServer, { appendSystemPrompt, extraMcp }).catch(
|
|
20845
|
+
() => {
|
|
20846
|
+
}
|
|
20847
|
+
);
|
|
20795
20848
|
}
|
|
20796
20849
|
if (agentId === "cursor" && hookServerPort != null && mcpServer) {
|
|
20797
20850
|
void writeCursorConfigFiles(streamId, hookServerPort, mcpServer, extraMcp).catch(() => {
|
|
@@ -20806,7 +20859,7 @@ function runAgentOnce(agentId, prompt, cwd, workspaceId, streamId, stateHub, reg
|
|
|
20806
20859
|
void saveTerminalBuffer(workspaceId, streamId, output);
|
|
20807
20860
|
if (mcpConfigPath) unlink(mcpConfigPath).catch(() => {
|
|
20808
20861
|
});
|
|
20809
|
-
if (agentId
|
|
20862
|
+
if (isPluginConfigAgent(agentId)) void cleanupPluginAgentFiles(agentId, streamId);
|
|
20810
20863
|
if (agentId === "cursor") void cleanupCursorConfigDir(streamId);
|
|
20811
20864
|
resolve5(output);
|
|
20812
20865
|
});
|
|
@@ -20820,7 +20873,7 @@ function runAgentOnce(agentId, prompt, cwd, workspaceId, streamId, stateHub, reg
|
|
|
20820
20873
|
...buildTaskHookEnv(streamId, workspaceId),
|
|
20821
20874
|
...secretsEnv,
|
|
20822
20875
|
...slotType ? { WHIPPED_SLOT: slotType } : {},
|
|
20823
|
-
...agentId
|
|
20876
|
+
...pluginAgentConfigDirEnv(agentId, streamId),
|
|
20824
20877
|
...agentId === "cursor" ? { [CURSOR_CONFIG_DIR_ENV]: getCursorConfigDir(streamId) } : {}
|
|
20825
20878
|
},
|
|
20826
20879
|
hookSettingsPath: agentId === "claude" ? CLAUDE_TASK_SETTINGS_PATH : void 0,
|
|
@@ -20828,7 +20881,7 @@ function runAgentOnce(agentId, prompt, cwd, workspaceId, streamId, stateHub, reg
|
|
|
20828
20881
|
mcpConfigPath: agentId === "claude" ? mcpConfigPath : void 0,
|
|
20829
20882
|
mcpServer: agentId === "codex" ? mcpServer : void 0,
|
|
20830
20883
|
browserMcpServer: agentId === "codex" ? browserMcpServer : void 0,
|
|
20831
|
-
appendSystemPrompt: agentId
|
|
20884
|
+
appendSystemPrompt: isPluginConfigAgent(agentId) ? void 0 : appendSystemPrompt,
|
|
20832
20885
|
files: agentId === "claude" ? files : void 0,
|
|
20833
20886
|
effort,
|
|
20834
20887
|
model,
|
|
@@ -20843,7 +20896,7 @@ function runAgentOnce(agentId, prompt, cwd, workspaceId, streamId, stateHub, reg
|
|
|
20843
20896
|
void saveTerminalBuffer(workspaceId, streamId, output);
|
|
20844
20897
|
if (mcpConfigPath) unlink(mcpConfigPath).catch(() => {
|
|
20845
20898
|
});
|
|
20846
|
-
if (agentId
|
|
20899
|
+
if (isPluginConfigAgent(agentId)) void cleanupPluginAgentFiles(agentId, streamId);
|
|
20847
20900
|
if (agentId === "cursor") void cleanupCursorConfigDir(streamId);
|
|
20848
20901
|
resolve5(output);
|
|
20849
20902
|
}
|
|
@@ -21455,9 +21508,9 @@ ${rawSystemPrompt}` : rawSystemPrompt;
|
|
|
21455
21508
|
const systemPrompt = agentId === "cursor" ? `${withMemory}
|
|
21456
21509
|
|
|
21457
21510
|
After calling \`kanban_set_plan\`, call the \`task_complete\` MCP tool to signal that you are done.` : withMemory;
|
|
21458
|
-
const mcpConfigPath = agentId
|
|
21459
|
-
const hookServerPort = agentId === "codex" || agentId
|
|
21460
|
-
const mcpServer = agentId === "codex" || agentId
|
|
21511
|
+
const mcpConfigPath = !isPluginConfigAgent(agentId) && agentId !== "cursor" ? getMcpConfigPath(streamId) : void 0;
|
|
21512
|
+
const hookServerPort = agentId === "codex" || isPluginConfigAgent(agentId) || agentId === "cursor" ? getServerPort(options.serverUrl) : void 0;
|
|
21513
|
+
const mcpServer = agentId === "codex" || isPluginConfigAgent(agentId) || agentId === "cursor" ? buildWhippedMcpServerSpec(options.mcpBinary, options.serverUrl, workspaceId, agentId) : void 0;
|
|
21461
21514
|
if (agentId === "claude" && mcpConfigPath) {
|
|
21462
21515
|
await writeClaudeMcpConfig(options.mcpBinary, options.serverUrl, workspaceId, agentId, mcpConfigPath).catch(
|
|
21463
21516
|
() => {
|
|
@@ -21658,7 +21711,6 @@ After handling all children, call \`kanban_add_comment\` on the PARENT card (${p
|
|
|
21658
21711
|
// src/daemon/scheduler.ts
|
|
21659
21712
|
var FAST_EXIT_THRESHOLD_MS = 8e3;
|
|
21660
21713
|
var MAX_RECENT_BUFFERS = 100;
|
|
21661
|
-
var ASSISTANT_AGENT_PREFIX = "__assistant__:";
|
|
21662
21714
|
var TaskScheduler = class {
|
|
21663
21715
|
constructor(options) {
|
|
21664
21716
|
this.options = options;
|
|
@@ -21742,7 +21794,7 @@ ${assistantSystemPrompt}` : assistantSystemPrompt;
|
|
|
21742
21794
|
await writeClaudeAssistantSettings(getMcpServerPath(), serverUrl, workspaceId).catch((err) => {
|
|
21743
21795
|
logger.warn({ err }, "[scheduler] Failed to write assistant agent MCP settings");
|
|
21744
21796
|
});
|
|
21745
|
-
} else if (agentId
|
|
21797
|
+
} else if (isPluginConfigAgent(agentId)) {
|
|
21746
21798
|
const mcpSpec = buildWhippedMcpServerSpec(
|
|
21747
21799
|
getMcpServerPath(),
|
|
21748
21800
|
serverUrl,
|
|
@@ -21750,9 +21802,11 @@ ${assistantSystemPrompt}` : assistantSystemPrompt;
|
|
|
21750
21802
|
void 0,
|
|
21751
21803
|
buildMcpRoleArgs("assistant")
|
|
21752
21804
|
);
|
|
21753
|
-
await
|
|
21754
|
-
|
|
21755
|
-
|
|
21805
|
+
await writePluginAgentFiles(agentId, taskId, getServerPort(serverUrl), mcpSpec, { appendSystemPrompt }).catch(
|
|
21806
|
+
(err) => {
|
|
21807
|
+
logger.warn({ err }, `[scheduler] Failed to write ${agentId} assistant agent files`);
|
|
21808
|
+
}
|
|
21809
|
+
);
|
|
21756
21810
|
} else if (agentId === "cursor") {
|
|
21757
21811
|
const mcpSpec = buildWhippedMcpServerSpec(
|
|
21758
21812
|
getMcpServerPath(),
|
|
@@ -21778,7 +21832,7 @@ ${assistantSystemPrompt}` : assistantSystemPrompt;
|
|
|
21778
21832
|
...secretsEnv,
|
|
21779
21833
|
...buildTaskHookEnv(taskId, workspaceId),
|
|
21780
21834
|
WHIPPED_SLOT: "assistant",
|
|
21781
|
-
...agentId
|
|
21835
|
+
...pluginAgentConfigDirEnv(agentId, taskId),
|
|
21782
21836
|
...agentId === "cursor" ? { [CURSOR_CONFIG_DIR_ENV]: getCursorConfigDir(taskId) } : {}
|
|
21783
21837
|
},
|
|
21784
21838
|
mcpConfigPath: agentId === "claude" ? CLAUDE_ASSISTANT_MCP_CONFIG_PATH : void 0,
|
|
@@ -21791,7 +21845,7 @@ ${assistantSystemPrompt}` : assistantSystemPrompt;
|
|
|
21791
21845
|
) : void 0,
|
|
21792
21846
|
model: assistantModel?.model ?? null,
|
|
21793
21847
|
effort: assistantModel?.effort ?? null,
|
|
21794
|
-
appendSystemPrompt: agentId
|
|
21848
|
+
appendSystemPrompt: isPluginConfigAgent(agentId) ? void 0 : appendSystemPrompt,
|
|
21795
21849
|
onOutput: (data) => {
|
|
21796
21850
|
assistantTask.outputBuffer += data;
|
|
21797
21851
|
stateHub.broadcastTerminalOutput(workspaceId, taskId, data);
|
|
@@ -21824,6 +21878,11 @@ ${assistantSystemPrompt}` : assistantSystemPrompt;
|
|
|
21824
21878
|
get maxParallelTasks() {
|
|
21825
21879
|
return this.options.maxParallelTasks;
|
|
21826
21880
|
}
|
|
21881
|
+
// Config can change at runtime; the poller re-syncs this each tick so a
|
|
21882
|
+
// changed "Max Parallel Tasks" takes effect without restarting the daemon.
|
|
21883
|
+
setMaxParallelTasks(limit) {
|
|
21884
|
+
this.options.maxParallelTasks = limit;
|
|
21885
|
+
}
|
|
21827
21886
|
canAcceptTask(inFlightCount) {
|
|
21828
21887
|
const count = inFlightCount ?? this.running.size;
|
|
21829
21888
|
return count < this.options.maxParallelTasks;
|
|
@@ -21959,7 +22018,7 @@ ${assistantSystemPrompt}` : assistantSystemPrompt;
|
|
|
21959
22018
|
this.options.onTaskCompleted(taskId);
|
|
21960
22019
|
return;
|
|
21961
22020
|
}
|
|
21962
|
-
const mcpConfigPath = agentId
|
|
22021
|
+
const mcpConfigPath = !isPluginConfigAgent(agentId) && agentId !== "cursor" ? getMcpConfigPath(taskId) : void 0;
|
|
21963
22022
|
let sharedBranchName;
|
|
21964
22023
|
if (hasSharedWorktree) {
|
|
21965
22024
|
const ownerCard = board.cards[effectiveWorktreeId];
|
|
@@ -22094,9 +22153,9 @@ ${devSystemPromptResult.text}`;
|
|
|
22094
22153
|
mcpConfigPath
|
|
22095
22154
|
).catch(() => {
|
|
22096
22155
|
});
|
|
22097
|
-
} else if (agentId
|
|
22156
|
+
} else if (isPluginConfigAgent(agentId)) {
|
|
22098
22157
|
const mcpSpec = buildWhippedMcpServerSpec(getMcpServerPath(), this.options.serverUrl, workspaceId, agentId);
|
|
22099
|
-
await
|
|
22158
|
+
await writePluginAgentFiles(agentId, taskId, getServerPort(this.options.serverUrl), mcpSpec, {
|
|
22100
22159
|
appendSystemPrompt: devSystemPromptResult.text
|
|
22101
22160
|
}).catch(() => {
|
|
22102
22161
|
});
|
|
@@ -22130,14 +22189,14 @@ ${devSystemPromptResult.text}`;
|
|
|
22130
22189
|
...secretsEnv,
|
|
22131
22190
|
WHIPPED_SLOT: "dev",
|
|
22132
22191
|
...devPair.model ? { WHIPPED_MODEL: devPair.model } : {},
|
|
22133
|
-
...agentId
|
|
22192
|
+
...pluginAgentConfigDirEnv(agentId, taskId),
|
|
22134
22193
|
...agentId === "cursor" ? { [CURSOR_CONFIG_DIR_ENV]: getCursorConfigDir(taskId) } : {}
|
|
22135
22194
|
},
|
|
22136
22195
|
hookSettingsPath: agentId === "claude" ? CLAUDE_TASK_SETTINGS_PATH : void 0,
|
|
22137
22196
|
hookServerPort: agentId === "codex" ? getServerPort(this.options.serverUrl) : void 0,
|
|
22138
22197
|
mcpConfigPath: agentId === "claude" ? mcpConfigPath : void 0,
|
|
22139
22198
|
mcpServer: agentId === "codex" ? buildWhippedMcpServerSpec(getMcpServerPath(), this.options.serverUrl, workspaceId, agentId) : void 0,
|
|
22140
|
-
appendSystemPrompt: agentId
|
|
22199
|
+
appendSystemPrompt: isPluginConfigAgent(agentId) ? void 0 : agentId === "cursor" ? devSystemPromptResult.text + "\n\n4. Call the `task_complete` MCP tool to signal that the task is complete." : devSystemPromptResult.text,
|
|
22141
22200
|
files: agentId === "claude" ? devSystemPromptResult.files : void 0,
|
|
22142
22201
|
effort: devPair.effort ?? void 0,
|
|
22143
22202
|
model: devPair.model ?? void 0,
|
|
@@ -22155,7 +22214,7 @@ ${devSystemPromptResult.text}`;
|
|
|
22155
22214
|
if (runningTask.worktreeOwnerId) this.runningSharedWorktrees.delete(runningTask.worktreeOwnerId);
|
|
22156
22215
|
if (mcpConfigPath) unlink2(mcpConfigPath).catch(() => {
|
|
22157
22216
|
});
|
|
22158
|
-
if (agentId
|
|
22217
|
+
if (isPluginConfigAgent(agentId)) void cleanupPluginAgentFiles(agentId, taskId);
|
|
22159
22218
|
if (agentId === "cursor") void cleanupCursorConfigDir(taskId);
|
|
22160
22219
|
if (this.isShuttingDown) return;
|
|
22161
22220
|
if (this.manuallyStoppedTasks.has(taskId)) {
|
|
@@ -22532,9 +22591,9 @@ ${devSystemPromptResult.text}`;
|
|
|
22532
22591
|
let outputBuffer = "";
|
|
22533
22592
|
let hookHandled = false;
|
|
22534
22593
|
const conflictSystemPrompt = buildConflictResolutionSystemPrompt(card, conflictedFiles, conflictGitInstructions);
|
|
22535
|
-
if (defaultAgent
|
|
22594
|
+
if (isPluginConfigAgent(defaultAgent)) {
|
|
22536
22595
|
const mcpSpec = buildWhippedMcpServerSpec(getMcpServerPath(), this.options.serverUrl, workspaceId);
|
|
22537
|
-
await
|
|
22596
|
+
await writePluginAgentFiles(defaultAgent, streamId, getServerPort(this.options.serverUrl), mcpSpec, {
|
|
22538
22597
|
appendSystemPrompt: conflictSystemPrompt
|
|
22539
22598
|
}).catch(() => {
|
|
22540
22599
|
});
|
|
@@ -22551,10 +22610,10 @@ ${devSystemPromptResult.text}`;
|
|
|
22551
22610
|
hookServerPort: defaultAgent === "codex" ? getServerPort(this.options.serverUrl) : void 0,
|
|
22552
22611
|
env: {
|
|
22553
22612
|
...buildTaskHookEnv(streamId, workspaceId),
|
|
22554
|
-
...defaultAgent
|
|
22613
|
+
...pluginAgentConfigDirEnv(defaultAgent, streamId),
|
|
22555
22614
|
...defaultAgent === "cursor" ? { [CURSOR_CONFIG_DIR_ENV]: getCursorConfigDir(streamId) } : {}
|
|
22556
22615
|
},
|
|
22557
|
-
appendSystemPrompt: defaultAgent
|
|
22616
|
+
appendSystemPrompt: isPluginConfigAgent(defaultAgent) ? void 0 : conflictSystemPrompt,
|
|
22558
22617
|
onOutput: (data) => {
|
|
22559
22618
|
outputBuffer += data;
|
|
22560
22619
|
stateHub.broadcastTerminalOutput(workspaceId, streamId, data);
|
|
@@ -22575,7 +22634,7 @@ ${devSystemPromptResult.text}`;
|
|
|
22575
22634
|
this.setRecentBuffer(streamId, outputBuffer);
|
|
22576
22635
|
void saveTerminalBuffer(workspaceId, streamId, outputBuffer);
|
|
22577
22636
|
void endTerminalSession(workspaceId, card.id, streamId, Date.now(), "completed");
|
|
22578
|
-
if (defaultAgent
|
|
22637
|
+
if (isPluginConfigAgent(defaultAgent)) void cleanupPluginAgentFiles(defaultAgent, streamId);
|
|
22579
22638
|
if (defaultAgent === "cursor") void cleanupCursorConfigDir(streamId);
|
|
22580
22639
|
proc.kill();
|
|
22581
22640
|
onComplete(true).catch((err) => logger.error({ err }, `[scheduler] conflict onComplete failed for ${card.id}:`));
|
|
@@ -22820,7 +22879,7 @@ var RecurringAgentScheduler = class {
|
|
|
22820
22879
|
} catch (err) {
|
|
22821
22880
|
logger.warn({ err, agentId: agent.id }, "[recurring] failed to load project config");
|
|
22822
22881
|
}
|
|
22823
|
-
const
|
|
22882
|
+
const baseSystemPrompt = buildRecurringSystemPrompt(
|
|
22824
22883
|
repoPath,
|
|
22825
22884
|
agent.name,
|
|
22826
22885
|
agent.instructions,
|
|
@@ -22828,6 +22887,10 @@ var RecurringAgentScheduler = class {
|
|
|
22828
22887
|
secrets,
|
|
22829
22888
|
projectSystemPrompt
|
|
22830
22889
|
);
|
|
22890
|
+
const memContext = buildMemoryContext(workspaceId, 40, { readOnly: true });
|
|
22891
|
+
const appendSystemPrompt = memContext ? `${memContext}
|
|
22892
|
+
|
|
22893
|
+
${baseSystemPrompt}` : baseSystemPrompt;
|
|
22831
22894
|
const prompt = buildRecurringPrompt();
|
|
22832
22895
|
if (agentBinary === "claude" && mcpConfigPath) {
|
|
22833
22896
|
await writeClaudeMcpConfig(
|
|
@@ -22839,10 +22902,11 @@ var RecurringAgentScheduler = class {
|
|
|
22839
22902
|
void 0,
|
|
22840
22903
|
roleArgs
|
|
22841
22904
|
).catch((err) => logger.warn({ err }, "[recurring] failed to write claude MCP config"));
|
|
22842
|
-
} else if (agentBinary
|
|
22843
|
-
await
|
|
22844
|
-
|
|
22845
|
-
|
|
22905
|
+
} else if (isPluginConfigAgent(agentBinary)) {
|
|
22906
|
+
await writePluginAgentFiles(agentBinary, streamId, hookServerPort, mcpServer, {
|
|
22907
|
+
appendSystemPrompt,
|
|
22908
|
+
readOnly: true
|
|
22909
|
+
}).catch((err) => logger.warn({ err }, `[recurring] failed to write ${agentBinary} files`));
|
|
22846
22910
|
} else if (agentBinary === "cursor") {
|
|
22847
22911
|
await writeCursorConfigFiles(streamId, hookServerPort, mcpServer).catch(
|
|
22848
22912
|
(err) => logger.warn({ err }, "[recurring] failed to write cursor config")
|
|
@@ -22856,7 +22920,7 @@ var RecurringAgentScheduler = class {
|
|
|
22856
22920
|
const cleanup = () => {
|
|
22857
22921
|
if (mcpConfigPath) unlink3(mcpConfigPath).catch(() => {
|
|
22858
22922
|
});
|
|
22859
|
-
if (agentBinary
|
|
22923
|
+
if (isPluginConfigAgent(agentBinary)) void cleanupPluginAgentFiles(agentBinary, streamId);
|
|
22860
22924
|
if (agentBinary === "cursor") void cleanupCursorConfigDir(streamId);
|
|
22861
22925
|
};
|
|
22862
22926
|
const finish = (status) => {
|
|
@@ -22890,18 +22954,18 @@ var RecurringAgentScheduler = class {
|
|
|
22890
22954
|
...buildTaskHookEnv(streamId, workspaceId),
|
|
22891
22955
|
WHIPPED_SLOT: "recurring",
|
|
22892
22956
|
...agent.model.model ? { WHIPPED_MODEL: agent.model.model } : {},
|
|
22893
|
-
...agentBinary
|
|
22957
|
+
...pluginAgentConfigDirEnv(agentBinary, streamId),
|
|
22894
22958
|
...agentBinary === "cursor" ? { [CURSOR_CONFIG_DIR_ENV]: getCursorConfigDir(streamId) } : {}
|
|
22895
22959
|
},
|
|
22896
22960
|
hookSettingsPath: agentBinary === "claude" ? CLAUDE_TASK_SETTINGS_PATH : void 0,
|
|
22897
22961
|
hookServerPort: agentBinary === "codex" ? hookServerPort : void 0,
|
|
22898
22962
|
mcpConfigPath,
|
|
22899
22963
|
mcpServer: agentBinary === "codex" ? mcpServer : void 0,
|
|
22900
|
-
appendSystemPrompt: agentBinary
|
|
22964
|
+
appendSystemPrompt: isPluginConfigAgent(agentBinary) ? void 0 : appendSystemPrompt,
|
|
22901
22965
|
model: agent.model.model,
|
|
22902
22966
|
effort: agent.model.effort,
|
|
22903
22967
|
// Observer agents are read-only: claude blocks file/shell tools via
|
|
22904
|
-
// --disallowedTools, opencode
|
|
22968
|
+
// --disallowedTools, opencode/mimo disable them in their config. codex/cursor
|
|
22905
22969
|
// fall back to prompt-only enforcement.
|
|
22906
22970
|
readOnly: true,
|
|
22907
22971
|
onOutput: (data) => {
|
|
@@ -22957,8 +23021,10 @@ ${journalBlock}
|
|
|
22957
23021
|
|
|
22958
23022
|
When you finish, call \`update_journal\` with the full updated notes to keep for next time (what you checked, what you filed, what you're watching). It REPLACES the journal, so include everything still relevant.
|
|
22959
23023
|
|
|
23024
|
+
If your task is a one-off that is now complete, or the condition you were watching for is resolved and there is nothing left to do on future runs, call \`disable_self\` to stop running on schedule. This only pauses future runs (the user can re-enable you); the current run still finishes normally. Update your journal before disabling.
|
|
23025
|
+
|
|
22960
23026
|
## Available tools
|
|
22961
|
-
\`kanban_get_board\`, \`kanban_create_card\`, \`kanban_add_comment\`, \`kanban_get_workflows\`, \`whipped_search_memory\`, \`whipped_get_memory\`, \`update_journal\`, plus read-only repo tools (Read, Grep, Glob).`;
|
|
23027
|
+
\`kanban_get_board\`, \`kanban_create_card\`, \`kanban_add_comment\`, \`kanban_get_workflows\`, \`whipped_search_memory\`, \`whipped_get_memory\`, \`update_journal\`, \`disable_self\`, plus read-only repo tools (Read, Grep, Glob).`;
|
|
22962
23028
|
const secretsSection = buildSecretsSection(secrets);
|
|
22963
23029
|
if (secretsSection) prompt += `
|
|
22964
23030
|
|
|
@@ -25531,12 +25597,16 @@ import { z as z4 } from "zod";
|
|
|
25531
25597
|
|
|
25532
25598
|
// src/api/services/agents-service.ts
|
|
25533
25599
|
var listAvailableAgents = async () => getAvailableAgents();
|
|
25534
|
-
var listModels = async (agent) =>
|
|
25600
|
+
var listModels = async (agent) => {
|
|
25601
|
+
if (agent === "cursor") return getCursorModels();
|
|
25602
|
+
const models = agent === "mimo" ? getMimoModels() : getOpencodeModels();
|
|
25603
|
+
return models.map((m2) => ({ value: m2, label: m2 }));
|
|
25604
|
+
};
|
|
25535
25605
|
|
|
25536
25606
|
// src/api/routes/agents.ts
|
|
25537
25607
|
var agentsController = new Hono2().get("/available", async (c) => {
|
|
25538
25608
|
return c.json(await listAvailableAgents());
|
|
25539
|
-
}).get("/models", zv("query", z4.object({ agent: z4.enum(["opencode", "cursor"]) })), async (c) => {
|
|
25609
|
+
}).get("/models", zv("query", z4.object({ agent: z4.enum(["opencode", "cursor", "mimo"]) })), async (c) => {
|
|
25540
25610
|
return c.json(await listModels(c.req.valid("query").agent));
|
|
25541
25611
|
});
|
|
25542
25612
|
|
|
@@ -25973,21 +26043,22 @@ var deleteReviewCommentService = async (input) => {
|
|
|
25973
26043
|
await updateCard(input.workspaceId, input.cardId, { reviewComments: next });
|
|
25974
26044
|
return { ok: true };
|
|
25975
26045
|
};
|
|
25976
|
-
var submitHumanFeedbackService = async (workspaceId, cardId, comment, attachments) => {
|
|
26046
|
+
var submitHumanFeedbackService = async (workspaceId, cardId, comment, attachments, type, metadata) => {
|
|
25977
26047
|
const board = await loadBoard(workspaceId);
|
|
25978
26048
|
const card = board.cards[cardId];
|
|
25979
26049
|
if (!card) throw NotFoundError("Card");
|
|
25980
26050
|
const trimmed = comment?.trim();
|
|
25981
|
-
const hasContent = trimmed || (attachments?.length ?? 0) > 0;
|
|
26051
|
+
const hasContent = trimmed || (attachments?.length ?? 0) > 0 || metadata != null;
|
|
25982
26052
|
const updatedComments = hasContent ? [
|
|
25983
26053
|
...card.reviewComments ?? [],
|
|
25984
26054
|
{
|
|
25985
26055
|
id: generateTaskId(),
|
|
25986
|
-
type: "human",
|
|
26056
|
+
type: type ?? "human",
|
|
25987
26057
|
actor: { type: "human", id: "human" },
|
|
25988
26058
|
createdAt: Date.now(),
|
|
25989
26059
|
summary: trimmed ?? "Feedback with attachments",
|
|
25990
|
-
attachments: attachments?.length ? attachments : void 0
|
|
26060
|
+
attachments: attachments?.length ? attachments : void 0,
|
|
26061
|
+
...metadata ? { metadata } : {}
|
|
25991
26062
|
}
|
|
25992
26063
|
] : card.reviewComments ?? [];
|
|
25993
26064
|
await updateCard(workspaceId, cardId, { reviewComments: updatedComments, autoFixAttempts: 0 });
|
|
@@ -26300,13 +26371,15 @@ var cardsController = new Hono2().post("/", zv("json", runtimeCardCreateRequestS
|
|
|
26300
26371
|
workspaceId: z5.string(),
|
|
26301
26372
|
cardId: z5.string(),
|
|
26302
26373
|
comment: z5.string().optional(),
|
|
26303
|
-
attachments: z5.array(reviewAttachmentSchema).optional()
|
|
26374
|
+
attachments: z5.array(reviewAttachmentSchema).optional(),
|
|
26375
|
+
type: z5.string().optional(),
|
|
26376
|
+
metadata: z5.record(z5.string(), z5.unknown()).optional()
|
|
26304
26377
|
})
|
|
26305
26378
|
),
|
|
26306
26379
|
async (c) => {
|
|
26307
26380
|
const ctx = c.var.ctx;
|
|
26308
|
-
const { workspaceId, cardId, comment, attachments } = c.req.valid("json");
|
|
26309
|
-
const result = await submitHumanFeedbackService(workspaceId, cardId, comment, attachments);
|
|
26381
|
+
const { workspaceId, cardId, comment, attachments, type, metadata } = c.req.valid("json");
|
|
26382
|
+
const result = await submitHumanFeedbackService(workspaceId, cardId, comment, attachments, type, metadata);
|
|
26310
26383
|
ctx.stateHub.broadcastWorkspaceUpdate(workspaceId);
|
|
26311
26384
|
if (result.reopenCascade) {
|
|
26312
26385
|
const scheduler = ctx.getScheduler(workspaceId);
|
|
@@ -28565,7 +28638,7 @@ process.on("uncaughtException", (err) => {
|
|
|
28565
28638
|
if (err.code === "EPIPE" || err.code === "ECONNRESET") return;
|
|
28566
28639
|
throw err;
|
|
28567
28640
|
});
|
|
28568
|
-
var VERSION9 = true ? "0.
|
|
28641
|
+
var VERSION9 = true ? "0.4.0" : "0.0.0-dev";
|
|
28569
28642
|
async function isPortAvailable(port, host) {
|
|
28570
28643
|
return new Promise((resolve5) => {
|
|
28571
28644
|
const probe = createServer2();
|
package/dist/mcp-server.js
CHANGED
|
@@ -33,11 +33,12 @@ function highestWorkflowLevel(workflow) {
|
|
|
33
33
|
}
|
|
34
34
|
return LEVEL_ORDER[bestIdx] ?? "medium";
|
|
35
35
|
}
|
|
36
|
-
var runtimeAgentIdSchema, effortLevelSchema, agentModelChoiceSchema, workflowSlotTypeSchema, tierLevelSchema, LEVEL_ORDER, modelPairSchema, pairSelectionModeSchema, SLOT_TOOL_IDS, slotToolSchema, slotModelConfigSchema, cardModelConfigSchema, promptValueSchema, EMPTY_INLINE_PROMPT, workflowSlotSchema, DEFAULT_MODEL_PAIR, DEFAULT_SLOT_MODEL_FIELDS, workflowSchema, DEFAULT_WORKFLOW, DEFAULT_STORY_WORKFLOW, DEFAULT_GIT_INSTRUCTIONS, runtimeBoardColumnIdSchema, BOARD_COLUMNS, reviewActorSchema, reviewIssueSchema, reviewAttachmentSchema, runtimeReviewCommentSchema, runtimeActivityEntrySchema, runtimeTaskSessionStateSchema, runtimeTerminalSessionEntrySchema, runtimeCardPrioritySchema, cardTypeSchema, runtimePrMetaSchema, runtimeBoardCardSchema, runtimeBoardColumnSchema, runtimeBoardDataSchema, runtimeGlobalConfigSchema, runtimeGithubConfigSchema, runtimeWorktreeSetupSchema, runtimeProjectSecretSchema, runtimeProjectConfigSchema, runtimeWorkspaceStateResponseSchema, runtimeWorkspaceStateSaveRequestSchema, runtimeVisualElementSchema, runtimeVisualCommentSchema, runtimeCardCreateRequestSchema, runtimeCardMoveRequestSchema, runtimeCardUpdateRequestSchema, memoryScopeSchema, memoryTypeSchema, memorySourceTypeSchema, memoryStatusSchema, runtimeMemoryOriginAgentSchema, runtimeMemorySchema, recurringScheduleKindSchema, recurringScheduleSchema, recurringRunStatusSchema, recurringRunTriggerSchema, recurringAgentRunSchema, recurringAgentSchema, recurringAgentCreateRequestSchema, recurringAgentUpdateRequestSchema, projectFolderSchema, topLevelItemSchema, projectsLayoutSchema, runtimeProjectSchema;
|
|
36
|
+
var ASSISTANT_AGENT_PREFIX, runtimeAgentIdSchema, effortLevelSchema, agentModelChoiceSchema, workflowSlotTypeSchema, tierLevelSchema, LEVEL_ORDER, modelPairSchema, pairSelectionModeSchema, SLOT_TOOL_IDS, slotToolSchema, slotModelConfigSchema, cardModelConfigSchema, promptValueSchema, EMPTY_INLINE_PROMPT, workflowSlotSchema, DEFAULT_MODEL_PAIR, DEFAULT_SLOT_MODEL_FIELDS, workflowSchema, DEFAULT_WORKFLOW, DEFAULT_STORY_WORKFLOW, DEFAULT_GIT_INSTRUCTIONS, runtimeBoardColumnIdSchema, BOARD_COLUMNS, reviewActorSchema, reviewIssueSchema, reviewAttachmentSchema, runtimeReviewCommentSchema, runtimeActivityEntrySchema, runtimeTaskSessionStateSchema, runtimeTerminalSessionEntrySchema, runtimeCardPrioritySchema, cardTypeSchema, runtimePrMetaSchema, runtimeBoardCardSchema, runtimeBoardColumnSchema, runtimeBoardDataSchema, runtimeGlobalConfigSchema, runtimeGithubConfigSchema, runtimeWorktreeSetupSchema, runtimeProjectSecretSchema, runtimeProjectConfigSchema, runtimeWorkspaceStateResponseSchema, runtimeWorkspaceStateSaveRequestSchema, runtimeVisualElementSchema, runtimeVisualCommentSchema, runtimeCardCreateRequestSchema, runtimeCardMoveRequestSchema, runtimeCardUpdateRequestSchema, memoryScopeSchema, memoryTypeSchema, memorySourceTypeSchema, memoryStatusSchema, runtimeMemoryOriginAgentSchema, runtimeMemorySchema, recurringScheduleKindSchema, recurringScheduleSchema, recurringRunStatusSchema, recurringRunTriggerSchema, recurringAgentRunSchema, recurringAgentSchema, recurringAgentCreateRequestSchema, recurringAgentUpdateRequestSchema, projectFolderSchema, topLevelItemSchema, projectsLayoutSchema, runtimeProjectSchema;
|
|
37
37
|
var init_api_contract = __esm({
|
|
38
38
|
"src/core/api-contract.ts"() {
|
|
39
39
|
"use strict";
|
|
40
|
-
|
|
40
|
+
ASSISTANT_AGENT_PREFIX = "__assistant__:";
|
|
41
|
+
runtimeAgentIdSchema = z.enum(["claude", "codex", "opencode", "cursor", "mimo"]);
|
|
41
42
|
effortLevelSchema = z.enum(["low", "medium", "high", "xhigh", "max"]);
|
|
42
43
|
agentModelChoiceSchema = z.object({
|
|
43
44
|
agentId: runtimeAgentIdSchema.default("claude"),
|
|
@@ -1661,7 +1662,8 @@ var RECURRING_OBSERVER_TOOLS = /* @__PURE__ */ new Set([
|
|
|
1661
1662
|
"kanban_get_system_prompt",
|
|
1662
1663
|
"whipped_search_memory",
|
|
1663
1664
|
"whipped_get_memory",
|
|
1664
|
-
"update_journal"
|
|
1665
|
+
"update_journal",
|
|
1666
|
+
"disable_self"
|
|
1665
1667
|
]);
|
|
1666
1668
|
var baseRegisterTool = server.registerTool;
|
|
1667
1669
|
var registerTool = ((name, ...rest) => {
|
|
@@ -2134,7 +2136,7 @@ registerTool(
|
|
|
2134
2136
|
id: z2.string().describe("Unique pair ID within this slot"),
|
|
2135
2137
|
level: z2.enum(["minimal", "low", "medium", "high", "max"]).describe("Capability level. The card's active level selects which pair runs."),
|
|
2136
2138
|
isFree: z2.boolean().describe("Whether this pair uses a zero-cost model"),
|
|
2137
|
-
binary: z2.enum(["claude", "codex", "opencode", "cursor"]).describe("Agent binary: claude / codex / opencode / cursor."),
|
|
2139
|
+
binary: z2.enum(["claude", "codex", "opencode", "cursor", "mimo"]).describe("Agent binary: claude / codex / opencode / cursor / mimo."),
|
|
2138
2140
|
model: z2.string().nullable().optional().describe("Model override, or null for the agent default (e.g. 'claude-opus-4-6', 'gpt-5.5')."),
|
|
2139
2141
|
effort: z2.enum(["low", "medium", "high", "xhigh", "max"]).nullable().optional().describe("Reasoning effort override, or null for the agent default.")
|
|
2140
2142
|
})
|
|
@@ -2322,7 +2324,8 @@ registerTool(
|
|
|
2322
2324
|
}
|
|
2323
2325
|
);
|
|
2324
2326
|
var agentSlot = process.env.WHIPPED_SLOT ?? "";
|
|
2325
|
-
var
|
|
2327
|
+
var hookTaskId = process.env.WHIPPED_HOOK_TASK_ID ?? "";
|
|
2328
|
+
var memoryCardId = hookTaskId.startsWith(ASSISTANT_AGENT_PREFIX) ? "" : hookTaskId;
|
|
2326
2329
|
var memoryModel = process.env.WHIPPED_MODEL ?? "";
|
|
2327
2330
|
registerTool(
|
|
2328
2331
|
"whipped_search_memory",
|
|
@@ -2441,7 +2444,7 @@ var scheduleShape = {
|
|
|
2441
2444
|
intervalSeconds: z2.number().int().positive().optional().describe("Required when scheduleKind=interval (e.g. 3600 = hourly)"),
|
|
2442
2445
|
cronExpr: z2.string().optional().describe("Required when scheduleKind=calendar, e.g. '0 9 * * 1' = every Monday 09:00"),
|
|
2443
2446
|
timezone: z2.string().optional().describe("IANA timezone, required when scheduleKind=calendar (e.g. 'Asia/Yangon')"),
|
|
2444
|
-
agentBinary: z2.enum(["claude", "codex", "opencode", "cursor"]).optional().describe("Which agent runs this; defaults to claude"),
|
|
2447
|
+
agentBinary: z2.enum(["claude", "codex", "opencode", "cursor", "mimo"]).optional().describe("Which agent runs this; defaults to claude"),
|
|
2445
2448
|
model: z2.string().optional().describe("Model id, e.g. 'claude-opus-4-8' or 'gpt-5.5'"),
|
|
2446
2449
|
effort: z2.enum(["low", "medium", "high", "xhigh", "max"]).optional(),
|
|
2447
2450
|
enabled: z2.boolean().optional()
|
|
@@ -2547,6 +2550,21 @@ if (mcpRole === "recurring" && recurringAgentId) {
|
|
|
2547
2550
|
}
|
|
2548
2551
|
}
|
|
2549
2552
|
);
|
|
2553
|
+
registerTool(
|
|
2554
|
+
"disable_self",
|
|
2555
|
+
{
|
|
2556
|
+
description: "Disable yourself so you stop running on schedule. Use this once your assigned task is complete and there is nothing left to watch for \u2014 e.g. a one-off job that has finished, or a condition you were waiting on that is now resolved. This only pauses future runs; the user can re-enable you later. It does not affect the current run, which finishes normally.",
|
|
2557
|
+
inputSchema: {}
|
|
2558
|
+
},
|
|
2559
|
+
async () => {
|
|
2560
|
+
try {
|
|
2561
|
+
await apiMutate("recurring.update", { id: recurringAgentId, enabled: false });
|
|
2562
|
+
return { content: [{ type: "text", text: "Disabled. No further scheduled runs until re-enabled." }] };
|
|
2563
|
+
} catch (err) {
|
|
2564
|
+
return { content: [{ type: "text", text: `Disable failed: ${err.message}` }] };
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
);
|
|
2550
2568
|
}
|
|
2551
2569
|
var transport = new StdioServerTransport();
|
|
2552
2570
|
await server.connect(transport);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
-- =====================================================================
|
|
2
|
+
-- 013 add 'mimo' to the agent_id CHECK constraint
|
|
3
|
+
--
|
|
4
|
+
-- mimo (mimocode) is a new supported agent binary. The agent_id columns on
|
|
5
|
+
-- `cards` and `terminal_sessions` carry a CHECK constraint that only allowed
|
|
6
|
+
-- claude/codex/opencode/cursor, so persisting a mimo card or terminal session
|
|
7
|
+
-- failed with "CHECK constraint failed: agent_id IN (...)".
|
|
8
|
+
--
|
|
9
|
+
-- SQLite can't alter a CHECK constraint in place, so each table is rebuilt with
|
|
10
|
+
-- the widened constraint (the standard recreate-and-copy procedure, as in 003).
|
|
11
|
+
-- The migration runner disables foreign_keys for the duration of migrations, so
|
|
12
|
+
-- dropping the `cards` parent here does NOT cascade-delete its many child tables
|
|
13
|
+
-- (activity_log, review_comments, terminal_sessions, card_subtasks, …).
|
|
14
|
+
-- =====================================================================
|
|
15
|
+
|
|
16
|
+
-- ── cards: rebuild with widened agent_id CHECK ──────────────────────────────
|
|
17
|
+
CREATE TABLE cards_new (
|
|
18
|
+
id TEXT PRIMARY KEY,
|
|
19
|
+
workspace_id TEXT NOT NULL,
|
|
20
|
+
description TEXT NOT NULL,
|
|
21
|
+
description_attachments_json TEXT NOT NULL DEFAULT '[]',
|
|
22
|
+
column_id TEXT NOT NULL CHECK (column_id IN (
|
|
23
|
+
'todo','in_progress','reopened',
|
|
24
|
+
'ready_for_review','blocked','done'
|
|
25
|
+
)),
|
|
26
|
+
column_position INTEGER NOT NULL,
|
|
27
|
+
type TEXT NOT NULL DEFAULT 'task' CHECK (type IN ('task','story','subtask')),
|
|
28
|
+
ready_for_dev INTEGER NOT NULL DEFAULT 0,
|
|
29
|
+
agent_id TEXT CHECK (agent_id IN ('claude','codex','opencode','cursor','mimo')),
|
|
30
|
+
priority TEXT CHECK (priority IN ('urgent','high','medium','low')),
|
|
31
|
+
auto_fix_attempts INTEGER NOT NULL DEFAULT 0,
|
|
32
|
+
base_ref TEXT NOT NULL,
|
|
33
|
+
workflow_id TEXT,
|
|
34
|
+
github_issue_url TEXT,
|
|
35
|
+
pr_json TEXT,
|
|
36
|
+
jira_key TEXT,
|
|
37
|
+
jira_url TEXT,
|
|
38
|
+
github_comment_ids_json TEXT NOT NULL DEFAULT '[]',
|
|
39
|
+
worktree_path TEXT,
|
|
40
|
+
branch_name TEXT,
|
|
41
|
+
slack_message_ts TEXT,
|
|
42
|
+
slack_channel_id TEXT,
|
|
43
|
+
created_at INTEGER NOT NULL,
|
|
44
|
+
updated_at INTEGER NOT NULL,
|
|
45
|
+
depends_on_id TEXT REFERENCES cards(id) ON DELETE SET NULL,
|
|
46
|
+
plan TEXT,
|
|
47
|
+
active_level TEXT NOT NULL DEFAULT 'medium',
|
|
48
|
+
model_config_json TEXT,
|
|
49
|
+
FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
INSERT INTO cards_new (
|
|
53
|
+
id, workspace_id, description, description_attachments_json, column_id, column_position,
|
|
54
|
+
type, ready_for_dev, agent_id, priority, auto_fix_attempts, base_ref, workflow_id,
|
|
55
|
+
github_issue_url, pr_json, jira_key, jira_url, github_comment_ids_json, worktree_path,
|
|
56
|
+
branch_name, slack_message_ts, slack_channel_id, created_at, updated_at, depends_on_id,
|
|
57
|
+
plan, active_level, model_config_json
|
|
58
|
+
)
|
|
59
|
+
SELECT
|
|
60
|
+
id, workspace_id, description, description_attachments_json, column_id, column_position,
|
|
61
|
+
type, ready_for_dev, agent_id, priority, auto_fix_attempts, base_ref, workflow_id,
|
|
62
|
+
github_issue_url, pr_json, jira_key, jira_url, github_comment_ids_json, worktree_path,
|
|
63
|
+
branch_name, slack_message_ts, slack_channel_id, created_at, updated_at, depends_on_id,
|
|
64
|
+
plan, active_level, model_config_json
|
|
65
|
+
FROM cards;
|
|
66
|
+
|
|
67
|
+
DROP INDEX IF EXISTS idx_cards_workspace_column;
|
|
68
|
+
DROP INDEX IF EXISTS idx_cards_workflow;
|
|
69
|
+
DROP TABLE cards;
|
|
70
|
+
ALTER TABLE cards_new RENAME TO cards;
|
|
71
|
+
CREATE INDEX idx_cards_workspace_column ON cards(workspace_id, column_id, column_position);
|
|
72
|
+
CREATE INDEX idx_cards_workflow ON cards(workflow_id);
|
|
73
|
+
|
|
74
|
+
-- ── terminal_sessions: rebuild with widened agent_id CHECK ──────────────────
|
|
75
|
+
CREATE TABLE terminal_sessions_new (
|
|
76
|
+
card_id TEXT NOT NULL,
|
|
77
|
+
stream_id TEXT NOT NULL,
|
|
78
|
+
type TEXT NOT NULL,
|
|
79
|
+
started_at INTEGER NOT NULL,
|
|
80
|
+
ended_at INTEGER,
|
|
81
|
+
agent_id TEXT CHECK (agent_id IN ('claude','codex','opencode','cursor','mimo')),
|
|
82
|
+
state TEXT CHECK (state IN ('running','stopped','completed','failed','killed')),
|
|
83
|
+
PRIMARY KEY (card_id, stream_id),
|
|
84
|
+
FOREIGN KEY (card_id) REFERENCES cards(id) ON DELETE CASCADE
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
INSERT INTO terminal_sessions_new (card_id, stream_id, type, started_at, ended_at, agent_id, state)
|
|
88
|
+
SELECT card_id, stream_id, type, started_at, ended_at, agent_id, state FROM terminal_sessions;
|
|
89
|
+
|
|
90
|
+
DROP INDEX IF EXISTS idx_terminal_sessions_card;
|
|
91
|
+
DROP TABLE terminal_sessions;
|
|
92
|
+
ALTER TABLE terminal_sessions_new RENAME TO terminal_sessions;
|
|
93
|
+
CREATE INDEX idx_terminal_sessions_card ON terminal_sessions(card_id);
|
|
@@ -5115,6 +5115,10 @@
|
|
|
5115
5115
|
border-color: oklab(70.4871% .125896 .137895 / .25);
|
|
5116
5116
|
}
|
|
5117
5117
|
|
|
5118
|
+
.border-\[\#fb8147\]\/25 {
|
|
5119
|
+
border-color: oklab(73.1647% .118121 .115767 / .25);
|
|
5120
|
+
}
|
|
5121
|
+
|
|
5118
5122
|
.border-\[var\(--color-border-secondary\)\] {
|
|
5119
5123
|
border-color: var(--color-border-secondary);
|
|
5120
5124
|
}
|
|
@@ -5509,6 +5513,14 @@
|
|
|
5509
5513
|
background-color: oklab(70.4871% .125896 .137895 / .1);
|
|
5510
5514
|
}
|
|
5511
5515
|
|
|
5516
|
+
.bg-\[\#fb8147\] {
|
|
5517
|
+
background-color: #fb8147;
|
|
5518
|
+
}
|
|
5519
|
+
|
|
5520
|
+
.bg-\[\#fb8147\]\/10 {
|
|
5521
|
+
background-color: oklab(73.1647% .118121 .115767 / .1);
|
|
5522
|
+
}
|
|
5523
|
+
|
|
5512
5524
|
.bg-amber-400\/10 {
|
|
5513
5525
|
background-color: #fcbb001a;
|
|
5514
5526
|
}
|
|
@@ -6472,6 +6484,10 @@
|
|
|
6472
6484
|
color: #facc15;
|
|
6473
6485
|
}
|
|
6474
6486
|
|
|
6487
|
+
.text-\[\#fb8147\] {
|
|
6488
|
+
color: #fb8147;
|
|
6489
|
+
}
|
|
6490
|
+
|
|
6475
6491
|
.text-\[\#ffd0d2\] {
|
|
6476
6492
|
color: #ffd0d2;
|
|
6477
6493
|
}
|
|
@@ -27169,12 +27169,13 @@ const ZodIssueCode = {
|
|
|
27169
27169
|
function number$1(params) {
|
|
27170
27170
|
return /* @__PURE__ */ _coercedNumber(ZodNumber, params);
|
|
27171
27171
|
}
|
|
27172
|
-
const runtimeAgentIdSchema = _enum(["claude", "codex", "opencode", "cursor"]);
|
|
27172
|
+
const runtimeAgentIdSchema = _enum(["claude", "codex", "opencode", "cursor", "mimo"]);
|
|
27173
27173
|
const AGENT_BINARY_OPTIONS = [
|
|
27174
27174
|
{ value: "claude", label: "claude" },
|
|
27175
27175
|
{ value: "codex", label: "codex" },
|
|
27176
27176
|
{ value: "opencode", label: "opencode" },
|
|
27177
|
-
{ value: "cursor", label: "cursor" }
|
|
27177
|
+
{ value: "cursor", label: "cursor" },
|
|
27178
|
+
{ value: "mimo", label: "mimo" }
|
|
27178
27179
|
];
|
|
27179
27180
|
const effortLevelSchema = _enum(["low", "medium", "high", "xhigh", "max"]);
|
|
27180
27181
|
const EFFORT_OPTIONS = [
|
|
@@ -27206,7 +27207,10 @@ const MODEL_OPTIONS = {
|
|
|
27206
27207
|
opencode: [],
|
|
27207
27208
|
// cursor supports many models — no fixed presets.
|
|
27208
27209
|
// The UI fetches the live list via agents.cursorModels and renders a Select.
|
|
27209
|
-
cursor: []
|
|
27210
|
+
cursor: [],
|
|
27211
|
+
// mimo (mimocode) lists models as provider/model strings, like opencode —
|
|
27212
|
+
// no fixed presets; the UI fetches the live list and renders a Select.
|
|
27213
|
+
mimo: []
|
|
27210
27214
|
};
|
|
27211
27215
|
const agentModelChoiceSchema = object({
|
|
27212
27216
|
agentId: runtimeAgentIdSchema.default("claude"),
|
|
@@ -31758,10 +31762,10 @@ function ModelSelect({
|
|
|
31758
31762
|
menuClassName
|
|
31759
31763
|
}) {
|
|
31760
31764
|
const staticOptions = MODEL_OPTIONS[agentId];
|
|
31761
|
-
const isDynamic = agentId === "opencode" || agentId === "cursor";
|
|
31765
|
+
const isDynamic = agentId === "opencode" || agentId === "cursor" || agentId === "mimo";
|
|
31762
31766
|
const modelsRead = useRead(
|
|
31763
31767
|
(api) => api("agents/models").GET({
|
|
31764
|
-
query: { agent: agentId === "cursor" ? "cursor" : "opencode" }
|
|
31768
|
+
query: { agent: agentId === "cursor" ? "cursor" : agentId === "mimo" ? "mimo" : "opencode" }
|
|
31765
31769
|
}),
|
|
31766
31770
|
{ enabled: isDynamic }
|
|
31767
31771
|
);
|
|
@@ -31829,7 +31833,7 @@ function ModelSelect({
|
|
|
31829
31833
|
{
|
|
31830
31834
|
value,
|
|
31831
31835
|
onChange: (e) => onChange(e.target.value),
|
|
31832
|
-
placeholder: agentId === "opencode" ? "e.g. anthropic/claude-opus-4-7" : agentId === "cursor" ? "e.g. claude-opus-4-7-thinking-max" : agentId === "claude" ? "e.g. claude-opus-4-7" : "e.g. gpt-5-codex"
|
|
31836
|
+
placeholder: agentId === "opencode" || agentId === "mimo" ? "e.g. anthropic/claude-opus-4-7" : agentId === "cursor" ? "e.g. claude-opus-4-7-thinking-max" : agentId === "claude" ? "e.g. claude-opus-4-7" : "e.g. gpt-5-codex"
|
|
31833
31837
|
}
|
|
31834
31838
|
)
|
|
31835
31839
|
] });
|
|
@@ -41299,7 +41303,8 @@ function RunBar({ workspaceId }) {
|
|
|
41299
41303
|
claude: { dot: "bg-[#7c6aff]", text: "text-[#7c6aff]", bg: "bg-[#7c6aff]/10" },
|
|
41300
41304
|
codex: { dot: "bg-[#22c55e]", text: "text-[#22c55e]", bg: "bg-[#22c55e]/10" },
|
|
41301
41305
|
cursor: { dot: "bg-[#3b82f6]", text: "text-[#3b82f6]", bg: "bg-[#3b82f6]/10" },
|
|
41302
|
-
opencode: { dot: "bg-[#f97316]", text: "text-[#f97316]", bg: "bg-[#f97316]/10" }
|
|
41306
|
+
opencode: { dot: "bg-[#f97316]", text: "text-[#f97316]", bg: "bg-[#f97316]/10" },
|
|
41307
|
+
mimo: { dot: "bg-[#fb8147]", text: "text-[#fb8147]", bg: "bg-[#fb8147]/10" }
|
|
41303
41308
|
};
|
|
41304
41309
|
const ac2 = colors[cardAgentId] ?? { dot: "bg-gray-500", text: "text-gray-400", bg: "bg-gray-500/10" };
|
|
41305
41310
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
@@ -50067,6 +50072,13 @@ const AGENT_DISPLAY = {
|
|
|
50067
50072
|
bg: "bg-[#f97316]/10",
|
|
50068
50073
|
border: "border-[#f97316]/25",
|
|
50069
50074
|
dotColor: "bg-[#f97316]"
|
|
50075
|
+
},
|
|
50076
|
+
mimo: {
|
|
50077
|
+
label: "MiMo",
|
|
50078
|
+
color: "text-[#fb8147]",
|
|
50079
|
+
bg: "bg-[#fb8147]/10",
|
|
50080
|
+
border: "border-[#fb8147]/25",
|
|
50081
|
+
dotColor: "bg-[#fb8147]"
|
|
50070
50082
|
}
|
|
50071
50083
|
};
|
|
50072
50084
|
function formatElapsed(sec) {
|
|
@@ -50987,7 +50999,13 @@ function CommentComposer({ card, workspaceId, onRefresh }) {
|
|
|
50987
50999
|
const atts = uploaded.length > 0 ? uploaded : void 0;
|
|
50988
51000
|
if (requestChanges) {
|
|
50989
51001
|
await submitHumanFeedbackTrigger({
|
|
50990
|
-
body: {
|
|
51002
|
+
body: {
|
|
51003
|
+
workspaceId,
|
|
51004
|
+
cardId: card.id,
|
|
51005
|
+
comment: summaryText || void 0,
|
|
51006
|
+
attachments: atts,
|
|
51007
|
+
...visualComment ? { type: "visual-comment", metadata: { visualComment } } : {}
|
|
51008
|
+
}
|
|
50991
51009
|
});
|
|
50992
51010
|
} else {
|
|
50993
51011
|
await addReviewCommentTrigger({
|
|
@@ -75916,7 +75934,8 @@ const AGENT_COLORS = {
|
|
|
75916
75934
|
claude: { dot: "bg-[#7c6aff]", text: "text-[#7c6aff]", bg: "bg-[#7c6aff]/10" },
|
|
75917
75935
|
codex: { dot: "bg-[#22c55e]", text: "text-[#22c55e]", bg: "bg-[#22c55e]/10" },
|
|
75918
75936
|
cursor: { dot: "bg-[#3b82f6]", text: "text-[#3b82f6]", bg: "bg-[#3b82f6]/10" },
|
|
75919
|
-
opencode: { dot: "bg-[#f97316]", text: "text-[#f97316]", bg: "bg-[#f97316]/10" }
|
|
75937
|
+
opencode: { dot: "bg-[#f97316]", text: "text-[#f97316]", bg: "bg-[#f97316]/10" },
|
|
75938
|
+
mimo: { dot: "bg-[#fb8147]", text: "text-[#fb8147]", bg: "bg-[#fb8147]/10" }
|
|
75920
75939
|
};
|
|
75921
75940
|
const PRIORITY_STYLES = {
|
|
75922
75941
|
urgent: "text-red-400 bg-red-400/10",
|
package/dist/web-ui/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
|
7
7
|
<title>whipped</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-C-dcfzmG.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-C-IzbCaE.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|