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

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 (89) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/cli.js +3563 -3540
  3. package/dist/types/async/job-manager.d.ts +8 -0
  4. package/dist/types/config/settings-schema.d.ts +18 -8
  5. package/dist/types/config/settings.d.ts +3 -2
  6. package/dist/types/discovery/helpers.d.ts +2 -2
  7. package/dist/types/extensibility/extensions/types.d.ts +36 -0
  8. package/dist/types/irc/bus.d.ts +4 -0
  9. package/dist/types/modes/components/__tests__/pause-screen.test.d.ts +1 -0
  10. package/dist/types/modes/components/ask-dialog.d.ts +27 -0
  11. package/dist/types/modes/components/index.d.ts +2 -1
  12. package/dist/types/modes/components/model-browser.d.ts +97 -0
  13. package/dist/types/modes/components/model-hub.d.ts +51 -0
  14. package/dist/types/modes/components/pause-screen.d.ts +43 -0
  15. package/dist/types/modes/components/session-selector.d.ts +13 -0
  16. package/dist/types/modes/components/tool-execution.d.ts +2 -0
  17. package/dist/types/modes/controllers/extension-ui-controller.d.ts +2 -1
  18. package/dist/types/modes/interactive-mode.d.ts +1 -0
  19. package/dist/types/modes/shared.d.ts +1 -1
  20. package/dist/types/modes/types.d.ts +2 -0
  21. package/dist/types/session/agent-session.d.ts +1 -1
  22. package/dist/types/session/session-context.d.ts +9 -0
  23. package/dist/types/task/executor.d.ts +26 -13
  24. package/dist/types/task/index.d.ts +12 -11
  25. package/dist/types/task/label.d.ts +4 -0
  26. package/dist/types/task/repair-args.d.ts +8 -8
  27. package/dist/types/task/types.d.ts +31 -56
  28. package/dist/types/tools/ask.d.ts +12 -0
  29. package/dist/types/tools/conflict-detect.d.ts +17 -1
  30. package/dist/types/tools/job.d.ts +16 -0
  31. package/package.json +12 -12
  32. package/src/async/job-manager.ts +9 -0
  33. package/src/commit/agentic/tools/analyze-file.ts +2 -3
  34. package/src/config/settings-schema.ts +17 -6
  35. package/src/config/settings.ts +13 -4
  36. package/src/discovery/helpers.ts +3 -4
  37. package/src/extensibility/custom-tools/loader.ts +70 -37
  38. package/src/extensibility/extensions/types.ts +46 -0
  39. package/src/irc/bus.ts +61 -20
  40. package/src/modes/components/__tests__/pause-screen.test.ts +143 -0
  41. package/src/modes/components/advisor-config.ts +30 -22
  42. package/src/modes/components/ask-dialog.ts +888 -0
  43. package/src/modes/components/index.ts +2 -1
  44. package/src/modes/components/model-browser.ts +688 -0
  45. package/src/modes/components/model-hub.ts +1655 -0
  46. package/src/modes/components/pause-screen.ts +208 -0
  47. package/src/modes/components/session-selector.ts +299 -42
  48. package/src/modes/components/tool-execution.ts +2 -0
  49. package/src/modes/controllers/event-controller.ts +8 -2
  50. package/src/modes/controllers/extension-ui-controller.ts +252 -5
  51. package/src/modes/controllers/selector-controller.ts +143 -87
  52. package/src/modes/controllers/tan-command-controller.ts +1 -1
  53. package/src/modes/interactive-mode.ts +3 -0
  54. package/src/modes/shared.ts +1 -1
  55. package/src/modes/theme/theme.ts +3 -3
  56. package/src/modes/types.ts +2 -0
  57. package/src/modes/utils/ui-helpers.ts +31 -4
  58. package/src/prompts/agents/scout.md +0 -1
  59. package/src/prompts/agents/task.md +1 -1
  60. package/src/prompts/system/subagent-system-prompt.md +1 -5
  61. package/src/prompts/system/subagent-yield-reminder.md +10 -0
  62. package/src/prompts/system/task-label.md +23 -0
  63. package/src/prompts/tools/job.md +1 -1
  64. package/src/prompts/tools/task-summary.md +3 -0
  65. package/src/prompts/tools/task.md +17 -18
  66. package/src/session/agent-session.ts +2 -1
  67. package/src/session/session-context.test.ts +73 -0
  68. package/src/session/session-context.ts +43 -26
  69. package/src/slash-commands/builtin-registry.ts +9 -0
  70. package/src/task/agents.ts +2 -0
  71. package/src/task/executor.ts +159 -46
  72. package/src/task/index.ts +377 -239
  73. package/src/task/label.ts +38 -0
  74. package/src/task/render.ts +74 -22
  75. package/src/task/repair-args.ts +20 -31
  76. package/src/task/spawn-policy.test.ts +4 -4
  77. package/src/task/types.ts +46 -66
  78. package/src/tools/ask.ts +233 -40
  79. package/src/tools/conflict-detect.ts +102 -5
  80. package/src/tools/index.ts +1 -0
  81. package/src/tools/irc.ts +20 -11
  82. package/src/tools/job.ts +158 -18
  83. package/src/tools/write.ts +70 -6
  84. package/src/vibe/runtime.ts +1 -1
  85. package/src/web/search/providers/browser-headers.ts +30 -13
  86. package/dist/types/modes/components/model-selector.d.ts +0 -37
  87. package/dist/types/tools/bash-command-fixup.d.ts +0 -3
  88. package/src/modes/components/model-selector.ts +0 -1291
  89. package/src/tools/bash-command-fixup.ts +0 -4
