whipped 0.3.0 → 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 CHANGED
@@ -3529,10 +3529,11 @@ 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
+ ASSISTANT_AGENT_PREFIX = "__assistant__:";
3536
3537
  runtimeAgentIdSchema = z.enum(["claude", "codex", "opencode", "cursor", "mimo"]);
3537
3538
  effortLevelSchema = z.enum(["low", "medium", "high", "xhigh", "max"]);
3538
3539
  agentModelChoiceSchema = z.object({
@@ -14307,6 +14308,7 @@ init_logger();
14307
14308
  init_task_id();
14308
14309
 
14309
14310
  // src/daemon/poller.ts
14311
+ init_runtime_config();
14310
14312
  init_logger();
14311
14313
  init_task_id();
14312
14314
  import { spawnSync as spawnSync3 } from "node:child_process";
@@ -18655,6 +18657,8 @@ var BoardPoller = class {
18655
18657
  async poll() {
18656
18658
  const { workspaceId, repoPath, scheduler, stateHub, onCardReadyForReview } = this.options;
18657
18659
  const state = await loadWorkspaceState(workspaceId, repoPath);
18660
+ const effectiveLimit = state.projectConfig.maxParallelTasks ?? (await loadGlobalConfig()).maxParallelTasks;
18661
+ scheduler.setMaxParallelTasks(effectiveLimit);
18658
18662
  const board = state.board;
18659
18663
  const pendingCards = [];
18660
18664
  const todoColumn = board.columns.find((c) => c.id === "todo");
@@ -19074,6 +19078,7 @@ function buildAgentArgs(agentId, prompt, ctx = {}) {
19074
19078
  case "mimo": {
19075
19079
  if (mode === "print") {
19076
19080
  const args2 = ["run", "--agent", "build"];
19081
+ if (agentId === "mimo") args2.push("--never-ask", "--trust");
19077
19082
  if (ctx.model) args2.push("-m", ctx.model);
19078
19083
  if (ctx.effort) {
19079
19084
  const OPENCODE_EFFORT_VARIANT = {
@@ -19089,6 +19094,7 @@ function buildAgentArgs(agentId, prompt, ctx = {}) {
19089
19094
  return args2;
19090
19095
  }
19091
19096
  const args = ["--agent", "build"];
19097
+ if (agentId === "mimo") args.push("--never-ask", "--trust");
19092
19098
  if (ctx.model) args.push("-m", ctx.model);
19093
19099
  if (prompt.trim()) args.push("--prompt", prompt);
19094
19100
  return args;
@@ -20010,38 +20016,6 @@ function markRecurringRan(recurringAgentId) {
20010
20016
  getDb().prepare("UPDATE recurring_agents SET last_run_at = ?, next_run_at = ? WHERE id = ?").run(now, nextRunAt, recurringAgentId);
20011
20017
  }
20012
20018
 
20013
- // src/daemon/recurring-agent-scheduler.ts
20014
- init_workspace_state();
20015
- init_workspace_state();
20016
-
20017
- // src/daemon/scheduler.ts
20018
- import { spawn as spawn5 } from "node:child_process";
20019
- import { cpSync, existsSync as existsSync10, mkdirSync as mkdirSync9 } from "node:fs";
20020
- import { unlink as unlink2 } from "node:fs/promises";
20021
- import { dirname as dirname5, join as join16, resolve as resolve2 } from "node:path";
20022
- import { fileURLToPath as fileURLToPath3 } from "node:url";
20023
- init_api_contract();
20024
- init_logger();
20025
-
20026
- // src/core/prompt-resolver.ts
20027
- init_logger();
20028
- import { readFileSync as readFileSync5 } from "node:fs";
20029
- import { isAbsolute, join as join14 } from "node:path";
20030
- function resolvePromptText(prompt, repoPath) {
20031
- if (!prompt) return "";
20032
- if (prompt.source === "inline") return prompt.text;
20033
- const path2 = isAbsolute(prompt.path) ? prompt.path : join14(repoPath, prompt.path);
20034
- try {
20035
- return readFileSync5(path2, "utf-8");
20036
- } catch (err) {
20037
- logger.warn({ err: err.message, path: path2 }, "Slot prompt file unreadable \u2014 falling back to empty prompt");
20038
- return "";
20039
- }
20040
- }
20041
-
20042
- // src/daemon/scheduler.ts
20043
- init_task_id();
20044
-
20045
20019
  // src/state/memory-store.ts
20046
20020
  init_api_contract();
20047
20021
  init_task_id();
@@ -20159,6 +20133,7 @@ function createMemory(input) {
20159
20133
  throw new Error("global-scoped memory requires at least one tag");
20160
20134
  }
20161
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;
20162
20137
  const tx = db.transaction(() => {
20163
20138
  db.prepare(
20164
20139
  `INSERT INTO memories (
@@ -20175,7 +20150,7 @@ function createMemory(input) {
20175
20150
  input.content,
20176
20151
  input.sourceType,
20177
20152
  input.importance ?? 1,
20178
- input.originCardId ?? null,
20153
+ originCardId,
20179
20154
  input.originAgent ? JSON.stringify(input.originAgent) : null,
20180
20155
  input.status ?? "approved",
20181
20156
  now,
@@ -20299,7 +20274,7 @@ function searchMemories(query, workspaceId, limit = 20) {
20299
20274
  ).all({ q: ftsQuery, ws: workspaceId, limit });
20300
20275
  return hydrate(rows.map(rowToMemory));
20301
20276
  }
20302
- function buildMemoryContext(workspaceId, memoryLimit = 40) {
20277
+ function buildMemoryContext(workspaceId, memoryLimit = 40, opts) {
20303
20278
  const sections = [];
20304
20279
  const fmt = (m2) => {
20305
20280
  const tagSuffix = m2.tags.length > 0 ? ` _(tags: ${m2.tags.join(", ")})_` : "";
@@ -20317,18 +20292,50 @@ ${projectMem.map(fmt).join("\n")}`);
20317
20292
  ${globalMem.map(fmt).join("\n")}`);
20318
20293
  }
20319
20294
  if (sections.length === 0) return "";
20295
+ const readOnly = opts?.readOnly ?? false;
20320
20296
  const knownTags = listTags();
20321
- const tagLine = knownTags.length > 0 ? `
20297
+ const tagLine = !readOnly && knownTags.length > 0 ? `
20322
20298
 
20323
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.";
20324
20301
  return [
20325
20302
  "## Memory",
20326
- `This is whipped's persistent project memory \u2014 durable knowledge from past work. Each entry is prefixed with its id. Use \`whipped_search_memory\` / \`whipped_get_memory\` to recall more, and \`whipped_update_memory\` to correct an entry that's now wrong. Treat these as hints, not gospel: if a memory references a file, symbol, or rule, verify it still holds before relying on it.${tagLine}`,
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}`,
20327
20304
  ...sections
20328
20305
  ].join("\n\n");
20329
20306
  }
20330
20307
 
20308
+ // src/daemon/recurring-agent-scheduler.ts
20309
+ init_workspace_state();
20310
+ init_workspace_state();
20311
+
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
+
20331
20337
  // src/daemon/scheduler.ts
20338
+ init_task_id();
20332
20339
  init_workspace_state();
20333
20340
 
20334
20341
  // src/daemon/review-pipeline.ts
@@ -21704,7 +21711,6 @@ After handling all children, call \`kanban_add_comment\` on the PARENT card (${p
21704
21711
  // src/daemon/scheduler.ts
21705
21712
  var FAST_EXIT_THRESHOLD_MS = 8e3;
21706
21713
  var MAX_RECENT_BUFFERS = 100;
21707
- var ASSISTANT_AGENT_PREFIX = "__assistant__:";
21708
21714
  var TaskScheduler = class {
21709
21715
  constructor(options) {
21710
21716
  this.options = options;
@@ -21872,6 +21878,11 @@ ${assistantSystemPrompt}` : assistantSystemPrompt;
21872
21878
  get maxParallelTasks() {
21873
21879
  return this.options.maxParallelTasks;
21874
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
+ }
21875
21886
  canAcceptTask(inFlightCount) {
21876
21887
  const count = inFlightCount ?? this.running.size;
21877
21888
  return count < this.options.maxParallelTasks;
@@ -22868,7 +22879,7 @@ var RecurringAgentScheduler = class {
22868
22879
  } catch (err) {
22869
22880
  logger.warn({ err, agentId: agent.id }, "[recurring] failed to load project config");
22870
22881
  }
22871
- const appendSystemPrompt = buildRecurringSystemPrompt(
22882
+ const baseSystemPrompt = buildRecurringSystemPrompt(
22872
22883
  repoPath,
22873
22884
  agent.name,
22874
22885
  agent.instructions,
@@ -22876,6 +22887,10 @@ var RecurringAgentScheduler = class {
22876
22887
  secrets,
22877
22888
  projectSystemPrompt
22878
22889
  );
22890
+ const memContext = buildMemoryContext(workspaceId, 40, { readOnly: true });
22891
+ const appendSystemPrompt = memContext ? `${memContext}
22892
+
22893
+ ${baseSystemPrompt}` : baseSystemPrompt;
22879
22894
  const prompt = buildRecurringPrompt();
22880
22895
  if (agentBinary === "claude" && mcpConfigPath) {
22881
22896
  await writeClaudeMcpConfig(
@@ -23006,8 +23021,10 @@ ${journalBlock}
23006
23021
 
23007
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.
23008
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
+
23009
23026
  ## Available tools
23010
- \`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).`;
23011
23028
  const secretsSection = buildSecretsSection(secrets);
23012
23029
  if (secretsSection) prompt += `
23013
23030
 
@@ -26026,21 +26043,22 @@ var deleteReviewCommentService = async (input) => {
26026
26043
  await updateCard(input.workspaceId, input.cardId, { reviewComments: next });
26027
26044
  return { ok: true };
26028
26045
  };
26029
- var submitHumanFeedbackService = async (workspaceId, cardId, comment, attachments) => {
26046
+ var submitHumanFeedbackService = async (workspaceId, cardId, comment, attachments, type, metadata) => {
26030
26047
  const board = await loadBoard(workspaceId);
26031
26048
  const card = board.cards[cardId];
26032
26049
  if (!card) throw NotFoundError("Card");
26033
26050
  const trimmed = comment?.trim();
26034
- const hasContent = trimmed || (attachments?.length ?? 0) > 0;
26051
+ const hasContent = trimmed || (attachments?.length ?? 0) > 0 || metadata != null;
26035
26052
  const updatedComments = hasContent ? [
26036
26053
  ...card.reviewComments ?? [],
26037
26054
  {
26038
26055
  id: generateTaskId(),
26039
- type: "human",
26056
+ type: type ?? "human",
26040
26057
  actor: { type: "human", id: "human" },
26041
26058
  createdAt: Date.now(),
26042
26059
  summary: trimmed ?? "Feedback with attachments",
26043
- attachments: attachments?.length ? attachments : void 0
26060
+ attachments: attachments?.length ? attachments : void 0,
26061
+ ...metadata ? { metadata } : {}
26044
26062
  }
26045
26063
  ] : card.reviewComments ?? [];
26046
26064
  await updateCard(workspaceId, cardId, { reviewComments: updatedComments, autoFixAttempts: 0 });
@@ -26353,13 +26371,15 @@ var cardsController = new Hono2().post("/", zv("json", runtimeCardCreateRequestS
26353
26371
  workspaceId: z5.string(),
26354
26372
  cardId: z5.string(),
26355
26373
  comment: z5.string().optional(),
26356
- 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()
26357
26377
  })
26358
26378
  ),
26359
26379
  async (c) => {
26360
26380
  const ctx = c.var.ctx;
26361
- const { workspaceId, cardId, comment, attachments } = c.req.valid("json");
26362
- 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);
26363
26383
  ctx.stateHub.broadcastWorkspaceUpdate(workspaceId);
26364
26384
  if (result.reopenCascade) {
26365
26385
  const scheduler = ctx.getScheduler(workspaceId);
@@ -28618,7 +28638,7 @@ process.on("uncaughtException", (err) => {
28618
28638
  if (err.code === "EPIPE" || err.code === "ECONNRESET") return;
28619
28639
  throw err;
28620
28640
  });
28621
- var VERSION9 = true ? "0.3.0" : "0.0.0-dev";
28641
+ var VERSION9 = true ? "0.4.0" : "0.0.0-dev";
28622
28642
  async function isPortAvailable(port, host) {
28623
28643
  return new Promise((resolve5) => {
28624
28644
  const probe = createServer2();
@@ -33,10 +33,11 @@ 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
+ ASSISTANT_AGENT_PREFIX = "__assistant__:";
40
41
  runtimeAgentIdSchema = z.enum(["claude", "codex", "opencode", "cursor", "mimo"]);
41
42
  effortLevelSchema = z.enum(["low", "medium", "high", "xhigh", "max"]);
42
43
  agentModelChoiceSchema = z.object({
@@ -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) => {
@@ -2322,7 +2324,8 @@ registerTool(
2322
2324
  }
2323
2325
  );
2324
2326
  var agentSlot = process.env.WHIPPED_SLOT ?? "";
2325
- var memoryCardId = process.env.WHIPPED_HOOK_TASK_ID ?? "";
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",
@@ -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);
@@ -50999,7 +50999,13 @@ function CommentComposer({ card, workspaceId, onRefresh }) {
50999
50999
  const atts = uploaded.length > 0 ? uploaded : void 0;
51000
51000
  if (requestChanges) {
51001
51001
  await submitHumanFeedbackTrigger({
51002
- body: { workspaceId, cardId: card.id, comment: summaryText || void 0, attachments: atts }
51002
+ body: {
51003
+ workspaceId,
51004
+ cardId: card.id,
51005
+ comment: summaryText || void 0,
51006
+ attachments: atts,
51007
+ ...visualComment ? { type: "visual-comment", metadata: { visualComment } } : {}
51008
+ }
51003
51009
  });
51004
51010
  } else {
51005
51011
  await addReviewCommentTrigger({
@@ -5,7 +5,7 @@
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-DG3DMON0.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-C-dcfzmG.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/assets/index-C-IzbCaE.css">
10
10
  </head>
11
11
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whipped",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Autonomous AI agent kanban board for Claude, Codex, Opencode, and Cursor.",
5
5
  "type": "module",
6
6
  "files": [