omp packages

@hcflabs/pi-task-models9.3.1

Shared task model profiles and routing for hcflabs Pi extensions.

GitHub Actions Published 1 day ago MIT ~1.4K downloads/mo

Untested on omp

This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.

Did it work for you?

Result

One report per person. A new report replaces your old one.

Reports 0

No reports yet.

Readme

@hcflabs/pi-task-models

Configure shared Task Profiles for fast, balanced, frontier, and fav. Each Task Route pairs a model with a thinking level; each consumer owns its Model Tasks and defaults.

Pi showing task model profiles and task routes A Model Task selects a Task Profile and resolves Task Routes; config and profile errors occur before resolution, and no-route follows it.

Install

pi install npm:@hcflabs/pi-task-models

Works with

Package Relationship Purpose
@hcflabs/pi-auto-compact Consumer Compaction defaults to fast.
@hcflabs/pi-herdr-tools Consumer Side-thread and rename tasks default to fast.
@hcflabs/pi-memory Consumer Candidate review, /remember preparation, and /dream promotion default to balanced.
@hcflabs/pi-prompt-creator Consumer Prompt drafting defaults to fast.
@hcflabs/pi-subagent Consumer Delegation defaults to fast. Callers can declare their own task.

Use

Run /task-models, select fast, then choose a primary model and thinking level. Choose a different fallback model and thinking level, or None. Reopen /task-models: fast shows the saved route instead of not configured.

Surface Type Purpose
/task-models command For people: configure profiles and assign them to active tasks.
/task-models preset command For people: apply a saved profile setup.
/task-models status command For people: inspect current task routes, unavailable slots, and config issues.
Option+S / Alt+S in /task-models shortcut For people: save configured profiles as a named preset in the interactive terminal.
Session start ui For people: warn about missing config and config or preset problems. Show an error when routing is disabled.
task/fast, task/balanced, task/frontier, task/fav model For people and agents: select a Task Profile Model. It fails over from the primary route to the fallback route.

Configure each profile your consumers require. fav has no fallback. Select an active task to override its declared profile; choosing its declared default removes the override.

Status

Run /task-models status to inspect each active Model Task with the current config and session scope. The command does not execute routes or change files.

Each task line contains the task name and ID, primary model and thinking level, fallback route or none, and issues. Unavailable primary and fallback slots remain visible. Route errors contain the TaskRouteError code and message. Config and preset issues appear on every status request, including issues reported earlier in the session.

With UI, each notification uses one line and the current theme. The notification truncates text to the terminal width. Without UI, stdout contains the complete lines. The command does not display prompts, messages, reviews, or model output.

Task Profile Models

A Task Profile Model is a Pi virtual model, task/<profile>, for one Task Profile. Select it with /model or --model task/frontier. Each request uses a route of the profile: first the primary, then the fallback.

  1. Pi gets a retryable error on a route: 429, 5xx, overloaded, a network error, or a timeout.
  2. Pi retries the request. The Task Profile Model then uses the next route of the profile.
  3. The failed route gets a cooldown of 10 minutes in this process. New turns use the other route during the cooldown.

A tool continuation keeps the route of the previous response, so the prompt cache stays valid. A retry after a context overflow keeps the same route. Profile thinking is authoritative: the selected thinking level does not change the route.

Pi retries only the errors that it classifies as retryable. A 400, 401, 404, or quota error ends the turn on the failed route. Pi waits between retries and uses retry.maxRetries from Pi settings. A Task Route cannot name a virtual model, for example a Task Profile Model or an alias/<name> model. The picker does not show virtual models, and the router skips a Task Route that names one.

@hcflabs/pi-subagent gives a worker the Task Profile Model when its profile has two usable routes. The worker loads only extensions/task-profile-models.ts, because workers run with --no-extensions.

Presets

Run /task-models preset and choose a name. Included profiles replace complete routes, thinking levels, and fallbacks in one atomic update. Omitted profiles and task assignments stay unchanged. Cancellation writes nothing.

To save, open /task-models, press Option+S (macOS) or Alt+S, then enter a name. Configure at least one profile first. The preset contains all configured profiles, not task assignments. Replacing an existing name requires confirmation; other presets stay unchanged. Cancelling either prompt writes nothing.

The shortcut requires an interactive terminal that sends Option/Alt as Meta (Escape); RPC does not support it.

Flow

The menu shows effective profiles for active tasks. Disabled consumers' assignments remain stored but hidden.

