Compare commits

...

1 Commits

Author SHA1 Message Date
Nate
f480ca0dfc feat: Add CLI support for agent notepads (feature-flagged)
- Add --beta-notepad CLI flag with global config
- Conditionally inject workflow notepad into system prompt
- Add WorkflowNotepad tool for updating notepad content
- Both features gated behind --beta-notepad flag + WORKFLOW_ID

This complements the backend changes in remote-config-server.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-23 14:31:19 -08:00
8 changed files with 108 additions and 1 deletions

View File

@@ -27,6 +27,8 @@ export interface BaseCommandOptions {
betaUploadArtifactTool?: boolean;
/** Enable beta Subagent tool */
betaSubagentTool?: boolean;
/** Enable beta Notepad feature */
betaNotepad?: boolean;
}
/**

View File

@@ -1,6 +1,7 @@
import { loadAuthConfig } from "../auth/workos.js";
import { initializeWithOnboarding } from "../onboarding.js";
import {
setBetaNotepadToolEnabled,
setBetaSubagentToolEnabled,
setBetaUploadArtifactToolEnabled,
} from "../tools/toolsConfig.js";
@@ -68,6 +69,9 @@ export async function initializeServices(initOptions: ServiceInitOptions = {}) {
if (commandOptions.betaSubagentTool) {
setBetaSubagentToolEnabled(true);
}
if (commandOptions.betaNotepad) {
setBetaNotepadToolEnabled(true);
}
// Handle onboarding for TUI mode (headless: false) unless explicitly skipped
if (!initOptions.headless && !initOptions.skipOnboarding) {
const authConfig = loadAuthConfig();

View File

@@ -15,6 +15,7 @@ export function addCommonOptions(command: Command): Command {
.option("--auto", "Start in auto mode (all tools allowed)")
.option("--verbose", "Enable verbose logging")
.option("--beta-status-tool", "Enable beta status tool")
.option("--beta-notepad", "Enable beta notepad feature")
.option(
"--rule <rule>",
"Add a rule (can be a file path, hub slug, or string content). Can be specified multiple times.",
@@ -104,6 +105,8 @@ export function mergeParentOptions(parentCommand: Command, options: any): any {
"auto",
"tools",
"verbose",
"betaStatusTool",
"betaNotepad",
"rule",
"mcp",
"model",

View File

@@ -9,6 +9,7 @@ import { processRule } from "./hubLoader.js";
import { PermissionMode } from "./permissions/types.js";
import { serviceContainer } from "./services/ServiceContainer.js";
import { ConfigServiceState, SERVICE_NAMES } from "./services/types.js";
import { isBetaNotepadToolEnabled } from "./tools/toolsConfig.js";
const { WalkerSync } = pkg;
/**
@@ -115,6 +116,26 @@ async function getConfigYamlRules(): Promise<string[]> {
return [];
}
/**
* Fetch workflow notepad content from the API if WORKFLOW_ID is set and beta flag is enabled
*/
async function getWorkflowNotepad(): Promise<string | null> {
const workflowId = process.env.WORKFLOW_ID;
if (!workflowId || !isBetaNotepadToolEnabled()) {
return null;
}
try {
const { get } = await import("./util/apiClient.js");
const response = await get(`workflows/${workflowId}/notepad`);
return response.data?.notepad || null;
} catch (error) {
// Silently fail if API call fails (don't break agent startup)
console.warn("Could not fetch workflow notepad:", error);
return null;
}
}
/**
* Load and construct a comprehensive system message with base message and rules section
* @param additionalRules - Additional rules from --rule flags
@@ -165,9 +186,21 @@ export async function constructSystemMessage(
const configYamlRules = await getConfigYamlRules();
processedRules.push(...configYamlRules);
// Fetch workflow notepad if WORKFLOW_ID is set
const notepad = await getWorkflowNotepad();
// Construct the comprehensive system message
let systemMessage = baseSystemMessage;
// Add workflow notepad context if available
if (notepad) {
systemMessage += `
<context name="workflowNotepad">This is your persistent notepad for this workflow. It contains notes from previous runs. You can update it using the WorkflowNotepad tool.
${notepad}
</context>`;
}
// Add plan mode specific instructions if in plan mode
if (mode === "plan") {
systemMessage +=

View File

@@ -15,6 +15,7 @@ import { uploadArtifactTool } from "./uploadArtifact.js";
import { viewDiffTool } from "./viewDiff.js";
import { writeChecklistTool } from "./writeChecklist.js";
import { writeFileTool } from "./writeFile.js";
import { workflowNotepadTool } from "./workflowNotepad.js";
// putting in here for circular import issue
export const ALL_BUILT_IN_TOOLS = [
@@ -35,3 +36,5 @@ export const ALL_BUILT_IN_TOOLS = [
writeChecklistTool,
writeFileTool,
];
export { workflowNotepadTool };

View File

@@ -18,7 +18,7 @@ import type {
import { telemetryService } from "../telemetry/telemetryService.js";
import { logger } from "../util/logger.js";
import { ALL_BUILT_IN_TOOLS } from "./allBuiltIns.js";
import { ALL_BUILT_IN_TOOLS, workflowNotepadTool } from "./allBuiltIns.js";
import { editTool } from "./edit.js";
import { exitTool } from "./exit.js";
import { fetchTool } from "./fetch.js";
@@ -31,6 +31,7 @@ import { checkIfRipgrepIsInstalled, searchCodeTool } from "./searchCode.js";
import { skillsTool } from "./skills.js";
import { subagentTool } from "./subagent.js";
import {
isBetaNotepadToolEnabled,
isBetaSubagentToolEnabled,
isBetaUploadArtifactToolEnabled,
} from "./toolsConfig.js";
@@ -94,6 +95,11 @@ export async function getAllAvailableTools(
}
}
// Add workflow notepad tool if WORKFLOW_ID is present and beta flag is enabled
if (process.env.WORKFLOW_ID && isBetaNotepadToolEnabled()) {
tools.push(workflowNotepadTool);
}
// If model is capable, exclude editTool in favor of multiEditTool
const modelState = await serviceContainer.get<ModelServiceState>(
SERVICE_NAMES.MODEL,

View File

@@ -5,6 +5,7 @@
let betaUploadArtifactToolEnabled = false;
let betaSubagentToolEnabled = false;
let betaNotepadToolEnabled = false;
export function setBetaUploadArtifactToolEnabled(enabled: boolean): void {
betaUploadArtifactToolEnabled = enabled;
@@ -21,3 +22,11 @@ export function setBetaSubagentToolEnabled(enabled: boolean): void {
export function isBetaSubagentToolEnabled(): boolean {
return betaSubagentToolEnabled;
}
export function setBetaNotepadToolEnabled(enabled: boolean): void {
betaNotepadToolEnabled = enabled;
}
export function isBetaNotepadToolEnabled(): boolean {
return betaNotepadToolEnabled;
}

View File

@@ -0,0 +1,47 @@
import type { Tool } from "./types.js";
function getWorkflowIdFromEnv(): string | undefined {
return process.env.WORKFLOW_ID;
}
export const workflowNotepadTool: Tool = {
name: "WorkflowNotepad",
displayName: "Workflow Notepad",
description:
"Update your persistent notepad for this workflow. The current notepad content is already visible in your system prompt under <context name='workflowNotepad'>. Use this tool to replace the notepad with new content.",
parameters: {
type: "object",
required: ["content"],
properties: {
content: {
type: "string",
description:
"New content to write to notepad (max 100KB). This will replace the entire notepad.",
},
},
},
readonly: false,
isBuiltIn: true,
run: async (args: { content: string }) => {
const workflowId = getWorkflowIdFromEnv();
if (!workflowId) {
throw new Error(
"Workflow notepad only available when running in a workflow",
);
}
if (!args.content) {
throw new Error("content is required");
}
// Import dynamically to avoid circular dependencies
const { put } = await import("../util/apiClient.js");
// PUT /workflows/:workflowId/notepad
await put(`workflows/${workflowId}/notepad`, {
notepad: args.content,
});
return "Notepad updated successfully. Note: The updated content will be visible in the system prompt when you restart or in your next workflow run.";
},
};