@@ -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.5",
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.5",
56
+ "@oh-my-pi/omp-stats": "16.4.5",
57
+ "@oh-my-pi/pi-agent-core": "16.4.5",
58
+ "@oh-my-pi/pi-ai": "16.4.5",
59
+ "@oh-my-pi/pi-catalog": "16.4.5",
60
+ "@oh-my-pi/pi-mnemopi": "16.4.5",
61
+ "@oh-my-pi/pi-natives": "16.4.5",
62
+ "@oh-my-pi/pi-tui": "16.4.5",
63
+ "@oh-my-pi/pi-utils": "16.4.5",
64
+ "@oh-my-pi/pi-wire": "16.4.5",
65
+ "@oh-my-pi/snapcompact": "16.4.5",
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",
@@ -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
 
@@ -83,10 +83,9 @@ export function createAnalyzeFileTool(options: {
83
83
  related_files: relatedFiles,
84
84
  });
85
85
  const taskParams: TaskParams = {
86
+ name: `AnalyzeFile${index + 1}`,
86
87
  agent: "sonic",
87
- id: `AnalyzeFile${index + 1}`,
88
- description: `Analyze ${file}`,
89
- assignment,
88
+ task: assignment,
90
89
  };
91
90
  return taskTool.execute(`${toolCallId}-${index + 1}`, taskParams, signal);
92
91
  }),
@@ -3633,6 +3633,17 @@ export const SETTINGS_SCHEMA = {
3633
3633
  },
3634
3634
  },
3635
3635
 
