@developerz.ai/aitm
Advanced tools
| import { type ToolLoopAgent } from 'ai'; | ||
| import { z } from 'zod'; | ||
| import { type SubagentInit } from './factory.ts'; | ||
| import type { PlannerTools } from './planner.ts'; | ||
| export declare const GOAL_ASSESSOR_MAX_STEPS = 1000; | ||
| export declare const GoalAssessmentSchema: z.ZodObject<{ | ||
| complete: z.ZodBoolean; | ||
| remaining: z.ZodDefault<z.ZodString>; | ||
| rationale: z.ZodDefault<z.ZodString>; | ||
| }, z.core.$strip>; | ||
| export type GoalAssessment = z.infer<typeof GoalAssessmentSchema>; | ||
| export type GoalAssessorAgent = ToolLoopAgent<never, PlannerTools>; | ||
| export type GoalAssessorInput = { | ||
| goal: string; | ||
| criteria?: string; | ||
| delivered: readonly string[]; | ||
| contextBlock?: string; | ||
| }; | ||
| export { GOAL_ASSESSOR_SYSTEM_PREFIX } from './prompts/role-guidance.ts'; | ||
| export declare function createGoalAssessorAgent(init: SubagentInit<PlannerTools>): GoalAssessorAgent; | ||
| export declare function runGoalAssessor(agent: GoalAssessorAgent, input: GoalAssessorInput): Promise<GoalAssessment>; |
| import { createSubagent, runWithSchemaRetry } from '@developerz.ai/ai-claude-compat'; | ||
| import { tool } from 'ai'; | ||
| import { z } from 'zod'; | ||
| import { AGENT_STEP_BACKSTOP, forwardInit } from "./factory.js"; | ||
| export const GOAL_ASSESSOR_MAX_STEPS = AGENT_STEP_BACKSTOP; | ||
| export const GoalAssessmentSchema = z.object({ | ||
| complete: z.boolean(), | ||
| remaining: z.string().default(''), | ||
| rationale: z.string().default(''), | ||
| }); | ||
| export { GOAL_ASSESSOR_SYSTEM_PREFIX } from "./prompts/role-guidance.js"; | ||
| const assessorInitRegistry = new WeakMap(); | ||
| export function createGoalAssessorAgent(init) { | ||
| const agent = createSubagent({ | ||
| model: init.model, | ||
| tools: init.tools, | ||
| systemPrompt: init.systemPrompt, | ||
| submit: tool({ | ||
| description: 'Submit the verdict: is the goal delivered, and if not, what remains.', | ||
| inputSchema: GoalAssessmentSchema, | ||
| execute: async (assessment) => assessment, | ||
| }), | ||
| ...forwardInit(init), | ||
| }, GOAL_ASSESSOR_MAX_STEPS); | ||
| assessorInitRegistry.set(agent, init); | ||
| return agent; | ||
| } | ||
| export async function runGoalAssessor(agent, input) { | ||
| const onUsage = assessorInitRegistry.get(agent)?.onUsage; | ||
| try { | ||
| const submitted = await runWithSchemaRetry(agent, GoalAssessmentSchema, buildAssessorPrompt(input), { ...(onUsage ? { onUsage } : {}) }); | ||
| if (!submitted.ok) { | ||
| return { complete: true, remaining: '', rationale: 'assessor did not submit a verdict' }; | ||
| } | ||
| const value = submitted.value; | ||
| if (!value.complete && value.remaining.trim() === '') { | ||
| return { ...value, complete: true, rationale: 'no remaining work named' }; | ||
| } | ||
| return value; | ||
| } | ||
| catch { | ||
| return { complete: true, remaining: '', rationale: 'assessor failed' }; | ||
| } | ||
| } | ||
| function buildAssessorPrompt(input) { | ||
| const lines = [`Goal: ${input.goal}`]; | ||
| if (input.criteria?.trim()) | ||
| lines.push(`Acceptance criteria: ${input.criteria}`); | ||
| lines.push('', 'Already delivered by this run:'); | ||
| lines.push(...(input.delivered.length > 0 | ||
| ? input.delivered.map((d) => `- ${d}`) | ||
| : ['- (nothing yet — no group has landed)'])); | ||
| lines.push('', 'Check the repo against the goal with the read-only tools, then call submit.'); | ||
| const body = lines.join('\n'); | ||
| return input.contextBlock ? `${input.contextBlock}\n\n${body}` : body; | ||
| } |
@@ -6,2 +6,3 @@ import type { RunLoopInput } from '../composition/run-input.ts'; | ||
| import type { OnUsage } from '../subagents/factory.ts'; | ||
| import { type GoalAssessment } from '../subagents/goal-assessor.ts'; | ||
| import { sanitizeBranchComponent } from '../workspace/branch-name.ts'; | ||
@@ -21,5 +22,7 @@ export type PlanGroupsOutcome = { | ||
| export declare function planToPrGroups(plan: Plan, branch?: string, takenBranches?: ReadonlySet<string>): PrGroup[]; | ||
| export declare function namespaceWaveGroups(fresh: readonly PrGroup[], taken: readonly PrGroup[], wave: number): PrGroup[]; | ||
| export declare function remoteBranchNames(cwd: string, signal?: AbortSignal): Promise<Set<string>>; | ||
| export declare function listTrackedFiles(cwd: string): Promise<string[]>; | ||
| export declare function parseRemoteHeads(stdout: string): string[]; | ||
| export declare const SURVEY_MIN_TRACKED_FILES = 25; | ||
| export declare function surveyRepoForPlanner(params: { | ||
@@ -34,1 +37,2 @@ input: RunLoopInput; | ||
| export declare function defaultPlanGroups(input: RunLoopInput, mcp: McpClientManager, fetchHtmlAvailable: boolean): Promise<PlanGroupsOutcome>; | ||
| export declare function defaultAssessGoal(input: RunLoopInput, mcp: McpClientManager, fetchHtmlAvailable: boolean, delivered: readonly string[]): Promise<GoalAssessment>; |
| import { harnessProgress, shortModelName } from "../observability/step-progress.js"; | ||
| import { roleUsageSink } from "../observability/usage-tracker.js"; | ||
| import { createGoalAssessorAgent, GOAL_ASSESSOR_SYSTEM_PREFIX, runGoalAssessor, } from "../subagents/goal-assessor.js"; | ||
| import { createPlannerAgent, PLANNER_SYSTEM_PREFIX, runPlanner, } from "../subagents/planner.js"; | ||
@@ -47,2 +48,36 @@ import { createScoutRunner, SCOUT_SYSTEM_PREFIX, synthesizeSurveyBrief, } from "../subagents/planner-scouts.js"; | ||
| } | ||
| export function namespaceWaveGroups(fresh, taken, wave) { | ||
| const takenIds = new Set(taken.map((g) => g.id)); | ||
| const takenBranches = new Set(taken.map((g) => g.branch).filter((b) => typeof b === 'string')); | ||
| const idMap = new Map(); | ||
| for (const group of fresh) { | ||
| let id = group.id; | ||
| if (takenIds.has(id)) { | ||
| id = `w${wave}-${group.id}`; | ||
| for (let n = 2; takenIds.has(id); n++) | ||
| id = `w${wave}-${group.id}-${n}`; | ||
| } | ||
| takenIds.add(id); | ||
| idMap.set(group.id, id); | ||
| } | ||
| return fresh.map((group) => { | ||
| const id = idMap.get(group.id) ?? group.id; | ||
| let branch = group.branch; | ||
| if (typeof branch === 'string' && takenBranches.has(branch)) { | ||
| const base = branch; | ||
| branch = `${base}-w${wave}`; | ||
| for (let n = 2; takenBranches.has(branch); n++) | ||
| branch = `${base}-w${wave}-${n}`; | ||
| } | ||
| if (typeof branch === 'string') | ||
| takenBranches.add(branch); | ||
| return { | ||
| ...group, | ||
| id, | ||
| branch, | ||
| dependsOn: group.dependsOn.map((dep) => idMap.get(dep) ?? dep), | ||
| tasks: group.tasks.map((task, i) => ({ ...task, id: `${id}-${i + 1}` })), | ||
| }; | ||
| }); | ||
| } | ||
| export async function remoteBranchNames(cwd, signal) { | ||
@@ -79,2 +114,3 @@ try { | ||
| } | ||
| export const SURVEY_MIN_TRACKED_FILES = 25; | ||
| export async function surveyRepoForPlanner(params) { | ||
@@ -84,2 +120,7 @@ const { input, style, plannerModelId, plannerUsage, mcp, fetchHtmlAvailable } = params; | ||
| const repoMap = skeleton.totalFiles === 0 ? '' : renderRepoSkeleton(skeleton); | ||
| if (skeleton.totalFiles > 0 && skeleton.totalFiles < SURVEY_MIN_TRACKED_FILES) { | ||
| harnessProgress(`survey: skipped — ${skeleton.totalFiles} tracked file(s), the planner reads the repo directly`, { phase: 'planning' }); | ||
| const mapOnly = synthesizeSurveyBrief([], repoMap); | ||
| return mapOnly === '' ? undefined : mapOnly; | ||
| } | ||
| const base = { | ||
@@ -196,1 +237,26 @@ model: input.credentials.modelFor('planner'), | ||
| } | ||
| export async function defaultAssessGoal(input, mcp, fetchHtmlAvailable, delivered) { | ||
| const style = resolveStyleContents(input); | ||
| const modelId = input.credentials.modelIdFor('planner'); | ||
| const usage = roleUsageSink(input.usage, 'planner', modelId); | ||
| harnessProgress('checking the goal against the repo', { phase: 'planning' }); | ||
| const agent = createGoalAssessorAgent({ | ||
| model: input.credentials.modelFor('planner'), | ||
| tools: applyHooks(resolvePlannerTools(mcp.toolsForRole('planner'), input.cwd, fetchHtmlAvailable, buildExploreFor(input, input.cwd, usage)), input, input.cwd), | ||
| systemPrompt: reminderAgentSystemPrompt({ | ||
| style, | ||
| roleGuidance: GOAL_ASSESSOR_SYSTEM_PREFIX, | ||
| cwd: input.cwd, | ||
| modelId, | ||
| }), | ||
| timeout: { stepMs: input.resolved.llmStepTimeoutMs }, | ||
| ...(usage ? { onUsage: usage } : {}), | ||
| ...(input.signal ? { signal: input.signal } : {}), | ||
| }); | ||
| return runGoalAssessor(agent, { | ||
| goal: input.goal, | ||
| delivered, | ||
| contextBlock: harnessContextBlock(), | ||
| ...(input.criteria !== undefined ? { criteria: input.criteria } : {}), | ||
| }); | ||
| } |
@@ -7,2 +7,3 @@ import { type BackgroundProcessTools } from '@developerz.ai/ai-claude-compat'; | ||
| import { providerOptionsWithServerTools } from '../openrouter/server-tools.ts'; | ||
| import type { GoalAssessment } from '../subagents/goal-assessor.ts'; | ||
| import { harnessContextBlock, reminderAgentSystemPrompt } from '../subagents/role-prompt.ts'; | ||
@@ -33,2 +34,3 @@ import { discoverSpecialists } from '../subagents/specialist-registry.ts'; | ||
| planGroups?: (input: RunLoopInput, mcp: McpClientManager, fetchHtmlAvailable: boolean) => Promise<PlanGroupsOutcome>; | ||
| assessGoal?: (input: RunLoopInput, mcp: McpClientManager, fetchHtmlAvailable: boolean, delivered: readonly string[]) => Promise<GoalAssessment>; | ||
| makeOrchestrator?: (ctx: OrchestratorBridgeCtx) => WorkLoopOrchestrator | Promise<WorkLoopOrchestrator>; | ||
@@ -35,0 +37,0 @@ makeCheckout?: (input: RunLoopInput) => CheckoutHome; |
@@ -31,3 +31,3 @@ import { existsSync } from 'node:fs'; | ||
| import { Disposer, disposeQuietly } from "./disposer.js"; | ||
| import { defaultPlanGroups } from "./planner-wiring.js"; | ||
| import { defaultAssessGoal, defaultPlanGroups, namespaceWaveGroups, } from "./planner-wiring.js"; | ||
| import { makeProgressTee } from "./progress-file.js"; | ||
@@ -75,43 +75,8 @@ import { hasInterruptedGroup, normalizeResumeStatus } from "./resume-normalize.js"; | ||
| const fetchHtmlAvailable = await isFetchHtmlAvailable(); | ||
| let groups; | ||
| let freshPlan = false; | ||
| if (current.prGroups.length > 0) { | ||
| if (hasInterruptedGroup(current.prGroups)) { | ||
| const next = await state.update((s) => ({ | ||
| ...s, | ||
| prGroups: normalizeResumeStatus(s.prGroups), | ||
| })); | ||
| groups = next.prGroups; | ||
| } | ||
| else { | ||
| groups = current.prGroups; | ||
| } | ||
| } | ||
| else { | ||
| const planFn = seams.planGroups ?? defaultPlanGroups; | ||
| const outcome = await planFn(input, mcp, fetchHtmlAvailable); | ||
| if (outcome.kind === 'blocked') { | ||
| return { kind: 'blocked', reason: outcome.reason, outcomes: [] }; | ||
| } | ||
| if (outcome.kind === 'error') { | ||
| return { kind: 'blocked', reason: `planner error: ${outcome.error}`, outcomes: [] }; | ||
| } | ||
| if (outcome.groups.length === 0) { | ||
| return { kind: 'blocked', reason: 'planner produced no PR groups', outcomes: [] }; | ||
| } | ||
| groups = outcome.groups; | ||
| freshPlan = true; | ||
| } | ||
| const stepCounter = makeStepCounter(groups, current.options.prPerTask ?? false); | ||
| try { | ||
| PlanGraph.validate(groups); | ||
| } | ||
| catch (err) { | ||
| const msg = err instanceof Error ? err.message : String(err); | ||
| return { kind: 'blocked', reason: msg, outcomes: [] }; | ||
| } | ||
| if (freshPlan) { | ||
| await state.update((s) => ({ ...s, status: 'working', prGroups: groups })); | ||
| } | ||
| let liveGroups = groups; | ||
| const effectiveConcurrency = 1; | ||
| const checkout = seams.makeCheckout?.(input) ?? | ||
| new InPlaceCheckout(input.cwd, { allowDirty: input.resolved.allowDirty ?? false }); | ||
| const github = seams.makeGithub?.(input) ?? input.github; | ||
| const budgetCheck = makeBudgetCheck(input.usage, input.resolved.maxCostUsd, input.resolved.maxTotalTokens); | ||
| let liveGroups = []; | ||
| const graph = { | ||
@@ -121,2 +86,3 @@ ready: () => PlanGraph.trusted(liveGroups).ready(), | ||
| }; | ||
| let liveSessionCount = current.sessionCount; | ||
| const workLoopState = { | ||
@@ -126,2 +92,3 @@ update: async (mutator) => { | ||
| liveGroups = next.prGroups; | ||
| liveSessionCount = next.sessionCount; | ||
| return next; | ||
@@ -133,38 +100,107 @@ }, | ||
| }; | ||
| const effectiveConcurrency = 1; | ||
| const checkout = seams.makeCheckout?.(input) ?? | ||
| new InPlaceCheckout(input.cwd, { allowDirty: input.resolved.allowDirty ?? false }); | ||
| const github = seams.makeGithub?.(input) ?? input.github; | ||
| const orchestrator = await (seams.makeOrchestrator ?? defaultMakeOrchestrator)({ | ||
| input, | ||
| mcp, | ||
| rollingContext, | ||
| fetchHtmlAvailable, | ||
| state, | ||
| stepCounter, | ||
| background, | ||
| }); | ||
| const budgetCheck = makeBudgetCheck(input.usage, input.resolved.maxCostUsd, input.resolved.maxTotalTokens); | ||
| const loop = new WorkLoop({ | ||
| orchestrator, | ||
| github, | ||
| state: workLoopState, | ||
| home: checkout, | ||
| graph, | ||
| prContext: new PrContextStore(resolvePath(input.cwd, '.ai-task-master')), | ||
| concurrency: effectiveConcurrency, | ||
| autoMerge: input.resolved.autoMerge, | ||
| selfReview: input.resolved.selfReview, | ||
| prPerTask: current.options.prPerTask ?? false, | ||
| maxSessions: input.resolved.maxSessions, | ||
| maxCiFixAttempts: input.resolved.maxCiFixAttempts, | ||
| mergeMethod: input.resolved.mergeMethod, | ||
| adminMerge: input.resolved.adminMerge ?? false, | ||
| initialSessionCount: current.sessionCount, | ||
| progress: makeProgressTee(state.appendProgress ? { append: state.appendProgress.bind(state) } : {}), | ||
| stepCounter, | ||
| ...(input.signal ? { signal: input.signal } : {}), | ||
| ...(budgetCheck ? { budget: budgetCheck } : {}), | ||
| }); | ||
| return await loop.run(); | ||
| const allOutcomes = []; | ||
| const assessGoal = seams.assessGoal ?? defaultAssessGoal; | ||
| let waveGoal = input.goal; | ||
| let landed = []; | ||
| const plannedGoals = new Set([input.goal]); | ||
| for (let wave = 1;; wave++) { | ||
| let groups; | ||
| let freshPlan = false; | ||
| if (wave === 1 && current.prGroups.length > 0) { | ||
| if (hasInterruptedGroup(current.prGroups)) { | ||
| const next = await state.update((s) => ({ | ||
| ...s, | ||
| prGroups: normalizeResumeStatus(s.prGroups), | ||
| })); | ||
| groups = next.prGroups; | ||
| } | ||
| else { | ||
| groups = current.prGroups; | ||
| } | ||
| } | ||
| else { | ||
| const planFn = seams.planGroups ?? defaultPlanGroups; | ||
| const outcome = await planFn({ ...input, goal: waveGoal }, mcp, fetchHtmlAvailable); | ||
| if (outcome.kind === 'blocked') { | ||
| if (wave > 1) | ||
| return { kind: 'success', outcomes: allOutcomes }; | ||
| return { kind: 'blocked', reason: outcome.reason, outcomes: [] }; | ||
| } | ||
| if (outcome.kind === 'error') { | ||
| if (wave > 1) | ||
| return { kind: 'success', outcomes: allOutcomes }; | ||
| return { kind: 'blocked', reason: `planner error: ${outcome.error}`, outcomes: [] }; | ||
| } | ||
| if (outcome.groups.length === 0) { | ||
| if (wave > 1) | ||
| return { kind: 'success', outcomes: allOutcomes }; | ||
| return { kind: 'blocked', reason: 'planner produced no PR groups', outcomes: [] }; | ||
| } | ||
| groups = [...landed, ...namespaceWaveGroups(outcome.groups, landed, wave)]; | ||
| freshPlan = true; | ||
| } | ||
| const stepCounter = makeStepCounter(groups, current.options.prPerTask ?? false); | ||
| try { | ||
| PlanGraph.validate(groups); | ||
| } | ||
| catch (err) { | ||
| const msg = err instanceof Error ? err.message : String(err); | ||
| if (wave > 1) | ||
| return { kind: 'success', outcomes: allOutcomes }; | ||
| return { kind: 'blocked', reason: msg, outcomes: [] }; | ||
| } | ||
| if (freshPlan) { | ||
| await state.update((s) => ({ ...s, status: 'working', prGroups: groups })); | ||
| } | ||
| liveGroups = groups; | ||
| const waveContext = (await state.readContext?.()) ?? rollingContext; | ||
| const orchestrator = await (seams.makeOrchestrator ?? defaultMakeOrchestrator)({ | ||
| input, | ||
| mcp, | ||
| rollingContext: waveContext, | ||
| fetchHtmlAvailable, | ||
| state, | ||
| stepCounter, | ||
| background, | ||
| }); | ||
| const loop = new WorkLoop({ | ||
| orchestrator, | ||
| github, | ||
| state: workLoopState, | ||
| home: checkout, | ||
| graph, | ||
| prContext: new PrContextStore(resolvePath(input.cwd, '.ai-task-master')), | ||
| concurrency: effectiveConcurrency, | ||
| autoMerge: input.resolved.autoMerge, | ||
| selfReview: input.resolved.selfReview, | ||
| prPerTask: current.options.prPerTask ?? false, | ||
| maxSessions: input.resolved.maxSessions, | ||
| maxCiFixAttempts: input.resolved.maxCiFixAttempts, | ||
| mergeMethod: input.resolved.mergeMethod, | ||
| adminMerge: input.resolved.adminMerge ?? false, | ||
| initialSessionCount: liveSessionCount, | ||
| progress: makeProgressTee(state.appendProgress ? { append: state.appendProgress.bind(state) } : {}), | ||
| stepCounter, | ||
| ...(input.signal ? { signal: input.signal } : {}), | ||
| ...(budgetCheck ? { budget: budgetCheck } : {}), | ||
| }); | ||
| const result = await loop.run(); | ||
| allOutcomes.push(...result.outcomes); | ||
| if (result.kind !== 'success') | ||
| return { ...result, outcomes: allOutcomes }; | ||
| landed = [...liveGroups]; | ||
| const assessment = await assessGoal(input, mcp, fetchHtmlAvailable, deliveredSummary(landed)); | ||
| if (assessment.complete) { | ||
| return { kind: 'success', outcomes: allOutcomes }; | ||
| } | ||
| if (plannedGoals.has(assessment.remaining)) { | ||
| harnessProgress(`goal still not met, but the remaining work is unchanged from a wave already run — stopping: ${assessment.remaining}`, { phase: 'planning' }); | ||
| return { kind: 'success', outcomes: allOutcomes }; | ||
| } | ||
| plannedGoals.add(assessment.remaining); | ||
| harnessProgress(`goal not yet met — planning another wave: ${assessment.remaining}`, { | ||
| phase: 'planning', | ||
| }); | ||
| waveGoal = assessment.remaining; | ||
| } | ||
| } | ||
@@ -175,2 +211,8 @@ finally { | ||
| } | ||
| function deliveredSummary(groups) { | ||
| return groups.map((g) => { | ||
| const pr = typeof g.pr === 'number' ? ` (PR #${g.pr}, ${g.status})` : ` (${g.status})`; | ||
| return `${g.id}: ${g.title}${pr}`; | ||
| }); | ||
| } | ||
| export function selfReviewVerifyCommand(configured, cwd) { | ||
@@ -177,0 +219,0 @@ if (configured) |
@@ -32,3 +32,3 @@ import { createSubagent, formatSubmitIssues, runWithSchemaRetry, } from '@developerz.ai/ai-claude-compat'; | ||
| try { | ||
| const submitted = await runWithSchemaRetry(agent, PlanSchema, buildUserPrompt(input), { | ||
| const submitted = await runWithSchemaRetry(agent, cappedPlanSchema(input.maxPrs), buildUserPrompt(input), { | ||
| ...(onUsage ? { onUsage } : {}), | ||
@@ -45,10 +45,3 @@ }); | ||
| } | ||
| const plan = submitted.value; | ||
| if (input.maxPrs !== null && plan.groups.length > input.maxPrs) { | ||
| return { | ||
| kind: 'error', | ||
| error: `planner emitted ${plan.groups.length} PR groups, exceeding maxPrs ${input.maxPrs}. Raise --max-prs (or pass 0 for unbounded) — the plan is not truncated, because dropping groups would silently drop work.`, | ||
| }; | ||
| } | ||
| return { kind: 'ok', plan }; | ||
| return { kind: 'ok', plan: submitted.value }; | ||
| } | ||
@@ -59,2 +52,9 @@ catch (err) { | ||
| } | ||
| function cappedPlanSchema(maxPrs) { | ||
| if (maxPrs === null) | ||
| return PlanSchema; | ||
| return PlanSchema.refine((plan) => plan.groups.length <= maxPrs, { | ||
| message: `too many PR groups: emit at most ${maxPrs}. Group everything to fit — never drop the tail, and never leave part of the goal unplanned.`, | ||
| }); | ||
| } | ||
| function buildUserPrompt(input) { | ||
@@ -61,0 +61,0 @@ const lines = [`Goal: ${input.goal}`]; |
| export declare const PLANNER_SYSTEM_PREFIX: string; | ||
| export declare const GOAL_ASSESSOR_SYSTEM_PREFIX: string; | ||
| export declare const WORKER_SYSTEM_PREFIX: string; | ||
| export declare const EDITOR_SYSTEM_PREFIX: string; | ||
| export declare const EXPLORE_SYSTEM_PROMPT: string; |
@@ -7,10 +7,7 @@ export const PLANNER_SYSTEM_PREFIX = [ | ||
| '', | ||
| 'COVER THE WHOLE GOAL. The groups together must deliver everything the goal asks for — a plan that', | ||
| 'implements a subset and leaves the rest unplanned is INVALID, however well-sized its groups are.', | ||
| 'Size the plan to the goal, not to a number you expect to be comfortable: "implement the whole', | ||
| 'system" over a greenfield repo is a large plan (a dozen groups or more), and emitting one group', | ||
| 'for the first component silently ships a fraction of what was asked. Nothing downstream re-plans', | ||
| 'the remainder — what you leave out is never built. There is no cap on how many groups you may', | ||
| 'emit unless a `maxPrs:` line appears below; when it does, group everything to fit it rather than', | ||
| 'dropping the tail.', | ||
| 'COVER THE WHOLE GOAL. The groups together MUST deliver everything the goal asks. A plan covering a', | ||
| 'subset is INVALID, however well-sized its groups. Nothing re-plans the remainder — what you omit is', | ||
| 'NEVER built. Size the plan to the goal: "implement the whole system" on a greenfield repo is a dozen', | ||
| 'groups or more. No cap on group count unless a `maxPrs:` line appears below; when it does, group', | ||
| 'everything to fit — never drop the tail.', | ||
| '', | ||
@@ -57,2 +54,18 @@ 'Each group = one cohesive PR that delivers a whole capability end to end, tests included —', | ||
| ].join('\n'); | ||
| export const GOAL_ASSESSOR_SYSTEM_PREFIX = [ | ||
| '', | ||
| 'You judge ONE question: does the goal still have work left?', | ||
| '', | ||
| 'A wave of PR groups just landed. Read the repo as it NOW is (glob/grep/readFile, `explore` when', | ||
| 'present) and check it against the GOAL — never against the plan. A plan that covered only part of', | ||
| 'the goal is exactly what you are here to catch.', | ||
| '', | ||
| '- `complete: true` only when the goal is genuinely delivered: the behaviour exists, it is wired in,', | ||
| ' and it has tests. "The plan finished" is NOT the test — the plan can be wrong.', | ||
| '- `complete: false` → `remaining` names the work still owed, phrased as a GOAL a planner can plan', | ||
| ' from ("implement the REST API gateway and its schema"), never a critique ("the API is missing").', | ||
| '- Polish, refactors, renames, and nice-to-haves are NEVER remaining work. Scope creep here loops', | ||
| ' forever and spends the operator money.', | ||
| '- Unsure → `complete: true`. Report what you can verify; a wrong "false" buys another whole wave.', | ||
| ].join('\n'); | ||
| export const WORKER_SYSTEM_PREFIX = [ | ||
@@ -59,0 +72,0 @@ '', |
+2
-2
| { | ||
| "name": "@developerz.ai/aitm", | ||
| "version": "0.0.53", | ||
| "version": "0.0.54", | ||
| "description": "Autonomous task orchestrator. Goal in, merged PRs out.", | ||
@@ -46,3 +46,3 @@ "license": "MIT", | ||
| "@ai-sdk/mcp": "^1.0.42", | ||
| "@developerz.ai/ai-claude-compat": "0.0.53", | ||
| "@developerz.ai/ai-claude-compat": "0.0.54", | ||
| "@openrouter/ai-sdk-provider": "^2.9.0", | ||
@@ -49,0 +49,0 @@ "ai": "^6.0.182", |
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 2 instances
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 2 instances
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
818307
1.28%279
0.72%20146
1.03%+ Added
- Removed