@flowdular/sandbox 0.2.5 → 0.2.7

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.
@@ -688,14 +688,31 @@ export async function* runTurn(
688
688
  );
689
689
 
690
690
  const history = historyFrom(await readChat(context.workspaceRoot, session));
691
+ // Provider conversations retain instructions and tool history. Reuse one only
692
+ // while its role, module, skill, write ceiling, specification and model match.
693
+ const resumePrefix = `${driverId}:scope:`;
694
+ const resumeKey =
695
+ resumePrefix +
696
+ createHash('sha256')
697
+ .update(
698
+ JSON.stringify([
699
+ instruction,
700
+ active.specHash ?? null,
701
+ session.model ?? context.configuration.driverModel,
702
+ ]),
703
+ )
704
+ .digest('hex');
705
+ const previousKeys = Object.keys(session.resumeIds).filter(
706
+ (key) => key === driverId || key.startsWith(resumePrefix),
707
+ );
691
708
  const resumeId = input.freshContext
692
709
  ? null
693
- : (session.resumeIds[driverId] ?? null);
694
- if (input.freshContext) {
710
+ : (session.resumeIds[resumeKey] ?? null);
711
+ if (input.freshContext || (!resumeId && previousKeys.length > 0)) {
695
712
  yield await appendChatEntry(context.workspaceRoot, session, {
696
713
  kind: 'system',
697
714
  role: roleId,
698
- text: 'Starting with fresh agent context. Draft files, approved specification and sandbox history are preserved; the agent receives the brief and recent messages.',
715
+ text: `Starting fresh agent context for ${role.name} in ${active.id} with the current task and write scope. Draft files, approved specification and sandbox history are preserved; the agent receives the brief and recent messages.`,
699
716
  });
700
717
  }
701
718
  let nextResumeId = resumeId;
@@ -973,8 +990,9 @@ export async function* runTurn(
973
990
  }
974
991
 
975
992
  const resumeIds = { ...session.resumeIds };
976
- if (nextResumeId) resumeIds[driverId] = nextResumeId;
977
- else if (input.freshContext) delete resumeIds[driverId];
993
+ // Retain at most one conversation per driver, including after many handoffs.
994
+ for (const key of previousKeys) delete resumeIds[key];
995
+ if (nextResumeId) resumeIds[resumeKey] = nextResumeId;
978
996
  const updated = await updateSession(context.workspaceRoot, session.id, {
979
997
  resumeIds,
980
998
  state: failed
package/src/styles.css CHANGED
@@ -683,13 +683,16 @@
683
683
  color: var(--danger);
684
684
  }
685
685
 
686
- .chat__auto {
686
+ .chat-settings {
687
687
  display: inline-flex;
688
- align-items: center;
689
- gap: 6px;
690
- font-size: var(--text-sm);
691
- color: var(--ink-3);
692
- white-space: nowrap;
688
+ }
689
+
690
+ .chat-settings__panel {
691
+ position: fixed;
692
+ z-index: 20;
693
+ width: min(320px, calc(100vw - 32px));
694
+ max-height: calc(100dvh - 32px);
695
+ overflow-y: auto;
693
696
  }
694
697
 
695
698
  .chat__selection {
@@ -1571,3 +1574,102 @@
1571
1574
  font-size: var(--text-md);
1572
1575
  color: var(--ink-3);
1573
1576
  }
1577
+
1578
+ @keyframes chat-activity-spin {
1579
+ to {
1580
+ transform: rotate(360deg);
1581
+ }
1582
+ }
1583
+ .chat__event--active > svg {
1584
+ animation: chat-activity-spin 1.5s linear infinite;
1585
+ }
1586
+ @media (prefers-reduced-motion: reduce) {
1587
+ .chat__event--active > svg {
1588
+ animation: none;
1589
+ }
1590
+ }
1591
+
1592
+ /* Compact operation summaries with the original details available on demand. */
1593
+ .chat-tool {
1594
+ min-width: 0;
1595
+ color: var(--ink-2);
1596
+ font-size: var(--text-sm);
1597
+ }
1598
+ .chat-tool__summary {
1599
+ display: flex;
1600
+ align-items: center;
1601
+ gap: 8px;
1602
+ min-width: 0;
1603
+ padding: 6px 0;
1604
+ cursor: pointer;
1605
+ list-style: none;
1606
+ }
1607
+ .chat-tool__summary::-webkit-details-marker {
1608
+ display: none;
1609
+ }
1610
+ .chat-tool__summary:focus-visible {
1611
+ outline: 2px solid var(--focus);
1612
+ outline-offset: 2px;
1613
+ }
1614
+ .chat-tool__summary > svg {
1615
+ flex-shrink: 0;
1616
+ }
1617
+ .chat-tool__action {
1618
+ flex-shrink: 0;
1619
+ }
1620
+ .chat-tool__target {
1621
+ min-width: 0;
1622
+ flex: 1;
1623
+ overflow: hidden;
1624
+ text-overflow: ellipsis;
1625
+ white-space: nowrap;
1626
+ font-family: var(--font-mono);
1627
+ }
1628
+ .chat-tool__status,
1629
+ .chat-tool__duration {
1630
+ color: var(--ink-3);
1631
+ flex-shrink: 0;
1632
+ font-variant-numeric: tabular-nums;
1633
+ }
1634
+ .chat-tool--failed .chat-tool__status,
1635
+ .chat-tool--failed .chat-tool__summary > svg:first-child {
1636
+ color: var(--danger);
1637
+ }
1638
+ .chat-tool[open] .chat-tool__summary > svg:last-child {
1639
+ transform: rotate(180deg);
1640
+ }
1641
+ .chat-tool__details {
1642
+ margin: 0 0 8px 22px;
1643
+ padding: 12px;
1644
+ border: 1px solid var(--line);
1645
+ border-radius: var(--r-sm);
1646
+ }
1647
+ .chat-tool__details dt {
1648
+ color: var(--ink-3);
1649
+ margin-bottom: 4px;
1650
+ }
1651
+ .chat-tool__details dd {
1652
+ margin: 0 0 8px;
1653
+ white-space: pre-wrap;
1654
+ overflow-wrap: anywhere;
1655
+ font-family: var(--font-mono);
1656
+ }
1657
+ .chat-tool__details dd:last-child {
1658
+ margin-bottom: 0;
1659
+ }
1660
+ .chat-tool--running .chat-tool__summary > svg:first-child {
1661
+ animation: chat-activity-spin 1.5s linear infinite;
1662
+ }
1663
+ @media (prefers-reduced-motion: reduce) {
1664
+ .chat-tool--running .chat-tool__summary > svg:first-child {
1665
+ animation: none;
1666
+ }
1667
+ }
1668
+ @media (max-width: 600px) {
1669
+ .chat-tool__summary {
1670
+ flex-wrap: wrap;
1671
+ }
1672
+ .chat-tool__target {
1673
+ flex-basis: calc(100% - 32px);
1674
+ }
1675
+ }
package/vite.config.ts CHANGED
@@ -9,6 +9,7 @@ import { defineConfig } from 'vite';
9
9
  import { sandboxDirectory } from './src/server/config.ts';
10
10
  import { findFlowdularWorkspace } from './src/server/workspace-root.ts';
11
11
  import { resolvePreviewModules } from './src/server/preview-modules.ts';
12
+ import { isolatePreviewHotUpdates } from './src/server/preview-hot-updates.ts';
12
13
  import type { SandboxSession } from './src/server/sessions.ts';
13
14
 
14
15
  Object.assign(process.env, flowdularEnvironment(process.env));
@@ -103,7 +104,10 @@ function previewModules(workspaceRoot: string): Plugin {
103
104
 
104
105
  const config = {
105
106
  root: appRoot,
106
- plugins: [previewModules(workspace.root), octane()],
107
+ plugins: [
108
+ previewModules(workspace.root),
109
+ ...isolatePreviewHotUpdates(octane(), workspace.root),
110
+ ],
107
111
  resolve: {
108
112
  /* Draft module code is loaded from a session workspace outside this app.
109
113
  It resolves the workspace packages through the node_modules link the