osborn 0.9.153 → 0.9.154

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.
@@ -45,7 +45,8 @@ RUN apt-get update -qq && \
45
45
  git \
46
46
  make \
47
47
  g++ \
48
- python-is-python3 && \
48
+ python-is-python3 \
49
+ util-linux && \
49
50
  rm -rf /var/lib/apt/lists/*
50
51
 
51
52
  ARG OSBORN_VERSION=latest
@@ -122,6 +123,25 @@ fi
122
123
  echo "[sandbox-d] === boot at $(date -Iseconds) ===" >> "$LOGFILE"
123
124
  exec > >(tee -a "$LOGFILE") 2>&1
124
125
 
126
+ # === Swap on volume ===
127
+ # 8GB swapfile on /workspace (persistent volume). Created once on first boot,
128
+ # reactivated on every subsequent boot (swapon state is lost across cold starts).
129
+ # Gives 8GB RAM + 8GB swap = 16GB addressable — pressure relief when the dev
130
+ # server or concurrent sub-agents spike memory without killing the voice loop.
131
+ SWAPFILE=/workspace/swapfile
132
+ if [ ! -f "$SWAPFILE" ]; then
133
+ echo "[sandbox-d] Creating 8GB swapfile at $SWAPFILE..."
134
+ fallocate -l 8G "$SWAPFILE" 2>/dev/null || dd if=/dev/zero of="$SWAPFILE" bs=1M count=8192 status=none
135
+ chmod 600 "$SWAPFILE"
136
+ mkswap "$SWAPFILE"
137
+ echo "[sandbox-d] Swapfile created"
138
+ fi
139
+ if swapon "$SWAPFILE" 2>/dev/null; then
140
+ echo "[sandbox-d] Swap active: $(free -h | grep Swap)"
141
+ else
142
+ echo "[sandbox-d] swapon skipped (already active or kernel restriction)"
143
+ fi
144
+
125
145
  # Onboarding-suppression JSON
126
146
  ONBOARDING_JSON='{"numStartups":10,"installMethod":"npm","autoUpdates":false,"hasCompletedOnboarding":true,"hasTrustDialogAccepted":true,"hasTrustDialogHooksAccepted":true,"hasCompletedProjectOnboarding":true,"hasAcknowledgedCostThreshold":true,"effortCalloutV2Dismissed":true,"theme":"dark","projects":{"/workspace":{"hasTrustDialogAccepted":true,"hasTrustDialogHooksAccepted":true,"hasCompletedProjectOnboarding":true}}}'
127
147
 
package/dist/index.js CHANGED
@@ -2709,7 +2709,7 @@ async function main() {
2709
2709
  const { readSessionHistory } = await import('./session-access.js');
2710
2710
  const history = readSessionHistory(sessionId, workingDir, {
2711
2711
  lastN: 10,
2712
- types: ['assistant'],
2712
+ types: ['assistant', 'tool_use'], // tool_use entries also carry Claude's spoken text
2713
2713
  });
2714
2714
  recentMessages = history
2715
2715
  .filter((m) => m.text)
@@ -2725,8 +2725,14 @@ async function main() {
2725
2725
  // BEFORE the previous TTS completed, and we may have already suppressed in-flight
2726
2726
  // tts_say events that arrived during that overlap).
2727
2727
  const carriedSuppressed = lastInterruption?.suppressedText ?? '';
2728
- lastInterruption = { spokenText: fullText, recentMessages, suppressedText: carriedSuppressed, timestamp: Date.now() };
2729
- console.log(`📋 Interruption context stored (text: ${fullText.length} chars, JSONL: ${recentMessages.length} chars, suppressed carried: ${carriedSuppressed.length} chars)`);
2728
+ lastInterruption = {
2729
+ spokenText: fullText,
2730
+ recentMessages,
2731
+ suppressedText: carriedSuppressed,
2732
+ fullBlockText: fullBlockText && fullBlockText !== fullText ? fullBlockText : undefined,
2733
+ timestamp: Date.now(),
2734
+ };
2735
+ console.log(`📋 Interruption context stored (text: ${fullText.length} chars, JSONL: ${recentMessages.length} chars, block: ${fullBlockText?.length ?? 0} chars, suppressed carried: ${carriedSuppressed.length} chars)`);
2730
2736
  }
2731
2737
  /**
2732
2738
  * Append text the agent tried to say while the user was speaking, but which we
@@ -2766,6 +2772,7 @@ async function main() {
2766
2772
  spokenText: lastInterruption.spokenText,
2767
2773
  recentMessages: lastInterruption.recentMessages,
2768
2774
  suppressedText: lastInterruption.suppressedText,
2775
+ fullBlockText: lastInterruption.fullBlockText,
2769
2776
  };
2770
2777
  lastInterruption = null;
2771
2778
  return ctx;
@@ -21,6 +21,8 @@ export interface InterruptionContext {
21
21
  * and can re-articulate the relevant bits in its next response.
22
22
  */
23
23
  suppressedText: string;
24
+ /** Full text of the interrupted TTS block — fallback when JSONL read returns empty */
25
+ fullBlockText?: string;
24
26
  }
25
27
  export interface PipelineDirectOptions extends ClaudeLLMOptions {
26
28
  onFastBrainResult?: (result: FastBrainPanelResult) => void;
@@ -204,7 +204,10 @@ export class PipelineDirectLLM extends llm.LLM {
204
204
  `Anything in "Your recent messages" below that appears AFTER the quoted heard text is content the user did not hear. The user has no memory of it.`,
205
205
  ``,
206
206
  `Your recent messages (full untruncated — you wrote these):`,
207
- interruptCtx.recentMessages || '(no recent messages found)',
207
+ interruptCtx.recentMessages
208
+ || (interruptCtx.fullBlockText
209
+ ? `(JSONL unavailable — full TTS block text follows)\n"${interruptCtx.fullBlockText}"`
210
+ : '(no recent messages found)'),
208
211
  suppressedBlock,
209
212
  ``,
210
213
  `User's message: "${userText}"`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "osborn",
3
- "version": "0.9.153",
3
+ "version": "0.9.154",
4
4
  "description": "Voice AI coding assistant - local agent that connects to Osborn frontend",
5
5
  "type": "module",
6
6
  "bin": {