@mastra/factory 0.24.0-alpha.11 → 0.24.0-alpha.12

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.
Files changed (136) hide show
  1. package/README.md +4 -4
  2. package/dist/environment/build-runner.js +2 -1
  3. package/dist/environment/build-runner.js.map +1 -1
  4. package/dist/environment/build-workflow.js +1 -1
  5. package/dist/environment/build-workflow.js.map +1 -1
  6. package/dist/environment/build.js +16 -14
  7. package/dist/environment/build.js.map +1 -1
  8. package/dist/factory.js.map +1 -1
  9. package/dist/integrations/github/app-identity.js.map +1 -1
  10. package/dist/integrations/github/integration.js +1 -1
  11. package/dist/integrations/github/integration.js.map +1 -1
  12. package/dist/integrations/github/reconcile-worker.js +1 -1
  13. package/dist/integrations/github/reconcile-worker.js.map +1 -1
  14. package/dist/integrations/github/routes.js +1 -1
  15. package/dist/integrations/github/routes.js.map +1 -1
  16. package/dist/integrations/github/rules.js +3 -2
  17. package/dist/integrations/github/rules.js.map +1 -1
  18. package/dist/integrations/github/sandbox.js +2 -2
  19. package/dist/integrations/github/sandbox.js.map +1 -1
  20. package/dist/integrations/gitlab/api.js.map +1 -1
  21. package/dist/integrations/gitlab/integration.js.map +1 -1
  22. package/dist/integrations/gitlab/reconciliation-config.js +2 -1
  23. package/dist/integrations/gitlab/reconciliation-config.js.map +1 -1
  24. package/dist/integrations/gitlab/rules.js.map +1 -1
  25. package/dist/integrations/incidentio/api.js.map +1 -1
  26. package/dist/integrations/incidentio/integration.js.map +1 -1
  27. package/dist/integrations/incidentio/rules.js.map +1 -1
  28. package/dist/integrations/issue-reconcile-worker.js +1 -1
  29. package/dist/integrations/issue-reconcile-worker.js.map +1 -1
  30. package/dist/integrations/issue-reconciler.js +1 -1
  31. package/dist/integrations/issue-reconciler.js.map +1 -1
  32. package/dist/integrations/jira/api.js.map +1 -1
  33. package/dist/integrations/jira/integration.js.map +1 -1
  34. package/dist/integrations/jira/rules.js.map +1 -1
  35. package/dist/integrations/linear/integration.js.map +1 -1
  36. package/dist/integrations/linear/rules.js.map +1 -1
  37. package/dist/integrations/platform/api-client.js.map +1 -1
  38. package/dist/integrations/platform/github/event-worker.js +1 -1
  39. package/dist/integrations/platform/github/event-worker.js.map +1 -1
  40. package/dist/integrations/platform/github/integration.js +2 -2
  41. package/dist/integrations/platform/github/integration.js.map +1 -1
  42. package/dist/integrations/platform/gitlab/event-worker.js.map +1 -1
  43. package/dist/integrations/platform/gitlab/integration.js.map +1 -1
  44. package/dist/integrations/platform/incidentio/integration.js +2 -1
  45. package/dist/integrations/platform/incidentio/integration.js.map +1 -1
  46. package/dist/integrations/platform/jira/integration.js.map +1 -1
  47. package/dist/integrations/platform/linear/event-worker.js +1 -1
  48. package/dist/integrations/platform/linear/event-worker.js.map +1 -1
  49. package/dist/integrations/platform/linear/integration.js.map +1 -1
  50. package/dist/integrations/slack/connect-route.js +1 -1
  51. package/dist/integrations/slack/connect-route.js.map +1 -1
  52. package/dist/integrations/slack/feed-publisher.js +1 -1
  53. package/dist/integrations/slack/feed-publisher.js.map +1 -1
  54. package/dist/integrations/slack/integration.js +1 -1
  55. package/dist/integrations/slack/integration.js.map +1 -1
  56. package/dist/integrations/slack/slack.js +2 -1
  57. package/dist/integrations/slack/slack.js.map +1 -1
  58. package/dist/integrations/workos/integration.js.map +1 -1
  59. package/dist/packages/_internals/workspace/dist/index.js +18 -1
  60. package/dist/packages/_internals/workspace/dist/index.js.map +1 -1
  61. package/dist/routes/attention-activity.js.map +1 -1
  62. package/dist/routes/attention-parked.js.map +1 -1
  63. package/dist/routes/attention-providers.js.map +1 -1
  64. package/dist/routes/automation-runs.js.map +1 -1
  65. package/dist/routes/contracts.js +1 -1
  66. package/dist/routes/contracts.js.map +1 -1
  67. package/dist/routes/custom-provider-source.js.map +1 -1
  68. package/dist/routes/fs.d.ts.map +1 -1
  69. package/dist/routes/fs.js +4 -3
  70. package/dist/routes/fs.js.map +1 -1
  71. package/dist/routes/intake.js +2 -1
  72. package/dist/routes/intake.js.map +1 -1
  73. package/dist/routes/knowledge.js +1 -1
  74. package/dist/routes/knowledge.js.map +1 -1
  75. package/dist/routes/oauth.js +3 -2
  76. package/dist/routes/oauth.js.map +1 -1
  77. package/dist/routes/projects.js +32 -30
  78. package/dist/routes/projects.js.map +1 -1
  79. package/dist/routes/skills.js.map +1 -1
  80. package/dist/routes/source-control-sessions.js +1 -1
  81. package/dist/routes/source-control-sessions.js.map +1 -1
  82. package/dist/routes/surface.js +8 -2
  83. package/dist/routes/surface.js.map +1 -1
  84. package/dist/routes/tenant-credentials.js.map +1 -1
  85. package/dist/routes/work-items.js.map +1 -1
  86. package/dist/rules/binding-context.js +4 -2
  87. package/dist/rules/binding-context.js.map +1 -1
  88. package/dist/rules/dispatcher.js +4 -4
  89. package/dist/rules/dispatcher.js.map +1 -1
  90. package/dist/rules/start-coordinator.js.map +1 -1
  91. package/dist/rules/tools.js +2 -2
  92. package/dist/rules/tools.js.map +1 -1
  93. package/dist/rules/transition-service.js.map +1 -1
  94. package/dist/sandbox/callback-factory-sandbox.js.map +1 -1
  95. package/dist/sandbox/local-factory-sandbox.js.map +1 -1
  96. package/dist/sandbox/session-retirement.js.map +1 -1
  97. package/dist/session/live-sessions.js.map +1 -1
  98. package/dist/session/memory-settings-hydration.js +2 -1
  99. package/dist/session/memory-settings-hydration.js.map +1 -1
  100. package/dist/spa-static.js.map +1 -1
  101. package/dist/state-signing.js +1 -1
  102. package/dist/state-signing.js.map +1 -1
  103. package/dist/storage/domains/audit/base.js.map +1 -1
  104. package/dist/storage/domains/audit/domain.js.map +1 -1
  105. package/dist/storage/domains/channel-identity/base.js.map +1 -1
  106. package/dist/storage/domains/comments/base.js.map +1 -1
  107. package/dist/storage/domains/comments/domain.js.map +1 -1
  108. package/dist/storage/domains/comments/feed-context.js.map +1 -1
  109. package/dist/storage/domains/credentials/base.js.map +1 -1
  110. package/dist/storage/domains/custom-providers/base.js.map +1 -1
  111. package/dist/storage/domains/intake/base.js.map +1 -1
  112. package/dist/storage/domains/integrations/base.js.map +1 -1
  113. package/dist/storage/domains/memory-settings/base.js.map +1 -1
  114. package/dist/storage/domains/model-defaults/base.js.map +1 -1
  115. package/dist/storage/domains/projects/base.js.map +1 -1
  116. package/dist/storage/domains/queue-health/base.js.map +1 -1
  117. package/dist/storage/domains/source-control/base.js.map +1 -1
  118. package/dist/storage/domains/work-items/base.js +2 -1
  119. package/dist/storage/domains/work-items/base.js.map +1 -1
  120. package/dist/storage/domains/work-items/board-version.js.map +1 -1
  121. package/dist/storage/domains/work-items/metrics.js.map +1 -1
  122. package/dist/tools/messaging-tools.d.ts.map +1 -1
  123. package/dist/tools/messaging-tools.js +201 -73
  124. package/dist/tools/messaging-tools.js.map +1 -1
  125. package/dist/tools/run-watch.d.ts +2 -0
  126. package/dist/tools/run-watch.d.ts.map +1 -1
  127. package/dist/tools/run-watch.js +42 -2
  128. package/dist/tools/run-watch.js.map +1 -1
  129. package/dist/tools/session-tools.d.ts.map +1 -1
  130. package/dist/tools/session-tools.js +24 -9
  131. package/dist/tools/session-tools.js.map +1 -1
  132. package/dist/tools/sessions-state-processor.js +2 -2
  133. package/dist/tools/sessions-state-processor.js.map +1 -1
  134. package/dist/workspace.js +15 -12
  135. package/dist/workspace.js.map +1 -1
  136. package/package.json +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"sessions-state-processor.js","names":["#deps","#workerLine","#signal","#userState","#render","newest","report"],"sources":["../../src/tools/sessions-state-processor.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport type { NotificationsStorage } from '@mastra/core/notifications';\nimport type { ComputeStateSignalArgs, ComputeStateSignalResult, Processor } from '@mastra/core/processors';\n\nimport { boardForWorkItem } from '../boards/index.js';\nimport type { BoardRegistry } from '../boards/registry.js';\nimport type { SourceControlSessionLookup } from '../session/factory-session.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport { resolveFactoryToolScope } from './scope.js';\nimport type { FactoryToolScope, ResolveFactoryToolScopeOptions } from './scope.js';\nimport {\n itemLabel,\n lastReport as report,\n listSessionNotifications,\n listSideSessions,\n newestNotification as newest,\n} from './side-sessions.js';\nimport type { FactorySessionRunState, LastReport } from './side-sessions.js';\n\nconst STATE_ID = 'factory-sessions';\n/** Finished side sessions and items that reached a terminal stage drop out after this long. */\nexport const FACTORY_SESSIONS_MAX_AGE_MS = 24 * 60 * 60 * 1000;\n/** Asks a long run to show it is still moving; a silent half hour reads the same as a stuck one. */\nconst PROGRESS_ASK = 'On long work, send a progress report after each plan step.';\n\n/** A running worker whose thread has not moved for this long is shown as quiet, so a stuck run stands out. */\nexport const FACTORY_SESSIONS_QUIET_MS = 10 * 60 * 1000;\n/** Rows shown before the rest collapse into \"+N more\". */\nexport const FACTORY_SESSIONS_MAX_ROWS = 20;\nconst EMPTY_CACHE_KEY = 'empty';\n\nexport type { FactorySessionRunState } from './side-sessions.js';\n\nexport interface FactorySessionsStateDependencies {\n authEnabled: boolean;\n sessions: Pick<SourceControlSessionLookup, 'getBySessionId' | 'listByOrigin'>;\n projects: ResolveFactoryToolScopeOptions['projects'];\n workItems: Pick<\n WorkItemsStorage,\n | 'hasRunBindingForSession'\n | 'listSessionSubscriptions'\n | 'getForProject'\n | 'listRunBindings'\n | 'findActiveRunBindingForSession'\n | 'listWorkItemSubscribers'\n >;\n boards: BoardRegistry;\n /** Whether the session's agent is mid-run in this process; `idle` when it isn't live here. */\n isRunning: (sessionId: string) => Promise<boolean>;\n /** When a thread last changed; tells a long run that is still moving from one that stopped. */\n lastActivity?: (threadId: string) => Promise<Date | null>;\n notifications: () => Promise<Pick<NotificationsStorage, 'listNotifications'> | null>;\n now?: () => number;\n}\n\ninterface SideSessionRow {\n sessionId: string;\n title: string;\n state: FactorySessionRunState;\n lastReport: LastReport | null;\n muted: boolean;\n}\n\ninterface FollowedRow {\n workItemId: string;\n label: string;\n title: string;\n stage: string;\n worker: { role: string; sessionId: string; state: FactorySessionRunState; quietSince: string | null } | null;\n lastReport: LastReport | null;\n}\n\ninterface SessionsState {\n sideSessions: SideSessionRow[];\n following: FollowedRow[];\n more: number;\n}\n\nfunction escapeText(value: string): string {\n return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');\n}\n\nfunction cacheKeyFor(value: unknown): string {\n return `sessions:${createHash('sha256').update(JSON.stringify(value)).digest('hex').slice(0, 16)}`;\n}\n\n/**\n * The `<factory-sessions>` state signal. A user session sees the side sessions\n * it started and the work items it follows; a side session or item worker sees\n * who it reports to. Sessions it only messaged never appear. Silent until there\n * is something to show; when the last entry ages out it sends one final empty\n * snapshot, then stays silent.\n */\nexport class FactorySessionsStateProcessor implements Processor<'factory-sessions'> {\n readonly id = STATE_ID;\n readonly stateId = STATE_ID;\n readonly #deps: FactorySessionsStateDependencies;\n\n constructor(deps: FactorySessionsStateDependencies) {\n this.#deps = deps;\n }\n\n async computeStateSignal(args: ComputeStateSignalArgs): Promise<ComputeStateSignalResult> {\n const scope = await resolveFactoryToolScope({\n requestContext: args.requestContext!,\n authEnabled: this.#deps.authEnabled,\n sessions: this.#deps.sessions,\n projects: this.#deps.projects,\n workItems: this.#deps.workItems,\n });\n if (!scope) return;\n\n const hasBase = Boolean(args.lastSnapshot) && args.contextWindow.hasSnapshot;\n const previousKey = hasBase\n ? (args.tracking?.currentCacheKey ?? (args.lastSnapshot?.metadata?.state as { cacheKey?: string })?.cacheKey)\n : undefined;\n const unchanged = (cacheKey: string) => previousKey === cacheKey;\n // Silence rule: one final empty snapshot replaces a non-empty one, then nothing.\n const cleared = () => hasBase && previousKey !== undefined && previousKey !== EMPTY_CACHE_KEY;\n\n if (scope.kind === 'worker') {\n const contents = await this.#workerLine(scope);\n if (!contents) {\n return cleared()\n ? this.#signal(EMPTY_CACHE_KEY, 'This session is not reporting on any work now.', {}, { entries: 0 })\n : undefined;\n }\n const cacheKey = cacheKeyFor(contents);\n if (unchanged(cacheKey)) return;\n return this.#signal(cacheKey, escapeText(contents), { worker: contents }, { entries: 1 });\n }\n\n const state = await this.#userState(scope);\n if (state.sideSessions.length === 0 && state.following.length === 0) {\n if (!cleared()) return;\n return this.#signal(EMPTY_CACHE_KEY, 'No sub-sessions or followed work items.', state, { entries: 0 });\n }\n const cacheKey = cacheKeyFor(state);\n if (unchanged(cacheKey)) return;\n return this.#signal(cacheKey, this.#render(state), state, {\n sideSessions: state.sideSessions.length,\n following: state.following.length,\n });\n }\n\n #signal(cacheKey: string, contents: string, value: unknown, attributes: Record<string, number>) {\n return {\n id: STATE_ID,\n cacheKey,\n mode: 'snapshot' as const,\n tagName: 'factory-sessions',\n contents,\n value: { sessions: value },\n attributes,\n metadata: { value: { sessions: value } },\n };\n }\n\n async #workerLine(scope: FactoryToolScope): Promise<string | null> {\n if (scope.origin) {\n return `Started by session ${scope.origin.sessionId} (thread ${scope.origin.threadId}). Report back with factory_message_origin. ${PROGRESS_ASK}`;\n }\n const binding = await this.#deps.workItems.findActiveRunBindingForSession({\n orgId: scope.orgId,\n factoryProjectId: scope.factoryProjectId,\n sessionId: scope.sessionId,\n });\n if (!binding) return null;\n const item = await this.#deps.workItems.getForProject(scope.orgId, scope.factoryProjectId, binding.workItemId);\n const subscribers = await this.#deps.workItems.listWorkItemSubscribers({\n orgId: scope.orgId,\n workItemId: binding.workItemId,\n });\n const following = subscribers.filter(subscriber => subscriber.sessionId !== scope.sessionId).length;\n // Nobody to report to: nothing worth showing (most board-started workers).\n if (following === 0) return null;\n const label = item ? itemLabel(item) : binding.workItemId;\n return `Working on ${label} as ${binding.role}. Reports go to ${following} following session${following === 1 ? '' : 's'}. ${PROGRESS_ASK}`;\n }\n\n async #userState(scope: FactoryToolScope): Promise<SessionsState> {\n const now = this.#deps.now?.() ?? Date.now();\n const notifications = await this.#deps.notifications();\n const records = await listSessionNotifications(notifications, scope.threadId);\n\n const sideSessions: Array<SideSessionRow & { active: boolean }> = [];\n const listed = await listSideSessions(\n {\n sessions: this.#deps.sessions,\n workItems: this.#deps.workItems,\n isRunning: this.#deps.isRunning,\n notifications,\n },\n scope,\n { records },\n );\n for (const row of listed) {\n if (row.state !== 'running' && now - Date.parse(row.lastActivityAt) > FACTORY_SESSIONS_MAX_AGE_MS) continue;\n sideSessions.push({\n sessionId: row.sessionId,\n title: row.title,\n state: row.state,\n lastReport: row.lastReport,\n muted: row.mutedAt !== null,\n active: row.state !== 'idle',\n });\n }\n\n const subscriptions = await this.#deps.workItems.listSessionSubscriptions({\n orgId: scope.orgId,\n factoryProjectId: scope.factoryProjectId,\n sessionId: scope.sessionId,\n });\n const following: Array<FollowedRow & { active: boolean }> = [];\n for (const subscription of subscriptions) {\n const item = await this.#deps.workItems.getForProject(\n scope.orgId,\n scope.factoryProjectId,\n subscription.workItemId,\n );\n if (!item) continue;\n const stage = item.stages[0] ?? 'unknown';\n const terminal = this.#deps.boards.get(boardForWorkItem(item))?.isTerminal(stage) ?? false;\n if (terminal && now - item.updatedAt.getTime() > FACTORY_SESSIONS_MAX_AGE_MS) continue;\n const bindings = await this.#deps.workItems.listRunBindings(scope.orgId, scope.factoryProjectId, item.id);\n const active = bindings.find(binding => binding.status === 'active');\n const label = itemLabel(item);\n const last = newest(\n records,\n record =>\n record.attributes?.['work-item'] === label || (record.kind === 'stage' && record.sourceId === item.id),\n );\n const running = active ? await this.#deps.isRunning(active.sessionId) : false;\n const lastActivity = active && running ? await this.#deps.lastActivity?.(active.threadId) : null;\n // Only a quiet run is shown with its time; a moving one would change the snapshot on every step.\n const quiet = lastActivity && now - lastActivity.getTime() >= FACTORY_SESSIONS_QUIET_MS ? lastActivity : null;\n const lastFromWorker = active\n ? newest(records, record => record.attributes?.['from-session'] === active.sessionId)\n : undefined;\n following.push({\n workItemId: item.id,\n label,\n title: item.title,\n stage,\n worker: active\n ? {\n role: active.role,\n sessionId: active.sessionId,\n state: running ? 'running' : lastFromWorker?.kind === 'blocked' ? 'parked' : 'idle',\n quietSince: quiet ? quiet.toISOString() : null,\n }\n : null,\n lastReport: report(last),\n active: running && !terminal,\n });\n }\n\n // Active entries first, then within the cap; the rest collapse into \"+N more\".\n const ordered = [\n ...sideSessions.map(row => ({ section: 'side' as const, row })),\n ...following.map(row => ({ section: 'following' as const, row })),\n ].sort((a, b) => Number(b.row.active) - Number(a.row.active));\n const shown = ordered.slice(0, FACTORY_SESSIONS_MAX_ROWS);\n const strip = <T extends { active: boolean }>({ active: _active, ...row }: T) => row;\n return {\n sideSessions: shown.flatMap(entry =>\n entry.section === 'side' ? [strip(entry.row as SideSessionRow & { active: boolean })] : [],\n ),\n following: shown.flatMap(entry =>\n entry.section === 'following' ? [strip(entry.row as FollowedRow & { active: boolean })] : [],\n ),\n more: ordered.length - shown.length,\n };\n }\n\n #render(state: SessionsState): string {\n const lastReport = (last: LastReport | null) => (last ? `; last report ${last.kind} at ${last.at}` : '');\n const lines: string[] = [];\n if (state.sideSessions.length > 0) {\n lines.push('Side sessions you started:');\n for (const row of state.sideSessions) {\n lines.push(\n `- \"${escapeText(row.title)}\" (session ${row.sessionId}): ${row.state}${row.muted ? ' (muted)' : ''}${lastReport(row.lastReport)}`,\n );\n }\n }\n if (state.following.length > 0) {\n lines.push('Work items you follow:');\n for (const row of state.following) {\n const worker = row.worker\n ? `; ${escapeText(row.worker.role)} session ${row.worker.sessionId} ${row.worker.state}${row.worker.quietSince ? ` but quiet since ${row.worker.quietSince}` : ''}`\n : '';\n lines.push(\n `- ${escapeText(row.label)} \"${escapeText(row.title)}\": ${escapeText(row.stage)}${worker}${lastReport(row.lastReport)}`,\n );\n }\n }\n if (state.more > 0) lines.push(`+${state.more} more`);\n lines.push(\n 'Message any of these sessions with factory_signal_session; read full reports with notification_inbox; list side sessions with factory_list_side_sessions; on a turn a person started, mute one with factory_unsubscribe_session.',\n );\n return lines.join('\\n');\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sessions-state-processor.js","names":["newest","report"],"sources":["../../src/tools/sessions-state-processor.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport type { NotificationsStorage } from '@mastra/core/notifications';\nimport type { ComputeStateSignalArgs, ComputeStateSignalResult, Processor } from '@mastra/core/processors';\n\nimport { boardForWorkItem } from '../boards/index.js';\nimport type { BoardRegistry } from '../boards/registry.js';\nimport type { SourceControlSessionLookup } from '../session/factory-session.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport { resolveFactoryToolScope } from './scope.js';\nimport type { FactoryToolScope, ResolveFactoryToolScopeOptions } from './scope.js';\nimport {\n itemLabel,\n lastReport as report,\n listSessionNotifications,\n listSideSessions,\n newestNotification as newest,\n} from './side-sessions.js';\nimport type { FactorySessionRunState, LastReport } from './side-sessions.js';\n\nconst STATE_ID = 'factory-sessions';\n/** Finished side sessions and items that reached a terminal stage drop out after this long. */\nexport const FACTORY_SESSIONS_MAX_AGE_MS = 24 * 60 * 60 * 1000;\n/** Asks a long run to show it is still moving; a silent half hour reads the same as a stuck one. */\nconst PROGRESS_ASK = 'On long work, send a progress report after each plan step.';\n\n/** A running worker whose thread has not moved for this long is shown as quiet, so a stuck run stands out. */\nexport const FACTORY_SESSIONS_QUIET_MS = 10 * 60 * 1000;\n/** Rows shown before the rest collapse into \"+N more\". */\nexport const FACTORY_SESSIONS_MAX_ROWS = 20;\nconst EMPTY_CACHE_KEY = 'empty';\n\nexport type { FactorySessionRunState } from './side-sessions.js';\n\nexport interface FactorySessionsStateDependencies {\n authEnabled: boolean;\n sessions: Pick<SourceControlSessionLookup, 'getBySessionId' | 'listByOrigin'>;\n projects: ResolveFactoryToolScopeOptions['projects'];\n workItems: Pick<\n WorkItemsStorage,\n | 'hasRunBindingForSession'\n | 'listSessionSubscriptions'\n | 'getForProject'\n | 'listRunBindings'\n | 'findActiveRunBindingForSession'\n | 'listWorkItemSubscribers'\n >;\n boards: BoardRegistry;\n /** Whether the session's agent is mid-run in this process; `idle` when it isn't live here. */\n isRunning: (sessionId: string) => Promise<boolean>;\n /** When a thread last changed; tells a long run that is still moving from one that stopped. */\n lastActivity?: (threadId: string) => Promise<Date | null>;\n notifications: () => Promise<Pick<NotificationsStorage, 'listNotifications'> | null>;\n now?: () => number;\n}\n\ninterface SideSessionRow {\n sessionId: string;\n title: string;\n state: FactorySessionRunState;\n lastReport: LastReport | null;\n muted: boolean;\n}\n\ninterface FollowedRow {\n workItemId: string;\n label: string;\n title: string;\n stage: string;\n worker: { role: string; sessionId: string; state: FactorySessionRunState; quietSince: string | null } | null;\n lastReport: LastReport | null;\n}\n\ninterface SessionsState {\n sideSessions: SideSessionRow[];\n following: FollowedRow[];\n more: number;\n}\n\nfunction escapeText(value: string): string {\n return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');\n}\n\nfunction cacheKeyFor(value: unknown): string {\n return `sessions:${createHash('sha256').update(JSON.stringify(value)).digest('hex').slice(0, 16)}`;\n}\n\n/**\n * The `<factory-sessions>` state signal. A user session sees the side sessions\n * it started and the work items it follows; a side session or item worker sees\n * who it reports to. Sessions it only messaged never appear. Silent until there\n * is something to show; when the last entry ages out it sends one final empty\n * snapshot, then stays silent.\n */\nexport class FactorySessionsStateProcessor implements Processor<'factory-sessions'> {\n readonly id = STATE_ID;\n readonly stateId = STATE_ID;\n readonly #deps: FactorySessionsStateDependencies;\n\n constructor(deps: FactorySessionsStateDependencies) {\n this.#deps = deps;\n }\n\n async computeStateSignal(args: ComputeStateSignalArgs): Promise<ComputeStateSignalResult> {\n const scope = await resolveFactoryToolScope({\n requestContext: args.requestContext!,\n authEnabled: this.#deps.authEnabled,\n sessions: this.#deps.sessions,\n projects: this.#deps.projects,\n workItems: this.#deps.workItems,\n });\n if (!scope) return;\n\n const hasBase = Boolean(args.lastSnapshot) && args.contextWindow.hasSnapshot;\n const previousKey = hasBase\n ? (args.tracking?.currentCacheKey ?? (args.lastSnapshot?.metadata?.state as { cacheKey?: string })?.cacheKey)\n : undefined;\n const unchanged = (cacheKey: string) => previousKey === cacheKey;\n // Silence rule: one final empty snapshot replaces a non-empty one, then nothing.\n const cleared = () => hasBase && previousKey !== undefined && previousKey !== EMPTY_CACHE_KEY;\n\n if (scope.kind === 'worker') {\n const contents = await this.#workerLine(scope);\n if (!contents) {\n return cleared()\n ? this.#signal(EMPTY_CACHE_KEY, 'This session is not reporting on any work now.', {}, { entries: 0 })\n : undefined;\n }\n const cacheKey = cacheKeyFor(contents);\n if (unchanged(cacheKey)) return;\n return this.#signal(cacheKey, escapeText(contents), { worker: contents }, { entries: 1 });\n }\n\n const state = await this.#userState(scope);\n if (state.sideSessions.length === 0 && state.following.length === 0) {\n if (!cleared()) return;\n return this.#signal(EMPTY_CACHE_KEY, 'No sub-sessions or followed work items.', state, { entries: 0 });\n }\n const cacheKey = cacheKeyFor(state);\n if (unchanged(cacheKey)) return;\n return this.#signal(cacheKey, this.#render(state), state, {\n sideSessions: state.sideSessions.length,\n following: state.following.length,\n });\n }\n\n #signal(cacheKey: string, contents: string, value: unknown, attributes: Record<string, number>) {\n return {\n id: STATE_ID,\n cacheKey,\n mode: 'snapshot' as const,\n tagName: 'factory-sessions',\n contents,\n value: { sessions: value },\n attributes,\n metadata: { value: { sessions: value } },\n };\n }\n\n async #workerLine(scope: FactoryToolScope): Promise<string | null> {\n if (scope.origin) {\n return `Started by session ${scope.origin.sessionId} (thread ${scope.origin.threadId}). Report back with factory_message_origin. ${PROGRESS_ASK}`;\n }\n const binding = await this.#deps.workItems.findActiveRunBindingForSession({\n orgId: scope.orgId,\n factoryProjectId: scope.factoryProjectId,\n sessionId: scope.sessionId,\n });\n if (!binding) return null;\n const item = await this.#deps.workItems.getForProject(scope.orgId, scope.factoryProjectId, binding.workItemId);\n const subscribers = await this.#deps.workItems.listWorkItemSubscribers({\n orgId: scope.orgId,\n workItemId: binding.workItemId,\n });\n const following = subscribers.filter(subscriber => subscriber.sessionId !== scope.sessionId).length;\n // Nobody to report to: nothing worth showing (most board-started workers).\n if (following === 0) return null;\n const label = item ? itemLabel(item) : binding.workItemId;\n return `Working on ${label} as ${binding.role}. Reports go to ${following} following session${following === 1 ? '' : 's'}. ${PROGRESS_ASK}`;\n }\n\n async #userState(scope: FactoryToolScope): Promise<SessionsState> {\n const now = this.#deps.now?.() ?? Date.now();\n const notifications = await this.#deps.notifications();\n const records = await listSessionNotifications(notifications, scope.threadId);\n\n const sideSessions: Array<SideSessionRow & { active: boolean }> = [];\n const listed = await listSideSessions(\n {\n sessions: this.#deps.sessions,\n workItems: this.#deps.workItems,\n isRunning: this.#deps.isRunning,\n notifications,\n },\n scope,\n { records },\n );\n for (const row of listed) {\n if (row.state !== 'running' && now - Date.parse(row.lastActivityAt) > FACTORY_SESSIONS_MAX_AGE_MS) continue;\n sideSessions.push({\n sessionId: row.sessionId,\n title: row.title,\n state: row.state,\n lastReport: row.lastReport,\n muted: row.mutedAt !== null,\n active: row.state !== 'idle',\n });\n }\n\n const subscriptions = await this.#deps.workItems.listSessionSubscriptions({\n orgId: scope.orgId,\n factoryProjectId: scope.factoryProjectId,\n sessionId: scope.sessionId,\n });\n const following: Array<FollowedRow & { active: boolean }> = [];\n for (const subscription of subscriptions) {\n const item = await this.#deps.workItems.getForProject(\n scope.orgId,\n scope.factoryProjectId,\n subscription.workItemId,\n );\n if (!item) continue;\n const stage = item.stages[0] ?? 'unknown';\n const terminal = this.#deps.boards.get(boardForWorkItem(item))?.isTerminal(stage) ?? false;\n if (terminal && now - item.updatedAt.getTime() > FACTORY_SESSIONS_MAX_AGE_MS) continue;\n const bindings = await this.#deps.workItems.listRunBindings(scope.orgId, scope.factoryProjectId, item.id);\n const active = bindings.find(binding => binding.status === 'active');\n const label = itemLabel(item);\n const last = newest(\n records,\n record =>\n record.attributes?.['work-item'] === label || (record.kind === 'stage' && record.sourceId === item.id),\n );\n const running = active ? await this.#deps.isRunning(active.sessionId) : false;\n const lastActivity = active && running ? await this.#deps.lastActivity?.(active.threadId) : null;\n // Only a quiet run is shown with its time; a moving one would change the snapshot on every step.\n const quiet = lastActivity && now - lastActivity.getTime() >= FACTORY_SESSIONS_QUIET_MS ? lastActivity : null;\n const lastFromWorker = active\n ? newest(records, record => record.attributes?.['from-session'] === active.sessionId)\n : undefined;\n following.push({\n workItemId: item.id,\n label,\n title: item.title,\n stage,\n worker: active\n ? {\n role: active.role,\n sessionId: active.sessionId,\n state: running ? 'running' : lastFromWorker?.kind === 'blocked' ? 'parked' : 'idle',\n quietSince: quiet ? quiet.toISOString() : null,\n }\n : null,\n lastReport: report(last),\n active: running && !terminal,\n });\n }\n\n // Active entries first, then within the cap; the rest collapse into \"+N more\".\n const ordered = [\n ...sideSessions.map(row => ({ section: 'side' as const, row })),\n ...following.map(row => ({ section: 'following' as const, row })),\n ].sort((a, b) => Number(b.row.active) - Number(a.row.active));\n const shown = ordered.slice(0, FACTORY_SESSIONS_MAX_ROWS);\n const strip = <T extends { active: boolean }>({ active: _active, ...row }: T) => row;\n return {\n sideSessions: shown.flatMap(entry =>\n entry.section === 'side' ? [strip(entry.row as SideSessionRow & { active: boolean })] : [],\n ),\n following: shown.flatMap(entry =>\n entry.section === 'following' ? [strip(entry.row as FollowedRow & { active: boolean })] : [],\n ),\n more: ordered.length - shown.length,\n };\n }\n\n #render(state: SessionsState): string {\n const lastReport = (last: LastReport | null) => (last ? `; last report ${last.kind} at ${last.at}` : '');\n const lines: string[] = [];\n if (state.sideSessions.length > 0) {\n lines.push('Side sessions you started:');\n for (const row of state.sideSessions) {\n lines.push(\n `- \"${escapeText(row.title)}\" (session ${row.sessionId}): ${row.state}${row.muted ? ' (muted)' : ''}${lastReport(row.lastReport)}`,\n );\n }\n }\n if (state.following.length > 0) {\n lines.push('Work items you follow:');\n for (const row of state.following) {\n const worker = row.worker\n ? `; ${escapeText(row.worker.role)} session ${row.worker.sessionId} ${row.worker.state}${row.worker.quietSince ? ` but quiet since ${row.worker.quietSince}` : ''}`\n : '';\n lines.push(\n `- ${escapeText(row.label)} \"${escapeText(row.title)}\": ${escapeText(row.stage)}${worker}${lastReport(row.lastReport)}`,\n );\n }\n }\n if (state.more > 0) lines.push(`+${state.more} more`);\n lines.push(\n 'Message any of these sessions with factory_signal_session; read full reports with notification_inbox; list side sessions with factory_list_side_sessions; on a turn a person started, mute one with factory_unsubscribe_session.',\n );\n return lines.join('\\n');\n }\n}\n"],"mappings":"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"}
