@gotcos/glasses-server 6.21.0 → 6.21.1

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 CHANGED
@@ -1,3 +1,12 @@
1
+ ## 6.21.1
2
+
3
+ - Make the transactional Claude readiness proof explicitly use Haiku instead
4
+ of inheriting a user's heavyweight default model. The no-tool proof now has
5
+ a 45-second bound, while normal COS queries keep their selected models.
6
+ - Preserve timeout and cancellation reasons across the provider-process close
7
+ race. A timed-out proof now reports `provider proof timed out` instead of the
8
+ misleading `provider process exited before launch`.
9
+
1
10
  ## 6.21.0
2
11
 
3
12
  Transcription quality is now a machine-owned, observable two-tier policy.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gotcos/glasses-server",
3
- "version": "6.21.0",
3
+ "version": "6.21.1",
4
4
  "description": "COS Glasses — 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": {
@@ -17,7 +17,7 @@ const PROOF_PROMPT = `This is an automated local readiness check. Do not use too
17
17
  const successCache = new Map<ProofProvider, ProviderProofResult>()
18
18
  const inFlight = new Map<ProofProvider, Promise<ProviderProofResult>>()
19
19
 
20
- interface ProcessResult {
20
+ export interface ProcessResult {
21
21
  code: number | null
22
22
  stdout: string
23
23
  stderr: string
@@ -25,7 +25,9 @@ interface ProcessResult {
25
25
  aborted: boolean
26
26
  }
27
27
 
28
- function runBounded(
28
+ type TerminationReason = 'timeout' | 'abort' | null
29
+
30
+ export function runBounded(
29
31
  command: string,
30
32
  args: string[],
31
33
  input: string,
@@ -48,6 +50,11 @@ function runBounded(
48
50
  let stdout = ''
49
51
  let stderr = ''
50
52
  let settled = false
53
+ // The child `close` event normally wins the race against the async
54
+ // terminateProviderProcess() result. Record WHY termination began before
55
+ // sending a signal so that close cannot misreport a timeout as
56
+ // "exited before launch" (Control 0.3.5 field failure, 2026-08-03).
57
+ let terminationReason: TerminationReason = null
51
58
  const cap = (value: string) => value.slice(-256_000)
52
59
  child.stdout.on('data', chunk => { stdout = cap(stdout + chunk.toString()) })
53
60
  child.stderr.on('data', chunk => { stderr = cap(stderr + chunk.toString()) })
@@ -58,27 +65,57 @@ function runBounded(
58
65
  signal?.removeEventListener('abort', abort)
59
66
  resolvePromise(result)
60
67
  }
68
+ const terminatedResult = (code: number | null): ProcessResult => ({
69
+ code,
70
+ stdout,
71
+ stderr,
72
+ timedOut: terminationReason === 'timeout',
73
+ aborted: terminationReason === 'abort',
74
+ })
61
75
  const timer = setTimeout(() => {
76
+ if (settled || terminationReason) return
77
+ terminationReason = 'timeout'
62
78
  void terminateProviderProcess(child, { termGraceMs: 50 }).then(result => {
63
- if (result.closed) finish({ code: result.code, stdout, stderr, timedOut: true, aborted: false })
79
+ if (result.closed) finish(terminatedResult(result.code))
64
80
  else console.error('[provider-proof] timed-out provider did not close after SIGKILL; retaining request ownership')
65
81
  })
66
82
  }, timeoutMs)
67
83
  timer.unref?.()
68
84
  const abort = () => {
85
+ if (settled || terminationReason) return
86
+ terminationReason = 'abort'
69
87
  void terminateProviderProcess(child).then(result => {
70
- if (result.closed) finish({ code: result.code, stdout, stderr, timedOut: false, aborted: true })
88
+ if (result.closed) finish(terminatedResult(result.code))
71
89
  else console.error('[provider-proof] canceled provider did not close after SIGKILL; retaining request ownership')
72
90
  })
73
91
  }
74
- child.once('error', err => finish({ code: null, stdout, stderr: err.message, timedOut: false, aborted: false }))
75
- child.once('close', code => finish({ code, stdout, stderr, timedOut: false, aborted: false }))
92
+ child.once('error', err => {
93
+ stderr = cap(stderr + err.message)
94
+ finish(terminatedResult(null))
95
+ })
96
+ child.once('close', code => finish(terminatedResult(code)))
76
97
  signal?.addEventListener('abort', abort, { once: true })
77
98
  child.stdin.on('error', () => { /* close/error is authoritative */ })
78
99
  child.stdin.end(input)
79
100
  })
80
101
  }
81
102
 
103
+ export const CLAUDE_PROOF_MODEL = 'haiku'
104
+ export const CLAUDE_PROOF_TIMEOUT_MS = 45_000
105
+
106
+ export function claudeProofArgs(): string[] {
107
+ return [
108
+ '-p',
109
+ '--model', CLAUDE_PROOF_MODEL,
110
+ '--output-format', 'json',
111
+ '--permission-mode', 'dontAsk',
112
+ '--tools', '',
113
+ '--allowedTools', '',
114
+ '--system-prompt', PROOF_PROMPT,
115
+ PROOF_PROMPT,
116
+ ]
117
+ }
118
+
82
119
  export function claudeProofText(stdout: string): string {
83
120
  try {
84
121
  const parsed = JSON.parse(stdout) as { result?: unknown }
@@ -120,15 +157,7 @@ function safeProofError(result: ProcessResult): string {
120
157
  async function executeProof(provider: ProofProvider, signal?: AbortSignal): Promise<ProviderProofResult> {
121
158
  const started = Date.now()
122
159
  const result = provider === 'claude'
123
- ? await runBounded('claude', [
124
- '-p',
125
- '--output-format', 'json',
126
- '--permission-mode', 'dontAsk',
127
- '--tools', '',
128
- '--allowedTools', '',
129
- '--system-prompt', PROOF_PROMPT,
130
- PROOF_PROMPT,
131
- ], '', 120_000, signal)
160
+ ? await runBounded('claude', claudeProofArgs(), '', CLAUDE_PROOF_TIMEOUT_MS, signal)
132
161
  : await runBounded('codex', [
133
162
  'exec',
134
163
  '--sandbox', 'read-only',