<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Orwa Mahmoud</title>
    <description>The latest articles on DEV Community by Orwa Mahmoud (@orwamahmoud).</description>
    <link>https://dev.to/orwamahmoud</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4014610%2F5cd91360-5867-4fc4-ae37-093d3219eb9c.jpg</url>
      <title>DEV Community: Orwa Mahmoud</title>
      <link>https://dev.to/orwamahmoud</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/orwamahmoud"/>
    <language>en</language>
    <item>
      <title>Inside AdaptTable’s AI layer: discovery, context, and capabilities</title>
      <dc:creator>Orwa Mahmoud</dc:creator>
      <pubDate>Sat, 03 Oct 2026 15:01:33 +0000</pubDate>
      <link>https://dev.to/orwamahmoud/inside-adapttables-ai-layer-discovery-context-and-capabilities-3jk1</link>
      <guid>https://dev.to/orwamahmoud/inside-adapttables-ai-layer-discovery-context-and-capabilities-3jk1</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/orwamahmoud/adapttable-v3-is-live-a-framework-neutral-engine-smaller-feature-imports-and-optional-ai-2fgb"&gt;Part 3&lt;/a&gt;, I introduced the optional AI layer in AdaptTable v3. This article goes deeper into how to connect it, how an agent discovers the table’s capabilities, how context is selected, and how an application can extend that capability set without building a separate AI tool layer for the table.&lt;/p&gt;

&lt;p&gt;For developers building an agentic CRM, dashboard, admin panel, or SaaS application, the starting point is the normal table configuration. Connect the optional AI layer and supply your model or agent connection. AdaptTable supplies the supported table capabilities and their execution path, so you do not have to implement each table action again as a separate agent tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  One configuration for the React data table and the agent
&lt;/h2&gt;

&lt;p&gt;Consider an orders table. Its columns define order numbers, statuses, customers, and amounts. Its features enable filtering, sorting, grouping, and editing. Its callbacks connect changes to your application.&lt;/p&gt;

&lt;p&gt;A person can operate that table through its controls. An agent can operate it through the capabilities exposed by its AI session.&lt;/p&gt;

&lt;p&gt;For example, a connected agent can translate:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Show pending orders, group them by customer, and sort by amount.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;into the supported filtering, grouping, and sorting actions on that table. Those actions reuse the configured table behavior.&lt;/p&gt;

&lt;p&gt;A read-only report and an editable admin screen therefore expose different capabilities, even when they use the same library.&lt;/p&gt;

&lt;p&gt;If editing is disabled, there are no editing capabilities or instructions taking up context. The same goes for other disabled features. You spend context on the capabilities enabled on that table, not everything AdaptTable supports.&lt;/p&gt;

&lt;p&gt;AdaptTable’s AI capabilities include search, filtering, sorting, pagination, grouping, aggregation configuration, selection, column management, pinning, saved views, exports, and configured write operations. Existing row and bulk actions can also contribute capabilities. See the &lt;a href="https://adapttable.orwamahmoud.com/react/agent-capabilities/" rel="noopener noreferrer"&gt;AI capabilities guide&lt;/a&gt; for details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The table configuration is the shared starting point for the interface and the agent.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The live session is the connection point
&lt;/h2&gt;

&lt;p&gt;The architecture separates the configured AdaptTable instance, the live agent session, and the integration used to connect your model or agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your model or agent
        ↕
Choose an integration path
├── AdaptTable HTTP protocol
│   └── JSON response or streamed SSE
├── AI SDK adapter
│   └── AI SDK tool/message stream format
├── AG-UI adapter
│   └── AG-UI events, state, and tool calls
└── Custom agent integration
    └── Your agent maps its actions to the AgentSession
        ↕
Live AgentSession
├── catalog()   → discover available capabilities
├── describe()  → load detailed guidance when needed
└── execute()   → run AdaptTable actions
        ↕
Your configured AdaptTable
├── Features and columns
├── Current state and data source
└── Application callbacks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AdaptTable supports several ways to connect a model or agent to the same live &lt;code&gt;AgentSession&lt;/code&gt;: the AdaptTable HTTP protocol, the AI SDK adapter, the AG-UI adapter, or a custom integration that maps your own agent to the session.&lt;/p&gt;

&lt;p&gt;Each option changes how the agent connection is wired, while capability discovery and execution still go through the same &lt;code&gt;AgentSession&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The connections are bidirectional: table context goes to the agent, proposed actions come back, and execution results can be returned when the workflow needs them.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@adapttable/ai&lt;/code&gt; supplies the provider-neutral session and executor. &lt;code&gt;@adapttable/ai-react&lt;/code&gt; connects them to the live React table.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where the session comes from
&lt;/h3&gt;

&lt;p&gt;In React, &lt;code&gt;tableAgent(...)&lt;/code&gt; is added to the table’s &lt;code&gt;features&lt;/code&gt; prop. Its &lt;code&gt;bridge.attach&lt;/code&gt; callback supplies the live session.&lt;/p&gt;

&lt;p&gt;For an existing React AdaptTable instance, first receive that session in component state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AgentSession&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@adapttable/ai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Inside your existing table component:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSession&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AgentSession&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add the AI feature alongside the table features you already use. This example keeps an existing filtering feature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tableAgent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@adapttable/ai-react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// In your existing component's JSX:&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DataTable&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;rowKey&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;
  &lt;span class="na"&gt;features&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;([]),&lt;/span&gt;
    &lt;span class="nf"&gt;tableAgent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;tableId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;orders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;bridge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;setSession&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;DataTable&lt;/code&gt;, &lt;code&gt;filters&lt;/code&gt;, &lt;code&gt;rows&lt;/code&gt;, and &lt;code&gt;columns&lt;/code&gt; belong to the existing table setup. Keep its other feature entries too.&lt;/p&gt;

&lt;p&gt;Adding &lt;code&gt;tableAgent(...)&lt;/code&gt; to this table attaches the integration. &lt;code&gt;tableId: "orders"&lt;/code&gt; assigns a stable identity used in the AI contract; it does not look up an HTML element. You assign that identity here rather than defining a matching DOM ID elsewhere.&lt;/p&gt;

&lt;p&gt;The session received by &lt;code&gt;setSession&lt;/code&gt; is what the assistant or custom agent integration uses. Your existing data, columns, and callbacks remain in place. AI access settings can further restrict what the session exposes, as we’ll see below.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://adapttable.orwamahmoud.com/ai/" rel="noopener noreferrer"&gt;AI setup guide&lt;/a&gt; contains the complete assistant wiring. The important architectural boundary is that the agent connects to a live table session.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capability discovery and progressive disclosure
&lt;/h2&gt;

