kira-glasses-bridge 2.40.0 → 2.40.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kira-glasses-bridge",
3
- "version": "2.40.0",
3
+ "version": "2.40.1",
4
4
  "description": "Connects your Mac to Kira, the assistant for Even G2 glasses: Claude Code sessions, files, folders and apps, by voice.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -3,7 +3,7 @@
3
3
  // SDK expects.
4
4
  import { homedir } from 'node:os'
5
5
  import { relative, resolve, sep } from 'node:path'
6
- import { PLAN_OPTIONS, permissionOptions, requestId } from './protocol.mjs'
6
+ import { PLAN_OPTIONS, permissionOptions, requestId, unanswered } from './protocol.mjs'
7
7
 
8
8
  /** What an agent is told when it may not do something. */
9
9
  export const NOT_ANOTHER_WAY =
@@ -122,8 +122,23 @@ export function toRequest(name, input, cwd, verdict) {
122
122
  }
123
123
  }
124
124
 
125
- /** Turn a protocol answer back into what the SDK expects. */
126
- export function applyAnswer(name, input, request, answer) {
125
+ /**
126
+ * What the agent is told when nobody answered a card in time, by the card's type. Said as no refusal,
127
+ * so it neither gives up on the task nor reads the timeout's words as the person's.
128
+ */
129
+ const UNANSWERED = {
130
+ permission: 'Nobody answered the permission card in time. This was not a refusal: ' +
131
+ 'carry on without this step, or try again later.',
132
+ choice: 'Nobody answered the question in time. This was not a refusal: ' +
133
+ 'carry on with your best judgment and say what you assumed.',
134
+ plan: 'Nobody answered about the plan in time. This was not a refusal: ' +
135
+ 'stay in plan mode and say the plan is waiting for their go-ahead.',
136
+ }
137
+
138
+ /** Turn a protocol answer (`{ answer, by }`) back into what the SDK expects. */
139
+ export function applyAnswer(name, input, request, resolved) {
140
+ if (unanswered(resolved)) return { behavior: 'deny', message: UNANSWERED[request.type] ?? UNANSWERED.permission }
141
+ const answer = resolved.answer ?? {}
127
142
  const chosen = answer.optionIds ?? []
128
143
  if (request.type === 'choice') {
129
144
  const answers = {}
@@ -86,7 +86,7 @@ async function run({ prompt, sessionId, cwd, persona, settings, abort }, io) {
86
86
  resolved = { answer: { optionIds: [], text: err.message }, by: 'timeout' }
87
87
  }
88
88
  io.emit({ kind: 'request_resolved', id: request.id, answer: resolved.answer, by: resolved.by })
89
- return applyAnswer(toolName, input, request, resolved.answer)
89
+ return applyAnswer(toolName, input, request, resolved)
90
90
  }
91
91
 
92
92
  // Background work (subagents, `run_in_background` shell commands, monitors)
@@ -3,7 +3,7 @@
3
3
  // a link to open or a plain yes/no as a choice; and the answer back as MCP's accept, decline or cancel
4
4
  // with the form's content. Pure.
5
5
  import { short } from './claude-calls.mjs'
6
- import { requestId } from './protocol.mjs'
6
+ import { requestId, unanswered } from './protocol.mjs'
7
7
 
8
8
  const ACCEPT = [
9
9
  { id: 'accept', label: 'Accept' },
@@ -93,7 +93,7 @@ function valueOf(f, a) {
93
93
  */
94
94
  export function formResponse(params, request, resolved) {
95
95
  if (!request) return { action: 'decline' }
96
- if (resolved?.by === 'timeout') return { action: 'cancel' }
96
+ if (unanswered(resolved)) return { action: 'cancel' }
97
97
  const answer = resolved?.answer ?? {}
98
98
  if (!request.questions) return { action: (answer.optionIds ?? []).includes('accept') ? 'accept' : 'decline' }
99
99
  const content = {}
@@ -3,7 +3,7 @@
3
3
  // and an MCP server's form (mcpServer/elicitation/request), each as a request card; and the person's
4
4
  // answer back in Codex's own response shape (an MCP server's form: codex-forms.mjs). Pure; codex-ask.mjs asks.
5
5
  import { displayPath, short } from './claude-calls.mjs'
6
- import { permissionOptions, requestId } from './protocol.mjs'
6
+ import { permissionOptions, requestId, unanswered } from './protocol.mjs'
7
7
 
8
8
  const options = list => list.map((o, k) => ({ id: `o${k + 1}`, ...o }))
9
9
  /** A question's answer: per question, else (the first one) the answer's own options and words. */
@@ -37,7 +37,7 @@ export function userInputRequest(params) {
37
37
 
38
38
  /** The answers as Codex takes them, by its question ids; unanswered (timed out, secret) ones are empty. */
39
39
  export function userInputResponse(params, request, resolved) {
40
- const answer = resolved?.by === 'timeout' ? undefined : resolved?.answer
40
+ const answer = unanswered(resolved) ? undefined : resolved?.answer
41
41
  const asked = (params.questions ?? []).filter(q => !q.isSecret)
42
42
  const answers = Object.fromEntries((params.questions ?? []).map(q => [q.id, { answers: [] }]))
43
43
  asked.forEach((codexQ, n) => {
@@ -58,6 +58,9 @@ export const PLAN_OPTIONS = [
58
58
  { id: 'revise', label: 'Revise', description: 'Change the plan first; say what to change' },
59
59
  ]
60
60
 
61
+ /** Nobody answered in time (or the job stopped first): not a refusal, so never reported as one. */
62
+ export const unanswered = resolved => resolved?.by === 'timeout'
63
+
61
64
  /** Normalize the many ways an answer can arrive into option ids and text. */
62
65
  export function normalizeAnswer(answer) {
63
66
  const a = answer ?? {}