Menus and resolution use ctx.scopedModels, including pinned thinking levels. An empty scope uses available text models from Pi's registry. Duplicate numbered Codex aliases collapse into one choice; fallback choices exclude the primary.

Config

Package-owned: ~/.pi/agent/config/pi-task-models/config.json. Only explicit /task-models actions save it. Use exact model IDs from Pi's registry, not these placeholders:

{
  "profiles": {
    "fast": {
      "primary": { "model": "<provider>/<model>", "thinkingLevel": "low" }
    }
  },
  "tasks": {}
}
Name Description Values Default
profiles Stores shared routes. Object keyed by fast, balanced, frontier, or fav. {}
profiles.<profile>.primary.model Required primary model. Canonical provider/model without whitespace or NUL. —
profiles.<profile>.primary.thinkingLevel Required primary thinking level. off, minimal, low, medium, high, xhigh, or max; model support required at resolution. —
profiles.<profile>.fallback Route to try when the primary is unavailable or fails with a retryable error. Object requiring model and thinkingLevel, with primary rules. Forbidden for fav. No fallback.
tasks Explicit profile overrides. Object mapping <package>/<task> IDs to profiles. {}
tasks.<taskId> Overrides the declared profile. fast, balanced, frontier, or fav. Task declaration's defaultProfile.

Task defaults belong to consumer declarations. Existing overrides equal to those defaults remain valid. Numbered openai-codex-N aliases resolve through Pi's registry and save as openai-codex/<model>.

Preset file

You can edit ~/.pi/agent/config/pi-task-models/presets.json manually. It maps names to any subset of the four profiles, using the same route format:

{
  "my-setup": {
    "fast": {
      "primary": { "model": "<provider>/<model>", "thinkingLevel": "low" }
    }
  }
}

Replace placeholders with exact references from /task-models. Omit fallback to remove it; omit the whole profile to keep its routes. Names must be non-empty, without surrounding whitespace or control characters. The picker sorts names alphabetically.

The file must be valid UTF-8 JSON, at most 64 KiB. Missing, empty, or invalid files do not change active config. Invalid names, presets, and profiles produce warnings and are ignored; valid entries remain available.

Saving refuses any preset-file problem and preserves that file. It reads the latest active profiles, including concurrent changes, before saving. It creates a missing preset file with a private atomic write. An open confirmation prompt does not block other sessions. Saves wait briefly for another save. If another session saves the same new name first, the save reports the conflict and writes nothing; try again.

Applying a preset can create missing active config. Config problems require confirmation before replacement. Applying does not check authentication, availability, or thinking support; task execution uses normal scope and resolution rules.

API

Surface Type Purpose
PROFILE_NAMES constant Lists fast, balanced, frontier, and fav.
THINKING_LEVELS constant Lists thinking levels.
ProfileName type Shared profile name.
ThinkingLevel type Thinking level name.
ModelTask type Consumer-owned independent model operation.
TaskModelRoute type Model reference and thinking level.
TaskModelProfile type Primary route and optional fallback.
TaskModelsConfig type Profiles and explicit assignments.
AvailableModel type Pi registry model.
ResolvedTaskRoute type Session-resolved model and thinking level.
TaskRouteErrorCode type Route failure code.
TaskRouteError type Error with code and optional profile name.
TaskRouteAttempt type Frozen record for one executed route, with model, outcome, duration, and an optional bounded error code.
registerModelTask(pi, task) function Registers a consumer declaration at extension load.
loadTaskModelsConfig(agentDir?) function Reads and validates config; throws when disabled.
registerTaskModelsConfigReporting(pi, agentDir?) function Reports problems at session start; returns report(ctx) for later problems.
canonicalModelReference(model) function Validates and canonicalizes provider/model, including numbered Codex aliases.
modelReference(model) function Formats provider/model.
dedupeAvailableModels(models, preferredProvider?) function Removes duplicate canonical references, with optional provider preference.
resolveAvailableModel(models, reference, preferredProvider?) function Finds a reference, including numbered Codex aliases.
availableTaskModels(ctx) function Lists scoped text models, or registry models when scope is empty.
taskThinkingLevels(ctx, model) function Lists supported levels, respecting session-pinned thinking.
resolveTaskModelRoute(ctx, route, thinking?) function Resolves one available route.
resolveConfiguredTaskRoute(ctx, task, agentDir?, thinking?) function Resolves the first usable task route.
resolveConfiguredTaskRoutes(ctx, task, agentDir?, thinking?) function Resolves configured task route candidates.
executeTaskRoutes(routes, attempt, { shouldFallback, signal?, onAttempt? }) function Tries resolved routes in order. Returns first success and can report each executed route.
orderedProfileRoutes(profile) function Returns primary, then optional fallback.
createTaskModelsExtension(pi, options?) (also default export) function Registers /task-models, missing-config warnings, issue reporting, and the Task Profile Models.
createTaskProfileModelsExtension(pi, options?) function Registers only the Task Profile Models. extensions/task-profile-models.ts exports it as default.
TASK_MODEL_PROVIDER constant Provider of the Task Profile Models: task.
TASK_PROFILE_MODELS_EXTENSION constant Absolute path of extensions/task-profile-models.ts, for a child Pi process.
TASK_ROUTE_COOLDOWN_MINUTES constant Minutes that a failed route stays skipped: 10.
taskProfileModelReference(profile) function Returns task/<profile>.
routeTaskProfile(routes, request, now?, cooldownMinutes?) function Picks the route for one request of a Task Profile Model.