&lt;p&gt;The session separates discovering an operation, understanding it, and executing it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Session method&lt;/th&gt;
&lt;th&gt;What it provides&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;catalog()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Available capability keys and short summaries.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;describe(key)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Detailed instructions, accepted arguments, and result schema for a capability.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;execute(...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Execution through the table integration, with a structured result.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Available capability catalog
├── view.setPage     → Change the page
├── view.setFilters  → Apply filters
└── edit.cells       → Edit cell values
              │
User: "Mark order ORD-1042 as reviewed"
              │
              ▼
Is the edit.cells guide in context?
              │
        ┌─────┴─────┐
       yes          no
        │            │
        │            ▼
        │     describe("edit.cells")
        │     (arguments, row references,
        │      write guidance)
        │            │
        └─────┬──────┘
              ▼
Agent constructs the edit action
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;catalog()&lt;/code&gt; is derived from the live AdaptTable instance. Its enabled features, columns, source capabilities, configured actions and callbacks, and AI access settings determine which capabilities appear. If that configuration changes, the catalog reflects what the table can currently support and permit.&lt;/p&gt;

&lt;p&gt;The distinction between &lt;code&gt;catalog()&lt;/code&gt; and &lt;code&gt;describe(...)&lt;/code&gt; becomes useful when an AdaptTable instance exposes many capabilities.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;catalog()&lt;/code&gt; gives the agent every capability currently available on that AdaptTable instance, with its key and a short summary. The agent therefore knows what it can do without needing the full instructions for every operation in its context.&lt;/p&gt;

&lt;p&gt;The catalog does not need to carry every capability’s full instructions. Each available capability remains visible by key and short summary, while its detailed guide and input schema can be supplied separately.&lt;/p&gt;

&lt;p&gt;If the user asks for an operation whose detailed guidance is not already in the model’s context, the agent already knows that capability exists and knows its key from the catalog. It can request that key’s guidance, and the integration retrieves it through &lt;code&gt;describe(...)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This separation keeps capability discovery lightweight without hiding available operations from the agent.&lt;/p&gt;

&lt;p&gt;The same general pattern appears in &lt;a href="https://agentskills.io/home" rel="noopener noreferrer"&gt;Agent Skills&lt;/a&gt;, where a skill’s name and description support discovery and its full instructions are loaded when relevant. AdaptTable applies that pattern to capability guides and schemas; the analogy does not mean table capabilities are &lt;code&gt;SKILL.md&lt;/code&gt; files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom capabilities use the same session
&lt;/h3&gt;

&lt;p&gt;The catalog is not limited to AdaptTable’s built-in operations. A developer can expose application-specific logic to the agent by registering a custom capability with its own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;key&lt;/code&gt; — the stable capability identifier, such as &lt;code&gt;orders.archive&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;summary&lt;/code&gt; — the short description that appears in &lt;code&gt;catalog()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;guide&lt;/code&gt; — the detailed instructions the agent receives through &lt;code&gt;describe(...)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;input&lt;/code&gt; schema — the arguments the agent is allowed to send.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;output&lt;/code&gt; schema — the structured result the capability returns.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isEnabled&lt;/code&gt; rule — whether the capability is available for the current table state and policy.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;execute&lt;/code&gt; handler — the application code that actually performs the operation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, an orders application can expose an &lt;code&gt;orders.archive&lt;/code&gt; operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AgentCapabilityDefinition&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@adapttable/ai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;archiveInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;rowKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rowKey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;additionalProperties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;archiveOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;archived&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;archived&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;additionalProperties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AgentCapabilityDefinition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;orders.archive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Archive an order.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;write&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;guide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;guide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Archive an inactive order by its stable row key.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;archiveInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;archiveOutput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;isEnabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;observation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writePolicy&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;archiveOrder&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;rowKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;rowKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;archived&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register it on the same &lt;code&gt;tableAgent(...)&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;tableAgent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;tableId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;orders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;immediate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;approval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;writes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;bridge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;setSession&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This archive handler performs the operation directly and does not support staging. Here, &lt;code&gt;commit: "immediate"&lt;/code&gt; applies to all AI writes on this table, while &lt;code&gt;approval: "writes"&lt;/code&gt; keeps human approval enabled. Approved writes execute without waiting for a separate Save step.&lt;/p&gt;

&lt;p&gt;When enabled, &lt;code&gt;orders.archive&lt;/code&gt; appears in &lt;code&gt;catalog()&lt;/code&gt; like any other capability. &lt;code&gt;describe("orders.archive")&lt;/code&gt; exposes its detailed guide and schemas, and execution still goes through the live session.&lt;/p&gt;

&lt;p&gt;Because this capability is declared as a write, it also enters the same governance path as built-in write capabilities: write policy, commit compatibility, approval when required, revision and permission checks, and only then the application handler.&lt;/p&gt;

&lt;p&gt;Custom capabilities therefore extend what the agent can do without creating a second execution architecture beside the table.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full and compact context
&lt;/h3&gt;

&lt;p&gt;AdaptTable offers two profiles for deciding how much capability guidance is sent to the model up front.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;full&lt;/code&gt; is the default.&lt;/strong&gt; It has no soft token budget unless the application supplies one. Enabled capability guides and input schemas, together with the available column descriptions, are therefore included up front rather than intentionally deferred for compactness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;compact&lt;/code&gt; uses a soft token budget.&lt;/strong&gt; The default is 1,000 estimated tokens, and the application can override it with &lt;code&gt;tokenBudget&lt;/code&gt;. The budget is a selection target, not permission to cut a schema or guide in half.&lt;/p&gt;

&lt;h4&gt;
  
  
  How the context builder selects what travels up front
&lt;/h4&gt;

&lt;p&gt;Both profiles use the same context builder. What changes is the budget and therefore how much detail is selected before discovery is needed.&lt;/p&gt;

&lt;p&gt;The builder keeps the permitted contract intact while deciding how much descriptive detail travels with it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Available capability guide
            │
            ▼
Required up front?
            │
      ┌─────┴─────┐
     yes          no
      │            │
      │            ▼
      │      Selection order
      │      ├── priority
      │      └── remaining capabilities
      │            │
      │            ▼
      │      Fits soft token budget?
      │        ┌───┴───┐
      │       yes      no
      │        │        │
      │        │        ▼
      │        │   Defer guide + input schema
      │        │   reason: budget
      │        │
      └────┬───┘
           ▼
Would attaching guide + input schema
exceed the 128,000-byte hard limit?
           │
      ┌────┴────┐
     no        yes
      │          │
      ▼          ▼
Attach guide   Defer guide + input schema
+ input schema reason: hard-limit

In every case:
capability key + summary remain discoverable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The selection rules are explicit:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;include&lt;/code&gt; marks a capability guide as required up front. It may take the context over the soft token target; only the hard capability-context size limit can still defer it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Compact mode also treats its built-in common operations as required up front: filtering, sorting, search, pagination, row reads, grouping, and aggregation configuration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Guides loaded through discovery are remembered for the same connection and contract version. If the context is built again under that same contract, those previously requested guides are considered first and treated as required against the soft budget, avoiding another discovery round for the same instructions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;priority&lt;/code&gt; is the application’s ordering preference for the remaining guides. It moves those guides earlier in selection, but does not guarantee that they will fit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After that, the remaining capabilities are considered in deterministic registry order.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optional guides are included only when their complete guide and input schema fit within the remaining soft token budget. If they do not fit, the guide is deferred while the capability key and short summary remain available for discovery.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The selector handles required guides before optional ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Required guides
├── previously requested through discovery
├── built-in common compact operations
└── guides named in `include`
        │
        ▼
Optional guides
├── application `priority`
└── remaining capabilities in deterministic registry order
        │
        ▼
Fit optional guides within
the remaining soft token budget
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;include&lt;/code&gt; and &lt;code&gt;priority&lt;/code&gt; therefore serve different purposes: &lt;code&gt;include&lt;/code&gt; makes a guide required against the soft budget, while &lt;code&gt;priority&lt;/code&gt; only changes how early an optional guide is considered.&lt;/p&gt;

&lt;p&gt;The soft budget is token-based. Without a tokenizer supplied by the application, AdaptTable estimates tokens; an application can provide its own &lt;code&gt;estimateTokens&lt;/code&gt; implementation when it wants model-specific counting.&lt;/p&gt;

&lt;p&gt;Capability guide attachment also has a separate hard ceiling of 128,000 UTF-8 bytes. A guide is atomic: if attaching its guide and input schema would cross that ceiling, the guide is deferred with a &lt;code&gt;hard-limit&lt;/code&gt; reason rather than truncated, even if it would otherwise be required up front.&lt;/p&gt;

&lt;p&gt;This means the application can force important guides up front with &lt;code&gt;include&lt;/code&gt;, influence which optional guides are most likely to fit with &lt;code&gt;priority&lt;/code&gt;, and control the soft target with &lt;code&gt;tokenBudget&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;assistantHttpTransport&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@adapttable/ai/http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;assistantHttpTransport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/agent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;compact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;tokenBudget&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;edit.cells&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;export.run&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;edit.cells&lt;/code&gt; is required up front, while &lt;code&gt;export.run&lt;/code&gt; is only considered earlier than the normal remaining capabilities.&lt;/p&gt;

&lt;p&gt;The HTTP discovery path can retrieve multiple missing guides together and cache them for the connection and contract version. It does not require a separate discovery exchange for every capability.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Deferred guidance does not mean a disabled capability.&lt;/strong&gt; The operation remains available; only its detailed instructions were left out of the initial context.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The trade-off is between more initial context and additional discovery exchanges. The &lt;a href="https://github.com/orwa-mahmoud/adapttable/blob/main/packages/shared/ai/src/contextSelection.ts" rel="noopener noreferrer"&gt;context-selection implementation&lt;/a&gt; exposes the budget and selection information so the application can log it, feed it into observability, debug selection behavior, and tune the context strategy.&lt;/p&gt;

&lt;p&gt;Neither profile grants additional permissions. Context selection decides how much to explain, not which actions the agent is allowed to perform.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the agent knows about the table
&lt;/h2&gt;

&lt;p&gt;There are three different kinds of information involved: the table’s contract, its current view, and its row data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Agent context
├── Contract
│   ├── Available capabilities
│   ├── Column metadata
│   ├── Access settings and limits
│   └── Write / approval / commit policies
│
├── Current view
│   ├── Page and page size
│   ├── Search and sorting
│   ├── Grouping
│   └── Filter state
│
└── Row data
    └── Read only when the task needs it,
        through bounded rows.read
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The contract: what this table supports
&lt;/h3&gt;

&lt;p&gt;The contract describes available capabilities, column metadata, access settings, limits, and execution policies.&lt;/p&gt;

&lt;p&gt;For the orders table, it can explain which columns are writable, which filters exist, and which operations are supported. Column descriptions can also communicate business meaning, units, or storage conventions that are not obvious from the column type alone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Order total stored in cents.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model can then interpret 12500 as $125.00 instead of assuming the stored value is already in dollars.&lt;/p&gt;

&lt;p&gt;Column descriptions and examples can be deferred to save context without removing the column itself from the contract.&lt;/p&gt;

&lt;p&gt;The contract changes when something structural changes: for example, the available capabilities, relevant column metadata, or execution policy.&lt;/p&gt;

&lt;h3&gt;
  
  
  The view: where the table is now
&lt;/h3&gt;

&lt;p&gt;The view describes the current page, sorting, filters, grouping, and other relevant state.&lt;/p&gt;

&lt;p&gt;A user changing the page does not necessarily change the capability contract. Keeping these separate lets an integration update the current view without treating every interaction as a completely different set of tools.&lt;/p&gt;

&lt;p&gt;This keeps the agent grounded in the same current view the user is working with: page, search, sorting, grouping, and filters are sent from the live table state. If that view changes while the agent is reasoning, the view revision lets execution detect that the plan is stale instead of applying it against a different table state.&lt;/p&gt;

&lt;p&gt;That distinction also matters for HTTP delivery: a relatively stable contract can be reused while the current view still travels fresh with each turn.&lt;/p&gt;

&lt;h3&gt;
  
  
  Row data: what the task actually needs to read
&lt;/h3&gt;

&lt;p&gt;Publishing the capability catalog does not automatically send the whole dataset to the model.&lt;/p&gt;

&lt;p&gt;When the task needs cell values, &lt;code&gt;rows.read&lt;/code&gt; provides bounded reads under the table’s readable-column and source-scope rules. An application can also explicitly opt columns into limited example sampling; that is a separate data read, not an automatic dump of table rows.&lt;/p&gt;

&lt;p&gt;This matters with server-side pagination. Reading one loaded page does not mean the agent has inspected every matching record in the database. Broader reads depend on what the configured data source supports.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://adapttable.orwamahmoud.com/ai/#what-the-agent-is-told" rel="noopener noreferrer"&gt;agent context documentation&lt;/a&gt; describes the contract/view separation, column guidance, and optional sampling.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI access can differ from the table interface
&lt;/h2&gt;

&lt;p&gt;An operation may be useful to a person using the table but inappropriate for the agent on that screen.&lt;/p&gt;

&lt;p&gt;For example, the orders table might retain its export and delete controls while excluding those capabilities from AI. It might also display internal notes to the user without exposing their cell values through the AI session.&lt;/p&gt;

&lt;p&gt;These options go in the same &lt;code&gt;tableAgent(...)&lt;/code&gt; configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Access-setting excerpt from the existing tableAgent entry:&lt;/span&gt;
&lt;span class="nf"&gt;tableAgent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;tableId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;orders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;bridge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;setSession&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;excludeCapabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;export.run&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rows.delete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;internalNotes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;readable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;excludeCapabilities&lt;/code&gt; removes those keys from the agent’s catalog and execution path. The table’s normal controls are unaffected.&lt;/p&gt;

&lt;p&gt;The column settings prevent the AI session from reading or editing &lt;code&gt;internalNotes&lt;/code&gt; cell values.&lt;/p&gt;

&lt;p&gt;For an individual row or bulk action, &lt;code&gt;ai: false&lt;/code&gt; on its definition excludes that action from AI while preserving its normal table behavior.&lt;/p&gt;

&lt;p&gt;For filters, &lt;code&gt;ai: false&lt;/code&gt; excludes that specific filter from AI entirely, so the agent does not know about or use it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rowActions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;refund&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Refund&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;refundOrder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;internalFlag&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Internal flag&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;refund&lt;/code&gt; is still available to the user but not to AI, and &lt;code&gt;internalFlag&lt;/code&gt; is not exposed to the agent at all.&lt;/p&gt;

&lt;p&gt;To remove filtering entirely instead of excluding individual filters, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;tableAgent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;tableId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;orders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;excludeCapabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;view.setFilters&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These &lt;a href="https://adapttable.orwamahmoud.com/ai/" rel="noopener noreferrer"&gt;access controls&lt;/a&gt; are separate from approval and saving. Availability answers whether the agent can use an operation. Approval answers whether a person must confirm it. Commit behavior determines what happens after it is accepted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this leaves the architecture
&lt;/h2&gt;

&lt;p&gt;The result is one configured AdaptTable instance that can expose a live AI contract without duplicating the table into a second set of hand-written tools.&lt;/p&gt;

&lt;p&gt;The session tells an agent what the table can do. The catalog keeps those capabilities discoverable. Detailed guidance can be supplied progressively. The context builder makes the upfront selection explicit. And applications can add their own governed capabilities when the built-in operations are not enough.&lt;/p&gt;

&lt;p&gt;Try the &lt;a href="https://adapttable.orwamahmoud.com/react/demo/mantine/ai/" rel="noopener noreferrer"&gt;live AI demo&lt;/a&gt; or explore the &lt;a href="https://github.com/orwa-mahmoud/adapttable" rel="noopener noreferrer"&gt;source on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The next article follows the other half of the path: what happens &lt;strong&gt;after the agent decides to act&lt;/strong&gt; — revisions, idempotency, approvals, staged and immediate writes, execution receipts, HTTP contract pinning, streaming, and existing agent stacks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next in Building AdaptTable: &lt;em&gt;From AI intent to safe table execution in AdaptTable&lt;/em&gt;.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>ai</category>
      <category>webdev</category>
      <category>typescript</category>
    </item>
    <item>
      <title>AdaptTable v3 is live: a framework-neutral engine, smaller feature imports, and optional AI</title>
      <dc:creator>Orwa Mahmoud</dc:creator>
      <pubDate>Sun, 20 Sep 2026 13:16:09 +0000</pubDate>
      <link>https://dev.to/orwamahmoud/adapttable-v3-is-live-a-framework-neutral-engine-smaller-feature-imports-and-optional-ai-2fgb</link>
      <guid>https://dev.to/orwamahmoud/adapttable-v3-is-live-a-framework-neutral-engine-smaller-feature-imports-and-optional-ai-2fgb</guid>
      <description>&lt;p&gt;In my &lt;a href="https://dev.to/orwamahmoud/from-v1-to-v29-how-adapttable-grew-into-a-full-react-data-grid-toolkit-3pac"&gt;last AdaptTable update&lt;/a&gt;, I wrote about how much the project had grown by v2.9.&lt;/p&gt;

&lt;p&gt;By then, AdaptTable already had grouping, tree data, pivoting, editing, exports, virtualization, responsive layouts, and a lot more.&lt;/p&gt;

&lt;p&gt;That growth created a different problem.&lt;/p&gt;

&lt;p&gt;I didn't want every table to carry the weight of every feature, and I didn't want React itself to define the engine underneath the library.&lt;/p&gt;

&lt;p&gt;v3 is the release where I cleaned that up.&lt;/p&gt;

&lt;h2&gt;
  
  
  React is no longer the engine
&lt;/h2&gt;

&lt;p&gt;AdaptTable is still built for React developers today, and the same idea remains: use the UI kit already in your project instead of bringing in a table with a completely different look and feel.&lt;/p&gt;

&lt;p&gt;What changed is the architecture underneath it.&lt;/p&gt;

&lt;p&gt;Framework-neutral models and table operations now live in &lt;code&gt;@adapttable/core&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;React-specific hooks and subscriptions live in &lt;code&gt;@adapttable/react&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The adapters then handle the actual UI for Mantine, MUI, Chakra UI, Ant Design, Radix Themes, Base UI, shadcn/ui, and the unstyled/Tailwind route.&lt;/p&gt;

&lt;p&gt;Roughly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@adapttable/core
        ↓
@adapttable/react
        ↓
Mantine / MUI / Chakra / Ant / Radix / Base UI / shadcn / unstyled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The engine no longer needs to know how React subscribes to state, while the React layer only handles the React-specific binding around the table logic.&lt;/p&gt;

&lt;p&gt;It gives AdaptTable a cleaner foundation for other framework bindings later.&lt;/p&gt;

&lt;p&gt;Vue and Angular bindings are not part of v3. The engine is ready for that direction, but React is still the shipped framework binding today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optional features are actually optional now
&lt;/h2&gt;

&lt;p&gt;v2 had already started moving features behind separate import paths.&lt;/p&gt;

&lt;p&gt;But there was still a problem.&lt;/p&gt;

&lt;p&gt;A feature can have its own public subpath and still end up in the main dependency graph if the root &lt;code&gt;DataTable&lt;/code&gt; imports its implementation internally.&lt;/p&gt;

&lt;p&gt;So an import can look modular without really being a bundle boundary.&lt;/p&gt;

&lt;p&gt;In v3, the old feature-enabling props are replaced by feature factories.&lt;/p&gt;

&lt;p&gt;A basic table can stay basic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DataTable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@adapttable/mantine&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DataTable&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;rowKey&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that table needs grouping, it adds grouping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DataTable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@adapttable/mantine&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;grouping&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@adapttable/mantine/grouping&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DataTable&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;rowKey&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;features&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;grouping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;team&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same approach is used for editing, virtualization, row reordering, column menus and other optional features.&lt;/p&gt;

&lt;p&gt;For applications that want the common controls together, there is still a convenient preset:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;standardFeatures&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@adapttable/mantine/preset&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So you can start with the preset for the common setup, or import individual features when you want a narrower table.&lt;/p&gt;

&lt;p&gt;In practice, the bundle cost follows the features your table actually uses.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI follows the same AdaptTable philosophy
&lt;/h2&gt;

&lt;p&gt;v3 also adds &lt;code&gt;@adapttable/ai&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I didn't want AI support to mean choosing a specific model provider, SDK or agent architecture just to use the table.&lt;/p&gt;

&lt;p&gt;The AI layer is separate from the normal table packages.&lt;/p&gt;

&lt;p&gt;A table exposes only the operations it actually supports. If editing is not enabled, the agent does not get editing tools. If a write needs approval or an application callback, it still has to go through that path.&lt;/p&gt;

&lt;p&gt;The application keeps control of authorization, validation, and persistence.&lt;/p&gt;

&lt;p&gt;There is a ready-made assistant UI, but it is optional. The same capabilities can also be connected to an existing chat interface or agent runtime.&lt;/p&gt;

&lt;p&gt;This follows the same idea as the UI adapters: AdaptTable should work with the stack already used by the application.&lt;/p&gt;

&lt;p&gt;AdaptTable has its own provider-neutral session and capability contract, and it also ships adapters for AI SDK and AG-UI, alongside integrations for OpenAI tools, MCP, and lower-level JSON.&lt;/p&gt;

&lt;p&gt;So an application can keep its existing provider, backend, and agent setup instead of rebuilding around AdaptTable.&lt;/p&gt;

&lt;p&gt;And like the rest of v3, AI is opt-in. If you never import the AI packages, the normal table does not depend on them.&lt;/p&gt;

&lt;p&gt;I'll cover capability discovery, revisions, approvals, execution results, and the boundary between the agent, table, and application in a separate post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving from v2
&lt;/h2&gt;

&lt;p&gt;v3 is a major release, so there are breaking changes.&lt;/p&gt;

&lt;p&gt;React-specific hooks that previously came from &lt;code&gt;@adapttable/core&lt;/code&gt; move to &lt;code&gt;@adapttable/react&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The old feature-enabling props are replaced by feature factories.&lt;/p&gt;

&lt;p&gt;Deprecated routes and aliases that were being kept around for compatibility are removed.&lt;/p&gt;

&lt;p&gt;The CLI can help with supported import migrations, but I would still review application behavior after upgrading. A codemod can move an import; it can't decide how your application wants editing, persistence, or a custom feature to behave.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://adapttable.orwamahmoud.com/react/migrate-from-v2/" rel="noopener noreferrer"&gt;v2 → v3 migration guide&lt;/a&gt; covers the changes and replacements.&lt;/p&gt;

&lt;p&gt;AdaptTable remains MIT licensed.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://adapttable.orwamahmoud.com/react/demo/" rel="noopener noreferrer"&gt;try the live demo&lt;/a&gt;, &lt;a href="https://adapttable.orwamahmoud.com/" rel="noopener noreferrer"&gt;read the documentation&lt;/a&gt; or &lt;a href="https://github.com/orwa-mahmoud/adapttable" rel="noopener noreferrer"&gt;browse the source on GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
    <item>
      <title>From v1 to v2.9: how AdaptTable grew into a full React data-grid toolkit</title>
      <dc:creator>Orwa Mahmoud</dc:creator>
      <pubDate>Thu, 03 Sep 2026 19:44:50 +0000</pubDate>
      <link>https://dev.to/orwamahmoud/from-v1-to-v29-how-adapttable-grew-into-a-full-react-data-grid-toolkit-3pac</link>
      <guid>https://dev.to/orwamahmoud/from-v1-to-v29-how-adapttable-grew-into-a-full-react-data-grid-toolkit-3pac</guid>
      <description>&lt;p&gt;Back in July I wrote the first post about &lt;a href="https://dev.to/orwamahmoud/one-headless-table-engine-every-react-ui-kit-2dm4"&gt;AdaptTable&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The project was still v1, and the idea was fairly focused: one table engine that could sit behind the UI kit a React project already uses.&lt;/p&gt;

&lt;p&gt;MUI project? Use the MUI adapter.&lt;/p&gt;

&lt;p&gt;Mantine? Use Mantine.&lt;/p&gt;

&lt;p&gt;Ant Design, Chakra, Radix, shadcn, Base UI, or your own Tailwind styles all sit on the same engine too.&lt;/p&gt;

&lt;p&gt;That model is still there. The engine behind it has just grown a lot since then.&lt;/p&gt;

&lt;p&gt;The core is at v2.9 now.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/orwa-mahmoud/adapttable" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://orwa-mahmoud.github.io/adapttable/demo/" rel="noopener noreferrer"&gt;Live demo&lt;/a&gt; · &lt;a href="https://orwa-mahmoud.github.io/adapttable/getting-started/" rel="noopener noreferrer"&gt;Getting started&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The feature set got much bigger
&lt;/h2&gt;

&lt;p&gt;The first release already covered the things I kept needing in application tables: sorting, filtering, pagination, URL state, saved views, column controls, server-side data, mobile layouts, selection, and row/card virtualization.&lt;/p&gt;

&lt;p&gt;Since then I've added quite a bit on top of that.&lt;/p&gt;

&lt;p&gt;Grouping now supports multiple levels and aggregates.&lt;/p&gt;

&lt;p&gt;There is tree data, pivot tables, formula/computed columns, and sparklines.&lt;/p&gt;

&lt;p&gt;The editing side grew a lot too.&lt;/p&gt;

&lt;p&gt;AdaptTable now has keyboard cell navigation, rectangular range selection, clipboard copy/paste, cut, fill handle behavior, and edit history.&lt;/p&gt;

&lt;p&gt;Editing can happen at cell level, row level, or as batched changes. There is validation, async validation, dirty-state tracking, and conflict handling for cases where live data changes while somebody is editing.&lt;/p&gt;

&lt;p&gt;Rows have more control now as well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Row reordering&lt;/li&gt;
&lt;li&gt;Top and bottom row pinning&lt;/li&gt;
&lt;li&gt;Row and column spanning&lt;/li&gt;
&lt;li&gt;Full-width rows&lt;/li&gt;
&lt;li&gt;Custom row heights and styling&lt;/li&gt;
&lt;li&gt;Tree and nested structures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Export also moved beyond CSV and print, with XLSX and PDF support now available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping the feature API manageable
&lt;/h2&gt;

&lt;p&gt;As the feature set grew, I wanted a basic table to stay basic.&lt;/p&gt;

&lt;p&gt;A normal table can still look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DataTable&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;rowKey&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;v2 introduced a feature composition API for optional behavior.&lt;/p&gt;

&lt;p&gt;For example, row reordering can be added through its own entry point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DataTable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@adapttable/mantine&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rowReorder&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@adapttable/mantine/row-reorder&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DataTable&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;rowKey&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;features&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;rowReorder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handleReorder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Grouping, editing, virtualization, cell navigation, saved views, and other features follow the same direction.&lt;/p&gt;

&lt;p&gt;There is also a public &lt;code&gt;TableFeature&lt;/code&gt; API behind this. A feature can register its own editors, aggregators, commands, filters, menu items, panels, and other extensions through the same feature system.&lt;/p&gt;

&lt;p&gt;That gives me a cleaner way to keep expanding AdaptTable without turning every new capability into another permanent special case inside &lt;code&gt;DataTable&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some of the heavier code is already separate
&lt;/h2&gt;

&lt;p&gt;I also started splitting heavier capabilities into their own entry points instead of exporting everything from the main core package.&lt;/p&gt;

&lt;p&gt;Today the core has separate entries for things like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@adapttable/core/xlsx
@adapttable/core/pdf
@adapttable/core/formula
@adapttable/core/pivot
@adapttable/core/stream
@adapttable/core/sparkline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those are outside the main core entry, so the application pays the bundle-size cost for those features only when it imports and uses them.&lt;/p&gt;

&lt;p&gt;The adapters have feature-specific entry points too. Mantine, for example, exposes paths such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@adapttable/mantine/row-reorder
@adapttable/mantine/grouping
@adapttable/mantine/editing
@adapttable/mantine/virtualize
@adapttable/mantine/cell-navigation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;v2 still keeps some optional feature implementations reachable from the main &lt;code&gt;DataTable&lt;/code&gt; import graph, so the adapter side doesn't get the full bundle-size benefit from those entry points yet.&lt;/p&gt;

&lt;p&gt;v3 will move those optional implementations out of the root import graph so applications only pay for the features they actually import and use.&lt;/p&gt;

&lt;p&gt;The deprecated v2 APIs will be removed at the same time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The server side changed a lot too
&lt;/h2&gt;

&lt;p&gt;The original &lt;code&gt;TableSource&lt;/code&gt; idea is still there: the table can work with local data or with data controlled by a backend.&lt;/p&gt;

&lt;p&gt;That area has expanded quite a bit.&lt;/p&gt;

&lt;p&gt;There is now a separate &lt;a href="https://www.npmjs.com/package/@adapttable/server" rel="noopener noreferrer"&gt;&lt;code&gt;@adapttable/server&lt;/code&gt;&lt;/a&gt; package for parsing and validating AdaptTable query state on the server.&lt;/p&gt;

&lt;p&gt;Pagination, sorting, filters, filter trees, grouping, and pivot state can use the same table query model on both sides instead of needing a second representation just for the backend.&lt;/p&gt;

&lt;p&gt;Real-time updates are in now too.&lt;/p&gt;

&lt;p&gt;AdaptTable can consume row patches coming through WebSocket or SSE flows instead of reloading the entire dataset whenever something changes.&lt;/p&gt;

&lt;p&gt;The live-update work also connects with editing. A remote update can arrive while somebody is changing the same row locally, so those two parts of the table need to understand each other.&lt;/p&gt;

&lt;p&gt;There is support for SSR, React Server Components, and streaming as well.&lt;/p&gt;

&lt;p&gt;A lot of the work after v1 ended up happening around the data flow and lifecycle of the table, not only in the rendered grid.&lt;/p&gt;

&lt;h2&gt;
  
  
  Eight adapters, one engine
&lt;/h2&gt;

&lt;p&gt;The public adapters today are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mantine&lt;/li&gt;
&lt;li&gt;MUI&lt;/li&gt;
&lt;li&gt;Chakra UI&lt;/li&gt;
&lt;li&gt;Ant Design&lt;/li&gt;
&lt;li&gt;Radix Themes&lt;/li&gt;
&lt;li&gt;Base UI&lt;/li&gt;
&lt;li&gt;shadcn/ui&lt;/li&gt;
&lt;li&gt;unstyled/Tailwind&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They all use the same underlying table state and feature logic while rendering with the components and conventions of their own UI stack.&lt;/p&gt;

&lt;p&gt;A Mantine project gets Mantine components. An MUI project gets MUI components. A project with its own design system can use the unstyled path.&lt;/p&gt;

&lt;p&gt;The core behavior stays shared across them.&lt;/p&gt;

&lt;p&gt;Everything is still MIT licensed.&lt;/p&gt;

&lt;h2&gt;
  
  
  v3 is next
&lt;/h2&gt;

&lt;p&gt;While working on v2, I kept running into one thing I want to fix properly in v3: too much of the engine is still tied to React.&lt;/p&gt;

&lt;p&gt;A lot of what AdaptTable does has nothing inherently React-specific about it.&lt;/p&gt;

&lt;p&gt;Sorting, filtering, grouping, pivots, formulas, query state, editing state, row models, and feature logic can all exist without React.&lt;/p&gt;

&lt;p&gt;For v3 I'm moving that logic into framework-neutral engine modules, with React hooks and components living in a separate binding layer.&lt;/p&gt;

&lt;p&gt;React will remain fully supported, but it won't define the architecture of the engine underneath it.&lt;/p&gt;

&lt;p&gt;That also opens the door to Vue and Angular integrations later without rebuilding the same table logic for each framework.&lt;/p&gt;

&lt;p&gt;v3 will also remove the deprecated v2 APIs and continue the bundle cleanup by moving optional feature implementations out of the root import graph.&lt;/p&gt;

&lt;p&gt;I'm also working on optional AI capabilities for v3.&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>typescript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I Stopped Treating Long AI Coding Runs Like Chats — Nightshift Now Treats Them Like Shifts</title>
      <dc:creator>Orwa Mahmoud</dc:creator>
      <pubDate>Wed, 02 Sep 2026 16:28:10 +0000</pubDate>
      <link>https://dev.to/orwamahmoud/i-stopped-treating-long-ai-coding-runs-like-chats-nightshift-now-treats-them-like-shifts-40ec</link>
      <guid>https://dev.to/orwamahmoud/i-stopped-treating-long-ai-coding-runs-like-chats-nightshift-now-treats-them-like-shifts-40ec</guid>
      <description>&lt;p&gt;A while ago I wrote about a problem I kept hitting with Claude Code: I could give it a large task, walk away, and come back later to find that it had stopped somewhere in the middle.&lt;/p&gt;

&lt;p&gt;That problem is what originally pushed me to build &lt;strong&gt;Nightshift&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At first, the goal was mostly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How do I keep the agent working?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once I got the agent to reliably work for hours, my focus shifted.&lt;/p&gt;

&lt;p&gt;The problem was no longer just keeping it alive. I started focusing on improving the &lt;strong&gt;shift itself&lt;/strong&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If I leave a coding agent working for several hours, how do I make that run something I can actually understand, trust, review, recover, and keep as history afterward?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That changed how I think about long-running agent work.&lt;/p&gt;

&lt;p&gt;I don't really want a six-hour conversation.&lt;/p&gt;

&lt;p&gt;I want a &lt;strong&gt;shift&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The shift should exist outside the chat
&lt;/h2&gt;

&lt;p&gt;The core idea in Nightshift is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The work contract and run state live on disk, not only in the conversation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;During a shift, I want to be able to open the files and see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what is done&lt;/li&gt;
&lt;li&gt;what is being worked on now&lt;/li&gt;
&lt;li&gt;what is still open&lt;/li&gt;
&lt;li&gt;what is parked or blocked&lt;/li&gt;
&lt;li&gt;what decisions were made&lt;/li&gt;
&lt;li&gt;what was tried and rejected&lt;/li&gt;
&lt;li&gt;what still needs verification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The conversation is still useful for instructions, reasoning, and following the work as it happens. But for a multi-hour shift, I don’t want it to be the only place that remembers what was agreed, what changed, what failed, and what is still left.&lt;/p&gt;

&lt;p&gt;That becomes especially important when context gets compacted, a session fails, or I come back the next morning and need to understand what happened without scrolling through one giant chat.&lt;/p&gt;




&lt;h2&gt;
  
  
  Two kinds of shifts
&lt;/h2&gt;

&lt;p&gt;Nightshift currently supports two main ways of working.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Planned shifts
&lt;/h3&gt;

&lt;p&gt;You define the work up front as a concrete feature plan.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] build the Products module&lt;/li&gt;
&lt;li&gt;[ ] add product categories&lt;/li&gt;
&lt;li&gt;[ ] add coupons and discount rules&lt;/li&gt;
&lt;li&gt;[ ] add inventory tracking&lt;/li&gt;
&lt;li&gt;[ ] add product search and filters&lt;/li&gt;
&lt;li&gt;[ ] audit the whole module end-to-end and make sure every backend flow has the required UI and works correctly&lt;/li&gt;
&lt;li&gt;[ ] add tests and verification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each item can have its own quality or verification requirements, and/Or you can also define checks that apply to the whole shift.&lt;/p&gt;

