claude-threads 0.33.8 → 0.33.9

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
@@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11
11
 
12
12
  ### Fixed
13
13
  - **Session resume broken after v0.33.7** - Fixed migration issue where sessions persisted with the old `timeoutPostId` field name couldn't be resumed after upgrading to v0.33.7. The `timeoutPostId` → `lifecyclePostId` rename now includes a proper migration that converts existing sessions on first load.
14
+ - **Defensive defaults for persisted session fields** - Session resume now uses safe defaults for all optional fields, preventing crashes when loading sessions from older versions that may have missing fields. Fields like `sessionAllowedUsers`, `planApproved`, `forceInteractivePermissions`, etc. now gracefully default instead of potentially causing undefined errors.
15
+ - **Validate required fields before resume** - Sessions with missing critical fields (`threadId`, `platformId`, `claudeSessionId`, `workingDir`) are now skipped gracefully with a warning instead of crashing.
14
16
 
15
17
  ## [0.33.7] - 2026-01-04
16
18
 
package/dist/index.js CHANGED
@@ -20708,6 +20708,16 @@ async function startSession(options, username, displayName, replyToPostId, platf
20708
20708
  claude.sendMessage(content);
20709
20709
  }
20710
20710
  async function resumeSession(state, ctx) {
20711
+ if (!state.threadId || !state.platformId || !state.claudeSessionId || !state.workingDir) {
20712
+ const missing = [
20713
+ !state.threadId && "threadId",
20714
+ !state.platformId && "platformId",
20715
+ !state.claudeSessionId && "claudeSessionId",
20716
+ !state.workingDir && "workingDir"
20717
+ ].filter(Boolean).join(", ");
20718
+ log10.warn(`Skipping session with missing required fields: ${missing}`);
20719
+ return;
20720
+ }
20711
20721
  const shortId = state.threadId.substring(0, 8);
20712
20722
  const platforms = ctx.state.platforms;
20713
20723
  const platform = platforms.get(state.platformId);
@@ -20756,11 +20766,11 @@ Please start a new session.`, state.threadId), { action: "Post resume failure no
20756
20766
  sessionId,
20757
20767
  platform,
20758
20768
  claudeSessionId: state.claudeSessionId,
20759
- startedBy: state.startedBy,
20769
+ startedBy: state.startedBy || "unknown",
20760
20770
  startedByDisplayName: state.startedByDisplayName,
20761
- startedAt: new Date(state.startedAt),
20771
+ startedAt: state.startedAt ? new Date(state.startedAt) : new Date,
20762
20772
  lastActivityAt: new Date,
20763
- sessionNumber: state.sessionNumber,
20773
+ sessionNumber: state.sessionNumber ?? 1,
20764
20774
  workingDir: state.workingDir,
20765
20775
  claude,
20766
20776
  currentPostId: null,
@@ -20768,11 +20778,11 @@ Please start a new session.`, state.threadId), { action: "Post resume failure no
20768
20778
  pendingApproval: null,
20769
20779
  pendingQuestionSet: null,
20770
20780
  pendingMessageApproval: null,
20771
- planApproved: state.planApproved,
20772
- sessionAllowedUsers: new Set(state.sessionAllowedUsers),
20773
- forceInteractivePermissions: state.forceInteractivePermissions,
20774
- sessionStartPostId: state.sessionStartPostId,
20775
- tasksPostId: state.tasksPostId,
20781
+ planApproved: state.planApproved ?? false,
20782
+ sessionAllowedUsers: new Set(state.sessionAllowedUsers || [state.startedBy].filter(Boolean)),
20783
+ forceInteractivePermissions: state.forceInteractivePermissions ?? false,
20784
+ sessionStartPostId: state.sessionStartPostId ?? null,
20785
+ tasksPostId: state.tasksPostId ?? null,
20776
20786
  lastTasksContent: state.lastTasksContent ?? null,
20777
20787
  tasksCompleted: state.tasksCompleted ?? false,
20778
20788
  tasksMinimized: state.tasksMinimized ?? false,
@@ -20782,7 +20792,7 @@ Please start a new session.`, state.threadId), { action: "Post resume failure no
20782
20792
  timeoutWarningPosted: false,
20783
20793
  isRestarting: false,
20784
20794
  isResumed: true,
20785
- resumeFailCount: state.resumeFailCount || 0,
20795
+ resumeFailCount: state.resumeFailCount ?? 0,
20786
20796
  wasInterrupted: false,
20787
20797
  hasClaudeResponded: true,
20788
20798
  inProgressTaskStart: null,
@@ -20797,6 +20807,7 @@ Please start a new session.`, state.threadId), { action: "Post resume failure no
20797
20807
  sessionDescription: state.sessionDescription,
20798
20808
  pullRequestUrl: state.pullRequestUrl,
20799
20809
  messageCount: state.messageCount ?? 0,
20810
+ lifecyclePostId: state.lifecyclePostId,
20800
20811
  statusBarTimer: null
20801
20812
  };
