@oh-my-pi/pi-coding-agent 16.4.4 → 16.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/dist/cli.js +3799 -3729
  3. package/dist/types/async/job-manager.d.ts +8 -0
  4. package/dist/types/cli/bench-cli.d.ts +1 -7
  5. package/dist/types/cli/usage-cli.d.ts +1 -0
  6. package/dist/types/commands/usage.d.ts +7 -0
  7. package/dist/types/config/settings-schema.d.ts +19 -9
  8. package/dist/types/config/settings.d.ts +3 -2
  9. package/dist/types/discovery/helpers.d.ts +2 -2
  10. package/dist/types/extensibility/extensions/types.d.ts +36 -0
  11. package/dist/types/irc/bus.d.ts +4 -0
  12. package/dist/types/modes/components/__tests__/pause-screen.test.d.ts +1 -0
  13. package/dist/types/modes/components/ask-dialog.d.ts +27 -0
  14. package/dist/types/modes/components/custom-editor.d.ts +3 -8
  15. package/dist/types/modes/components/index.d.ts +2 -1
  16. package/dist/types/modes/components/model-browser.d.ts +100 -0
  17. package/dist/types/modes/components/model-hub.d.ts +52 -0
  18. package/dist/types/modes/components/pause-screen.d.ts +43 -0
  19. package/dist/types/modes/components/session-selector.d.ts +13 -0
  20. package/dist/types/modes/components/tool-execution.d.ts +2 -0
  21. package/dist/types/modes/controllers/extension-ui-controller.d.ts +2 -1
  22. package/dist/types/modes/controllers/input-controller.d.ts +2 -0
  23. package/dist/types/modes/interactive-mode.d.ts +3 -0
  24. package/dist/types/modes/queue-input.d.ts +8 -0
  25. package/dist/types/modes/shared.d.ts +1 -1
  26. package/dist/types/modes/types.d.ts +4 -0
  27. package/dist/types/session/agent-session.d.ts +1 -1
  28. package/dist/types/session/agent-storage.d.ts +57 -0
  29. package/dist/types/session/session-context.d.ts +9 -0
  30. package/dist/types/task/executor.d.ts +26 -13
  31. package/dist/types/task/index.d.ts +12 -11
  32. package/dist/types/task/label.d.ts +4 -0
  33. package/dist/types/task/repair-args.d.ts +8 -8
  34. package/dist/types/task/types.d.ts +31 -56
  35. package/dist/types/tools/ask.d.ts +12 -0
  36. package/dist/types/tools/conflict-detect.d.ts +17 -1
  37. package/dist/types/tools/job.d.ts +16 -0
  38. package/package.json +12 -12
  39. package/scripts/build-binary.ts +0 -1
  40. package/scripts/compile-binary.ts +4 -3
  41. package/src/async/job-manager.ts +9 -0
  42. package/src/cli/bench-cli.ts +7 -26
  43. package/src/cli/usage-cli.ts +11 -0
  44. package/src/commands/usage.ts +13 -2
  45. package/src/commit/agentic/tools/analyze-file.ts +2 -3
  46. package/src/config/settings-schema.ts +18 -7
  47. package/src/config/settings.ts +13 -4
  48. package/src/discovery/helpers.ts +3 -4
  49. package/src/extensibility/custom-tools/loader.ts +70 -37
  50. package/src/extensibility/extensions/types.ts +46 -0
  51. package/src/irc/bus.ts +61 -20
  52. package/src/modes/components/__tests__/pause-screen.test.ts +143 -0
  53. package/src/modes/components/advisor-config.ts +32 -22
  54. package/src/modes/components/ask-dialog.ts +888 -0
  55. package/src/modes/components/custom-editor.test.ts +58 -1
  56. package/src/modes/components/custom-editor.ts +42 -11
  57. package/src/modes/components/index.ts +2 -1
  58. package/src/modes/components/model-browser.ts +769 -0
  59. package/src/modes/components/model-hub.ts +2002 -0
  60. package/src/modes/components/pause-screen.ts +208 -0
  61. package/src/modes/components/session-selector.ts +299 -42
  62. package/src/modes/components/tool-execution.ts +2 -0
  63. package/src/modes/components/usage-row.ts +5 -6
  64. package/src/modes/controllers/event-controller.ts +8 -2
  65. package/src/modes/controllers/extension-ui-controller.ts +252 -5
  66. package/src/modes/controllers/input-controller.ts +140 -6
  67. package/src/modes/controllers/selector-controller.ts +160 -97
  68. package/src/modes/controllers/tan-command-controller.ts +1 -1
  69. package/src/modes/controllers/todo-command-controller.ts +1 -2
  70. package/src/modes/interactive-mode.ts +8 -0
  71. package/src/modes/queue-input.ts +132 -0
  72. package/src/modes/shared.ts +1 -1
  73. package/src/modes/theme/theme.ts +3 -3
  74. package/src/modes/types.ts +4 -0
  75. package/src/modes/utils/ui-helpers.ts +50 -24
  76. package/src/prompts/agents/scout.md +0 -1
  77. package/src/prompts/agents/task.md +1 -1
  78. package/src/prompts/system/subagent-system-prompt.md +1 -5
  79. package/src/prompts/system/subagent-yield-reminder.md +10 -0
  80. package/src/prompts/system/task-label.md +23 -0
  81. package/src/prompts/tools/job.md +1 -1
  82. package/src/prompts/tools/task-summary.md +3 -0
  83. package/src/prompts/tools/task.md +17 -18
  84. package/src/session/agent-session.ts +186 -49
  85. package/src/session/agent-storage.ts +330 -3
  86. package/src/session/history-storage.ts +1 -34
  87. package/src/session/session-context.test.ts +73 -0
  88. package/src/session/session-context.ts +43 -26
  89. package/src/slash-commands/builtin-registry.ts +18 -0
  90. package/src/task/agents.ts +2 -0
  91. package/src/task/executor.ts +159 -46
  92. package/src/task/index.ts +377 -239
  93. package/src/task/label.ts +38 -0
  94. package/src/task/render.ts +74 -22
  95. package/src/task/repair-args.ts +20 -31
  96. package/src/task/spawn-policy.test.ts +4 -4
  97. package/src/task/types.ts +46 -66
  98. package/src/tools/ask.ts +233 -40
  99. package/src/tools/conflict-detect.ts +102 -5
  100. package/src/tools/index.ts +1 -0
  101. package/src/tools/irc.ts +20 -11
  102. package/src/tools/job.ts +158 -18
  103. package/src/tools/write.ts +70 -6
  104. package/src/vibe/runtime.ts +1 -1
  105. package/src/web/search/providers/browser-headers.ts +30 -13
  106. package/dist/types/modes/components/model-selector.d.ts +0 -37
  107. package/dist/types/tools/bash-command-fixup.d.ts +0 -3
  108. package/src/modes/components/model-selector.ts +0 -1291
  109. package/src/tools/bash-command-fixup.ts +0 -4