&lt;p&gt;The shift keeps working until every contracted item is ticked off. Owner-defined quality checks describe what “done” should mean, but the model decides when an item is ready to tick. Nightshift tracks that state and mechanically prevents the model from clocking out while anything is still open. Whether the work is actually good enough is still for the human reviewer to decide.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Open-ended shifts
&lt;/h3&gt;

&lt;p&gt;This mode became one of my favorite parts of Nightshift.&lt;/p&gt;

&lt;p&gt;Instead of giving it a fixed checklist, I give it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;a goal + a strict time budget&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Improve this product for the next four hours.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent can inspect the product, codebase, history, tests, existing issues, comparable approaches, technical debt, UX problems, or other useful opportunities.&lt;/p&gt;

&lt;p&gt;It finds useful work, ranks it, works on the strongest opportunities, and then keeps going.&lt;/p&gt;

&lt;p&gt;If it finishes one thing early and tries to stop while meaningful time is still left, the shift pushes it to continue exploring and improving until the time budget is actually over.&lt;/p&gt;

&lt;p&gt;The stopping condition becomes less:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The model feels finished."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and more:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The shift is over."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  A side effect I ended up liking a lot
&lt;/h2&gt;

&lt;p&gt;This created a use case I didn't originally build Nightshift for.&lt;/p&gt;

