@gotcos/glasses-server 6.50.4 → 6.52.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/.env.example +16 -0
- package/CHANGELOG.md +78 -0
- package/README.md +56 -3
- package/bin/hooks/cos-session-hook +24 -1
- package/managed-runtime-contract.json +1 -0
- package/package.json +1 -1
- package/server/index.ts +83 -5
- package/server/lib/activity-preview.ts +45 -2
- package/server/lib/agent-session-search.ts +3 -1
- package/server/lib/agent-session-store.ts +35 -5
- package/server/lib/agent-session-turns.ts +4 -1
- package/server/lib/claude-bridge.ts +9 -0
- package/server/lib/client-liveness.ts +50 -0
- package/server/lib/codex-bridge.ts +6 -0
- package/server/lib/codex-live-queue-deps.ts +135 -0
- package/server/lib/codex-live-queue.ts +232 -0
- package/server/lib/codex-turn-clock.ts +36 -0
- package/server/lib/cursor-bridge.ts +6 -0
- package/server/lib/cursor-stop-followup.ts +134 -0
- package/server/lib/job-trail.ts +758 -0
- package/server/lib/occupancy-probes.ts +52 -0
- package/server/lib/ollama-bridge.ts +17 -0
- package/server/lib/permission-broker.ts +1156 -0
- package/server/lib/query-job-coordinator.ts +17 -1
- package/server/lib/query-job-feature.ts +10 -0
- package/server/lib/query-job-runtime.ts +9 -1
- package/server/lib/query-job-store.ts +214 -5
- package/server/lib/query-job-types.ts +36 -0
- package/server/lib/session-signal-store.ts +42 -10
- package/server/lib/session-state-derive.ts +14 -4
- package/server/lib/session-stream-events.ts +600 -15
- package/server/lib/session-stream-producer.ts +25 -6
- package/server/lib/session-transcript-watcher.ts +120 -3
- package/server/lib/thread-occupancy.ts +30 -7
- package/server/routes/agent-session-bindings.ts +98 -6
- package/server/routes/agent-sessions.ts +24 -2
- package/server/routes/cursor-stop-followup.ts +74 -0
- package/server/routes/health.ts +34 -1
- package/server/routes/permission-broker.ts +176 -0
- package/server/routes/query-jobs.ts +84 -13
- package/server/routes/thread-turn-queue.ts +15 -5
|
@@ -834,6 +834,58 @@ export function withHookTurnClock(
|
|
|
834
834
|
}
|
|
835
835
|
}
|
|
836
836
|
|
|
837
|
+
/**
|
|
838
|
+
* Add the Codex turn clock (6.51.0) to a probe set, keeping whatever the set already
|
|
839
|
+
* answers for other providers.
|
|
840
|
+
*
|
|
841
|
+
* `codexTurnEndedAtMs` reads the thread's rollout tail and answers a time only when the
|
|
842
|
+
* newest turn marker CLOSED a turn (`task_complete`/`turn_complete`); a `task_started`
|
|
843
|
+
* after it, an unreadable file, or a tail with no marker all answer null, which keeps
|
|
844
|
+
* the 30 s transcript window. Measured on codex-cli 0.155: the app starts a turn queued
|
|
845
|
+
* in its own queue 10 ms after the `task_complete`, and that `task_started` is what the
|
|
846
|
+
* next read sees.
|
|
847
|
+
*/
|
|
848
|
+
export function withCodexTurnClock(
|
|
849
|
+
probes: OccupancyProbes,
|
|
850
|
+
codexTurnEndedAtMs: (threadId: string) => number | null,
|
|
851
|
+
): OccupancyProbes {
|
|
852
|
+
const inner = probes.holderTurnEndedAtMs
|
|
853
|
+
return {
|
|
854
|
+
...probes,
|
|
855
|
+
holderTurnEndedAtMs: (provider, threadId) => {
|
|
856
|
+
if (provider === 'codex') {
|
|
857
|
+
if (!NATIVE_THREAD_ID_RE.test(threadId)) return null
|
|
858
|
+
return codexTurnEndedAtMs(threadId)
|
|
859
|
+
}
|
|
860
|
+
return typeof inner === 'function' ? inner(provider, threadId) : null
|
|
861
|
+
},
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
/**
|
|
866
|
+
* Add the Cursor composer turn signal (6.51.0). `signalFor` is the hook signal store's
|
|
867
|
+
* reader; Cursor's hook events land there under the composer's conversation id. Open
|
|
868
|
+
* means: a turn was opened (prompt or tool) and no Stop has closed it, nothing ended the
|
|
869
|
+
* session, and the newest event is inside the open-turn ceiling -- a composer that died
|
|
870
|
+
* mid-turn must not read working for ever.
|
|
871
|
+
*/
|
|
872
|
+
export function withCursorComposerTurn(
|
|
873
|
+
probes: OccupancyProbes,
|
|
874
|
+
signalFor: (sessionId: string) => { turnOpen: boolean; ended: { at: number; reason: string } | null; lastEventAt: number } | undefined,
|
|
875
|
+
now: () => number,
|
|
876
|
+
ceilingMs: number,
|
|
877
|
+
): OccupancyProbes {
|
|
878
|
+
return {
|
|
879
|
+
...probes,
|
|
880
|
+
cursorComposerTurnOpen: (threadId: string) => {
|
|
881
|
+
if (!NATIVE_THREAD_ID_RE.test(threadId)) return null
|
|
882
|
+
const signal = signalFor(threadId)
|
|
883
|
+
if (!signal) return null
|
|
884
|
+
return signal.turnOpen === true && signal.ended === null && now() - signal.lastEventAt <= ceilingMs
|
|
885
|
+
},
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
837
889
|
/** The facts the hook turn clock reads off a session signal. */
|
|
838
890
|
export interface HookTurnSignal {
|
|
839
891
|
lastEvent: string
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
ollamaToolStatusLabel,
|
|
38
38
|
parseToolArguments,
|
|
39
39
|
} from './ollama-tools.js'
|
|
40
|
+
import { ollamaTrailOutcome, ollamaTrailStep, type JobTrailDraft } from './job-trail.js'
|
|
40
41
|
|
|
41
42
|
const INACTIVITY_MS = 60_000
|
|
42
43
|
const WALL_MAX_MS = 180_000
|
|
@@ -347,6 +348,14 @@ export async function callOllamaStreaming(
|
|
|
347
348
|
let postCount = 0
|
|
348
349
|
let toolsRetried = false
|
|
349
350
|
|
|
351
|
+
// 6.52.0, the Messages trail. This bridge owns its loop, so it reports each round's words
|
|
352
|
+
// and each tool call itself (there is no stdout to tee). A no-op without `onTrail`, and
|
|
353
|
+
// it never throws into the loop.
|
|
354
|
+
const trail = (draft: JobTrailDraft) => {
|
|
355
|
+
if (!callbacks.onTrail) return
|
|
356
|
+
try { callbacks.onTrail(draft) } catch { /* observation only */ }
|
|
357
|
+
}
|
|
358
|
+
|
|
350
359
|
/** One POST + its stream. Returns what the round produced. */
|
|
351
360
|
const runChatPost = async (sendTools: boolean): Promise<
|
|
352
361
|
| { kind: 'text' }
|
|
@@ -446,8 +455,14 @@ export async function callOllamaStreaming(
|
|
|
446
455
|
|
|
447
456
|
try {
|
|
448
457
|
for (;;) {
|
|
458
|
+
const roundStart = fullText.length
|
|
449
459
|
const round = await runChatPost(toolDefs.length > 0)
|
|
450
460
|
if (round.kind === 'handled') return sid
|
|
461
|
+
// One prose entry per round: what the model wrote before its calls, or its answer.
|
|
462
|
+
if (callbacks.onTrail && (round.kind === 'text' || round.kind === 'tools')) {
|
|
463
|
+
const roundText = fullText.slice(roundStart)
|
|
464
|
+
if (roundText.trim()) trail({ kind: 'prose', text: roundText })
|
|
465
|
+
}
|
|
451
466
|
if (round.kind === 'text') { await finalizeDone(); return sid }
|
|
452
467
|
|
|
453
468
|
if (round.kind === 'empty') {
|
|
@@ -472,6 +487,7 @@ export async function callOllamaStreaming(
|
|
|
472
487
|
if (finalized || abort.signal.aborted) break
|
|
473
488
|
const name = call.function.name
|
|
474
489
|
callbacks.onToolStatus?.(name)
|
|
490
|
+
if (callbacks.onTrail) trail(ollamaTrailStep(name, parseToolArguments(call.function.arguments)))
|
|
475
491
|
toolHeartbeat = setInterval(bumpInactivity, TOOL_HEARTBEAT_MS)
|
|
476
492
|
let result: string
|
|
477
493
|
try {
|
|
@@ -483,6 +499,7 @@ export async function callOllamaStreaming(
|
|
|
483
499
|
await finalizeError('ollama-bridge: tool call aborted.')
|
|
484
500
|
return sid
|
|
485
501
|
}
|
|
502
|
+
if (callbacks.onTrail) trail(ollamaTrailOutcome(result))
|
|
486
503
|
messages.push({
|
|
487
504
|
role: 'tool',
|
|
488
505
|
content: result,
|