pi-ultracode 0.1.0 → 0.1.2
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.
- package/README.md +401 -148
- package/extensions/ultracode.ts +134 -16
- package/package.json +22 -1
- package/src/commands.ts +32 -84
- package/src/index.ts +20 -0
- package/src/mode.ts +445 -34
- package/src/prompts.ts +2 -2
- package/src/thinking.ts +27 -0
- package/src/workflow/agent-runner.ts +903 -53
- package/src/workflow/agent-types.ts +3 -6
- package/src/workflow/display-text.ts +461 -0
- package/src/workflow/display.ts +188 -55
- package/src/workflow/journal.ts +0 -0
- package/src/workflow/registry.ts +106 -10
- package/src/workflow/run-details.ts +1276 -0
- package/src/workflow/runtime.ts +189 -38
- package/src/workflow/tool.ts +285 -47
- package/src/workflow/workflow-overlay.ts +719 -0
- package/types/workflow.d.ts +1 -1
package/src/mode.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Ultracode mode controller.
|
|
3
3
|
*
|
|
4
4
|
* Ultracode is a session-scoped effort mode. While on, it:
|
|
5
|
-
* - raises the thinking level to
|
|
5
|
+
* - raises the thinking level to the model's maximum (remembering the previous level),
|
|
6
6
|
* - keeps the `workflow` tool active,
|
|
7
7
|
* - injects a standing "author and run a workflow by default" system block on
|
|
8
8
|
* every turn, plus an optional token budget,
|
|
@@ -12,8 +12,14 @@
|
|
|
12
12
|
|
|
13
13
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
14
14
|
import { ULTRACODE_ACTIVE_REMINDER, ULTRACODE_TAGLINE, ultracodeSystemBlock } from "./prompts.ts";
|
|
15
|
+
import {
|
|
16
|
+
LEGACY_ULTRACODE_THINKING_LEVEL,
|
|
17
|
+
ULTRACODE_THINKING_LEVEL,
|
|
18
|
+
isThinkingLevel,
|
|
19
|
+
type ThinkingLevel,
|
|
20
|
+
} from "./thinking.ts";
|
|
15
21
|
|
|
16
|
-
export type ThinkingLevel
|
|
22
|
+
export type { ThinkingLevel } from "./thinking.ts";
|
|
17
23
|
|
|
18
24
|
export const MODE_ENTRY_TYPE = "ultracode-mode";
|
|
19
25
|
|
|
@@ -21,20 +27,76 @@ interface PersistedModeState {
|
|
|
21
27
|
enabled: boolean;
|
|
22
28
|
budgetTotal: number | null;
|
|
23
29
|
previousThinking?: ThinkingLevel;
|
|
30
|
+
/** `null` records that the setting was originally absent (Pi defaults to medium). */
|
|
31
|
+
previousDefaultThinking?: ThinkingLevel | null;
|
|
32
|
+
/** Deferred when the selected model cannot represent the pre-mode level. */
|
|
33
|
+
pendingPreviousThinking?: ThinkingLevel;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ThinkingPreferenceSnapshot {
|
|
37
|
+
/** Raw global value; undefined means Pi's implicit medium default. */
|
|
38
|
+
global: ThinkingLevel | undefined;
|
|
39
|
+
/** Global + project merged value used by Pi for model switches. */
|
|
40
|
+
effective: ThinkingLevel | undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Adapter for preserving Pi's raw global effort preference while this mode is active. */
|
|
44
|
+
export interface ThinkingPreferenceStore {
|
|
45
|
+
getThinkingPreference(): ThinkingPreferenceSnapshot;
|
|
46
|
+
setDefaultThinkingLevel(level: ThinkingLevel | undefined): void | Promise<void>;
|
|
47
|
+
flush?(): Promise<void>;
|
|
24
48
|
}
|
|
25
49
|
|
|
26
50
|
export class UltracodeMode {
|
|
27
51
|
private enabled = false;
|
|
52
|
+
private suspended = false;
|
|
28
53
|
private budgetTotal: number | null = null;
|
|
29
54
|
private previousThinking: ThinkingLevel | undefined;
|
|
30
|
-
|
|
55
|
+
private previousDefaultThinking: ThinkingLevel | null | undefined;
|
|
56
|
+
/** Restore a level later if the current non-reasoning model clamps it to off. */
|
|
57
|
+
private pendingPreviousThinking: ThinkingLevel | undefined;
|
|
58
|
+
/** The level Pi actually applied after clamping the maximum request. */
|
|
31
59
|
private appliedThinking: ThinkingLevel | undefined;
|
|
60
|
+
/** Prevent mode-owned thinking changes from being mistaken for manual overrides. */
|
|
61
|
+
private applyingThinking = false;
|
|
62
|
+
private thinkingPreferenceStore: ThinkingPreferenceStore | undefined;
|
|
63
|
+
private preferenceWriteQueue: Promise<void> = Promise.resolve();
|
|
64
|
+
private preferenceWriteGeneration = 0;
|
|
65
|
+
private legacyDefaultMigrationPending = false;
|
|
66
|
+
private pendingClearGeneration = 0;
|
|
67
|
+
private currentModelSupportsThinking: boolean | undefined;
|
|
68
|
+
private runtimeSupportsMaxThinking = true;
|
|
32
69
|
private readonly workflowToolName: string;
|
|
33
70
|
|
|
34
71
|
constructor(workflowToolName: string) {
|
|
35
72
|
this.workflowToolName = workflowToolName;
|
|
36
73
|
}
|
|
37
74
|
|
|
75
|
+
/** Bind the settings adapter once the session cwd is known. */
|
|
76
|
+
bindThinkingPreferenceStore(store: ThinkingPreferenceStore | undefined): void {
|
|
77
|
+
this.preferenceWriteGeneration++;
|
|
78
|
+
this.thinkingPreferenceStore = store;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Track capability explicitly; `off` alone cannot distinguish clamp from intent. */
|
|
82
|
+
setCurrentModelSupportsThinking(supportsThinking: boolean | undefined): void {
|
|
83
|
+
this.currentModelSupportsThinking = supportsThinking;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Configure migration behavior for Pi versions released before `max`. */
|
|
87
|
+
setRuntimeSupportsMaxThinking(supportsMaxThinking: boolean): void {
|
|
88
|
+
this.runtimeSupportsMaxThinking = supportsMaxThinking;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Wait for queued preference restoration before teardown or command completion. */
|
|
92
|
+
async flushThinkingPreference(): Promise<void> {
|
|
93
|
+
try {
|
|
94
|
+
await this.preferenceWriteQueue;
|
|
95
|
+
} catch {
|
|
96
|
+
// Pi owns settings error reporting; effort enforcement must remain usable.
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
38
100
|
/** Enable if off, disable if on. Returns the new enabled state. */
|
|
39
101
|
toggle(pi: ExtensionAPI, opts: { budget?: number | null } = {}): boolean {
|
|
40
102
|
if (this.enabled) {
|
|
@@ -45,29 +107,100 @@ export class UltracodeMode {
|
|
|
45
107
|
return true;
|
|
46
108
|
}
|
|
47
109
|
|
|
48
|
-
/** The thinking level
|
|
110
|
+
/** The thinking level Pi actually applied (`max`, or the model/runtime fallback). */
|
|
49
111
|
getAppliedThinking(): ThinkingLevel | undefined {
|
|
50
112
|
return this.appliedThinking;
|
|
51
113
|
}
|
|
52
114
|
|
|
53
|
-
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
|
|
57
|
-
* different model than the parent. Undefined when off, so subagents fall back
|
|
58
|
-
* to the session default. */
|
|
115
|
+
/**
|
|
116
|
+
* Return the raw maximum request, not the parent's applied value, so every
|
|
117
|
+
* workflow subagent is clamped independently against its own model.
|
|
118
|
+
*/
|
|
59
119
|
getSubagentThinkingLevel(): ThinkingLevel | undefined {
|
|
60
|
-
return this.
|
|
120
|
+
return this.isEnforcing() ? ULTRACODE_THINKING_LEVEL : undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Reassert the maximum before a turn or after a model change. */
|
|
124
|
+
reapplyMaximumThinking(pi: ExtensionAPI): boolean {
|
|
125
|
+
if (!this.isEnforcing()) return false;
|
|
126
|
+
this.applyUltracodeThinking(pi);
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Handle model switches both while active and after a clamped restoration.
|
|
132
|
+
* Returns true when Ultracode remains active and the UI should be refreshed.
|
|
133
|
+
*/
|
|
134
|
+
handleModelSelect(pi: ExtensionAPI): boolean {
|
|
135
|
+
if (this.suspended) return false;
|
|
136
|
+
this.pendingClearGeneration++;
|
|
137
|
+
if (this.enabled) {
|
|
138
|
+
this.applyUltracodeThinking(pi);
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
if (this.pendingPreviousThinking) {
|
|
142
|
+
const pending = this.pendingPreviousThinking;
|
|
143
|
+
this.applyCompatibleThinking(pi, pending);
|
|
144
|
+
if (this.pendingRestoreSucceeded(pending)) this.pendingPreviousThinking = undefined;
|
|
145
|
+
this.persist(pi);
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Restore the pre-mode effective effort without changing persisted mode state. */
|
|
151
|
+
restorePreviousThinking(pi: ExtensionAPI): void {
|
|
152
|
+
if (this.enabled && this.previousThinking) this.applyCompatibleThinking(pi, this.previousThinking);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Stop enforcing synchronously, then restore effort before session teardown. */
|
|
156
|
+
suspend(pi: ExtensionAPI): void {
|
|
157
|
+
if (this.suspended) {
|
|
158
|
+
this.syncWorkflowTool(pi);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
this.suspended = true;
|
|
162
|
+
this.restorePreviousThinking(pi);
|
|
163
|
+
this.syncWorkflowTool(pi);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Enforce the mode after an external thinking-level selection. Stale events
|
|
168
|
+
* and events emitted by this mode are ignored to avoid recursive updates.
|
|
169
|
+
* Returns true when a manual selection was overridden.
|
|
170
|
+
*/
|
|
171
|
+
handleThinkingLevelSelect(pi: ExtensionAPI, level: ThinkingLevel): boolean {
|
|
172
|
+
if (this.suspended || this.applyingThinking) return false;
|
|
173
|
+
const current = safeGetThinking(pi);
|
|
174
|
+
if (!current || current !== level) return false;
|
|
175
|
+
if (!this.enabled) {
|
|
176
|
+
// Pi emits the same event for a user selection and an automatic model
|
|
177
|
+
// re-clamp. Defer clearing until model_select has had a chance to consume it.
|
|
178
|
+
if (this.pendingPreviousThinking) this.deferPendingClear(pi, level);
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
if (current === this.appliedThinking) return false;
|
|
182
|
+
this.applyUltracodeThinking(pi);
|
|
183
|
+
return true;
|
|
61
184
|
}
|
|
62
185
|
|
|
63
186
|
isEnabled(): boolean {
|
|
64
187
|
return this.enabled;
|
|
65
188
|
}
|
|
66
189
|
|
|
190
|
+
isSuspended(): boolean {
|
|
191
|
+
return this.suspended;
|
|
192
|
+
}
|
|
193
|
+
|
|
67
194
|
getBudget(): number | null {
|
|
68
195
|
return this.budgetTotal;
|
|
69
196
|
}
|
|
70
197
|
|
|
198
|
+
/** Keep tool availability aligned with the current mode state. */
|
|
199
|
+
syncWorkflowTool(pi: ExtensionAPI): void {
|
|
200
|
+
if (this.isEnforcing()) this.activateWorkflowTool(pi);
|
|
201
|
+
else this.deactivateWorkflowTool(pi);
|
|
202
|
+
}
|
|
203
|
+
|
|
71
204
|
tagline(): string {
|
|
72
205
|
return ULTRACODE_TAGLINE;
|
|
73
206
|
}
|
|
@@ -75,19 +208,48 @@ export class UltracodeMode {
|
|
|
75
208
|
/** Turn ultracode on. Idempotent. */
|
|
76
209
|
enable(pi: ExtensionAPI, opts: { budget?: number | null } = {}): void {
|
|
77
210
|
if (opts.budget !== undefined) this.budgetTotal = opts.budget;
|
|
211
|
+
this.suspended = false;
|
|
212
|
+
this.pendingPreviousThinking = undefined;
|
|
213
|
+
this.pendingClearGeneration++;
|
|
78
214
|
if (!this.enabled) {
|
|
79
|
-
|
|
215
|
+
const current = safeGetThinking(pi);
|
|
216
|
+
const preference = this.captureThinkingPreference();
|
|
217
|
+
const effectivePreference = this.runtimeCompatibleThinking(preference.effective) as
|
|
218
|
+
| ThinkingLevel
|
|
219
|
+
| undefined;
|
|
220
|
+
const maxIsUnknownToRuntime = !this.runtimeSupportsMaxThinking
|
|
221
|
+
&& preference.effective === ULTRACODE_THINKING_LEVEL;
|
|
222
|
+
// A non-reasoning model exposes only `off`; preserve the merged default
|
|
223
|
+
// that Pi would use when the user later selects a reasoning model. A
|
|
224
|
+
// pre-max Pi also reports a persisted max preference as off.
|
|
225
|
+
this.previousThinking = current === "off"
|
|
226
|
+
&& (this.currentModelSupportsThinking !== true || maxIsUnknownToRuntime)
|
|
227
|
+
? effectivePreference ?? current
|
|
228
|
+
: current;
|
|
229
|
+
this.previousDefaultThinking = this.runtimeCompatibleThinking(preference.global);
|
|
230
|
+
this.legacyDefaultMigrationPending = false;
|
|
80
231
|
this.enabled = true;
|
|
81
232
|
}
|
|
82
|
-
this.
|
|
83
|
-
this.
|
|
233
|
+
this.applyUltracodeThinking(pi);
|
|
234
|
+
this.syncWorkflowTool(pi);
|
|
84
235
|
this.persist(pi);
|
|
85
236
|
}
|
|
86
237
|
|
|
87
238
|
/** Turn ultracode off, restoring the previous thinking level. */
|
|
88
239
|
disable(pi: ExtensionAPI): void {
|
|
89
|
-
if (this.enabled
|
|
240
|
+
if (!this.enabled) {
|
|
241
|
+
this.syncWorkflowTool(pi);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const previous = this.previousThinking;
|
|
245
|
+
this.restorePreviousThinking(pi);
|
|
246
|
+
this.pendingPreviousThinking = previous && !this.pendingRestoreSucceeded(previous)
|
|
247
|
+
? previous
|
|
248
|
+
: undefined;
|
|
249
|
+
this.pendingClearGeneration++;
|
|
90
250
|
this.enabled = false;
|
|
251
|
+
this.suspended = false;
|
|
252
|
+
this.syncWorkflowTool(pi);
|
|
91
253
|
this.persist(pi);
|
|
92
254
|
}
|
|
93
255
|
|
|
@@ -96,21 +258,131 @@ export class UltracodeMode {
|
|
|
96
258
|
this.persist(pi);
|
|
97
259
|
}
|
|
98
260
|
|
|
99
|
-
/** Restore mode state from
|
|
100
|
-
restore(
|
|
101
|
-
|
|
261
|
+
/** Restore mode state from the active session branch. */
|
|
262
|
+
restore(
|
|
263
|
+
pi: ExtensionAPI,
|
|
264
|
+
entries: Array<{
|
|
265
|
+
type?: string;
|
|
266
|
+
customType?: string;
|
|
267
|
+
data?: unknown;
|
|
268
|
+
thinkingLevel?: unknown;
|
|
269
|
+
}>,
|
|
270
|
+
): void {
|
|
271
|
+
const wasEnforcing = this.isEnforcing();
|
|
272
|
+
this.pendingClearGeneration++;
|
|
273
|
+
let latestData: unknown;
|
|
274
|
+
let branchThinking: ThinkingLevel | undefined;
|
|
275
|
+
let thinkingAfterLatestMode: ThinkingLevel | undefined;
|
|
102
276
|
for (const entry of entries) {
|
|
103
277
|
if (entry.type === "custom" && entry.customType === MODE_ENTRY_TYPE && entry.data) {
|
|
104
|
-
|
|
278
|
+
latestData = entry.data;
|
|
279
|
+
thinkingAfterLatestMode = undefined;
|
|
280
|
+
} else if (entry.type === "thinking_level_change" && isThinkingLevel(entry.thinkingLevel)) {
|
|
281
|
+
branchThinking = entry.thinkingLevel;
|
|
282
|
+
if (latestData !== undefined) thinkingAfterLatestMode = entry.thinkingLevel;
|
|
105
283
|
}
|
|
106
284
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
285
|
+
|
|
286
|
+
const current = safeGetThinking(pi);
|
|
287
|
+
const preference = this.captureThinkingPreference();
|
|
288
|
+
const effectivePreference = this.runtimeCompatibleThinking(preference.effective) as
|
|
289
|
+
| ThinkingLevel
|
|
290
|
+
| undefined;
|
|
291
|
+
const globalPreference = this.runtimeCompatibleThinking(preference.global);
|
|
292
|
+
const latest = parsePersistedModeState(latestData);
|
|
293
|
+
if (!latest) {
|
|
294
|
+
const target = this.runtimeCompatibleThinking(
|
|
295
|
+
branchThinking
|
|
296
|
+
?? (wasEnforcing ? this.previousThinking : undefined)
|
|
297
|
+
?? effectivePreference,
|
|
298
|
+
) as ThinkingLevel | undefined;
|
|
299
|
+
this.enabled = false;
|
|
300
|
+
this.suspended = false;
|
|
301
|
+
this.budgetTotal = null;
|
|
302
|
+
this.pendingPreviousThinking = undefined;
|
|
303
|
+
this.previousThinking = target;
|
|
304
|
+
this.previousDefaultThinking = globalPreference;
|
|
305
|
+
this.legacyDefaultMigrationPending = false;
|
|
306
|
+
this.appliedThinking = current;
|
|
307
|
+
this.syncWorkflowTool(pi);
|
|
308
|
+
if (target && current !== target) this.applyCompatibleThinking(pi, target);
|
|
309
|
+
if (target && !this.pendingRestoreSucceeded(target)) {
|
|
310
|
+
this.pendingPreviousThinking = target;
|
|
311
|
+
this.persist(pi);
|
|
312
|
+
} else {
|
|
313
|
+
this.previousThinking = undefined;
|
|
314
|
+
this.previousDefaultThinking = undefined;
|
|
315
|
+
}
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
this.suspended = false;
|
|
320
|
+
this.enabled = latest.enabled;
|
|
321
|
+
this.budgetTotal = latest.budgetTotal;
|
|
322
|
+
this.syncWorkflowTool(pi);
|
|
323
|
+
const maxIsUnknownToRuntime = !this.runtimeSupportsMaxThinking
|
|
324
|
+
&& preference.effective === ULTRACODE_THINKING_LEVEL;
|
|
325
|
+
this.previousThinking = latest.previousThinking
|
|
326
|
+
?? (current === "off"
|
|
327
|
+
&& (this.currentModelSupportsThinking !== true || maxIsUnknownToRuntime)
|
|
328
|
+
? effectivePreference ?? current
|
|
329
|
+
: current);
|
|
330
|
+
|
|
331
|
+
// Pre-preference-store releases persisted only previousThinking while their
|
|
332
|
+
// active xhigh request polluted Pi's global default. Recover that baseline
|
|
333
|
+
// once instead of treating the known Ultracode value as a user preference.
|
|
334
|
+
const migratesLegacyDefault = latest.enabled
|
|
335
|
+
&& latest.previousDefaultThinking === undefined
|
|
336
|
+
&& latest.previousThinking !== undefined
|
|
337
|
+
&& (preference.global === LEGACY_ULTRACODE_THINKING_LEVEL
|
|
338
|
+
|| preference.global === ULTRACODE_THINKING_LEVEL);
|
|
339
|
+
this.previousDefaultThinking = migratesLegacyDefault
|
|
340
|
+
? this.runtimeCompatibleThinking(latest.previousThinking)
|
|
341
|
+
: preference.global !== undefined
|
|
342
|
+
? globalPreference
|
|
343
|
+
: this.runtimeCompatibleThinking(latest.previousDefaultThinking);
|
|
344
|
+
this.legacyDefaultMigrationPending = migratesLegacyDefault;
|
|
345
|
+
|
|
346
|
+
this.pendingPreviousThinking = this.enabled ? undefined : latest.pendingPreviousThinking;
|
|
347
|
+
if (this.enabled) {
|
|
348
|
+
this.applyUltracodeThinking(pi);
|
|
349
|
+
if (migratesLegacyDefault) {
|
|
350
|
+
this.queueDefaultThinkingRestore(() => {
|
|
351
|
+
this.legacyDefaultMigrationPending = false;
|
|
352
|
+
this.persist(pi);
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (this.pendingPreviousThinking) {
|
|
359
|
+
if (thinkingAfterLatestMode !== undefined) {
|
|
360
|
+
// A thinking entry after the disabled mode snapshot is an explicit user
|
|
361
|
+
// choice and supersedes the older deferred restoration.
|
|
362
|
+
const target = this.runtimeCompatibleThinking(thinkingAfterLatestMode) as ThinkingLevel;
|
|
363
|
+
this.pendingPreviousThinking = undefined;
|
|
364
|
+
this.previousThinking = target;
|
|
365
|
+
this.appliedThinking = current;
|
|
366
|
+
if (current !== target) this.applyCompatibleThinking(pi, target);
|
|
367
|
+
if (!this.pendingRestoreSucceeded(target)) this.pendingPreviousThinking = target;
|
|
368
|
+
this.persist(pi);
|
|
369
|
+
} else if (this.currentModelSupportsThinking === true) {
|
|
370
|
+
const pending = this.pendingPreviousThinking;
|
|
371
|
+
this.applyCompatibleThinking(pi, pending);
|
|
372
|
+
if (this.pendingRestoreSucceeded(pending)) this.pendingPreviousThinking = undefined;
|
|
373
|
+
this.persist(pi);
|
|
374
|
+
}
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const target = this.runtimeCompatibleThinking(branchThinking ?? latest.previousThinking) as
|
|
379
|
+
| ThinkingLevel
|
|
380
|
+
| undefined;
|
|
381
|
+
this.appliedThinking = current;
|
|
382
|
+
if (target && current !== target) this.applyCompatibleThinking(pi, target);
|
|
383
|
+
if (target && !this.pendingRestoreSucceeded(target)) {
|
|
384
|
+
this.pendingPreviousThinking = target;
|
|
385
|
+
this.persist(pi);
|
|
114
386
|
}
|
|
115
387
|
}
|
|
116
388
|
|
|
@@ -119,7 +391,7 @@ export class UltracodeMode {
|
|
|
119
391
|
* turn's system prompt when enabled.
|
|
120
392
|
*/
|
|
121
393
|
beforeAgentStart(event: { systemPrompt: string }): { systemPrompt: string } | undefined {
|
|
122
|
-
if (!this.
|
|
394
|
+
if (!this.isEnforcing()) return undefined;
|
|
123
395
|
const block = ultracodeSystemBlock({ budgetTotal: this.budgetTotal });
|
|
124
396
|
return { systemPrompt: `${event.systemPrompt}\n\n${block}\n\n${ULTRACODE_ACTIVE_REMINDER}` };
|
|
125
397
|
}
|
|
@@ -127,23 +399,124 @@ export class UltracodeMode {
|
|
|
127
399
|
statusLine(): string {
|
|
128
400
|
if (!this.enabled) return `ultracode: off`;
|
|
129
401
|
const parts = ["ultracode: on"];
|
|
130
|
-
// Show the level that actually applied
|
|
131
|
-
if (this.appliedThinking) parts.push(
|
|
402
|
+
// Show the level that actually applied, including compatibility/model fallback.
|
|
403
|
+
if (this.appliedThinking) parts.push(this.appliedThinking);
|
|
132
404
|
if (this.budgetTotal) parts.push(`budget ~${formatTokens(this.budgetTotal)}`);
|
|
133
405
|
return parts.join(" · ");
|
|
134
406
|
}
|
|
135
407
|
|
|
408
|
+
private applyUltracodeThinking(pi: ExtensionAPI): void {
|
|
409
|
+
const writeGeneration = this.preferenceWriteGeneration;
|
|
410
|
+
this.applyCompatibleThinking(pi, ULTRACODE_THINKING_LEVEL);
|
|
411
|
+
// Pi normally skips persistence when the effective level is unchanged, but
|
|
412
|
+
// the extension API does not promise that. Defensively restore the raw
|
|
413
|
+
// baseline even after a stable max -> max request.
|
|
414
|
+
if (writeGeneration === this.preferenceWriteGeneration) {
|
|
415
|
+
this.queueDefaultThinkingRestore();
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/** Apply max compatibly and return the concrete level accepted as success. */
|
|
420
|
+
private applyCompatibleThinking(pi: ExtensionAPI, level: ThinkingLevel): ThinkingLevel {
|
|
421
|
+
this.applyThinking(pi, level);
|
|
422
|
+
if (level === ULTRACODE_THINKING_LEVEL && this.appliedThinking !== level) {
|
|
423
|
+
// Modern Pi clamps max per model. Pre-max Pi may treat it as unknown, so
|
|
424
|
+
// retry xhigh for both activation and restoration of persisted max state.
|
|
425
|
+
this.applyThinking(pi, LEGACY_ULTRACODE_THINKING_LEVEL);
|
|
426
|
+
return LEGACY_ULTRACODE_THINKING_LEVEL;
|
|
427
|
+
}
|
|
428
|
+
return level;
|
|
429
|
+
}
|
|
430
|
+
|
|
136
431
|
private applyThinking(pi: ExtensionAPI, level: ThinkingLevel): void {
|
|
432
|
+
const wasApplying = this.applyingThinking;
|
|
433
|
+
const before = safeGetThinking(pi);
|
|
434
|
+
this.applyingThinking = true;
|
|
137
435
|
try {
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
pi.setThinkingLevel(level);
|
|
436
|
+
// Pi clamps the request and persists the effective level. Preference
|
|
437
|
+
// restoration is queued after Pi's own SettingsManager chain drains.
|
|
438
|
+
pi.setThinkingLevel(level as any);
|
|
141
439
|
this.appliedThinking = safeGetThinking(pi) ?? level;
|
|
142
440
|
} catch {
|
|
143
441
|
this.appliedThinking = safeGetThinking(pi);
|
|
442
|
+
} finally {
|
|
443
|
+
if (this.appliedThinking !== before) this.queueDefaultThinkingRestore();
|
|
444
|
+
this.applyingThinking = wasApplying;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
isEnforcing(): boolean {
|
|
449
|
+
return this.enabled && !this.suspended;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
private pendingRestoreSucceeded(pending: ThinkingLevel): boolean {
|
|
453
|
+
if (this.appliedThinking === pending) return true;
|
|
454
|
+
return pending === ULTRACODE_THINKING_LEVEL
|
|
455
|
+
&& !this.runtimeSupportsMaxThinking
|
|
456
|
+
&& this.appliedThinking !== undefined
|
|
457
|
+
&& this.appliedThinking !== "off";
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
private runtimeCompatibleThinking(
|
|
461
|
+
level: ThinkingLevel | null | undefined,
|
|
462
|
+
): ThinkingLevel | null | undefined {
|
|
463
|
+
return level === ULTRACODE_THINKING_LEVEL && !this.runtimeSupportsMaxThinking
|
|
464
|
+
? LEGACY_ULTRACODE_THINKING_LEVEL
|
|
465
|
+
: level;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
private captureThinkingPreference(): {
|
|
469
|
+
global: ThinkingLevel | null | undefined;
|
|
470
|
+
effective: ThinkingLevel | undefined;
|
|
471
|
+
} {
|
|
472
|
+
if (!this.thinkingPreferenceStore) return { global: undefined, effective: undefined };
|
|
473
|
+
try {
|
|
474
|
+
const preference = this.thinkingPreferenceStore.getThinkingPreference();
|
|
475
|
+
return {
|
|
476
|
+
global: isThinkingLevel(preference.global) ? preference.global : null,
|
|
477
|
+
effective: isThinkingLevel(preference.effective) ? preference.effective : undefined,
|
|
478
|
+
};
|
|
479
|
+
} catch {
|
|
480
|
+
return { global: undefined, effective: undefined };
|
|
144
481
|
}
|
|
145
482
|
}
|
|
146
483
|
|
|
484
|
+
private queueDefaultThinkingRestore(onSuccess?: () => void): void {
|
|
485
|
+
const store = this.thinkingPreferenceStore;
|
|
486
|
+
const baseline = this.runtimeCompatibleThinking(this.previousDefaultThinking);
|
|
487
|
+
if (!store || baseline === undefined) return;
|
|
488
|
+
const generation = ++this.preferenceWriteGeneration;
|
|
489
|
+
this.preferenceWriteQueue = this.preferenceWriteQueue
|
|
490
|
+
.catch(() => {})
|
|
491
|
+
.then(waitForSettingsWrites)
|
|
492
|
+
.then(async () => {
|
|
493
|
+
if (generation !== this.preferenceWriteGeneration) return;
|
|
494
|
+
const currentGlobal = this.captureThinkingPreference().global;
|
|
495
|
+
if (currentGlobal !== baseline) {
|
|
496
|
+
await store.setDefaultThinkingLevel(baseline ?? undefined);
|
|
497
|
+
await store.flush?.();
|
|
498
|
+
}
|
|
499
|
+
if (generation === this.preferenceWriteGeneration) onSuccess?.();
|
|
500
|
+
})
|
|
501
|
+
.catch(() => {
|
|
502
|
+
// Keep the session usable even when settings cannot be restored.
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
private deferPendingClear(pi: ExtensionAPI, level: ThinkingLevel): void {
|
|
507
|
+
const generation = ++this.pendingClearGeneration;
|
|
508
|
+
setImmediate(() => {
|
|
509
|
+
if (
|
|
510
|
+
generation !== this.pendingClearGeneration
|
|
511
|
+
|| this.enabled
|
|
512
|
+
|| this.suspended
|
|
513
|
+
|| safeGetThinking(pi) !== level
|
|
514
|
+
) return;
|
|
515
|
+
this.pendingPreviousThinking = undefined;
|
|
516
|
+
this.persist(pi);
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
147
520
|
private activateWorkflowTool(pi: ExtensionAPI): void {
|
|
148
521
|
try {
|
|
149
522
|
const active = pi.getActiveTools();
|
|
@@ -151,7 +524,18 @@ export class UltracodeMode {
|
|
|
151
524
|
pi.setActiveTools([...active, this.workflowToolName]);
|
|
152
525
|
}
|
|
153
526
|
} catch {
|
|
154
|
-
//
|
|
527
|
+
// Mode enforcement remains usable when tool selection is unavailable.
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
private deactivateWorkflowTool(pi: ExtensionAPI): void {
|
|
532
|
+
try {
|
|
533
|
+
const active = pi.getActiveTools();
|
|
534
|
+
if (active.includes(this.workflowToolName)) {
|
|
535
|
+
pi.setActiveTools(active.filter((name) => name !== this.workflowToolName));
|
|
536
|
+
}
|
|
537
|
+
} catch {
|
|
538
|
+
// The execution guards still fail closed when selection cannot be updated.
|
|
155
539
|
}
|
|
156
540
|
}
|
|
157
541
|
|
|
@@ -160,6 +544,10 @@ export class UltracodeMode {
|
|
|
160
544
|
enabled: this.enabled,
|
|
161
545
|
budgetTotal: this.budgetTotal,
|
|
162
546
|
previousThinking: this.previousThinking,
|
|
547
|
+
previousDefaultThinking: this.legacyDefaultMigrationPending
|
|
548
|
+
? undefined
|
|
549
|
+
: this.previousDefaultThinking,
|
|
550
|
+
pendingPreviousThinking: this.pendingPreviousThinking,
|
|
163
551
|
};
|
|
164
552
|
try {
|
|
165
553
|
pi.appendEntry(MODE_ENTRY_TYPE, state);
|
|
@@ -169,9 +557,32 @@ export class UltracodeMode {
|
|
|
169
557
|
}
|
|
170
558
|
}
|
|
171
559
|
|
|
560
|
+
function parsePersistedModeState(data: unknown): PersistedModeState | undefined {
|
|
561
|
+
if (!data || typeof data !== "object") return undefined;
|
|
562
|
+
const value = data as Record<string, unknown>;
|
|
563
|
+
const budget = value.budgetTotal;
|
|
564
|
+
const previousDefault = value.previousDefaultThinking;
|
|
565
|
+
return {
|
|
566
|
+
enabled: value.enabled === true,
|
|
567
|
+
budgetTotal: typeof budget === "number" && Number.isFinite(budget) && budget > 0 ? budget : null,
|
|
568
|
+
previousThinking: isThinkingLevel(value.previousThinking) ? value.previousThinking : undefined,
|
|
569
|
+
previousDefaultThinking: previousDefault === null || isThinkingLevel(previousDefault)
|
|
570
|
+
? previousDefault
|
|
571
|
+
: undefined,
|
|
572
|
+
pendingPreviousThinking: isThinkingLevel(value.pendingPreviousThinking)
|
|
573
|
+
? value.pendingPreviousThinking
|
|
574
|
+
: undefined,
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function waitForSettingsWrites(): Promise<void> {
|
|
579
|
+
return new Promise((resolve) => setImmediate(resolve));
|
|
580
|
+
}
|
|
581
|
+
|
|
172
582
|
function safeGetThinking(pi: ExtensionAPI): ThinkingLevel | undefined {
|
|
173
583
|
try {
|
|
174
|
-
|
|
584
|
+
const level = pi.getThinkingLevel();
|
|
585
|
+
return isThinkingLevel(level) ? level : undefined;
|
|
175
586
|
} catch {
|
|
176
587
|
return undefined;
|
|
177
588
|
}
|
package/src/prompts.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
/** One-line description shown by `/ultracode status` and the footer. */
|
|
10
|
-
export const ULTRACODE_TAGLINE = "
|
|
10
|
+
export const ULTRACODE_TAGLINE = "max thinking + dynamic workflow orchestration";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* The standing system-prompt block injected on every turn while ultracode is on.
|
|
@@ -59,7 +59,7 @@ export const WORKFLOW_PROMPT_SNIPPET =
|
|
|
59
59
|
|
|
60
60
|
/** Guideline bullets appended to the Guidelines section when the tool is active. */
|
|
61
61
|
export const WORKFLOW_GUIDELINES: string[] = [
|
|
62
|
-
"
|
|
62
|
+
"While Ultracode is enabled, use the workflow tool to decompose-and-cover in parallel, gather independent perspectives that adversarially verify each other, or take on scale one context can't hold.",
|
|
63
63
|
"For the workflow tool, pass one raw JavaScript string in the required `script` parameter; no Markdown fences, no prose around the script.",
|
|
64
64
|
"For the workflow tool, the script's first statement must be `export const meta = { name: 'short_snake_case', description: 'non-empty human description' }`. meta must be a pure literal: no variables, function calls, spreads, or template interpolation. meta.phases is optional and should mirror your phase() titles.",
|
|
65
65
|
"For the workflow tool, write plain JavaScript after the meta export. No TypeScript syntax, imports, require(), fs, network, Date.now(), Math.random(), or new Date() (they break determinism and resume). Stamp timestamps after the workflow returns; vary randomness by agent index.",
|
package/src/thinking.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Shared thinking-level vocabulary for the mode, workflow agents, and config parser. */
|
|
2
|
+
|
|
3
|
+
export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"] as const;
|
|
4
|
+
|
|
5
|
+
export type ThinkingLevel = (typeof THINKING_LEVELS)[number];
|
|
6
|
+
|
|
7
|
+
/** Ultracode always asks Pi for the strongest effort the selected model supports. */
|
|
8
|
+
export const ULTRACODE_THINKING_LEVEL: ThinkingLevel = "max";
|
|
9
|
+
|
|
10
|
+
/** Compatibility retry for Pi versions released before the `max` level existed. */
|
|
11
|
+
export const LEGACY_ULTRACODE_THINKING_LEVEL: ThinkingLevel = "xhigh";
|
|
12
|
+
|
|
13
|
+
const THINKING_LEVEL_SET = new Set<string>(THINKING_LEVELS);
|
|
14
|
+
|
|
15
|
+
export function isThinkingLevel(value: unknown): value is ThinkingLevel {
|
|
16
|
+
return typeof value === "string" && THINKING_LEVEL_SET.has(value);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Whether this Pi runtime recognizes `max` as a first-class thinking level. */
|
|
20
|
+
export function piVersionSupportsMaxThinking(version: string): boolean {
|
|
21
|
+
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(version);
|
|
22
|
+
if (!match) return true;
|
|
23
|
+
const major = Number(match[1]);
|
|
24
|
+
const minor = Number(match[2]);
|
|
25
|
+
const patch = Number(match[3]);
|
|
26
|
+
return major > 0 || minor > 80 || (minor === 80 && patch >= 6);
|
|
27
|
+
}
|