&lt;p&gt;Sometimes my Claude, Codex, or Cursor usage is going to reset the next day, and I still have a chunk of allowance left.&lt;/p&gt;

&lt;p&gt;Maybe I don't have a feature prepared.&lt;/p&gt;

&lt;p&gt;Maybe nothing is currently planned on the timeline.&lt;/p&gt;

&lt;p&gt;Maybe I just have a few hours of usage that are going to disappear anyway.&lt;/p&gt;

&lt;p&gt;Instead of wasting that capacity, I can start an open-ended shift and say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You have three hours. Find useful work and improve the project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nightshift keeps the model working within that time budget instead of letting it finish one small task and stop after 20 minutes.&lt;/p&gt;

&lt;p&gt;So the ready-made and open-ended shifts also give me a way to &lt;strong&gt;spend otherwise-expiring usage usefully when I don't already have anything planned&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Ready-made shifts
&lt;/h2&gt;

&lt;p&gt;You don't always need to design the shift yourself.&lt;/p&gt;

&lt;p&gt;Nightshift also has a catalog of reusable shifts for things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;defect hunting&lt;/li&gt;
&lt;li&gt;test coverage&lt;/li&gt;
&lt;li&gt;quality debt&lt;/li&gt;
&lt;li&gt;dependency upgrades&lt;/li&gt;
&lt;li&gt;codebase review&lt;/li&gt;
&lt;li&gt;Product Evolution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, Product Evolution is an open-ended shift that studies the product, its history, user needs, and relevant standards, ranks opportunities by evidence, value, effort, reversibility, and risk, then works on the strongest ones within the available time.&lt;/p&gt;