Model and route helpers use only ctx.model, ctx.scopedModels, and ctx.modelRegistry.getAvailable(). They do not require UI methods, so consumers can use supported Pi releases. Profile thinking is authoritative; resolution uses config.tasks[task.id] ?? task.defaultProfile.

Consumers must not read or write shared config directly. Register registerTaskModelsConfigReporting(pi) in the extension factory and call its report(ctx) before resolution, even without the Task Models extension. Each problem appears once per session across reporters.

loadTaskModelsConfig() returns source: "file" or "missing". Missing config returns { source: "missing", value: { "profiles": {}, "tasks": {} } } without creating files. Disabled-config errors give the path and problems.

executeTaskRoutes() only executes supplied routes. It does not resolve, authenticate, inspect providers, store logs, or retry routes. It rejects empty lists and checks signal before each route. Cancellation or shouldFallback(error) === false stops execution. Allowed failures end with the final error unchanged. Each route call must be atomic and safe to repeat.

The optional onAttempt(record) receives one frozen TaskRouteAttempt after each executed route, including the last route. Routes cancelled before execution produce no record. A record contains no route result, request data, or error message text. The executor never copies error messages or thrown strings into records.

Field Meaning
routeIndex Zero-based index in the supplied routes array.
model Actual resolved provider/id, including a numbered Codex provider when selected.
outcome succeeded, failed, or aborted. An aborted signal or an AbortError marks a rejected route as aborted.
durationMs Elapsed route execution time in milliseconds.
errorCode Bounded failure code. Uses error when no accepted code, status, or name exists.

The executor selects errorCode in this order:

  1. Use a non-empty string taskRouteCode on one line, limited to 64 characters.
  2. Use a non-empty string error.code on one line, limited to 64 characters.
  3. Use a finite numeric error.status as http-<status>.
  4. Use error.name only if it matches /^[A-Za-z][A-Za-z0-9_]{0,63}$/.
  5. Use error otherwise.

If error metadata access throws, the record uses errorCode: "error". If abort classification fails, the outcome is failed unless the signal is aborted. These failures do not change the route result or fallback decision.

Successful records omit errorCode. The observer can return a promise, but execution does not wait for it. The executor catches and ignores observer failures. Observer failures do not change route results or fallback decisions.

Limits and recovery

Reads never change config. Problems appear at session start and when /task-models opens:

Problem Result
Unknown key, misspelled tasks, unknown profile name, invalid task ID or task profile Warn and ignore the entry. Tasks without valid overrides use declared defaults.
Invalid profile or route; fallback in fav Warn and ignore the profile. Tasks using it fail with profile-missing, including the problem.
Malformed JSON, invalid UTF-8, over 64 KiB, unreadable file, non-object root or profiles, misspelled profiles such as profile Disable routing. Every task fails with config-disabled.

Repair the file manually, or save a profile with /task-models. Problematic config requires confirmation. Saves retain only valid settings, including valid tasks when profiles is invalid. If the file changed while /task-models was open, saving writes nothing; reopen /task-models.

Saving refuses oversized, unreadable, or non-regular files. Fix, shrink, or remove the file first.

Resolution throws TaskRouteError; check taskRouteCode:

Code Meaning and recovery
config-missing Config is absent. Run /task-models to create it.
config-disabled Config disables routing; the error gives its path and problems. Repair it or save through /task-models.
profile-missing Profile is absent or invalid; errors identify invalid settings. Configure it in /task-models.
no-route No available route. Choose an available model and thinking level in /task-models.

Consumers may silence only config-missing, and only with a safe current-session fallback.