@gotcos/glasses-server 6.28.0 → 6.30.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/CHANGELOG.md +52 -0
- package/package.json +1 -1
- package/server/index.ts +45 -0
- package/server/lib/fork-thread.ts +957 -0
- package/server/lib/occupied-threads.ts +191 -0
- package/server/lib/thread-attach-capability.ts +200 -0
- package/server/routes/agent-session-bindings.ts +475 -1
- package/server/routes/agent-sessions.ts +62 -1
- package/server/routes/health.ts +24 -0
package/CHANGELOG.md
CHANGED
|
@@ -2219,6 +2219,58 @@ unsaved capture, and makes batch status stop lying about finished work.
|
|
|
2219
2219
|
|
|
2220
2220
|
# Changelog
|
|
2221
2221
|
|
|
2222
|
+
## [6.30.0] - 2026-08-16
|
|
2223
|
+
|
|
2224
|
+
### Sessions know which threads are live
|
|
2225
|
+
|
|
2226
|
+
- `GET /api/agent-sessions` now stamps each row with `running` (an agent is
|
|
2227
|
+
working in this thread right now) and `running_foreign` (it is held by
|
|
2228
|
+
something that is not COS, so a Continue would be refused), plus a
|
|
2229
|
+
`runningDegraded` flag on the payload when a probe could not see clearly.
|
|
2230
|
+
- `running` counts COS's own queued turn too. Two different questions: whether an
|
|
2231
|
+
agent is working (the badge) and whether a write would be refused (the
|
|
2232
|
+
affordance). Counting only foreign owners would hide your own turn from the
|
|
2233
|
+
screen you open to watch it.
|
|
2234
|
+
- **This is a display hint and never a write gate.** Attach and turn keep probing
|
|
2235
|
+
at the moment of the write, unchanged, so a desktop session opened between the
|
|
2236
|
+
list render and the tap is still caught.
|
|
2237
|
+
- Fails the opposite way to the gate: doubt reports `degraded` rather than
|
|
2238
|
+
painting every session busy.
|
|
2239
|
+
- One scan for the whole page, measured at 7ms for 45 real sessions (was 792ms
|
|
2240
|
+
before a scan-scoped memo and skipping lsof when no Codex writer lock exists).
|
|
2241
|
+
|
|
2242
|
+
**Required by COS Glasses 6.8.364.**
|
|
2243
|
+
|
|
2244
|
+
## [6.29.0] - 2026-08-16
|
|
2245
|
+
|
|
2246
|
+
### Continue is queued instead of holding the phone
|
|
2247
|
+
|
|
2248
|
+
- An attached turn is admitted with **202** and delivered in the background. A
|
|
2249
|
+
provider turn runs up to 21 minutes and iOS suspends the WebView the moment the
|
|
2250
|
+
phone is pocketed, so a synchronous turn was lost exactly when the user did the
|
|
2251
|
+
natural thing.
|
|
2252
|
+
- Every safety gate stays synchronous: body, replay, queued-prompt, lease, target,
|
|
2253
|
+
epoch, fence, claim, occupancy, head baseline, pin. A refusal still reaches the
|
|
2254
|
+
user immediately and precisely. Only the spawn moved.
|
|
2255
|
+
- **New:** `GET /api/agent-sessions/bindings/:bindingId/turns/:clientTurnId`
|
|
2256
|
+
serves the same durable ledger record the replay path serves, so a poll and a
|
|
2257
|
+
retry can never disagree about what a turn did. Reports `unknown` rather than
|
|
2258
|
+
`failed` for a key it has never seen.
|
|
2259
|
+
- Fixed: a post-202 refusal recorded nothing, so the status route would have
|
|
2260
|
+
answered `pending` forever. Pre-202 refusals keep the old semantics and stay
|
|
2261
|
+
re-evaluatable.
|
|
2262
|
+
|
|
2263
|
+
### Fork, ungated
|
|
2264
|
+
|
|
2265
|
+
- `POST /api/agent-sessions/:provider/:threadId/fork` is registered whether or not
|
|
2266
|
+
`COS_THREAD_ATTACH_ENABLED` is set. Fork appends to nothing: the original is left
|
|
2267
|
+
byte-identical. It is what every refusal tells the user to do instead, and a
|
|
2268
|
+
refusal pointing at a route that 404s is a dead end.
|
|
2269
|
+
|
|
2270
|
+
**Required by COS Glasses 6.8.363.** Continue still needs
|
|
2271
|
+
`COS_THREAD_ATTACH_ENABLED=1`; with it unset the write routes are not registered
|
|
2272
|
+
and behavior is unchanged.
|
|
2273
|
+
|
|
2222
2274
|
## 6.12.7
|
|
2223
2275
|
|
|
2224
2276
|
Pairs the public server with COS Glasses build 227+ meeting follow-ups while
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gotcos/glasses-server",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.30.0",
|
|
4
4
|
"description": "COS Glasses \u2014 self-hosted AI heads-up-display server for Even G2 smart glasses, powered by Claude Code, Codex, or Cursor Agent CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/server/index.ts
CHANGED
|
@@ -26,6 +26,7 @@ import { cosSpawnedPids } from './lib/agent-session-ownership-store.js'
|
|
|
26
26
|
import { realOccupancyDirs, realOccupancyProbes } from './lib/occupancy-probes.js'
|
|
27
27
|
import { realAttachedWorkspaceDeps, resolveAttachedWorkspace } from './lib/attached-workspace.js'
|
|
28
28
|
import { deliverAttachedTurn, realAttachedTurnDeps } from './lib/attached-provider-adapter.js'
|
|
29
|
+
import { forkThread, realForkDeps } from './lib/fork-thread.js'
|
|
29
30
|
import { nativeHead, realNativeHeadDeps } from './lib/native-head.js'
|
|
30
31
|
import { threadOccupancy } from './lib/thread-occupancy.js'
|
|
31
32
|
import { displayRouter } from './routes/display.js'
|
|
@@ -376,6 +377,43 @@ const deliverAttachedTurnForRoute = async (request: {
|
|
|
376
377
|
})
|
|
377
378
|
}
|
|
378
379
|
|
|
380
|
+
/**
|
|
381
|
+
* The shim for FORK — the action seventeen refusal strings in this feature already
|
|
382
|
+
* recommend, and which until 6.29 existed nowhere in the server or the app.
|
|
383
|
+
*
|
|
384
|
+
* Deliberately much thinner than the attached shim above, and every difference is
|
|
385
|
+
* load-bearing:
|
|
386
|
+
*
|
|
387
|
+
* - NO PREFLIGHT. `realAttachedTurnDeps` takes an occupancy re-check because it is
|
|
388
|
+
* about to APPEND to a thread someone may have open on their desktop. A fork
|
|
389
|
+
* appends to nothing: verified byte-for-byte on both providers on 2026-08-16,
|
|
390
|
+
* source sha256 unchanged across the run. A live desktop owner must therefore
|
|
391
|
+
* NOT block a fork — gating it here would refuse at exactly the moment fork is
|
|
392
|
+
* the only path the user has left.
|
|
393
|
+
* - NO `onSpawn` HAND-BACK. The turns route owns its own ledger record because it
|
|
394
|
+
* holds the `recordedPids` list that its `finally` releases from. The fork route
|
|
395
|
+
* holds no such list, so `fork-thread.ts` records and releases the child itself,
|
|
396
|
+
* exactly once, on every exit path — which keeps the recorded:released ratio the
|
|
397
|
+
* operator reads for leak detection at 1:1.
|
|
398
|
+
* - A WATERMARK IS SUPPLIED, and it is the point. `nativeHead` of the SOURCE, read
|
|
399
|
+
* once before the spawn and once after the child settles, is what turns "a fork
|
|
400
|
+
* does not touch the original" from a promise into a checked property. It is
|
|
401
|
+
* SYNCHRONOUS (native-head.ts:491), which is what lets the module read the
|
|
402
|
+
* baseline at the spawn boundary without an await. Unwired, the module can only
|
|
403
|
+
* ever report `unverified`, and it would report that honestly rather than
|
|
404
|
+
* claiming a verification it never performed.
|
|
405
|
+
*/
|
|
406
|
+
const forkThreadForRoute = (request: {
|
|
407
|
+
provider: 'claude' | 'codex'
|
|
408
|
+
nativeThreadId: string
|
|
409
|
+
prompt: string
|
|
410
|
+
cwd: string
|
|
411
|
+
policy: 'read_only'
|
|
412
|
+
}): Promise<unknown> => forkThread({
|
|
413
|
+
...request,
|
|
414
|
+
deps: realForkDeps((provider, threadId) => nativeHead(provider, threadId, nativeHeadDeps)),
|
|
415
|
+
})
|
|
416
|
+
|
|
379
417
|
// API routes
|
|
380
418
|
app.use('/api', healthRouter)
|
|
381
419
|
app.use('/api', diagRouter)
|
|
@@ -413,6 +451,13 @@ app.use('/api', createAgentSessionBindingsRouter({
|
|
|
413
451
|
},
|
|
414
452
|
nativeHead: (provider, threadId) => nativeHead(provider, threadId, nativeHeadDeps),
|
|
415
453
|
deliverAttachedTurn: deliverAttachedTurnForRoute as never,
|
|
454
|
+
forkThread: forkThreadForRoute,
|
|
455
|
+
// The fork's real spawn directory. Separate from `resolveTarget` above, which
|
|
456
|
+
// deliberately yields only fingerprints because plan 3.3 keeps a filesystem path
|
|
457
|
+
// off anything client-visible. Null refuses: never fall back to the server's own
|
|
458
|
+
// working directory, which is wherever the LaunchAgent happened to start.
|
|
459
|
+
resolveForkWorkspace: (provider, threadId) =>
|
|
460
|
+
resolveAttachedWorkspace(provider, threadId, attachedWorkspaceDeps)?.path ?? null,
|
|
416
461
|
// One instance per process. The epoch high-water mark is only monotonic if a
|
|
417
462
|
// single reader owns the durable store, so this must never be constructed twice.
|
|
418
463
|
// `open()` never throws: an unreadable store yields a DEGRADED registry whose
|