&lt;p&gt;The catalog is useful when I know I want to spend a few hours of agent time productively but don't have a detailed backlog ready.&lt;/p&gt;

&lt;p&gt;Longer term, I want this catalog to grow beyond my own workflows.&lt;/p&gt;

&lt;p&gt;If someone has a long-running workflow that consistently works well, it should be possible to turn that into a reusable shift that other people can install, customize, and contribute back.&lt;/p&gt;

&lt;p&gt;And I don't think those shifts necessarily need to be coding-only.&lt;/p&gt;

&lt;p&gt;Recurring workflows like SEO, research, audits, or other reviewable agent work can fit the same model too.&lt;/p&gt;




&lt;h2&gt;
  
  
  I didn't want to build another agent runtime
&lt;/h2&gt;

&lt;p&gt;This became an important design decision as Nightshift grew.&lt;/p&gt;

&lt;p&gt;I don't want Nightshift to replace Claude Code, Codex, or Cursor.&lt;/p&gt;

&lt;p&gt;Those tools already have their own ecosystems, commands, skills, hooks, capabilities, and ways of working.&lt;/p&gt;

&lt;p&gt;Nightshift is designed to &lt;strong&gt;augment those ecosystems&lt;/strong&gt;, not sit in front of them as another agent platform.&lt;/p&gt;

