feat: implement /loop command with fixed and dynamic scheduling (#621)
* feat: implement /loop command with fixed and dynamic scheduling modes Enable cron tools and /loop skill without the AGENT_TRIGGERS build flag by removing feature guards from tools.ts, REPL.tsx, and skill registration. The isKairosCronEnabled() runtime gate now enables cron unconditionally for open builds while preserving the GrowthBook kill switch for ant builds. The /loop skill supports four modes: fixed-interval with prompt, fixed-interval maintenance, dynamic-prompt (self-pacing), and dynamic maintenance (bare /loop). * chore: remove unused DEFAULT_INTERVAL constant from loop skill * revert: drop infra changes, scope PR to /loop skill rewrite only The cron activation layer (AGENT_TRIGGERS guard removal, isKairosCronEnabled hardcode) is covered by an in-flight stack (#633, #639). Scope this PR to just the loop.ts rewrite and its tests so it can land cleanly on top. * fix: restore infra changes needed for /loop in open build Bun's constant folder evaluates feature('AGENT_TRIGGERS') at bundle time through the bun:bundle shim — even when the flag is flipped to true in build.ts, the folded value is cached from the previous build and stays false. This means the feature-gated require() blocks for cron tools, useScheduledTasks, and loop skill registration all compile to dead code regardless of the flag. Fix by removing the AGENT_TRIGGERS guards from the specific paths /loop needs: - tools.ts: cron tools always registered (isEnabled gates visibility) - REPL.tsx: useScheduledTasks always mounted - index.ts: registerLoopSkill via static import, called unconditionally - prompt.ts: isKairosCronEnabled() bypasses feature flag for non-ant builds * fix: replace backslash line continuations with explicit delimiters in loop prompts The backslash-newline sequences inside template literals were acting as line continuations, collapsing newlines and merging prompt content with surrounding instruction text. Replace with --- BEGIN/END --- markers for unambiguous delimiting. Also add tests for trailing "every" clause parsing, human-readable unit normalization, and the non-interval "check every PR" case. * fix: remove remaining AGENT_TRIGGERS guards from print.ts and constants/tools.ts Completes the cron guard removal started in the previous commit. The cron scheduler in non-interactive (-p) mode was dead because print.ts still gated cronSchedulerModule/cronGate requires behind feature('AGENT_TRIGGERS'), which Bun constant-folds to false in open builds. Similarly, cron tool names were absent from IN_PROCESS_TEAMMATE_ALLOWED_TOOLS. Remove all three guards so the scheduler initialises (gated at runtime by isKairosCronEnabled) and cron tools are allowed for in-process teammates in all builds.
This commit is contained in:
@@ -9,39 +9,35 @@ export const DEFAULT_MAX_AGE_DAYS =
|
||||
DEFAULT_CRON_JITTER_CONFIG.recurringMaxAgeMs / (24 * 60 * 60 * 1000)
|
||||
|
||||
/**
|
||||
* Unified gate for the cron scheduling system. Combines the build-time
|
||||
* `feature('AGENT_TRIGGERS')` flag (dead code elimination) with the runtime
|
||||
* `tengu_kairos_cron` GrowthBook gate on a 5-minute refresh window.
|
||||
* Unified gate for the cron scheduling system.
|
||||
*
|
||||
* AGENT_TRIGGERS is independently shippable from KAIROS — the cron module
|
||||
* graph (cronScheduler/cronTasks/cronTasksLock/cron.ts + the three tools +
|
||||
* /loop skill) has zero imports into src/assistant/ and no feature('KAIROS')
|
||||
* calls. The REPL.tsx kairosEnabled read is safe:
|
||||
* kairosEnabled is unconditionally in AppStateStore with default false, so
|
||||
* when KAIROS is off the scheduler just gets assistantMode: false.
|
||||
* Open builds (USER_TYPE !== 'ant') enable cron unconditionally — the
|
||||
* cron tools and /loop skill are registered without the AGENT_TRIGGERS
|
||||
* build flag, so this gate is the sole runtime switch. Set the env var
|
||||
* `CLAUDE_CODE_DISABLE_CRON=1` to turn it off locally.
|
||||
*
|
||||
* Anthropic-internal (ant) builds additionally consult the
|
||||
* `tengu_kairos_cron` GrowthBook gate on a 5-minute refresh window,
|
||||
* serving as a fleet-wide kill switch.
|
||||
*
|
||||
* Called from Tool.isEnabled() (lazy, post-init) and inside useEffect /
|
||||
* imperative setup, never at module scope — so the disk cache has had a
|
||||
* chance to populate.
|
||||
*
|
||||
* The default is `true` — /loop is GA (announced in changelog). GrowthBook
|
||||
* is disabled for Bedrock/Vertex/Foundry and when DISABLE_TELEMETRY /
|
||||
* CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC are set; a `false` default would
|
||||
* break /loop for those users (GH #31759). The GB gate now serves purely as
|
||||
* a fleet-wide kill switch — flipping it to `false` stops already-running
|
||||
* schedulers on their next isKilled poll tick, not just new ones.
|
||||
*
|
||||
* `CLAUDE_CODE_DISABLE_CRON` is a local override that wins over GB.
|
||||
*/
|
||||
export function isKairosCronEnabled(): boolean {
|
||||
return feature('AGENT_TRIGGERS')
|
||||
? !isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_CRON) &&
|
||||
getFeatureValue_CACHED_WITH_REFRESH(
|
||||
'tengu_kairos_cron',
|
||||
true,
|
||||
KAIROS_CRON_REFRESH_MS,
|
||||
)
|
||||
: false
|
||||
if (isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_CRON)) return false
|
||||
|
||||
// OpenClaude open builds do not rely on Anthropic's internal runtime gates.
|
||||
// Expose cron support by default unless explicitly disabled.
|
||||
if (process.env.USER_TYPE !== 'ant') return true
|
||||
|
||||
return getFeatureValue_CACHED_WITH_REFRESH(
|
||||
'tengu_kairos_cron',
|
||||
true,
|
||||
KAIROS_CRON_REFRESH_MS,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user