@pi-orca/core
v0.0.5
Published
Shared types and utilities for Pi Orca extension suite
Maintainers
Readme
@pi-orca/core
Shared types and utilities for Pi Orca extension suite
Version: 0.0.1
License: MIT
Overview
@pi-orca/core is the foundation package for the Pi Orca multi-agent extension suite. It provides shared TypeScript types, utilities, and configuration helpers used by all Pi Orca extensions.
Features
- Type Definitions — Complete TypeScript types for tasks, agents, messages, teams, and configuration
- Label Filtering — Flexible label matching with special values (
_unset,_empty,_blank) - Filesystem Utilities — Atomic writes, path resolution, YAML frontmatter parsing
- Lock Management — Multi-session lock files with PID-based staleness detection
- DAG Utilities — Cycle detection and transitive dependency walking for task graphs
- Heartbeat System — Periodic status file writes with cost tracking
- Duration Parsing — Human-readable duration strings (
30s,5m,2h,7d) - Settings I/O — Atomic read/write of
~/.pi/agent/settings.jsonshared across extensions - Selection Persistence — Track and restore
SelectListcursor position across overlay rebuilds
Installation
npm install @pi-orca/coreUsage
Types
import { Task, TaskStatus, AgentTemplate, Labels } from "@pi-orca/core";
const task: Task = {
id: "task-001",
title: "Implement feature",
status: "pending",
// ... other fields
};Label Filtering
import { matchLabels, LABEL_BLANK } from "@pi-orca/core";
const entityLabels = { team: "frontend", priority: "high" };
const filter = { team: "frontend" };
if (matchLabels(entityLabels, filter)) {
console.log("Labels match!");
}
// Find unassigned tasks
const unassignedFilter = { assignedTo: LABEL_BLANK };Filesystem Utilities
import {
atomicWrite,
parseFrontmatter,
serializeFrontmatter,
bootstrapFile
} from "@pi-orca/core";
// Atomic file write
await atomicWrite("/path/to/file.md", content);
// Parse markdown with YAML frontmatter
const { frontmatter, body } = parseFrontmatter<Task>(content);
// Serialize to markdown
const markdown = serializeFrontmatter(frontmatter, body);
// Bootstrap config file with defaults
const { created, content } = await bootstrapFile(
"/path/to/config.yaml",
defaultConfig,
{ notify: true }
);Lock Management
import { acquireLock, releaseLock, isLockStale } from "@pi-orca/core";
const lockPath = "/path/to/task.lock";
const lockInfo = {
sessionId: "abc123",
sessionPath: "project/session.jsonl",
pid: process.pid,
acquiredAt: new Date().toISOString(),
};
// Acquire lock
if (await acquireLock(lockPath, lockInfo)) {
console.log("Lock acquired");
} else {
console.log("Lock held by another session");
}
// Check if lock is stale
if (await isLockStale(lockPath)) {
console.log("Lock is stale (PID dead)");
}
// Release lock
await releaseLock(lockPath, "abc123");DAG Utilities
import { detectCycle, getTransitiveDependents } from "@pi-orca/core";
const dependencies = new Map([
["task-001", ["task-002"]],
["task-002", ["task-003"]],
]);
// Detect cycles
const cycle = detectCycle("task-003", "task-001", dependencies);
if (cycle) {
console.log("Cycle detected:", cycle);
}
// Get transitive dependents
const dependents = getTransitiveDependents("task-002", dependentsMap);Heartbeat
import { createHeartbeat } from "@pi-orca/core";
const heartbeat = createHeartbeat({
filePath: "/path/to/status.yaml",
intervalMs: 30000, // 30 seconds
updateFn: (data) => ({
...data,
status: "running",
// Additional status fields
}),
});
heartbeat.start();
// Update accumulated data
heartbeat.update({ inputTokens: 1000, outputTokens: 500 });
// Stop heartbeat
heartbeat.stop();Path Utilities
import {
getOrcaRuntimeDir,
getOrcaTasksDir,
getUserOrcaDir,
getProjectOrcaDir
} from "@pi-orca/core";
const runtimeDir = getOrcaRuntimeDir(); // ~/.pi/agent/sessions/<slug>/orca/
const tasksDir = getOrcaTasksDir(); // .../orca/tasks/
const userDir = getUserOrcaDir(); // ~/.pi/agent/orca/
const projectDir = getProjectOrcaDir(); // <project>/.pi/orca/Settings I/O
import { getPiSettingsPath, readPiSettings, writePiSettings } from "@pi-orca/core";
// Read current settings (returns {} if missing/unparseable)
const settings = await readPiSettings();
// Patch and write back atomically
settings.defaultProvider = "my-provider";
settings.defaultModel = "my-model";
await writePiSettings(settings);
// Override path for tests via PI_SETTINGS_PATH env var
const path = getPiSettingsPath(); // ~/.pi/agent/settings.jsonSelection Persistence
When building interactive overlays with SelectList, rebuilding the list component
(e.g. after an edit, filter, or sort) resets the cursor to the first item.
Use createSelectionPersistence() to track and restore the user's selection:
import { createSelectionPersistence } from "@pi-orca/core";
import { SelectList } from "@earendil-works/pi-tui";
// Create once per overlay lifetime
const sel = createSelectionPersistence();
function buildList() {
const items = [{ value: "a", label: "A" }, { value: "b", label: "B" }];
const values = items.map(i => i.value);
const list = new SelectList(items, 10, theme);
// Track selection changes
list.onSelectionChange = sel.handler;
// Restore previous selection after rebuild
sel.restore(list, values);
}The tracker also exposes a value getter/setter for manual control
(e.g. setting the next item to highlight before a delete operation).
Cross-Extension Registries
@pi-orca/core hosts module-scoped single-slot registries that let extensions
collaborate without taking package-level dependencies on each other. The same
pattern is used by registry-client (postmaster session registration),
alias-registry (model alias lookup), and model-resolver (the centralized
model reference cascade). All three are last-write-wins; the writer's
session_start registers, session_shutdown reason: "quit" unregisters.
Alias registry (packages/core/src/utils/alias-registry.ts) — spec §8.6.
pi-orca-models registers an AliasResolver so other extensions can look up
alias names (e.g., "fast") without importing @pi-orca/models:
import {
registerAliasResolver,
unregisterAliasResolver,
resolveAlias,
} from "@pi-orca/core";
// Writer side (e.g., pi-orca-models in session_start):
registerAliasResolver({
resolve: (name) => /* return "provider/model-id" or null */ null,
});
// Reader side (any extension):
const canonical = resolveAlias("fast"); // "anthropic/claude-haiku-4-5" | nullModel resolver (packages/core/src/utils/model-resolver.ts) — spec
§8.6.1. The cascade is the single canonical way to interpret a model:
field. Order: alias → SDK ModelLookup → literal provider/model-id
passthrough → null. pi-orca-agents registers a ModelLookup adapter that
bridges to the Pi SDK's ModelRegistry, so bare ids like
"claude-haiku-4-5" resolve through the SDK even when @pi-orca/models is
not loaded.
import {
registerModelLookup,
unregisterModelLookup,
resolveModel,
} from "@pi-orca/core";
// Writer side (e.g., pi-orca-agents):
registerModelLookup({
findExact: (ref) => /* return "provider/model-id" or null */ null,
});
// Reader side:
resolveModel("fast"); // alias → provider/model-id
resolveModel("pi-orca-models/fast"); // synthetic-provider-qualified alias
resolveModel("claude-haiku-4-5"); // bare id via ModelLookup
resolveModel("anthropic/claude-haiku-4-5"); // literal passthroughAPI Documentation
Types
Labels— Key-value string pairsLabelFilter— Filter with special values supportArchivable— Interface for cleanup-eligible entitiesOrcaConfig— Shared configuration schemaTask,TaskStatus— Task types and state machineAgentTemplate,AgentStatus,AgentStatusFile— Agent typesMessage,InboxRef— Message typesTeamTemplate— Team composition schemaLockInfo— Lock file schema
Utilities
- Duration:
parseDuration(),formatDuration() - Path:
getOrcaRuntimeDir(),getOrcaTasksDir(),getUserOrcaDir(), etc. - YAML:
parseFrontmatter(),serializeFrontmatter() - Filesystem:
atomicWrite(),ensureDir(),bootstrapFile() - Lock:
acquireLock(),releaseLock(),isLockStale() - Process:
isPidAlive(),getCurrentPid() - DAG:
detectCycle(),getTransitiveDependents(),getTransitiveDependencies() - Heartbeat:
createHeartbeat() - Settings:
getPiSettingsPath(),readPiSettings(),writePiSettings() - Selection Persistence:
createSelectionPersistence()— track and restoreSelectListcursor across overlay rebuilds - Alias Registry:
registerAliasResolver(),unregisterAliasResolver(),resolveAlias()— cross-extension model alias lookup - Model Resolver:
registerModelLookup(),unregisterModelLookup(),getModelLookup(),resolveModel()— centralized model reference cascade
Development
# Build
npm run build
# Run tests
npm test
# Watch mode
npm run test:watch
# Type check
npm run typecheckRelated Packages
@pi-orca/tasks— Task management extension@pi-orca/messages— Inter-session messaging@pi-orca/models— Model alias management@pi-orca/agents— Subagent lifecycle management@pi-orca/teams— Team orchestration@pi-orca/cleanup— Retention and cleanup@pi-orca/profiles— Extension profiles
License
MIT License — see LICENSE for details.