3636
+ "ask.enabled": {
3637
+ type: "boolean",
3638
+ default: true,
3639
+ ui: {
3640
+ tab: "tools",
3641
+ group: "Available Tools",
3642
+ label: "Ask",
3643
+ description: "Enable the ask tool for interactive user questions",
3644
+ },
3645
+ },
3646
+
3636
3647
  "browser.enabled": {
3637
3648
  type: "boolean",
3638
3649
  default: true,
@@ -4142,31 +4153,31 @@ export const SETTINGS_SCHEMA = {
4142
4153
 
4143
4154
  "task.softRequestBudget": {
4144
4155
  type: "number",
4145
- default: 90,
4156
+ default: 200,
4146
4157
  ui: {
4147
4158
  tab: "tasks",
4148
4159
  group: "Subagents",
4149
4160
  label: "Soft Subagent Request Budget",
4150
4161
  description:
4151
- "Soft per-subagent request budget (assistant requests per run). Crossing it can inject a steering notice when task.softRequestBudgetNotice is enabled; at 1.5x the budget the run is aborted gracefully, salvaging partial output. 0 disables the guard. Bundled scout/sonic agents use a lower built-in budget.",
4162
+ "Soft per-subagent request budget (assistant requests per run). Crossing it injects a wrap-up steering notice (see task.softRequestBudgetNotice); at 1.5x the budget the run is force-stopped and the agent must yield its partial findings. 0 disables the guard. Bundled scout/sonic agents use a lower built-in budget.",
4152
4163
  options: [
4153
4164
  { value: "0", label: "Disabled" },
4154
- { value: "40", label: "40 requests" },
4155
- { value: "90", label: "90 requests", description: "Default" },
4165
+ { value: "90", label: "90 requests" },
4156
4166
  { value: "150", label: "150 requests" },
4167
+ { value: "200", label: "200 requests", description: "Default" },
4157
4168
  ],
4158
4169
  },
4159
4170
  },
4160
4171
 
4161
4172
  "task.softRequestBudgetNotice": {
4162
4173
  type: "boolean",
4163
- default: false,
4174
+ default: true,
4164
4175
  ui: {
4165
4176
  tab: "tasks",
4166
4177
  group: "Subagents",
4167
4178
  label: "Soft Request Budget Notice",
4168
4179
  description:
4169
- "Inject one steering notice when a subagent crosses its soft request budget. Off by default; enabling it asks the child to wrap up before the 1.5x graceful abort guard.",
4180
+ "Inject one steering notice when a subagent crosses its soft request budget, asking it to wrap up before the 1.5x forced-yield stop.",
4170
4181
  },
4171
4182
  },
4172
4183
 
@@ -604,9 +604,10 @@ export class Settings {
604
604
  }
605
605
 
606
606
  /**
607
- * Set a model role (helper for modelRoles record).
607
+ * Set a model role (helper for modelRoles record). Passing `undefined`
608
+ * clears the role from the persisted record and any runtime override.
608
609
  */
609
- setModelRole(role: ModelRole | string, modelId: string): void {
610
+ setModelRole(role: ModelRole | string, modelId: string | undefined): void {
610
611
  const current = this.#modelRolesFromLayer(this.#global);
611
612
  const runtimeOverrides = getByPath(this.#overrides, ["modelRoles"]);
612
613
  const updateRuntimeOverride =
@@ -615,12 +616,20 @@ export class Settings {
615
616
  !Array.isArray(runtimeOverrides) &&
616
617
  Object.hasOwn(runtimeOverrides, role);
617
618
 
618
- current[role] = modelId;
619
+ if (modelId === undefined) {
620
+ delete current[role];
621
+ } else {
622
+ current[role] = modelId;
623
+ }
619
624
  this.set("modelRoles", current);
620
625
 
621
626
  if (updateRuntimeOverride) {
622
627
  const nextRuntimeOverride = this.#modelRolesFromLayer(this.#overrides);
623
- nextRuntimeOverride[role] = modelId;
628
+ if (modelId === undefined) {
629
+ delete nextRuntimeOverride[role];
630
+ } else {
631
+ nextRuntimeOverride[role] = modelId;
632
+ }
624
633
  this.override("modelRoles", nextRuntimeOverride);
625
634
  }
626
635
  }
@@ -1,7 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as os from "node:os";
3
3
  import * as path from "node:path";
4
- import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
5
4
  import { FileType, glob } from "@oh-my-pi/pi-natives";
6
5
  import {
7
6
  CONFIG_DIR_NAME,
@@ -17,7 +16,7 @@ import { invalidate as invalidateFsCache, readDirEntries, readFile } from "../ca
17
16
  import { parseRuleConditionAndScope, type Rule, type RuleFrontmatter } from "../capability/rule";
18
17
  import type { Skill, SkillFrontmatter } from "../capability/skill";
19
18
  import type { LoadContext, LoadResult, SourceMeta } from "../capability/types";
20
- import { parseThinkingLevel } from "../thinking";
19
+ import { type ConfiguredThinkingLevel, parseConfiguredThinkingLevel } from "../thinking";
21
20
  import { normalizeToolNames } from "../tools/builtin-names";
22
21
 
23
22
  import { buildPluginDirRoot } from "./plugin-dir-roots";
@@ -229,7 +228,7 @@ export interface ParsedAgentFields {
229
228
  spawns?: string[] | "*";
230
229
  model?: string[];
231
230
  output?: unknown;
232
- thinkingLevel?: ThinkingLevel;
231
+ thinkingLevel?: ConfiguredThinkingLevel;
233
232
  autoloadSkills?: string[];
234
233
  readSummarize?: boolean;
235
234
  blocking?: boolean;
@@ -283,7 +282,7 @@ export function parseAgentFields(frontmatter: Record<string, unknown>): ParsedAg
283
282
  ? frontmatter.thinking
284
283
  : undefined;
285
284
 
286
- const thinkingLevel = parseThinkingLevel(rawThinkingLevel);
285
+ const thinkingLevel = parseConfiguredThinkingLevel(rawThinkingLevel);
287
286
  const model = parseModelList(frontmatter.model);
288
287
  const blocking = parseBoolean(frontmatter.blocking);
289
288
  const readSummarize = parseBoolean(frontmatter.readSummarize);