@tduyng/pi-goal 0.0.1 → 0.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tduyng/pi-goal",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Long-running objectives that survive reload and tree navigation, plus a timed or self-paced loop to drive them.",
5
5
  "keywords": [
6
6
  "pi",
package/src/goal.ts CHANGED
@@ -510,7 +510,7 @@ export function goalContextMessages<T>(messages: T[]): T[] {
510
510
  }
511
511
 
512
512
  export function goalStopStatusForAssistantError(message: { errorMessage?: string } | undefined): GoalStatus {
513
- return PROVIDER_LIMIT_PATTERN.test(message?.errorMessage ?? '') ? 'usageLimited' : 'blocked'
513
+ return PROVIDER_LIMIT_PATTERN.test(message?.errorMessage ?? '') ? 'usageLimited' : 'paused'
514
514
  }
515
515
 
516
516
  const RoleContentSchema = t.object(
@@ -952,7 +952,11 @@ export default function goalExtension(pi: ExtensionAPI) {
952
952
  }
953
953
 
954
954
  const checkArg = /^check(?:\s+([\s\S]+))?$/i.exec(trimmed)
955
- if (checkArg !== null && goal !== null) {
955
+ if (checkArg !== null) {
956
+ if (goal === null) {
957
+ showGoalMessage('No goal is currently set.')
958
+ return
959
+ }
956
960
  const raw = checkArg[1]?.trim() ?? ''
957
961
  if (raw === '') {
958
962
  showGoalMessage('Usage: /goal check <command>, or /goal check off')
package/src/loop/index.ts CHANGED
@@ -31,6 +31,7 @@ export function parseLoopState(value: JsonValue): LoopState | null {
31
31
  export default function loopExtension(pi: ExtensionAPI): void {
32
32
  let loop: LoopState | null = null
33
33
  let timer: ReturnType<typeof setTimeout> | null = null
34
+ let lastError: string | undefined
34
35
 
35
36
  function clearTimer(): void {
36
37
  if (timer) {
@@ -142,8 +143,27 @@ export default function loopExtension(pi: ExtensionAPI): void {
142
143
 
143
144
  pi.on('session_shutdown', async () => clearTimer())
144
145
 
146
+ pi.on('agent_end', async (event) => {
147
+ const assistant = event.messages.findLast((message) => message.role === 'assistant')
148
+ lastError =
149
+ assistant?.stopReason === 'error'
150
+ ? (assistant.errorMessage ?? 'the last turn ended with an error')
151
+ : undefined
152
+ })
153
+
145
154
  pi.on('agent_settled', async (event, ctx) => {
146
- if (!loop || loop.intervalMs !== null) return
155
+ if (!loop) return
156
+ if (lastError !== undefined) {
157
+ const reason = lastError
158
+ lastError = undefined
159
+ clearTimer()
160
+ loop = null
161
+ persist()
162
+ updateStatus(ctx)
163
+ ctx.ui.notify(`Loop stopped: ${reason}`, 'warning')
164
+ return
165
+ }
166
+ if (loop.intervalMs !== null) return
147
167
  if (event.aborted) return
148
168
  clearTimer()
149
169
  timer = setTimeout(() => fireSafely(ctx), SELF_PACE_DELAY_MS)
package/src/loop/logic.ts CHANGED
@@ -14,6 +14,7 @@ export type ParsedLoopArgs =
14
14
  const INTERVAL_PATTERN = /^(\d+)(ms|s|m|h|d)$/i
15
15
  const MS_PER_UNIT = { ms: 1, s: 1_000, m: 60_000, h: 3_600_000, d: 86_400_000 } satisfies Record<string, number>
16
16
  export const MAX_INTERVAL_MS = 2_147_483_647
17
+ export const MIN_INTERVAL_MS = 10_000
17
18
  const STOP_WORDS = new Set(['stop', 'cancel', 'clear', 'off'])
18
19
 
19
20
  export function parseInterval(token: string): number | null {
@@ -23,7 +24,7 @@ export function parseInterval(token: string): number | null {
23
24
  const unit = (match[2] ?? '').toLowerCase()
24
25
  const msPerUnit = MS_PER_UNIT[unit as keyof typeof MS_PER_UNIT]
25
26
  if (!Number.isFinite(amount) || amount <= 0 || msPerUnit === undefined) return null
26
- return Math.min(amount * msPerUnit, MAX_INTERVAL_MS)
27
+ return Math.min(Math.max(amount * msPerUnit, MIN_INTERVAL_MS), MAX_INTERVAL_MS)
27
28
  }
28
29
 
29
30
  export function parseLoopArgs(input: string): ParsedLoopArgs {