@zhushanwen/pi-goal 0.1.5 → 0.2.0
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/package.json +2 -2
- package/src/index.ts +49 -1
- package/src/state.ts +19 -0
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhushanwen/pi-goal",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Codex-style /goal command for Pi — persistent goal-driven autonomous loop with evidence-based completion, token/time budgets, blocked detection, and steering templates.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"pi": {
|
|
8
8
|
"extensions": [
|
|
9
|
-
"./
|
|
9
|
+
"./index.ts"
|
|
10
10
|
]
|
|
11
11
|
},
|
|
12
12
|
"keywords": [
|
package/src/index.ts
CHANGED
|
@@ -28,10 +28,12 @@ import {
|
|
|
28
28
|
MAX_HISTORY_ENTRIES,
|
|
29
29
|
} from "./constants";
|
|
30
30
|
import {
|
|
31
|
+
createInitialState,
|
|
31
32
|
deserializeState,
|
|
32
33
|
getCompletedCount,
|
|
33
34
|
isActiveStatus,
|
|
34
35
|
isTerminalStatus,
|
|
36
|
+
type GoalExternalInit,
|
|
35
37
|
} from "./state";
|
|
36
38
|
import {
|
|
37
39
|
executeGoalAction,
|
|
@@ -41,6 +43,7 @@ import {
|
|
|
41
43
|
HISTORY_ENTRY_TYPE,
|
|
42
44
|
isGoalEntry,
|
|
43
45
|
isStaleContextError,
|
|
46
|
+
persistGoalState,
|
|
44
47
|
updateWidget,
|
|
45
48
|
} from "./tool-handler";
|
|
46
49
|
import { toSingleLine } from "./widget";
|
|
@@ -180,7 +183,8 @@ export default function goalExtension(pi: ExtensionAPI) {
|
|
|
180
183
|
isProcessing: false, // P1-3: 防重入
|
|
181
184
|
};
|
|
182
185
|
|
|
183
|
-
//
|
|
186
|
+
// Capture latest ctx for external API (initializeGoalFromExternal needs it for persistGoalState)
|
|
187
|
+
let lastCtx: ExtensionContext | undefined;
|
|
184
188
|
|
|
185
189
|
pi.registerTool({
|
|
186
190
|
name: "goal_manager",
|
|
@@ -217,6 +221,7 @@ export default function goalExtension(pi: ExtensionAPI) {
|
|
|
217
221
|
parameters: GoalManagerParams,
|
|
218
222
|
|
|
219
223
|
async execute(_toolCallId: string, params: Static<typeof GoalManagerParams>, signal: AbortSignal | undefined, _onUpdate: unknown, ctx: ExtensionContext) {
|
|
224
|
+
lastCtx = ctx;
|
|
220
225
|
try {
|
|
221
226
|
// P1-4: 透传 signal
|
|
222
227
|
return await executeGoalAction(pi, session, params, ctx, signal);
|
|
@@ -303,6 +308,7 @@ export default function goalExtension(pi: ExtensionAPI) {
|
|
|
303
308
|
|
|
304
309
|
// ── Event: before_agent_start ──────────────────────
|
|
305
310
|
pi.on("before_agent_start", async (_event: BeforeAgentStartLikeEvent, ctx: ExtensionContext) => {
|
|
311
|
+
lastCtx = ctx;
|
|
306
312
|
return handleBeforeAgentStart(pi, session, ctx);
|
|
307
313
|
});
|
|
308
314
|
|
|
@@ -345,6 +351,7 @@ export default function goalExtension(pi: ExtensionAPI) {
|
|
|
345
351
|
|
|
346
352
|
// ── Event: session_start (state reconstruction) ───
|
|
347
353
|
pi.on("session_start", async (_event: SessionStartLikeEvent, ctx: ExtensionContext) => {
|
|
354
|
+
lastCtx = ctx;
|
|
348
355
|
reconstructGoalState(pi, session, ctx);
|
|
349
356
|
if (session.state) {
|
|
350
357
|
session.tasksCompletedAtAgentStart = getCompletedCount(session.state.tasks);
|
|
@@ -372,4 +379,45 @@ export default function goalExtension(pi: ExtensionAPI) {
|
|
|
372
379
|
return new Text(prefix + theme.fg("dim", content), 0, 0);
|
|
373
380
|
});
|
|
374
381
|
}
|
|
382
|
+
|
|
383
|
+
// ── External API: initializeGoalFromExternal ──────────
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Allow other extensions (e.g. coding-workflow) to programmatically initialize a goal.
|
|
387
|
+
* Skips the /goal command flow — directly creates state + tasks.
|
|
388
|
+
* Returns true if initialized, false if goal already active.
|
|
389
|
+
*/
|
|
390
|
+
function initializeGoalFromExternal(
|
|
391
|
+
objective: string,
|
|
392
|
+
tasks: string[],
|
|
393
|
+
budget?: { tokenBudget?: number; timeBudgetMinutes?: number; maxTurns?: number },
|
|
394
|
+
): boolean {
|
|
395
|
+
if (session.state && isActiveStatus(session.state.status)) {
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
session.state = createInitialState(objective, budget);
|
|
400
|
+
session.tasksCompletedAtAgentStart = 0;
|
|
401
|
+
|
|
402
|
+
// Create tasks (same logic as handleCreateTasks)
|
|
403
|
+
session.state.tasks = tasks.map((desc, i) => ({
|
|
404
|
+
id: i + 1,
|
|
405
|
+
description: desc.length > 60 ? desc.slice(0, 57) + "..." : desc,
|
|
406
|
+
status: "pending" as const,
|
|
407
|
+
lastUpdatedTurn: session.state!.currentTurnIndex,
|
|
408
|
+
}));
|
|
409
|
+
|
|
410
|
+
// Persist state so it survives session reconstruction
|
|
411
|
+
// Note: ctx is captured from the last event handler invocation — acceptable
|
|
412
|
+
// because initializeGoalFromExternal is called synchronously during tool execution.
|
|
413
|
+
if (lastCtx) {
|
|
414
|
+
persistGoalState(pi, session, lastCtx);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Expose on pi for cross-extension access
|
|
421
|
+
const api = pi as unknown as Record<string, unknown>;
|
|
422
|
+
api.__goalInit = initializeGoalFromExternal satisfies GoalExternalInit;
|
|
375
423
|
}
|
package/src/state.ts
CHANGED
|
@@ -15,6 +15,25 @@
|
|
|
15
15
|
|
|
16
16
|
import { MS_PER_SECOND } from "./constants";
|
|
17
17
|
|
|
18
|
+
// ── Cross-extension API type ─────────────────────────────
|
|
19
|
+
|
|
20
|
+
/** Budget options for external goal initialization. */
|
|
21
|
+
export interface GoalExternalBudget {
|
|
22
|
+
tokenBudget?: number;
|
|
23
|
+
timeBudgetMinutes?: number;
|
|
24
|
+
maxTurns?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Function signature for programmatically initializing a goal from another extension.
|
|
29
|
+
* Exposed via `pi.__goalInit` by the goal extension.
|
|
30
|
+
*/
|
|
31
|
+
export type GoalExternalInit = (
|
|
32
|
+
objective: string,
|
|
33
|
+
tasks: string[],
|
|
34
|
+
budget?: GoalExternalBudget,
|
|
35
|
+
) => boolean;
|
|
36
|
+
|
|
18
37
|
// ── Goal 状态枚举 ──────────────────────────────────────
|
|
19
38
|
|
|
20
39
|
export type GoalStatus =
|