&lt;p&gt;There is no Nightshift cloud service or account.&lt;/p&gt;

&lt;p&gt;You don't need another npm CLI or Homebrew-installed orchestration runtime that proxies the agent.&lt;/p&gt;

&lt;p&gt;Claude Code should still feel like Claude Code.&lt;/p&gt;

&lt;p&gt;Codex should still feel like Codex.&lt;/p&gt;

&lt;p&gt;Cursor should still feel like Cursor.&lt;/p&gt;

&lt;p&gt;Nightshift adds the &lt;strong&gt;shift contract, state, enforcement, recovery, review, and archive layer&lt;/strong&gt; around them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Long-running doesn't just mean "keep looping"
&lt;/h2&gt;

&lt;p&gt;There are already good ideas for keeping agents working for a long time.&lt;/p&gt;

&lt;p&gt;Ralph-style loops, for example, already established patterns around persistent plans, specs, repeated iterations, and state that survives outside a single chat.&lt;/p&gt;

&lt;p&gt;So Nightshift isn't trying to claim that file-backed plans or long-running loops are new.&lt;/p&gt;

&lt;p&gt;The part I'm interested in is the accountability around the run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what exactly was contracted&lt;/li&gt;
&lt;li&gt;what is still unfinished&lt;/li&gt;
&lt;li&gt;what quality bar applies&lt;/li&gt;
&lt;li&gt;what decisions were made&lt;/li&gt;
&lt;li&gt;what got blocked&lt;/li&gt;
&lt;li&gt;what failed&lt;/li&gt;
&lt;li&gt;what was verified&lt;/li&gt;
&lt;li&gt;whether the session died&lt;/li&gt;
&lt;li&gt;how it recovered&lt;/li&gt;
&lt;li&gt;what evidence exists afterward&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keeping an agent running for six hours is useful.&lt;/p&gt;

&lt;p&gt;Being able to understand &lt;strong&gt;what actually happened during those six hours&lt;/strong&gt; is much more useful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recovery should survive the agent itself
&lt;/h2&gt;

&lt;p&gt;Another part I didn't want to trust only to prompting was recovery.&lt;/p&gt;

&lt;p&gt;If the process doing the work is also the only thing responsible for noticing that it died, that isn't very helpful.&lt;/p&gt;

&lt;p&gt;Nightshift therefore has an external watchman.&lt;/p&gt;

&lt;p&gt;For example, if a Claude Code session hits a structured API failure such as:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;API Error: 500&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;the watchman can keep attempting to recover the recorded conversation when the API becomes available again.&lt;/p&gt;

&lt;p&gt;For Codex, the watchman can recover a session once it is proven dead, preferring the recorded session when its identity is resumable. If that isn't possible, the on-disk contract still provides the handoff for a fresh recovery.&lt;/p&gt;

&lt;p&gt;Failures will happen.&lt;/p&gt;

&lt;p&gt;The goal isn't to pretend they won't.&lt;/p&gt;

&lt;p&gt;The goal is that a temporary API or session failure doesn't silently throw away the remaining hours of work.&lt;/p&gt;




&lt;h2&gt;
  
  
  The shift doesn't disappear when it ends
&lt;/h2&gt;

&lt;p&gt;This is one of the parts I appreciate more the longer I use Nightshift.&lt;/p&gt;

&lt;p&gt;When I want to file away finished work, Nightshift can archive the completed part of the shift.&lt;/p&gt;

&lt;p&gt;Nightshift moves completed items, handled snags, and the rotated journal into a dated archive. Receipts are copied into the dated archive too, while the live copies stay in place.&lt;/p&gt;

&lt;p&gt;The active contract and any still-open work stay intact instead of being filed away as if they were finished.&lt;/p&gt;

&lt;p&gt;Over time, those dated archives become a much cleaner history of previous shifts.&lt;/p&gt;

&lt;p&gt;The original conversation might be thousands of lines long, but the archive gives me a short, structured record of what was completed, what decisions were made, what went wrong, and what evidence was left behind.&lt;/p&gt;

&lt;p&gt;So instead of reopening an old marathon chat and trying to reconstruct what happened, I can open the archive for that date and understand the shift much faster.&lt;/p&gt;

&lt;p&gt;Even a shift I decide not to merge can leave useful history.&lt;/p&gt;

&lt;p&gt;Maybe the implementation wasn't good enough, but the investigation found something important.&lt;/p&gt;

&lt;p&gt;Maybe an approach failed, and I don't want the next agent rediscovering the same dead end.&lt;/p&gt;

&lt;p&gt;Maybe a decision needs to be revisited later.&lt;/p&gt;

&lt;p&gt;That history is worth keeping.&lt;/p&gt;




&lt;h2&gt;
  
  
  Read the receipts, not the promise
&lt;/h2&gt;

&lt;p&gt;The direction I'm pushing Nightshift toward is pretty simple.&lt;/p&gt;

&lt;p&gt;I don't want the final handoff to just be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Done! Everything looks good."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I want to be able to inspect the actual state, diff, commits, verification, decisions, failures, and receipts myself.&lt;/p&gt;