package/dist/workspace.js CHANGED
@@ -902,17 +902,19 @@ function createWorkspaceFactory(options = {}) {
902
902
  }
903
903
  inFlight = void 0;
904
904
  const workspaceCommand = environment.workspaceSetupCommand;
905
- if (workspaceCommand && !gate.workspace.setupDone) if (hasFailedSetupCommand(session.id, `workspace:${workspaceCommand}`)) console.warn("[Mastra Factory] Skipping workspace setup command that already failed this session", {
906
- orgId: session.orgId,
907
- sessionId: session.sessionId
908
- });
909
- else try {
910
- await timedPhase("workspace.setup(workspace)", () => runSetupCommand(target, gate.root, workspaceCommand));
911
- await gate.workspace.markSetupDone();
912
- } catch (error) {
913
- if (!(error instanceof SetupCommandError)) throw error;
914
- recordFailedSetupCommand(session.id, `workspace:${workspaceCommand}`);
915
- throw new SetupCommandError(`${error.message}. The sandbox stays usable: the workspace setup command is skipped for the rest of the session. Retry your command, then fix it in the environment settings or run it manually.`, error.code);
905
+ if (workspaceCommand && !gate.workspace.setupDone) {
906
+ if (hasFailedSetupCommand(session.id, `workspace:${workspaceCommand}`)) console.warn("[Mastra Factory] Skipping workspace setup command that already failed this session", {
907
+ orgId: session.orgId,
908
+ sessionId: session.sessionId
909
+ });
910
+ else try {
911
+ await timedPhase("workspace.setup(workspace)", () => runSetupCommand(target, gate.root, workspaceCommand));
912
+ await gate.workspace.markSetupDone();
913
+ } catch (error) {
914
+ if (!(error instanceof SetupCommandError)) throw error;
915
+ recordFailedSetupCommand(session.id, `workspace:${workspaceCommand}`);
916
+ throw new SetupCommandError(`${error.message}. The sandbox stays usable: the workspace setup command is skipped for the rest of the session. Retry your command, then fix it in the environment settings or run it manually.`, error.code);
917
+ }
916
918
  }
917
919
  } finally {
918
920
  record();
@@ -962,13 +964,14 @@ function createWorkspaceFactory(options = {}) {
962
964
  ];
963
965
  const projectSkillPaths = checkoutDirs ? checkoutDirs.flatMap((dir) => skillRoots.map((skillPath) => path.posix.join(dir, skillPath))) : skillRoots;
964
966
  const guardedSkillFallback = new UnmaterializedAwareSkillSource(filesystem, () => sessionEntry.sandbox.status === "running");
967
+ const skillPaths = [...effectiveSkillExtension?.paths ?? [], ...projectSkillPaths];
965
968
  const workspace = new Workspace({
966
969
  id: workspaceId,
967
970
  name: "Mastra Code Factory Session Workspace",
968
971
  filesystem,
969
972
  sandbox: sessionSandbox,
970
973
  tools: MASTRACODE_WORKSPACE_TOOLS,
971
- skills: [...effectiveSkillExtension?.paths ?? [], ...projectSkillPaths],
974
+ skills: skillPaths,
972
975
  skillSource: effectiveSkillExtension?.createSource(guardedSkillFallback, projectSkillPaths) ?? guardedSkillFallback
973
976
  });
974
977
  mastra?.addWorkspace(workspace, workspaceId, { source: "mastra" });