@@ -1,5 +1,25 @@
1
1
  import { type AuthCredential, type AuthCredentialStore, type StoredAuthCredential } from "@oh-my-pi/pi-ai";
2
2
  import type { RawSettings as Settings } from "../config/settings.js";
3
+ /** One completed request's timing, folded into the per-model aggregates. */
4
+ export interface ModelPerfSample {
5
+ /** Output tokens the provider reported for the turn. */
6
+ outputTokens: number;
7
+ /** Total request duration in milliseconds. */
8
+ durationMs: number;
9
+ /** Time to first token in milliseconds; omit when the provider did not report one. */
10
+ ttftMs?: number;
11
+ }
12
+ /** Recency-weighted per-model performance averages. */
13
+ export interface ModelPerfStats {
14
+ /** Decayed sample count backing the averages. */
15
+ samples: number;
16
+ /** Average output tokens/sec over the total request duration. */
17
+ tps: number;
18
+ /** Average time-to-first-token in milliseconds; null when no sample reported one. */
19
+ ttftMs: number | null;
20
+ }
21
+ /** Current agent.db schema version; bump when schema changes require migration. */
22
+ export declare const SCHEMA_VERSION = 6;
3
23
  /**
4
24
  * Unified SQLite storage for agent settings, model usage, and auth credentials.
5
25
  * Delegates auth credential operations to AuthCredentialStore from @oh-my-pi/pi-ai.
@@ -36,6 +56,43 @@ export declare class AgentStorage {
36
56
  * @returns Array of model keys ("provider/modelId") in MRU order
37
57
  */
38
58
  getModelUsageOrder(): string[];
59
+ /**
60
+ * Folds one completed request's timing into the model's perf aggregates.
61
+ * TPS is measured over the total request duration — not the post-TTFT
62
+ * decode window, which undercounts generation time (and so inflates the
63
+ * rate) when reasoning tokens are generated before the first visible
64
+ * token. Invalid samples (no tokens, no duration) are dropped.
65
+ *
66
+ * Deferred like prompt history: samples are batched and written in one
67
+ * transaction after {@link MODEL_PERF_FLUSH_DELAY_MS}, keeping SQLite off
68
+ * the turn-completion hot path. Fire-and-forget safe — flush failures are
69
+ * logged, never thrown; await the returned promise only to observe the flush.
70
+ * @param modelKey - Model key in "provider/modelId" format
71
+ */
72
+ recordModelPerf(modelKey: string, sample: ModelPerfSample): Promise<void>;
73
+ /**
74
+ * Returns recency-weighted TPS/TTFT averages for every model with recorded
75
+ * requests, keyed by "provider/modelId". Read by the /models browser.
76
+ * Also kicks the one-time background stats.db import; until it completes,
77
+ * models without live samples are simply absent.
78
+ */
79
+ getModelPerf(): Map<string, ModelPerfStats>;
80
+ /**
81
+ * Imports recent measurable request rows from an `omp stats` database
82
+ * (`messages` table) into the model_perf aggregates. Walks newest-first
83
+ * over the timestamp index in {@link MODEL_PERF_BACKFILL_CHUNK}-row chunks,
84
+ * yielding to the event loop between chunks, and keeps at most
85
+ * {@link MODEL_PERF_DECAY_AT} rows per model within the
86
+ * {@link MODEL_PERF_BACKFILL_MAX_AGE_MS} window — beyond either bound the
87
+ * live decay would erase the contribution anyway. Errored turns are
88
+ * excluded; aborted turns with reported usage count, matching live capture.
89
+ * Sums land in one additive transaction at the end, so concurrent live
90
+ * samples merge correctly regardless of order.
91
+ * @param statsDbPath - Path to a stats.db file; opened read-only
92
+ * @returns Number of rows folded in
93
+ * @throws When the stats db cannot be opened or queried
94
+ */
95
+ backfillModelPerfFromStats(statsDbPath: string): Promise<number>;
39
96
  /**
40
97
  * Checks if any auth credentials exist in storage.
41
98
  * @returns True if at least one credential is stored
@@ -40,5 +40,14 @@ export interface BuildSessionContextOptions {
40
40
  transcript?: boolean;
41
41
  /** In transcript mode, elide entries replaced by the latest compaction. */