&lt;p&gt;Long-running autonomy becomes easier to trust when you don't have to trust the agent's summary of its own work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read the receipts, not the promise.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Where Nightshift is now
&lt;/h2&gt;

&lt;p&gt;Nightshift is open source under MIT and currently supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Claude Code&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenAI Codex&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cursor&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It runs locally and is designed to augment those tools rather than replace them.&lt;/p&gt;

&lt;p&gt;The project is still evolving, especially around reusable shifts, stronger review workflows, and making the same model useful beyond coding.&lt;/p&gt;

&lt;p&gt;But the basic idea has become pretty clear to me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Give the agent a real shift instead of one enormous chat.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Define the work or the time budget.&lt;/p&gt;

&lt;p&gt;Keep the state outside the conversation.&lt;/p&gt;

&lt;p&gt;Keep it working while the shift is active.&lt;/p&gt;

&lt;p&gt;Recover when things fail.&lt;/p&gt;

&lt;p&gt;Keep the evidence.&lt;/p&gt;

&lt;p&gt;Archive the result.&lt;/p&gt;

&lt;p&gt;Then review what actually happened.&lt;/p&gt;




&lt;h2&gt;
  
  
  Nightshift
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/orwa-mahmoud/nightshift" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chatgpt.com/plugins/plugins_6a7c58f65d708191b3a705a8625baffe" rel="noopener noreferrer"&gt;Official OpenAI Plugin Directory&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nightshift.orwamahmoud.com/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're already leaving coding agents working unattended for hours, I'd be especially interested in the workflows you've found that consistently work well.&lt;/p&gt;

&lt;p&gt;Some of them might make good reusable Nightshift shifts.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>agents</category>
      <category>agentskills</category>
    </item>
    <item>
      <title>Why Claude Code Stops Halfway Through Long Tasks—and the Harness I Built to Keep It Moving</title>
      <dc:creator>Orwa Mahmoud</dc:creator>
      <pubDate>Tue, 28 Jul 2026 17:08:23 +0000</pubDate>
      <link>https://dev.to/orwamahmoud/why-claude-code-stops-halfway-through-long-tasks-and-the-harness-i-built-to-keep-it-moving-532o</link>
      <guid>https://dev.to/orwamahmoud/why-claude-code-stops-halfway-through-long-tasks-and-the-harness-i-built-to-keep-it-moving-532o</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjc2bl73x5utii8linn8g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjc2bl73x5utii8linn8g.png" alt=" " width="800" height="791"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I kept giving Claude Code long task lists, stepping away, and coming back to find it had completed the easy parts and stopped before the difficult ones.&lt;/p&gt;

&lt;p&gt;Sometimes it was worse.&lt;/p&gt;

&lt;p&gt;Claude would work until 2 AM, ask one non-critical question, and then sit idle for six hours until I woke up at 8.&lt;/p&gt;

&lt;p&gt;The question was usually reasonable, but it was not truly blocking. Claude could have chosen a safe default, continued working, and left the decision for me to review later.&lt;/p&gt;

&lt;p&gt;That is a harness problem, not only a model problem. The layer around the agent loop decides what “done” means, when the run may stop, and what happens when human input is unavailable.&lt;/p&gt;

&lt;p&gt;That problem led me to create &lt;strong&gt;Nightshift&lt;/strong&gt;, an open-source reliability harness plugin for long unattended Claude Code sessions.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Update (v0.6, July 2026): the night the API died&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since publishing this, the &lt;em&gt;other&lt;/em&gt; overnight killer got its fix. Claude had elevated errors across multiple models one evening — &lt;code&gt;API Error: 500&lt;/code&gt;, &lt;code&gt;529 Overloaded&lt;/code&gt; — right as I was about to start an overnight run. That is the failure no prompt can fix: once a session dies on an API error, nothing inside it can restart it. And I was not going to sit up watching status.claude.com.&lt;/p&gt;

&lt;p&gt;nightshift now ships a &lt;strong&gt;night watchman&lt;/strong&gt; — a small process outside the session that wakes every 10 minutes and reads only the session's own signals: its transcript, its recorded process, Claude Code's session roster. A session that died — or one wedged with an API error as the transcript's last word — gets &lt;code&gt;claude --resume&lt;/code&gt; fired into the &lt;strong&gt;same conversation&lt;/strong&gt;, and the watchman keeps knocking until the API answers. Esc or Ctrl-C always wins: it revives the endings nobody chose, never the ones you did. In the morning the whole night is one thread — the error, the revival, the finished work — with a direct link back to it.&lt;/p&gt;

&lt;p&gt;Full details: &lt;a href="https://nightshift.orwamahmoud.com/" rel="noopener noreferrer"&gt;the nightshift site&lt;/a&gt; · &lt;a href="https://github.com/orwa-mahmoud/nightshift" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Keeping the task outside the conversation
&lt;/h2&gt;

&lt;p&gt;Nightshift uses a persistent Markdown punch list as the source of truth.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Nightshift Punch List&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; [ ] Add rate limiting
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Add tests for failed login attempts
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Update the API documentation
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Run the full test suite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Claude works through the list one item at a time.&lt;/p&gt;

&lt;p&gt;Because the work is stored in a file rather than only inside the current context window, the session can recover after context compaction, interruption, or restart.&lt;/p&gt;
&lt;h2&gt;
  
  
  Preventing quiet early exits
&lt;/h2&gt;

&lt;p&gt;Nightshift includes a stop hook that checks the punch list whenever Claude tries to finish.&lt;/p&gt;

&lt;p&gt;If unchecked items remain, the normal stop is blocked and Claude is instructed to continue.&lt;/p&gt;

&lt;p&gt;The shift ends only when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;every item is completed&lt;/li&gt;
&lt;li&gt;the deadline is reached&lt;/li&gt;
&lt;li&gt;an optional stall limit is reached&lt;/li&gt;
&lt;li&gt;or the owner explicitly stops it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If no stall limit is configured, stalled work stays held and visibly flagged rather than quietly clocking out.&lt;/p&gt;

&lt;p&gt;This is stronger than simply prompting Claude to “continue until finished” because the completion condition is checked against persistent state.&lt;/p&gt;
&lt;h2&gt;
  
  
  Non-blocking questions should not stop the night
&lt;/h2&gt;

&lt;p&gt;Most human-in-the-loop systems pause the agent whenever it has a question.&lt;/p&gt;

&lt;p&gt;That makes sense for destructive or high-risk decisions. It does not make sense for every naming choice, implementation preference, or reversible decision.&lt;/p&gt;

&lt;p&gt;During a Nightshift session, non-blocking questions are parked with the default Claude chose.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Parking Lot&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Question: Should the endpoint return 200 or 201?
&lt;span class="p"&gt;-&lt;/span&gt; Default used: 201, matching the existing creation endpoints.
&lt;span class="p"&gt;-&lt;/span&gt; Review later: Confirm API consistency.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Claude continues working, and the developer reviews those decisions in the morning.&lt;/p&gt;

&lt;p&gt;The goal is not to remove human oversight. It is to make some of that oversight asynchronous instead of forcing the whole session to wait.&lt;/p&gt;
&lt;h2&gt;
  
  
  Guardrails for unattended work
&lt;/h2&gt;

&lt;p&gt;Long-running autonomy needs clear boundaries.&lt;/p&gt;

&lt;p&gt;Nightshift can mechanically enforce restrictions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;blocking &lt;code&gt;git push&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;denying dangerous commands&lt;/li&gt;
&lt;li&gt;protecting selected directories&lt;/li&gt;
&lt;li&gt;checking diffs for secret-like content&lt;/li&gt;
&lt;li&gt;enforcing a specific Git identity&lt;/li&gt;
&lt;li&gt;preventing non-blocking questions from pausing the shift&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A prompt saying “do not push” is helpful.&lt;/p&gt;

&lt;p&gt;A hook that rejects &lt;code&gt;git push&lt;/code&gt; is stronger.&lt;/p&gt;

&lt;p&gt;These guardrails are not a security sandbox, but they reduce the risk of relying only on the model to remember every instruction.&lt;/p&gt;
&lt;h2&gt;
  
  
  Verification before completion
&lt;/h2&gt;

&lt;p&gt;A checked box is still a claim made by the agent.&lt;/p&gt;

&lt;p&gt;Nightshift cannot guarantee that the implementation is correct or replace code review. What it can do is enforce a more disciplined workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;implement the change&lt;/li&gt;
&lt;li&gt;run the relevant verification&lt;/li&gt;
&lt;li&gt;inspect the result&lt;/li&gt;
&lt;li&gt;commit the completed item&lt;/li&gt;
&lt;li&gt;mark the checkbox complete&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Verification can include commands such as:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;test
&lt;/span&gt;npm run lint
npm run typecheck
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Nightshift also encourages one commit per completed item, making the run easier to inspect or partially revert.&lt;/p&gt;
&lt;h2&gt;
  
  
  Resumability and receipts
