Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
commit
03634b1ca3
11340 files changed
+3442369
No files matched your search
@@ -0,0 +1,189 @@
|
||||
import { atom } from 'nanostores'
|
||||
|
||||
import { keyedTimeouts } from '@/lib/keyed-timeouts'
|
||||
|
||||
import { $gateway } from './gateway'
|
||||
import { isSessionGone, isSessionGoneForBackgroundPolling, markSessionGone } from './runtime-gone'
|
||||
import { ambientRequestFor } from './session-gone-latch'
|
||||
import { requestForOwnedSession } from './session-states'
|
||||
|
||||
export type GoalStatus = 'active' | 'done' | 'paused' | 'waiting'
|
||||
|
||||
export interface SessionGoal {
|
||||
detail?: string
|
||||
status: GoalStatus
|
||||
title: string
|
||||
updatedAt: number
|
||||
}
|
||||
|
||||
export const $goalsBySession = atom<Record<string, SessionGoal>>({})
|
||||
|
||||
const DONE_LINGER_MS = 8_000
|
||||
const clearTimers = keyedTimeouts()
|
||||
|
||||
export function setSessionGoal(sid: string, goal: SessionGoal) {
|
||||
if (!sid) {
|
||||
return
|
||||
}
|
||||
|
||||
clearTimers.cancel(sid)
|
||||
$goalsBySession.set({ ...$goalsBySession.get(), [sid]: goal })
|
||||
|
||||
if (goal.status === 'done') {
|
||||
clearTimers.schedule(sid, DONE_LINGER_MS, () => clearSessionGoal(sid))
|
||||
}
|
||||
}
|
||||
|
||||
export function clearSessionGoal(sid: string) {
|
||||
clearTimers.cancel(sid)
|
||||
|
||||
const map = $goalsBySession.get()
|
||||
|
||||
if (!(sid in map)) {
|
||||
return
|
||||
}
|
||||
|
||||
const { [sid]: _drop, ...rest } = map
|
||||
$goalsBySession.set(rest)
|
||||
}
|
||||
|
||||
const clean = (value: string): string => value.replace(/\r/g, '').trim()
|
||||
|
||||
const firstLine = (value: string): string => clean(value).split('\n')[0]?.trim() ?? ''
|
||||
|
||||
function goalTitleFromLine(line: string, pattern: RegExp): string {
|
||||
return (line.match(pattern)?.[1] ?? '').trim()
|
||||
}
|
||||
|
||||
function nextGoalFromText(text: string, previous?: SessionGoal): SessionGoal | null | undefined {
|
||||
const body = clean(text)
|
||||
const line = firstLine(body)
|
||||
|
||||
if (!line) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (
|
||||
/^No active goal\b/i.test(line) ||
|
||||
/^No goal (?:set|to resume)\b/i.test(line) ||
|
||||
/^✓ Goal cleared\b/i.test(line)
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
const now = Date.now()
|
||||
const fromSet = goalTitleFromLine(line, /^⊙ Goal set(?:\s*\([^)]*\))?:\s*(.+)$/)
|
||||
const fromActive = goalTitleFromLine(line, /^⊙ Goal\s*\([^)]*active[^)]*\):\s*(.+)$/)
|
||||
const fromResume = goalTitleFromLine(line, /^▶ Goal resumed:\s*(.+)$/)
|
||||
|
||||
if (fromSet || fromActive || fromResume) {
|
||||
return { status: 'active', title: fromSet || fromActive || fromResume, updatedAt: now }
|
||||
}
|
||||
|
||||
const fromWaiting = goalTitleFromLine(line, /^⏳ Goal\s*\([^)]*(?:parked|active)[^)]*\):\s*(.+)$/)
|
||||
|
||||
if (fromWaiting) {
|
||||
return { status: 'waiting', title: fromWaiting, updatedAt: now }
|
||||
}
|
||||
|
||||
const fromPaused = goalTitleFromLine(line, /^⏸ Goal(?:\s*\([^)]*\)| paused)?:\s*(.+)$/)
|
||||
|
||||
if (fromPaused) {
|
||||
return { status: 'paused', title: fromPaused, updatedAt: now }
|
||||
}
|
||||
|
||||
const fromDone = goalTitleFromLine(line, /^✓ Goal done\s*\([^)]*\):\s*(.+)$/)
|
||||
|
||||
if (fromDone) {
|
||||
return { status: 'done', title: fromDone, updatedAt: now }
|
||||
}
|
||||
|
||||
if (/^↻ Continuing toward goal\b/i.test(line)) {
|
||||
return {
|
||||
detail: line.replace(/^↻\s*/, ''),
|
||||
status: 'active',
|
||||
title: previous?.title || 'Standing goal',
|
||||
updatedAt: now
|
||||
}
|
||||
}
|
||||
|
||||
if (/^⏳ Goal parked\b/i.test(line)) {
|
||||
return {
|
||||
detail: line.replace(/^⏳\s*/, ''),
|
||||
status: 'waiting',
|
||||
title: previous?.title || 'Standing goal',
|
||||
updatedAt: now
|
||||
}
|
||||
}
|
||||
|
||||
if (/^⏸ Goal paused\b/i.test(line)) {
|
||||
return {
|
||||
detail: line.replace(/^⏸\s*/, ''),
|
||||
status: 'paused',
|
||||
title: previous?.title || 'Standing goal',
|
||||
updatedAt: now
|
||||
}
|
||||
}
|
||||
|
||||
if (/^✓ Goal achieved\b/i.test(line)) {
|
||||
return {
|
||||
detail: line.replace(/^✓\s*/, ''),
|
||||
status: 'done',
|
||||
title: previous?.title || 'Standing goal',
|
||||
updatedAt: now
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function applyGoalStatusText(sid: string, text: string, opts?: { hydrate?: boolean }) {
|
||||
if (!sid) {
|
||||
return
|
||||
}
|
||||
|
||||
const next = nextGoalFromText(text, $goalsBySession.get()[sid])
|
||||
|
||||
if (next === null) {
|
||||
clearSessionGoal(sid)
|
||||
} else if (next) {
|
||||
// A done goal is terminal state in the backend DB — it stays "done"
|
||||
// forever (only /goal clear or a new goal replaces it). The 8s linger is
|
||||
// for the LIVE completion moment; re-hydrating "✓ Goal done" on every
|
||||
// mount would resurrect the chip indefinitely. Bot Mode is the worst
|
||||
// case: one endless session means the completed layover would never go
|
||||
// away. On hydration, a terminal goal is the same as no goal.
|
||||
if (opts?.hydrate && next.status === 'done') {
|
||||
clearSessionGoal(sid)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
setSessionGoal(sid, next)
|
||||
}
|
||||
}
|
||||
|
||||
export async function refreshSessionGoal(sid: string): Promise<void> {
|
||||
const gateway = $gateway.get()
|
||||
|
||||
if (!sid || !gateway || isSessionGone(sid)) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await requestForOwnedSession<{ output?: string }>(sid, ambientRequestFor(gateway), 'slash.exec', {
|
||||
command: 'goal status',
|
||||
session_id: sid
|
||||
})
|
||||
|
||||
applyGoalStatusText(sid, result?.output ?? '', { hydrate: true })
|
||||
} catch (error) {
|
||||
if (isSessionGoneForBackgroundPolling(error)) {
|
||||
markSessionGone(sid)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Best-effort: older gateways or a transport blip simply won't hydrate it.
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user