42
42
  collapseCompactedHistory?: boolean;
43
+ /**
44
+ * Transcript mode only: keep `toolCall` blocks that have no matching
45
+ * `toolResult` on the path instead of stripping them. Pass this when the
46
+ * session is mid-turn (a tool is still executing, its result not yet
47
+ * persisted) so the rebuilt transcript renders the in-flight call as
48
+ * pending; without it a focus/unfocus or overlay-close rebuild silently
49
+ * hides the call the agent is still waiting on.
50
+ */
51
+ keepDanglingToolCalls?: boolean;
43
52
  }
44
53
  export declare function buildSessionContext(entries: SessionEntry[], leafId?: string | null, byId?: Map<string, SessionEntry>, options?: BuildSessionContextOptions): SessionContext;
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Runs each subagent on the main thread and forwards AgentEvents for progress tracking.
5
5
  */
6
- import type { AgentTelemetryConfig, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
6
+ import type { AgentTelemetryConfig } from "@oh-my-pi/pi-agent-core";
7
7
  import type { ServiceTierByFamily } from "@oh-my-pi/pi-ai";
8
8
  import type { Rule } from "../capability/rule.js";
9
9
  import { ModelRegistry } from "../config/model-registry.js";
@@ -20,23 +20,27 @@ import type { MnemopiSessionState } from "../mnemopi/state.js";
20
20
  import type { AgentSession } from "../session/agent-session.js";
21
21
  import type { ArtifactManager } from "../session/artifacts.js";
22
22
  import type { AuthStorage } from "../session/auth-storage.js";
23
+ import type { ConfiguredThinkingLevel } from "../thinking.js";
23
24
  import type { ContextFileEntry } from "../tools/index.js";
24
25
  import type { EventBus } from "../utils/event-bus.js";
25
26
  import type { WorkspaceTree } from "../workspace-tree.js";
26
27
  import { type AgentDefinition, type AgentProgress, type ReviewFinding, type SingleResult, type YieldItem } from "./types.js";
27
28
  export type { YieldItem } from "./types.js";
28
29
  /**
29
- * Soft per-agent request budgets (assistant requests per run). When a subagent
30
- * crosses its budget it can receive an optional steering notice asking it to
31
- * wrap up; at 1.5x the budget the run is aborted gracefully so partial output is
32
- * salvaged. The `default` key applies to agents without an explicit entry and
33
- * can be overridden via the `task.softRequestBudget` setting (0 disables the
34
- * guard). The notice is off by default and controlled separately by
35
- * `task.softRequestBudgetNotice`.
30
+ * Soft per-agent request budgets (assistant requests per run). Crossing the
31
+ * budget injects a wrap-up steering notice (`task.softRequestBudgetNotice`,
32
+ * on by default). At 1.5x the budget the free-running turn is stopped and the
33
+ * agent is driven to one forced final `yield` so partial findings come back
34
+ * as a real report; only if it still refuses to yield within
35
+ * {@link BUDGET_STOP_GRACE_REQUESTS} more requests is the run hard-aborted.
36
+ * The `default` key applies to agents without an explicit entry and can be
37
+ * overridden via the `task.softRequestBudget` setting (0 disables the guard).
36
38
  */
37
39
  export declare const SOFT_REQUEST_BUDGET: Record<string, number>;
38
- /** Optional steering notice injected when a subagent crosses its soft request budget. */
39
- export declare function buildBudgetNotice(requests: number): string;
40
+ /** Extra requests allowed after a budget stop for the forced yield to land before the run is hard-aborted. */
41
+ export declare const BUDGET_STOP_GRACE_REQUESTS = 5;
42
+ /** Steering notice injected when a subagent crosses its soft request budget. */
43
+ export declare function buildBudgetNotice(requests: number, budget: number): string;
40
44
  /** Options for subagent execution */
41
45
  export interface ExecutorOptions {
42
46
  cwd: string;
@@ -55,9 +59,8 @@ export interface ExecutorOptions {
55
59
  path: string;
56
60
  content: string;
57
61
  };
62
+ /** Pre-set UI label (e.g. eval bridge label). When absent, a tiny-model label is generated from the assignment. */
58
63
  description?: string;
59
- /** Specialist role/expertise for this spawn; drives the system-prompt preamble, display name, and telemetry identity. */
60
- role?: string;
61
64
  index: number;
62
65
  id: string;
63
66
  parentToolCallId?: string;
@@ -74,7 +77,7 @@ export interface ExecutorOptions {
74
77
  * if the resolved subagent model has no working credentials. See #985.
75
78
  */
76
79
  parentActiveModelPattern?: string;
77
- thinkingLevel?: ThinkingLevel;
80
+ thinkingLevel?: ConfiguredThinkingLevel;
78
81
  outputSchema?: unknown;
79
82
  /**
80
83
  * Caller supplied a schema that supersedes the agent's native output prompt.
@@ -199,10 +202,20 @@ export declare function finalizeSubprocessOutput(args: FinalizeSubprocessOutputA
199
202
  */
200
203
  export declare function createMCPProxyTools(mcpManager: MCPManager): CustomTool[];
201
204
  export declare function createSubagentSettings(baseSettings: Settings, overrides?: Partial<Record<SettingPath, unknown>>, inheritedServiceTier?: ServiceTierByFamily | null): Settings;
205
+ export type AbortReason = "signal" | "terminate" | "timeout" | "budget";
206
+ /**
207
+ * Settle a subagent's registry lifecycle after a run: terminal teardown for
208
+ * hard aborts, unregister for one-shot helpers, park for isolated runs, and
209
+ * idle + lifecycle adoption for kept-alive agents. A soft-budget abort on a
210
+ * kept-alive, revivable agent is treated as a self-inflicted stop rather than
211
+ * a kill — the agent stays interrogable and resumable (irc wake / revival).
212
+ */
202
213
  export declare function finalizeSubagentLifecycle(args: {
203
214
  id: string;
204
215
  session: AgentSession;
205
216
  aborted: boolean;
217
+ /** Which watchdog (if any) requested the abort; decides revivability. */
218
+ abortKind?: AbortReason;
206
219
  keepAlive: boolean;
207
220
  isolated: boolean;
208
221
  agentIdleTtlMs: number;
@@ -20,12 +20,12 @@ export declare function isReadOnlyAgent(agent: AgentDefinition): boolean;
20
20
  export declare function formatResultOutputFallback(result: Pick<SingleResult, "output" | "stderr" | "requests">): string;
21
21
  /**
22
22
  * Advisory — never a rejection — nudging the spawner toward tailored
23
- * specialists when it spawns generic role-less workers and still holds spawn
24
- * capacity (DepthCapacity: it currently has the `task` tool). Fires when a
25
- * generic `task`/`sonic` spawn carries no `role`, or when one call clones
26
- * the same agent ≥2× all without roles. Returns undefined when no nudge applies.
23
+ * specific agent types when one call resolves ≥2 items to a generic
24
+ * `task`/`sonic` worker and the spawner still holds spawn capacity
25
+ * (DepthCapacity: it currently has the `task` tool). `agentNames` are the
26
+ * per-item resolved agent types. Returns undefined when no nudge applies.
27
27
  */
28
- export declare function buildSpecializationAdvisory(agentName: string | undefined, items: TaskItem[], depthCapacity: boolean): string | undefined;
28
+ export declare function buildSpecializationAdvisory(agentNames: string[], depthCapacity: boolean): string | undefined;
29
29
  /**
30
30
  * Suggestion — never a rejection — nudging the spawner to coordinate via `irc`
31
31
  * when one call creates ≥2 live siblings and it still holds spawn capacity.
@@ -34,14 +34,15 @@ export declare function buildSpecializationAdvisory(agentName: string | undefine
34
34
  export declare function buildCoordinationAdvisory(items: TaskItem[], depthCapacity: boolean, ircEnabled: boolean): string | undefined;
35
35
  /**
36
36
  * Compose the non-blocking advisory appended to a `task` result: the
37
- * specialization nudge, plus only when the siblings keep running after this
38
- * call (`willRunAsync`) — the coordination suggestion. Coordination is gated on
39
- * async because a sync fanout's siblings have already finished, so a
40
- * "coordinate while they run" hint would misfire. Returns undefined when
41
- * neither applies.
37
+ * specialization nudge (from the per-item resolved agent types), plus only
38
+ * when some spawns keep running after this call (`willRunAsync`) — the
39
+ * coordination suggestion over those still-live spawns (`items`). Coordination
40
+ * is gated on async because a sync spawn has already finished by the time the
41
+ * call returns, so a "coordinate while they run" hint would misfire. Returns
42
+ * undefined when neither applies.
42
43
  */
43
44
  export declare function composeSpawnAdvisory(args: {
44
- agentName: string | undefined;
45
+ agents: string[];
45
46
  items: TaskItem[];
46
47
  depthCapacity: boolean;
47
48
  ircEnabled: boolean;
@@ -0,0 +1,4 @@
1
+ import type { ModelRegistry } from "../config/model-registry.js";
2
+ import type { Settings } from "../config/settings.js";
3
+ /** Compresses a delegated assignment into a one-sentence UI label via the tiny title model — fired by the executor spawn path because the task wire schema no longer carries a `description`; null on empty input or failure. */
4
+ export declare function generateTaskLabel(assignment: string, registry: ModelRegistry, settings: Settings, sessionId?: string): Promise<string | null>;
@@ -2,7 +2,7 @@
2
2
  * Repair double-encoded JSON string arguments for the task tool.
3
3
  *
4
4
  * Models occasionally JSON-escape a string value twice when emitting a
5
- * `task` tool call, so an `assignment` that should read
5
+ * `task` tool call, so a `task` field that should read
6
6
  *
7
7
  * # Role
8
8
  * You are a judge … "describe this" … return —
@@ -24,7 +24,8 @@
24
24
  * string.
25
25
  *
26
26
  * This is deliberately scoped to the task tool's natural-language fields
27
- * (`assignment`, `description`). It is NOT applied to code-bearing
27
+ * (`task`, shared `context`); identifier fields (`name`, `agent`)
28
+ * are never repaired. It is NOT applied to code-bearing
28
29
  * tools (write/edit/bash/search), where a backslash or quote is load-bearing
29
30
  * and a false-positive unescape would silently corrupt a file or command.
30
31
  */
@@ -43,11 +44,10 @@ import type { TaskParams } from "./types.js";
43
44
  */
44
45
  export declare function repairDoubleEncodedJsonString(value: string): string;
45
46
  /**
46
- * Repair double-encoded prose in task-tool params (`assignment`,
47
- * `description`, shared `context`, and each batch task item's prose fields).
48
- * Returns the same reference when nothing changed so callers can cheaply skip
49
- * work. Defensive against partially-streamed args (missing/undefined fields,
50
- * partial task arrays) so it is safe on the render path as well as on
51
- * execution.
47
+ * Repair double-encoded prose in task-tool params (flat `task`, shared
48
+ * `context`, and each batch task item's `task`). Returns the same reference
49
+ * when nothing changed so callers can cheaply skip work. Defensive against
50
+ * partially-streamed args (missing/undefined fields, partial task arrays) so
51
+ * it is safe on the render path as well as on execution.
52
52
  */
53
53
  export declare function repairTaskParams(params: TaskParams): TaskParams;
@@ -1,7 +1,7 @@
1
- import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
2
1
  import type { Usage } from "@oh-my-pi/pi-ai";
3
2
  import { type BaseType } from "arktype";
4
3
  import type { AgentSessionEvent } from "../session/agent-session.js";
4
+ import type { ConfiguredThinkingLevel } from "../thinking.js";
5
5
  import type { NestedRepoPatch } from "./worktree.js";
6
6
  /** Source of an agent definition */
7
7
  export type AgentSource = "bundled" | "user" | "project";
@@ -52,67 +52,52 @@ export interface SubagentLifecyclePayload {
52
52
  detached?: boolean;
53
53
  }
54
54
  /** Display cap for a normalized one-line label (roster line, registry `displayName`, prompt field). */
55
- export declare const ROLE_LABEL_MAX = 80;
56
- /** Schema bound on the raw `role` input, before it is label-normalized at every use site. */
57
- export declare const ROLE_INPUT_MAX = 256;
55
+ export declare const LABEL_MAX = 80;
58
56
  export declare const taskItemSchema: import("arktype/internal/variants/object.ts").ObjectType<{
59
- id?: string | undefined;
60
- description?: string | undefined;
61
- role?: string | undefined;
62
- assignment: string;
57
+ name?: string | undefined;
58
+ agent: import("arktype/internal/attributes.ts").Default<string, "task">;
59
+ task: string;
63
60
  }, {}>;
64
61
  /** Single task item. Fields are optional defensively: args stream in token by token. */
65
62
  export interface TaskItem {
66
- /** Stable agent id; default = generated AdjectiveNoun. */
67
- id?: string;
68
- /** UI label, not seen by the subagent. */
69
- description?: string;
70
- /** Specialist role/expertise this subagent embodies; shapes its system-prompt identity and display name. */
71
- role?: string;
63
+ /** Stable agent name; becomes the registry/IRC id. Default = generated AdjectiveNoun. */
64
+ name?: string;
65
+ /** Agent type to run this item (e.g. "scout"). Defaults to the spawn policy's default agent. */
66
+ agent?: string;
72
67
  /** The work; required by the schema. */
73
- assignment?: string;
68
+ task?: string;
74
69
  /** Run this spawn in an isolated worktree (batch form; flat form carries it top-level). */
75
70
  isolated?: boolean;
76
71
  }
77
72
  export declare const taskSchema: import("arktype/internal/variants/object.ts").ObjectType<{
73
+ name?: string | undefined;
78
74
  agent: import("arktype/internal/attributes.ts").Default<string, "task">;
79
- id?: string | undefined;
80
- description?: string | undefined;
81
- role?: string | undefined;
82
- assignment: string;
75
+ task: string;
83
76
  isolated?: boolean | undefined;
84
77
  }, {}>;
85
78
  declare const ALL_TASK_SCHEMAS: readonly [import("arktype/internal/variants/object.ts").ObjectType<{
79
+ name?: string | undefined;
86
80
  agent: import("arktype/internal/attributes.ts").Default<string, "task">;
87
- id?: string | undefined;
88
- description?: string | undefined;
89
- role?: string | undefined;
90
- assignment: string;
81
+ task: string;
91
82
  isolated?: boolean | undefined;
92
83
  }, {}>, import("arktype/internal/variants/object.ts").ObjectType<{
84
+ name?: string | undefined;
93
85
  agent: import("arktype/internal/attributes.ts").Default<string, "task">;
94
- id?: string | undefined;
95
- description?: string | undefined;
96
- role?: string | undefined;
97
- assignment: string;
86
+ task: string;
98
87
  }, {}>, import("arktype/internal/variants/object.ts").ObjectType<{
99
- agent: import("arktype/internal/attributes.ts").Default<string, "task">;
100
88
  context: string;
101
89
  tasks: {
102
- id?: string | undefined;
103
- description?: string | undefined;
104
- role?: string | undefined;
105
- assignment: string;
90
+ name?: string | undefined;
91
+ agent: import("arktype/internal/attributes.ts").Default<string, "task">;
92
+ task: string;
106
93
  isolated?: boolean | undefined;
107
94
  }[];
108
95
  }, {}>, import("arktype/internal/variants/object.ts").ObjectType<{
109
- agent: import("arktype/internal/attributes.ts").Default<string, "task">;
110
96
  context: string;
111
97
  tasks: {
112
- id?: string | undefined;
113
- description?: string | undefined;
114
- role?: string | undefined;
115
- assignment: string;
98
+ name?: string | undefined;
99
+ agent: import("arktype/internal/attributes.ts").Default<string, "task">;
100
+ task: string;
116
101
  }[];
117
102
  }, {}>];
118
103
  type DynamicTaskSchema = (typeof ALL_TASK_SCHEMAS)[number];
@@ -130,21 +115,17 @@ export declare function getTaskSchema(options: {
130
115
  }): TaskToolSchemaInstance;
131
116
  /**
132
117
  * Runtime params union over both wire shapes. The model sees exactly one shape
133
- * (`{ agent, context, tasks[] }` when `task.batch` is on, `{ agent, ...item }`
118
+ * (`{ context, tasks[] }` when `task.batch` is on, `{ name?, agent?, task }`
134
119
  * otherwise); runtime stays permissive so internal callers and stale
135
120
  * transcripts using the flat form keep working under either setting.
136
121
  */
137
122
  export interface TaskParams {
138
- /** Agent type to spawn; omitted values resolve from the session spawn policy. */
123
+ /** Stable agent name (flat form). */
124
+ name?: string;
125
+ /** Agent type to spawn (flat form); omitted values resolve from the session spawn policy. */
139
126
  agent?: string;
140
- /** Stable agent id (flat form); default = generated AdjectiveNoun. */
141
- id?: string;
142
- /** UI label (flat form), not seen by the subagent. */
143
- description?: string;
144
- /** Specialist role/expertise this subagent embodies; shapes its system-prompt identity and display name. */
145
- role?: string;
146
127
  /** The work (flat form). */
147
- assignment?: string;
128
+ task?: string;
148
129
  /** Batch form (`task.batch`): one subagent per item. */
149
130
  tasks?: TaskItem[];
150
131
  /** Batch form: shared background prepended to every assignment; required by the batch schema. */
@@ -157,17 +138,11 @@ export interface TaskParams {
157
138
  * `displayName`, or a system-prompt field. Collapses every run of whitespace
158
139
  * AND control/format characters — including U+0085 NEL, ESC/ANSI, and the
159
140
  * zero-width separators that `\s` misses — to a single space, then caps length.
160
- * So untrusted text (a spawn `role`, a peer activity gist) can neither break the
161
- * line, inject prompt structure, nor smuggle terminal escapes. Caps at `max`
162
- * characters (clamped to >= 1; default `ROLE_LABEL_MAX`), appending an ellipsis when truncated.
141
+ * So untrusted text (a generated task label, a peer activity gist) can neither
142
+ * break the line, inject prompt structure, nor smuggle terminal escapes. Caps at
143
+ * `max` characters (clamped to >= 1; default `LABEL_MAX`), appending an ellipsis when truncated.
163
144
  */
164
145
  export declare function oneLineLabel(text: string, max?: number): string;
165
- /**
166
- * Display name for a spawned subagent: its tailored `role` (label-normalized)
167
- * when one is given, else the agent type's name. Empty/whitespace roles fall
168
- * back to the agent name.
169
- */
170
- export declare function resolveSubagentDisplayName(role: string | undefined, agentName: string): string;
171
146
  /**
172
147
  * Whether an agent at `taskDepth` may still spawn children — i.e. it currently
173
148
  * holds the `task` tool. Mirrors the task-tool availability gate;
@@ -203,7 +178,7 @@ export interface AgentDefinition {
203
178
  tools?: string[];
204
179
  spawns?: string[] | "*";
205
180
  model?: string[];
206
- thinkingLevel?: ThinkingLevel;
181
+ thinkingLevel?: ConfiguredThinkingLevel;
207
182
  output?: unknown;
208
183
  blocking?: boolean;
209
184
  autoloadSkills?: string[];
@@ -24,9 +24,11 @@ declare const askSchema: import("arktype/internal/variants/object.ts").ObjectTyp
24
24
  questions: {
25
25
  id: string;
26
26
  question: string;
27
+ header?: string | undefined;
27
28
  options: {
28
29
  label: string;
29
30
  description?: string | undefined;
31
+ preview?: string | undefined;
30
32
  }[];
31
33
  multi?: boolean | undefined;
32
34
  recommended?: number | undefined;
@@ -41,6 +43,8 @@ export interface QuestionResult {
41
43
  multi: boolean;
42
44
  selectedOptions: string[];
43
45
  customInput?: string;
46
+ /** Optional note attached to the selected answer in the rich ask dialog. */
47
+ note?: string;
44
48
  /** True when the answer was auto-selected because the dialog timed out. */
45
49
  timedOut?: boolean;
46
50
  }
@@ -50,10 +54,16 @@ export interface AskToolDetails {
50
54
  multi?: boolean;
51
55
  selectedOptions?: string[];
52
56
  customInput?: string;
57
+ /** Optional note attached to the selected answer in the rich ask dialog. */
58
+ note?: string;
53
59
  /** True when the answer was auto-selected because the dialog timed out. */
54
60
  timedOut?: boolean;
55
61
  /** Multi-part question mode */
56
62
  results?: QuestionResult[];
63
+ /** Chat redirect: the user chose "Chat about this" instead of answering. */
64
+ chatRedirect?: boolean;
65
+ /** Questions surfaced when chatRedirect is true. */
66
+ questions?: string[];
57
67
  }
58
68
  type AskParams = AskToolInput;
59
69
  /**
@@ -74,9 +84,11 @@ export declare class AskTool implements AgentTool<typeof askSchema, AskToolDetai
74
84
  questions: {
75
85
  id: string;
76
86
  question: string;
87
+ header?: string | undefined;
77
88
  options: {
78
89
  label: string;
79
90
  description?: string | undefined;
91
+ preview?: string | undefined;
80
92
  }[];
81
93
  multi?: boolean | undefined;
82
94
  recommended?: number | undefined;
@@ -117,6 +117,14 @@ export interface ParsedConflictUri {
117
117
  * `@both` shorthand) to every currently-registered conflict in one shot.
118
118
  */
119
119
  export declare function parseConflictUri(raw: string): ParsedConflictUri | null;
120
+ /** Result of {@link spliceConflict}: the new file text plus any boundary-echo repair applied. */
121
+ export interface ConflictSplice {
122
+ text: string;
123
+ /** Replacement lines dropped because they duplicated the context directly above the region. */
124
+ trimmedLeading: number;
125
+ /** Replacement lines dropped because they duplicated the context directly below the region. */
126
+ trimmedTrailing: number;
127
+ }
120
128
  /**
121
129
  * Splice the conflict region recorded in `entry` out of `originalText`
122
130
  * and replace it with `replacement` (markers and all sides included).
@@ -126,8 +134,16 @@ export declare function parseConflictUri(raw: string): ParsedConflictUri | null;
126
134
  * match), so out-of-band edits earlier in the file that shift line
127
135
  * numbers don't break resolution. Throws clearly when the marker block
128
136
  * has actually been altered or removed.
137
+ *
138
+ * Boundary-echo repair (same philosophy as the edit tool's hashline
139
+ * keeper repair): models frequently paste the "whole resolved function"
140
+ * including the lines that live directly before/after the marker block,
141
+ * which the verbatim splice would duplicate. Replacement lines that
142
+ * exactly echo the adjacent context are dropped when the echo is
143
+ * unambiguous — two or more consecutive lines, or a single line whose
144
+ * removal fixes a delimiter-balance mismatch against the recorded sides.
129
145
  */
130
- export declare function spliceConflict(originalText: string, entry: ConflictEntry, replacement: string): string;
146
+ export declare function spliceConflict(originalText: string, entry: ConflictEntry, replacement: string): ConflictSplice;
131
147
  /**
132
148
  * True when two registered blocks record the same marker-block content
133
149
  * (labels and all sides). Out-of-band edits can shift a block's line
@@ -19,12 +19,28 @@ interface JobSnapshot {
19
19
  errorText?: string;
20
20
  }
21
21
  type CancelStatus = "cancelled" | "not_found" | "already_completed";
22
+ /**
23
+ * A live subagent from the AgentRegistry that has no backing job in the
24
+ * AsyncJobManager — e.g. an idle agent woken (or a parked agent revived) via
25
+ * `irc`, or a spawn owned by another agent. Surfaced by `list` and empty-poll
26
+ * snapshots so the job tool's picture matches the UI's running-agent count.
27
+ */
28
+ interface AgentActivitySnapshot {
29
+ id: string;
30
+ parentId?: string;
31
+ /** Latest activity gist recorded by the registry (display-only). */
32
+ activity?: string;
33
+ /** Time since the agent was registered. */
34
+ ageMs: number;
35
+ }
22
36
  export interface JobToolDetails {
23
37
  jobs: JobSnapshot[];
24
38
  cancelled?: {
25
39
  id: string;
26
40
  status: CancelStatus;
27
41
  }[];
42
+ /** Running subagents not represented by a job row in this result. */
43
+ agents?: AgentActivitySnapshot[];
28
44
  }
29
45
  /**
30
46
  * A poll snapshot where every watched job is still running and nothing was
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-coding-agent",
4
- "version": "16.4.4",
4
+ "version": "16.4.6",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -52,17 +52,17 @@
52
52
  "@agentclientprotocol/sdk": "0.25.0",
53
53
  "@babel/parser": "^7.29.7",
54
54
  "@mozilla/readability": "^0.6.0",
55
- "@oh-my-pi/hashline": "16.4.4",
56
- "@oh-my-pi/omp-stats": "16.4.4",
57
- "@oh-my-pi/pi-agent-core": "16.4.4",
58
- "@oh-my-pi/pi-ai": "16.4.4",
59
- "@oh-my-pi/pi-catalog": "16.4.4",
60
- "@oh-my-pi/pi-mnemopi": "16.4.4",
61
- "@oh-my-pi/pi-natives": "16.4.4",
62
- "@oh-my-pi/pi-tui": "16.4.4",
63
- "@oh-my-pi/pi-utils": "16.4.4",
64
- "@oh-my-pi/pi-wire": "16.4.4",
65
- "@oh-my-pi/snapcompact": "16.4.4",
55
+ "@oh-my-pi/hashline": "16.4.6",
56
+ "@oh-my-pi/omp-stats": "16.4.6",
57
+ "@oh-my-pi/pi-agent-core": "16.4.6",
58
+ "@oh-my-pi/pi-ai": "16.4.6",
59
+ "@oh-my-pi/pi-catalog": "16.4.6",
60
+ "@oh-my-pi/pi-mnemopi": "16.4.6",
61
+ "@oh-my-pi/pi-natives": "16.4.6",
62
+ "@oh-my-pi/pi-tui": "16.4.6",
63
+ "@oh-my-pi/pi-utils": "16.4.6",
64
+ "@oh-my-pi/pi-wire": "16.4.6",
65
+ "@oh-my-pi/snapcompact": "16.4.6",
66
66
  "@opentelemetry/api": "^1.9.1",
67
67
  "@opentelemetry/context-async-hooks": "^2.7.1",
68
68
  "@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
@@ -99,7 +99,6 @@ async function main(): Promise<void> {
99
99
  outfile: outputPath,
100
100
  transformersVersion,
101
101
  target: crossBuild?.target,
102
- external: ["fastembed", "onnxruntime-node"],
103
102
  skipBuiltinCodesign: shouldAdhocSignDarwinBinary(crossBuild),
104
103
  });
105
104
 
@@ -1,6 +1,9 @@
1
1
  import { buildDocsIndexPayload } from "./generate-docs-index";
2
2
  import { createLegacyPiVirtualModulePlugin } from "./legacy-pi-virtual-module";
3
3
 
4
+ /** Native runtime dependencies always resolved from the on-demand install instead of embedded into compiled binaries. */
5
+ export const COMPILED_EXTERNAL_DEPENDENCIES: readonly string[] = Object.freeze(["fastembed", "onnxruntime-node"]);
6
+
4
7
  /** Inputs shared by local and release coding-agent binary builds. */
5
8
  export interface CodingAgentCompileOptions {
6
9
  /** Absolute repository root used for package resolution. */
@@ -13,8 +16,6 @@ export interface CodingAgentCompileOptions {
13
16
  readonly transformersVersion: string;
14
17
  /** Optional cross-compilation runtime target. */
15
18
  readonly target?: Bun.Build.CompileTarget;
16
- /** Dependencies intentionally resolved from the runtime filesystem. */
17
- readonly external?: readonly string[];
18
19
  /** Match release builds that minify identifiers while retaining names. */
19
20
  readonly minifyIdentifiers?: boolean;
20
21
  /** Disable Bun's built-in Darwin signing before the caller re-signs. */
@@ -34,7 +35,7 @@ export async function compileCodingAgent(options: CodingAgentCompileOptions): Pr
34
35
  const output = await Bun.build({
35
36
  entrypoints: [options.entrypoint],
36
37
  root: options.repoRoot,
37
- external: options.external ? [...options.external] : undefined,
38
+ external: [...COMPILED_EXTERNAL_DEPENDENCIES],
38
39
  define: {
39
40
  "process.env.PI_COMPILED": JSON.stringify("true"),
40
41
  "process.env.PI_TINY_TRANSFORMERS_VERSION": JSON.stringify(options.transformersVersion),
@@ -44,6 +44,12 @@ export interface AsyncJob {
44
44
  * supply an id (e.g. legacy tests, SDK consumers without an agent context).
45
45
  */
46
46
  ownerId?: string;
47
+ /**
48
+ * Registry id of the subagent this job runs (task/tan/vibe jobs). Lets
49
+ * job-view code link a job row to its AgentRegistry ref even when the job
50
+ * id differs from the agent id (vibe turn jobs, tan clones).
51
+ */
52
+ agentId?: string;
47
53
  /**
48
54
  * Job is registered but parked behind a caller-managed gate (e.g. a task
49
55
  * batch semaphore). Queued jobs do not count toward the running-job limit
@@ -79,6 +85,8 @@ export interface AsyncJobRegisterOptions {
79
85
  id?: string;
80
86
  /** Registry id of the agent that owns this job; used to scope cancelAll. */
81
87
  ownerId?: string;
88
+ /** Registry id of the subagent this job runs; see {@link AsyncJob.agentId}. */
89
+ agentId?: string;
82
90
  onProgress?: (text: string, details?: Record<string, unknown>) => void | Promise<void>;
83
91
  /** Register the job in queued state; see {@link AsyncJob.queued}. */
84
92
  queued?: boolean;
@@ -192,6 +200,7 @@ export class AsyncJobManager {
192
200
  abortController,
193
201
  promise: Promise.resolve(),
194
202
  ownerId: options?.ownerId,
203
+ agentId: options?.agentId,
195
204
  queued: options?.queued === true,
196
205
  };
197
206