&lt;/h2&gt;

&lt;p&gt;Nightshift stores operational state inside &lt;code&gt;.nightshift/&lt;/code&gt;, including punch lists, parked questions, logs, deadlines, stop signals, and completion snapshots.&lt;/p&gt;

&lt;p&gt;This state can have its own local Git history, separate from the main project repository.&lt;/p&gt;

&lt;p&gt;That gives the developer two different records:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;project commits showing what changed&lt;/li&gt;
&lt;li&gt;Nightshift receipts showing what happened during the run&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the session is interrupted, a new session can inspect the same state and continue from the remaining work.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Nightshift does not solve
&lt;/h2&gt;

&lt;p&gt;Nightshift does not eliminate hallucinations.&lt;/p&gt;

&lt;p&gt;It does not guarantee that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;every completed item is actually correct&lt;/li&gt;
&lt;li&gt;tests cover every requirement&lt;/li&gt;
&lt;li&gt;every chosen default is good&lt;/li&gt;
&lt;li&gt;every unsafe command will be detected&lt;/li&gt;
&lt;li&gt;a poorly written task list will produce a good result&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It adds persistence, constraints, verification habits, resumability, and visibility around Claude Code.&lt;/p&gt;

&lt;p&gt;Human review is still required.&lt;/p&gt;
&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Inside Claude Code:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/plugin marketplace add orwa-mahmoud/nightshift
/plugin install nightshift
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then initialize it inside your project:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/nightshift:setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Review the generated punch list and start the shift:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/nightshift:start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Overview and FAQ:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://nightshift.orwamahmoud.com/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnightshift.orwamahmoud.com%2Fog.jpg" height="400" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://nightshift.orwamahmoud.com/" rel="noopener noreferrer" class="c-link"&gt;
            Nightshift — Accountable coding shifts: long runs, recovery
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Give Claude Code, Codex, or Cursor a durable punch list, safety rules, recovery, and a reviewable morning handoff.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fnightshift.orwamahmoud.com%2Fnightshift-logo.png" width="512" height="512"&gt;
          nightshift.orwamahmoud.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Nightshift is free, open source, and MIT licensed.&lt;/p&gt;

&lt;p&gt;Contributions, issues, and feedback are welcome:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/orwa-mahmoud/nightshift" rel="noopener noreferrer"&gt;https://github.com/orwa-mahmoud/nightshift&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>claude</category>
      <category>devtools</category>
    </item>
    <item>
      <title>One headless table engine, every React UI kit</title>
      <dc:creator>Orwa Mahmoud</dc:creator>
      <pubDate>Sat, 04 Jul 2026 08:06:43 +0000</pubDate>
      <link>https://dev.to/orwamahmoud/one-headless-table-engine-every-react-ui-kit-2dm4</link>
      <guid>https://dev.to/orwamahmoud/one-headless-table-engine-every-react-ui-kit-2dm4</guid>
      <description>&lt;p&gt;Every React project reaches for a data table eventually, and the choice always feels like a trap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reach for &lt;strong&gt;ag-Grid&lt;/strong&gt; or &lt;strong&gt;MUI X DataGrid&lt;/strong&gt; and you get power — but their look, their DOM, and (for the good features) their license.&lt;/li&gt;
&lt;li&gt;Reach for &lt;strong&gt;TanStack Table&lt;/strong&gt; and you get total freedom — but now &lt;em&gt;you&lt;/em&gt; build every cell, header, filter, and pagination control from scratch.&lt;/li&gt;
&lt;li&gt;Reach for a kit-specific table (Mantine, Chakra, Ant) and you're locked to that one kit, re-learning a new API each time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted one table that renders &lt;strong&gt;natively&lt;/strong&gt; in whatever UI kit a project already uses, with the same features everywhere, and a headless escape hatch when the defaults aren't enough. So I built &lt;strong&gt;AdaptTable&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  One API, real native components
&lt;/h2&gt;

&lt;p&gt;The whole table is five lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DataTable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useFrontendData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ColumnDef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@adapttable/mantine&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ColumnDef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;accessor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;sortable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;accessor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;People&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useFrontendData&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;columns&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DataTable&lt;/span&gt; &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;rowKey&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swap the import for &lt;code&gt;@adapttable/mui&lt;/code&gt;, &lt;code&gt;@adapttable/chakra&lt;/code&gt;, &lt;code&gt;@adapttable/antd&lt;/code&gt;, &lt;code&gt;@adapttable/radix&lt;/code&gt;, &lt;code&gt;@adapttable/shadcn&lt;/code&gt;, or &lt;code&gt;@adapttable/unstyled&lt;/code&gt; — same props, different kit.&lt;/p&gt;

&lt;p&gt;The key part: it is &lt;strong&gt;not&lt;/strong&gt; a wrapper that looks the same everywhere. Each adapter renders that kit's &lt;em&gt;actual&lt;/em&gt; components — MUI gets Material elevation, Ant gets Ant's compact density, Chakra gets Chakra's tokens. Same behavior, native skin. Flip the switcher in the live demo and watch one dataset re-render through each kit: &lt;a href="https://orwa-mahmoud.github.io/adapttable/demo/" rel="noopener noreferrer"&gt;https://orwa-mahmoud.github.io/adapttable/demo/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Filters and state that live in the URL
&lt;/h2&gt;

&lt;p&gt;My favorite part: open the filter drawer, set a status and a date range, copy the link — whoever opens it sees the exact same view. Search, filters, sort, and page all serialize into query params (&lt;code&gt;q&lt;/code&gt;, &lt;code&gt;page&lt;/code&gt;, &lt;code&gt;f_status&lt;/code&gt;, …), so every view is refresh-safe, back/forward-safe, and shareable. No &lt;code&gt;useState&lt;/code&gt; juggling, no wiring: declare the filters and the URL sync comes with them, identically for client-side data and server-side fetching (there's a router adapter for Next.js and react-router).&lt;/p&gt;

&lt;h2&gt;
  
  
  What you get for free
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Sorting, filtering (with removable chips), global search&lt;/li&gt;
&lt;li&gt;Row selection + bulk actions&lt;/li&gt;
&lt;li&gt;Column management — show/hide, reorder, pin (sticky), resize&lt;/li&gt;
&lt;li&gt;Pagination — numbered or infinite scroll, auto by device&lt;/li&gt;
&lt;li&gt;Shareable URL state + saved views&lt;/li&gt;
&lt;li&gt;First-class RTL / Arabic and dark mode&lt;/li&gt;
&lt;li&gt;Optional virtualization — a 10k-row list mounts ~24 DOM rows&lt;/li&gt;
&lt;li&gt;Client-side data &lt;em&gt;and&lt;/em&gt; server-side (you supply the fetch) through one contract&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Headless when you need it
&lt;/h2&gt;

&lt;p&gt;Under every adapter is &lt;code&gt;@adapttable/core&lt;/code&gt; — a headless engine with prop-getters (&lt;code&gt;getTableProps&lt;/code&gt;, &lt;code&gt;getRowProps&lt;/code&gt;, …). If the batteries-included adapter isn't enough, drop down and render your own markup on the same engine. You never hit a wall.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it's not
&lt;/h2&gt;

&lt;p&gt;No pivoting, grouping, or Excel-style editing yet— this is for the CRUD tables most apps ship, not analytics grids. If you need those, ag-Grid is the right tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Try it in your browser (no install): &lt;a href="https://stackblitz.com/github/orwa-mahmoud/adapttable/tree/main/starters/mantine" rel="noopener noreferrer"&gt;https://stackblitz.com/github/orwa-mahmoud/adapttable/tree/main/starters/mantine&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Live demo (flip the kit switcher): &lt;a href="https://orwa-mahmoud.github.io/adapttable/demo/" rel="noopener noreferrer"&gt;https://orwa-mahmoud.github.io/adapttable/demo/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://orwa-mahmoud.github.io/adapttable/getting-started/" rel="noopener noreferrer"&gt;https://orwa-mahmoud.github.io/adapttable/getting-started/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npx @adapttable/cli init&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;MIT, stable v1 (1.1 just brought every adapter to full feature parity): &lt;a href="https://github.com/orwa-mahmoud/adapttable" rel="noopener noreferrer"&gt;https://github.com/orwa-mahmoud/adapttable&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feedback very welcome — especially whether the per-kit rendering holds up in your stack, and how the declarative API feels next to what you use today.&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