20802
20813
  mutableSessions(ctx).set(sessionId, session);
@@ -20812,8 +20823,8 @@ Please start a new session.`, state.threadId), { action: "Post resume failure no
20812
20823
  try {
20813
20824
  claude.start();
20814
20825
  log10.info(`Resumed session ${shortId}... (@${state.startedBy})`);
20815
- if (state.lifecyclePostId) {
20816
- await withErrorHandling(() => session.platform.updatePost(state.lifecyclePostId, `\uD83D\uDD04 **Session resumed** by @${state.startedBy}
20826
+ if (session.lifecyclePostId) {
20827
+ await withErrorHandling(() => session.platform.updatePost(session.lifecyclePostId, `\uD83D\uDD04 **Session resumed** by @${session.startedBy}
20817
20828
  *Reconnected to Claude session. You can continue where you left off.*`), { action: "Update timeout/shutdown post for resume", session });
20818
20829
  session.lifecyclePostId = undefined;
20819
20830
  } else {
@@ -21602,8 +21613,9 @@ function formatHistoryEntry(session) {
21602
21613
  const topic = getHistorySessionTopic(session);
21603
21614
  const threadLink = `[${topic}](/_redirect/pl/${session.threadId})`;
21604
21615
  const displayName = session.startedByDisplayName || session.startedBy;
21605
- const isTimedOut = !session.cleanedAt && session.lifecyclePostId;
21606
- const lastActivity = new Date(session.lastActivityAt);
21616
+ const legacySession = session;
21617
+ const isTimedOut = !session.cleanedAt && (session.lifecyclePostId || legacySession.timeoutPostId);
21618
+ const lastActivity = session.lastActivityAt ? new Date(session.lastActivityAt) : new Date;
21607
21619
  const time = formatRelativeTimeShort(lastActivity);
21608
21620
  const prStr = session.pullRequestUrl ? ` \xB7 ${formatPullRequestLink(session.pullRequestUrl)}` : "";
21609
21621
  const indicator = isTimedOut ? "\u23F8\uFE0F" : "\u2713";
@@ -21990,7 +22002,7 @@ class SessionManager {
21990
22002
  }
21991
22003
  return false;
21992
22004
  }
21993
- const allowedUsers = new Set(persistedSession.sessionAllowedUsers);
22005
+ const allowedUsers = new Set(persistedSession.sessionAllowedUsers || []);
21994
22006
  const platform = this.platforms.get(platformId);
21995
22007
  if (!allowedUsers.has(username) && !platform?.isUserAllowed(username)) {
21996
22008
  if (platform) {
@@ -22401,7 +22413,7 @@ class SessionManager {
22401
22413
  if (!session) {
22402
22414
  const persisted = this.getPersistedSession(threadId);
22403
22415
  if (persisted) {
22404
- return persisted.sessionAllowedUsers.includes(username) || this.platforms.get(persisted.platformId)?.isUserAllowed(username) || false;
22416
+ return (persisted.sessionAllowedUsers || []).includes(username) || this.platforms.get(persisted.platformId)?.isUserAllowed(username) || false;
22405
22417
  }
22406
22418
  return false;
22407
22419
  }
@@ -22693,7 +22705,7 @@ Release notes not available. See [GitHub releases](https://github.com/anneschuth
22693
22705
  const content = mattermost.isBotMentioned(message) ? mattermost.extractPrompt(message) : message.trim();
22694
22706
  const persistedSession = session.getPersistedSession(threadRoot);
22695
22707
  if (persistedSession) {
22696
- const allowedUsers = new Set(persistedSession.sessionAllowedUsers);
22708
+ const allowedUsers = new Set(persistedSession.sessionAllowedUsers || []);
22697
22709
  if (!allowedUsers.has(username) && !mattermost.isUserAllowed(username)) {
22698
22710
  await mattermost.createPost(`\u26A0\uFE0F @${username} is not authorized to resume this session`, threadRoot);
22699
22711
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-threads",
3
- "version": "0.33.8",
3
+ "version": "0.33.9",
4
4
  "description": "Share Claude Code sessions live in a Mattermost channel with interactive features",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",