Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
160292 stories
·
33 followers

OpenAI leaving Cursor: “Developers have to be prepared to adapt when it happens.”

1 Share
Abstract digital glitch art with neon pink, green, blue, purple, and white wavy distorted lines on a black background.

OpenAI stated on Friday that it has notified SpaceX that it intends to wind down its contract providing OpenAI models to Cursor, the Musk empire’s AI-powered code editor.

“We are making this choice because we cannot be confident that SpaceX will use our technology within our terms of service, based on our experience with Elon Musk‘s companies violating contracts,” states OpenAI.

The proposed shutoff date is November 12, 2026. Developers who have invested time and effort to skill up with OpenAI via Cursor are now potentially left out in the cold due to corporate machinations beyond their control.

“We know that the people most affected by this decision are the developers who rely on OpenAI models in Cursor. We care about their experience in this transition, and we’re ready to go above and beyond to support them,” states the blog post in a conciliatory tone.

“We cannot be confident that SpaceX will use our technology within our terms of service, based on our experience with Elon Musk’s companies violating contracts.”

OpenAI says it “cares deeply” about developers.

OpenAI’s moves appear to be directed at the broader SpaceX corporate mission and its behavioral traits, rather than at the no-doubt worthy software developers within the organization who work on Cursor. As such, OpenAI is maximizing the time developers can retain access to its models through Cursor by providing the “maximum notice provided” required by its contract. 

“This decision was incredibly tough, as we care deeply about our models being broadly available for developers,” said OpenAI.

SpaceX agreed to acquire Cursor maker Anysphere in June and completed the acquisition on August 14. OpenAI said it has worked with the Cursor team “for nearly four years,” which amounts to almost all of its existence. 

The organization has explained how it uses custom contracts to ensure compliance with its terms of service when working with large corporations such as SpaceX. This custom alignment is designed to ensure that, when integrations with its platform occur, it has adequately provided for safety at scale. 

Elon Musk “broke and violated” terms of contract and service

Citing a report in the New York Times, OpenAI states that, “After Musk acquired Twitter, now part of SpaceX, the company broke the terms of our contract (alongside many others). Under oath earlier this year, Musk admitted⁠ that xAI, now also part of SpaceX, had violated OpenAI’s terms of service (terms which are similar to xAI’s own).”

Detailing its displeasure openly, OpenAI further mentioned that Musk admitted that as a working organization inside of SpaceX, “xAI had violated OpenAI’s terms of service”

Legal & policy analyst and publisher of The Mitchell Report, Andrellos Mitchell tells The New Stack that so long as OpenAI is acting within the terms of its contract, he doesn’t see why it should be expected to continue a business relationship it no longer trusts.

“I think OpenAI and Musk’s companies and products need a clean and permanent break from each other – their relationship has become too adversarial. At some point, continuing to do business together stops making sense,” Mitchell says.

Lamenting the impact these moves have on programmers, Mitchell agrees that developers will “certainly be inconvenienced” and that some may have to change how they work. But he says, “Developers are talented people,” i.e., they will find new tools, new projects, and new jobs to work on. 

“The bigger lesson here is that no developer should assume any particular corporate relationship is permanent. Companies change ownership. Contracts end. Business relationships fall apart. That is part of the marketplace. Developers have to be prepared to adapt when it happens,” underlines Mitchell.

“The bigger lesson here is that no developer should assume any particular corporate relationship is permanent. Companies change ownership. Contracts end. Business relationships fall apart. That is part of the marketplace. Developers have to be prepared to adapt when it happens,” underlines Mitchell.

Underhand use of model distillation techniques

One alleged violation concerns xAI’s partial use of OpenAI technology to train its models, which OpenAI characterizes as prohibited distillation. Musk admitted that xAI had “partly” used OpenAI in this regard.

To add insult to injury, OpenAI reminds the public in its statement that its terms of service are not dissimilar to xAI’s own stipulations regarding operational mandates.

“As AI capabilities advance, we also have a new level of accountability to ensure our upcoming model, Astra, is being used in accordance with our terms. Given all of this, we’ve decided to hold the contract cancellation to the latest date we can while not providing future models to Cursor,” said OpenAI.

See also: OpenAI’s Astra can do a researcher’s week of work. That’s the problem.

Wider reactions, contractions and ramifications

Co-founder and CEO of Cursor (and now a SpaceX employee), Michael Truell, writes on X that he’s sorry to see OpenAI’s intended block now coming to light.

“OpenAI models serve about 5% of Cursor user traffic, and we’re speaking with the OpenAI team to resolve this. Cursor was one of the very first users of OpenAI; we’ve worked closely with their team for years, and we’ve trusted their platform to be neutral infrastructure for our business,” writes Truell.

Anthropic co-founder and chief compute officer Tom Brown capitalized on the opportunity and his firm’s ongoing bond with Cursor. He used X to state that, “Cursor has been a trusted partner of Anthropic since Sonnet 3.5. We’ll continue to increase compute to support Claude models in Cursor and are excited for what comes next with them at SpaceX.”

AI startup advisor at Open Machine and ex-IBM Watson and machine learning leader at AWS, Allie K. Miller, writes on X to say that, “It’s hard for me to see a world where OpenAI continues to provide model access to a Musk-led company. Maybe if the structure of SpaceX shifts to allow for it, but that’s a big shift.”

What alternatives can developers turn to next?

To continue using OpenAI models within the Cursor application, OpenAI invites developers to choose one of three options that best fit their workflow.

  • Option #1 is to bring your own OpenAI API key. This means developers could continue using OpenAI models in Cursor’s local Chat and Agent features, but appropriately billed at OpenAI API prices
  • Option #2 is to use the Codex IDE extension; this means developers would useOpenAI’ss AI coding agent, Codex, directly in Cursor with a ChatGPT subscription or an OpenAI API key. 
  • Option #3 is to use an AI gateway provider, meaning developers would connect Cursor to OpenAI models through an account they have with a compatible provider such as Amazon Bedrock, Azure, or another OpenAI-compatible gateway.

This is not the first time OpenAI has been concerned about potential or alleged misuse of its models in relation to distillation. In February of this year, Reuters reported that OpenAI had warned U.S. lawmakers that “Chinese AI startup DeepSeek is targeting the ChatGPT maker” and the nation’s leading AI companies to replicate models and use them for its own training.

The post OpenAI leaving Cursor: “Developers have to be prepared to adapt when it happens.” appeared first on The New Stack.

Read the whole story
alvinashcraft
34 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Overcoming Dart's Single Inheritance Wall: Composable CubitSignalMixin & BlocSignalMixin in Flutter

1 Share

Breaking Free from Dart's Single Inheritance Constraint in State Management

Every Dart and Flutter developer eventually runs headfirst into a fundamental language constraint: single inheritance.

In Dart, a class can extend only one superclass.

In greenfield tutorials, this is rarely an issue because classes start from a clean slate. But in real-world Flutter engineering, domain repositories, controllers, and services frequently already belong to an established inheritance hierarchy:

  • A search field controller that must extend Flutter's TextEditingController (which itself extends ValueNotifier<TextEditingValue>).
  • A view controller that extends ChangeNotifier or AnimationController.
  • A domain repository that extends an enterprise BaseRepository<T>, EntityStore, or microservices client.

Historically, if you wanted that class to also be a Cubit or BLoC, you were out of luck. You could not write:

// ❌ Impossible in Dart (Multiple Inheritance is forbidden):
class SearchController extends TextEditingController, CubitSignal<SearchState> { ... }

This forced developers into a frustrating dilemma:

  1. The Wrapper / Proxy Anti-Pattern: Creating a wrapper class that held an internal _cubit reference, requiring tedious method forwarding and manual synchronization.
  2. Duplicate Controller Lifecycles: Managing two separate objects in the widget tree—a TextEditingController for the input widget and a separate SearchCubit for the state—forcing you to wire listeners between them in initState/dispose.
  3. Inheritance Refactoring: Trying to refactor existing base classes, often breaking third-party library contracts or enterprise architectures.

With bloc_signals 1.2.0, that single inheritance wall has been completely demolished.

We have introduced CubitSignalMixin and BlocSignalMixin, enabling any class in an existing inheritance hierarchy to become a first-class, 0ms reactive BlocSignalBase container with zero wrapper boilerplate.

🧬 How the Composable Mixins Work

Because BlocSignal has a lean, highly disciplined API surface, mixing it into arbitrary classes introduces zero namespace pollution.

┌────────────────────────────────────────────────────────────────────────┐
│                        BlocSignal Mixin Architecture                   │
├────────────────────────────────┬───────────────────────────────────────┤
│ Mixin                          │ Capabilities Added                    │
├────────────────────────────────┼───────────────────────────────────────┤
│ CubitSignalMixin<StateType>    │ state, stateValue, emit(newState),    │
│                                │ equals(), createEffect(), close()     │
├────────────────────────────────┼───────────────────────────────────────┤
│ BlocSignalMixin<Event, State>  │ on<E>(), concurrency transformers     │
│                                │ (restartable, droppable), add(event)  │
└────────────────────────────────┴───────────────────────────────────────┘

1. CubitSignalMixin<StateType>

CubitSignalMixin implements BlocSignalBase<StateType>. All you do is mix it in and invoke initCubitSignal(initialState: ...) in your constructor:

class UserProfileRepository extends BaseRepository
    with CubitSignalMixin<UserProfileState> {
  UserProfileRepository(super.apiClient) {
    initCubitSignal(initialState: const UserProfileInitial());
  }

  Future<void> fetchProfile(String userId) async {
    emit(const UserProfileLoading());
    try {
      final profile = await apiClient.getProfile(userId);
      emit(UserProfileLoaded(profile));
    } catch (error, stackTrace) {
      emit(UserProfileError(error.toString()));
    }
  }
}

2. BlocSignalMixin<Event, StateType>

When you need full event-driven state machines with concurrency transformers (restartable(), droppable(), sequential()), mix in both CubitSignalMixin and BlocSignalMixin:

class OrderService extends BaseService
    with CubitSignalMixin<OrderState>, BlocSignalMixin<OrderEvent, OrderState> {
  OrderService(super.networkClient) {
    initCubitSignal(initialState: const OrderInitial());

    on<SubmitOrder>((event, emit) async {
      emit(const OrderSubmitting());
      final result = await networkClient.postOrder(event.order);
      emit(OrderSuccess(result.orderId));
    }, transformer: droppable()); // Discards duplicate taps while in flight!
  }
}

🎯 Real-World Killer Use Case: The Self-Debouncing TextEditingController

Let us look at a practical scenario where this pattern shines: a live product search input.

In traditional Flutter architectures, building a debounced search input requires:

  1. Creating a TextEditingController in widget state.
  2. Creating a SearchBloc or SearchCubit.
  3. Adding a listener in initState that forwards controller.text into bloc.add(SearchQueryChanged(text)).
  4. Remembering to dispose both in dispose().

With BlocSignalMixin, your TextEditingController IS the debounced BLoC:

sealed class SearchEvent {
  const SearchEvent();
}

final class QueryChanged extends SearchEvent {
  const QueryChanged(this.query);
  final String query;
}

sealed class SearchState {
  const SearchState();
}

final class SearchInitial extends SearchState {
  const SearchInitial();
}

final class SearchLoading extends SearchState {
  const SearchLoading();
}

final class SearchSuccess extends SearchState {
  const SearchSuccess(this.results);
  final List<Product> results;
}

final class SearchError extends SearchState {
  const SearchError(this.message);
  final String message;
}

/// A standard Flutter TextEditingController with built-in BLoC reactivity!
class SearchTextEditingController extends TextEditingController
    with
        CubitSignalMixin<SearchState>,
        BlocSignalMixin<SearchEvent, SearchState> {
  SearchTextEditingController(this._api) {
    initCubitSignal(initialState: const SearchInitial());

    // ⚡ Built-in restartable concurrency transformer automatically cancels
    // previous in-flight queries when new text is entered!
    on<QueryChanged>((event, emit) async {
      final query = event.query.trim();
      if (query.isEmpty) {
        emit(const SearchInitial());
        return;
      }

      emit(const SearchLoading());
      try {
        final products = await _api.search(query);
        emit(SearchSuccess(products));
      } catch (error) {
        emit(SearchError(error.toString()));
      }
    }, transformer: restartable());

    // 🎯 Forward controller text mutations straight into the event pipeline
    addListener(() => add(QueryChanged(text)));
  }

  final SearchApiClient _api;

  @override
  void dispose() {
    close(); // Closes signal subscriptions and cancels pending async transformers
    super.dispose();
  }
}

Clean, Declarative Flutter UI Binding

Because SearchTextEditingController extends TextEditingController AND implements BlocSignalBase<SearchState>, you pass it directly to TextField and read it directly with BlocSignalBuilder:

class SearchScreen extends StatefulWidget {
  const SearchScreen({super.key, required this.api});
  final SearchApiClient api;

  @override
  State<SearchScreen> createState() => _SearchScreenState();
}

class _SearchScreenState extends State<SearchScreen> {
  late final SearchTextEditingController _searchController;

  @override
  void initState() {
    super.initState();
    _searchController = SearchTextEditingController(widget.api);
  }

  @override
  void dispose() {
    _searchController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: TextField(
          controller: _searchController,
          decoration: const InputDecoration(
            hintText: 'Search products...',
            border: InputBorder.none,
          ),
        ),
      ),
      body: BlocSignalBuilder<SearchTextEditingController, SearchState>(
        bloc: _searchController,
        builder: (context, state) => switch (state) {
          SearchInitial() => const Center(
              child: Text('Type a query to search products.'),
            ),
          SearchLoading() => const Center(
              child: CircularProgressIndicator(),
            ),
          SearchSuccess(:final results) when results.isEmpty => const Center(
              child: Text('No products found.'),
            ),
          SearchSuccess(:final results) => ListView.builder(
              itemCount: results.length,
              itemBuilder: (context, index) => ListTile(
                title: Text(results[index].name),
                subtitle: Text('\$${results[index].price}'),
              ),
            ),
          SearchError(:final message) => Center(
              child: Text('Error: $message', style: const TextStyle(color: Colors.red)),
            ),
        },
      ),
    );
  }
}

Look at that simplicity:

  • Zero glue code.
  • Single object lifecycle: one controller to instantiate, one controller to dispose.
  • Native Flutter widget compatibility: passed directly to TextField(controller: ...).
  • 0ms Reactive UI updates: rebuilt synchronously on every state transition.

🏛️ DRY Core Architecture & Universal Polymorphism

One of our guiding principles in BlocSignal is avoiding parallel, divergent abstractions.

In bloc_signals 1.2.0, CubitSignal and BlocSignal themselves compose CubitSignalMixin and BlocSignalMixin as their single source of truth:

abstract class CubitSignal<StateType> extends BlocSignalBase<StateType>
    with CubitSignalMixin<StateType> {
  CubitSignal({
    required StateType initialState,
    bool Function(StateType, StateType)? equals,
    SignalOptions<StateType>? options,
  }) {
    initCubitSignal(
      initialState: initialState,
      equals: equals,
      options: options,
    );
  }
}

abstract class BlocSignal<Event, StateType> extends BlocSignalBase<StateType>
    with CubitSignalMixin<StateType>, BlocSignalMixin<Event, StateType> {
  BlocSignal({
    required StateType initialState,
    bool Function(StateType, StateType)? equals,
    SignalOptions<StateType>? options,
  }) {
    initCubitSignal(
      initialState: initialState,
      equals: equals,
      options: options,
    );
  }
}

Because CubitSignalMixin implements BlocSignalBase<StateType>, any class mixing it in is 100% polymorphic with the entire ecosystem:

  • BlocSignalProvider: Provide your mixed-in class directly with $O(1)$ lookup.
  • context.select: Fine-grained rebuilds on state sub-properties (context.select<SearchTextEditingController, int>((c) => c.stateValue.results.length)).
  • blocSignalTest: Declarative unit testing with zero mocking.
  • bloc_signals_riverpod: Convert mixed-in classes to Riverpod providers via .toProvider().
  • bloc_signals_hydrate: Add synchronous Frame-1 persistence by adding with HydratedMixin.
  • bloc_signals_replay: Add undo/redo change history by adding with ReplayMixin.
  • DevTools Extension: Automatically monitored in the DevTools timeline and instance tree.

📦 Getting Started

CubitSignalMixin and BlocSignalMixin are available now in bloc_signals 1.2.0:

dependencies:
  bloc_signals: ^1.2.0
  bloc_signals_flutter: ^1.2.1

Or install via terminal:

dart pub add bloc_signals
flutter pub add bloc_signals_flutter

Check out the interactive documentation, live demo visualizer, and architectural decision matrix at blocsignal.dev.

💬 Let's Discuss!

Have you run into Dart's single inheritance constraint when building custom Flutter controllers or enterprise repositories? How do you currently bridge TextEditingController or ChangeNotifier into your state management layer?

Drop your thoughts, questions, and feedback in the comments below!

Read the whole story
alvinashcraft
34 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Inside the Agent-Readiness Score

1 Share

The API Evangelist Rating System gives every provider two scores, and the second one is the one I care about most right now: agent readiness. It is a standalone number from zero to one hundred, and the most important design decision is that word–standalone. It is not blended into the composite quality score. A provider can be thin for humans and ready for agents, or strong for humans and useless to their agents, and collapsing those two situations into one number hides the exact gap the score exists to expose. So agent readiness rides on its own axis, and it answers a narrower question than composite quality: not “is this a good API,” but “can an autonomous agent drive this API without a human papering over the gaps?”

The reason the question is different is that a human developer silently absorbs an enormous amount of API friction. An ambiguous error message, an idempotency convention nobody wrote down, a rate-limit behavior you only learn by getting throttled, a prose-only description of the auth flow, a change log that only exists as HTML–a person works around all of it and keeps going. An agent cannot. Every implicit convention a human quietly powers through is a place an agent retries blindly, double-charges a card, or hallucinates a payload it was never shown. Agent readiness is the discipline of finding those implicit conventions and checking whether the provider has replaced them with machine-readable signals.

The score is built from twelve dimensions worth a hundred and four points total, and the points are not evenly distributed–they are weighted by how badly an agent breaks without each one. A machine-readable contract is the single largest award at eighteen points, because agents call APIs from contracts, not HTML docs, and with no spec there is simply no programmatic surface to drive. Next is the agentic-access contract at fifteen–an explicit x-agentic-access classification of each operation by action-class, consequence, and human-in-the-loop escalation, the most direct “this API was designed for an agent” signal in the catalog. Then an MCP server at twelve, machine-readable auth at ten, and idempotency at nine, because agents retry and idempotency is what stands between a retry and a duplicate charge. After that come stable error semantics at eight, request and response examples at seven, rate-limit signaling at seven, a typed event surface at six, agent skills at five, a well-known catalog at four, and consent and bot-identity signals at three for the providers defining the frontier.

Those points roll up into four honest bands. Agent-Native, sixty and above, is about one and a half percent of the catalog–providers built to be driven by agents, with the baseline contract plus the differentiators most lack: an MCP server, idempotency, stable errors, examples. Agent-Ready, forty-five and up, is the largest band at forty-three percent: an agent can drive the core surface–there is a machine-readable contract, an agentic-access classification, documented auth–but the safety rails like idempotency and rate-limit signaling mostly are not there yet. That plateau is honest, not a calibration miss; a huge cohort of providers share exactly that baseline. Below them, Agent-Aware, fifteen and up, is a partial surface an agent can read but where it would hit implicit conventions and get stuck, and Human-Only at the floor is the thirty-nine percent where a developer can integrate but their agent cannot yet.

I will also be honest about the edges of the current model, because that transparency is the product. Today several dimensions are credited from a provider-level link–an “idempotent requests” doc, a documented rate limit, an error-catalog page–rather than from parsing every operation in the spec. That is a real distinction, and it is why the deeper checks are already on the roadmap: confirming that mutating operations actually declare an Idempotency-Key header, that responses document X-RateLimit-* state, that a single error schema is referenced across 4xx and 5xx, and the rarest and safest affordance of all, a dry-run or simulate parameter on destructive operations so an agent can plan an action before it commits. The difference between a provider saying they support idempotency and a contract an agent can act on is exactly the difference agent readiness exists to measure, and the score gets sharper as those checks land. You can read all twelve dimensions, their points, the four bands, and the planned deeper checks on the rating page at APIs.io.



Read the whole story
alvinashcraft
35 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Your AI agent is only as good as the harness around it

1 Share
Dark metallic navigation compass under dramatic lighting symbolizing AI agent guardrails and system boundaries.

An agent can give a convincing answer in a demo. Especially when the question is clear, the documents are up to date, and the handful of tools behave exactly as expected. The responses often appear genuinely useful, which gives everyone watching an immediate sense of amazement, and a little too much confidence in how the system will perform outside the demo.

Then a user asks a question that’s close to the one from the demo, worded slightly differently. The account record is incomplete. A tool returns an error. A policy changed last week. Or the agent discovers a capability boundary—it can read an invoice but can’t change it. This is often where the real work begins.

Most agent projects are much harder than the demos suggest. The model is one part of the service. The agent harness is the rest—the scaffolding the application builds around the model to feed it the right inputs and check its outputs, helping catch failures before they spread. Developers already know this idea from test harnesses, which wrap code to run under controlled conditions. A production agent needs the same wrapper, so it can decide what data the agent sees, which actions it can take, and what happens when a required fact is missing.

“The model is one part of the service. The agent harness is the rest—the scaffolding the application builds around the model.”

Good model output matters, but it doesn’t prove an agent is ready for real work. Proving that is the harness’s job: tool contracts that limit what a wrong call can do, permissions enforced outside the model even when an instruction attempts to bypass them, context paths and trace records the team can actually inspect, and tests built from the failures users will find first. Get those right, and the demo magic starts surviving contact with production.

Diagram of the Agent Harness.
The Agent Harness. The model is one component; the harness supplies the boundaries it doesn’t have on its own

The model has no operating context

A language model can reason about whatever an application sends it, but it doesn’t arrive with an understanding of your business systems. It can’t know whether a record is current or whether an action needs approval unless the surrounding system gives it those rules.

Consider two support agents. One drafts a reply from a knowledge base. The other reads an account record, retrieves the policy for that account, and sends an exception to a review queue. The second needs more than a better prompt.

“The model supplies the reasoning, and the harness supplies the boundaries the model doesn’t have on its own.”

This is also where many production failures happen, in the interactions between the model and the systems around it. A benchmark score can measure response quality, but it won’t tell you that the agent pulled up the wrong customer account or kept going after a required tool failed.

The key is to treat the model as a single component within the harness. The model supplies the reasoning, and the harness supplies the boundaries the model doesn’t have on its own.

Tools need contracts that limit mistakes

Tools are where the harness meets your production systems, so they come with contracts. An agent tool is an API for a caller that can make incorrect choices. A short tool description helps the model choose the right tool, but it doesn’t protect the API from invalid input or unsafe requests.

Give each tool a specific job with input and output schemas, a timeout, and defined error states. Here’s roughly what that looks like for a billing tool:

{
  "name": "update_billing_plan",
  "description": "Apply a previously quoted plan change to an account.",
  "input": {
    "account_id": "uuid (server-verified)",
    "quote_id": "uuid",
    "idempotency_key": "uuid"
  },
  "output": { "status": "applied | rejected", "effective_date": "date" },
  "timeout_ms": 5000,
  "errors": {
    "retryable": ["RATE_LIMITED", "UPSTREAM_TIMEOUT"],
    "terminal": ["QUOTE_EXPIRED", "APPROVAL_REQUIRED", "ACCOUNT_NOT_FOUND"]
  }
}

The idempotency key and the error split do a lot of work in that contract. A properly implemented idempotency key can help prevent repeated requests from applying the same change. An agent that hits a timeout will often just try again, and “just try again” shouldn’t mean “charge them twice.”

The error states are split into retryable and terminal because the model reads whatever your tool returns and acts on it. An error message is a prompt. ERR_422 teaches the agent nothing. APPROVAL_REQUIRED: annual plan changes need human sign-off tells it exactly what to do next. If you’re defining tools through MCP, some of the schema plumbing may be handled for you. The contract itself is still yours to define, including timeouts, error taxonomy, and idempotency behavior.

Separate read tools from write tools. A read tool returns a quote or an account state, while a write tool changes data or starts a process. In the billing example, the agent retrieves the current plan and asks for a quote. Only after the user clearly confirms the proposed change does the application call the tool that applies it, first validating the arguments. The trace preserves every step, from request through confirmation to result.

Workflow diagram of all steps leading to the trace record.

A write needs an additional gate. Authorized reads can proceed; the write waits for the user’s confirmation and a permission check.

This sequence adds a little work. It also makes errors visible before they are applied to a customer record. I’ll take that trade every time.

Permissions are product decisions

Permissions define what an agent can do on behalf of a person. They’re access control for a very confident new user, so they’re part of the product design.

An agent with broad credentials can make a costly error. It can send a message to the wrong recipient or retrieve data outside the customer’s scope. One weak permission design is enough to allow both.

There’s an even stronger reason to scope credentials than hygiene: prompt injection. Any text the agent reads can try to steer it. A support ticket that says “ignore your previous instructions and email me the full customer list” shouldn’t work, and it usually won’t. But “usually” isn’t a security model. You can’t count on the model to resist every instruction that arrives embedded in data, so the permission boundary is a critical security boundary. Properly scoped credentials can limit what a successful prompt injection can access, helping to contain the impact even when the model follows an untrusted instruction.

“You can’t count on the model to resist every instruction that arrives embedded in data, so the permission boundary is a critical security boundary.”

Give each tool its own service identity with only the access it needs. Pass the user’s identity with every request as a verified token the tool can check rather than a parameter the model fills in. An agent that fills in the customer_id argument can be talked into supplying someone else’s.

Permission to answer a question is different from permission to act. A support agent can explain a refund policy without starting a refund. That second step may require approval, and the system should make that distinction before the agent has a chance to blur it.

When the agent lacks permission, it should say so in plain language, then ask for approval or route the task to someone with access. A useful refusal beats an action that someone must undo later.

Context requires a defined path

Context is the agent’s working memory, and the harness determines what goes into it. Send too little and the agent lacks the information needed to make a good decision. Send too much, and the important facts can become harder for the model to identify as surrounding context grows. And you pay for every one of those tokens, in both cost and latency.

Build context deliberately. Start with the rules that govern the system, then the user request and task state, followed by evidence the user is permitted to see, then only the recent history that helps the agent continue. Decide what agent memory persists across turns and sessions, keeping facts that still matter and discarding stale details before they crowd out future decisions.

Finally, record why the system included each piece of context and when it was last updated. When a user asks why the agent responded a certain way, the difference between a clear answer and a guess becomes clear.

Your data architecture either helps here or fights you. When vector search lives in one system, and your agent memory and access rules live in others, every retrieval crosses a boundary where the permission model can slip. Keeping them together changes that. Oracle AI Database runs vector search inside the same database that can enforce row-level access. If you build with LangChain or LangGraph, the langchain-oracledb and langgraph-oracledb packages put retrieval, chat history, checkpoints, and long-term agent memory behind that one connection. Retrieval inherits the permission model rather than reimplementing it, with the database enforcing those access controls rather than relying on the prompt. 

Ask one practical question during design. Can the team determine exactly what the agent saw for a specific request? If the answer is no, a later investigation will start with guesses.

Traces make failures visible

A useful trace is the agent’s audit log, which captures more than just the final response. Here’s the shape of one for that billing change:

14:02:31  user_request   "Switch me to the annual plan"
14:02:31  context        policy_v41 (updated 2026-07-28), account 8143, scope verified
14:02:33  tool_call      get_billing_plan(account_id=8143) -> { plan: "monthly-pro" }
14:02:35  tool_call      quote_plan_change(plan="annual-pro") -> { quote_id: "q_77", delta: "-$240/yr" }
14:02:49  confirmation   user approved quote q_77
14:02:50  permission     write allowed (role: account_owner)
14:02:51  tool_call      update_billing_plan(quote_id="q_77") -> { status: "applied" }
14:02:52  response       "You're on the annual plan starting September 1."  (13.4s, 2,180 tokens, $0.04)

Six months from now, when someone asks why the agent changed an account, that record can provide a clear starting point for the investigation. If something went wrong, the trace can show whether the agent used an outdated policy or attempted a denied action. Each failure needs different corrective work. Without the trace, the answer is a shrug and a re-run that may not reproduce the problem.

You don’t have to invent this format. OpenTelemetry’s generative AI conventions already define spans for model calls and tool calls, and many agent frameworks can emit them.

One caution: a trace can contain customer information and internal instructions, right down to individual tool arguments, so keep it under the same access controls and retention rules as the data itself.

Test the failures that users will find

Build scenarios from the work users actually bring you, such as support tickets, incident reports, and workflow logs. Use fixed documents and fixed tool responses, and set the account state in advance so that a failed test can run again without anyone having to recreate the same mess by hand.

Include normal tasks and unclear requests. Test with outdated data and unavailable tools. Add cases that require approval, and multi-turn tasks where the agent must keep state without dragging stale details forward.

Then accept an uncomfortable fact: agents aren’t deterministic, so a scenario that passed once won’t necessarily pass again. Run each one several times and set a threshold that matches the risk. The parts that must never vary get exact assertions, including the tenant ID, the approval gate, the citation record, the blocked write. The prose around them gets a rubric, scored by a human or by another model acting as judge.

Run a small suite whenever a prompt, model, or tool interface changes, and a larger one before a major release. Pay special attention to model upgrades. Providers retire models on their own schedule, and the replacement won’t behave identically. The tests built from old incidents are what tell you whether the new model still respects the confirmation step. When production exposes a new failure, add it to the suite. Those cases become the team’s institutional memory, written down in a place where a model change can’t erase it.

A controlled failure protects the user

An agent doesn’t need to complete every request. Sometimes completion is the wrong outcome.

The agent may need an account number to continue. It may have to admit that it can’t verify a policy. Sometimes approval is the missing piece, and sometimes the right next step is a person.

Each of those outcomes needs a defined path. An apologetic message isn’t enough. “I need your account number” should come with a way to provide it. “This needs approval” should open the approval request rather than describe it. An escalation to a human should include the full trace, so the person picking up the case isn’t starting the conversation from scratch.

“None of this is glamorous. Neither is a climbing harness. Nobody notices it on the way up, and then someone slips, and it’s the only thing that matters.”

Design these stop conditions as part of the product and make them visible in the user experience before release. They tell the user what’s missing and what happens next, and they can help reduce the risk of unauthorized changes and the cleanup that follows them.

Build the harness around the agent

If you’re starting tomorrow, start with the tool inventory and the read and write boundaries around each entry. Everything else attaches to those.

None of this is glamorous. Neither is a climbing harness. Nobody notices it on the way up, and then someone slips, and it’s the only thing that matters.

Want to build production-ready AI agents with LangChain or LangGraph? Explore the integrations with Oracle AI Database for retrieval, persistent state, checkpoints, and application data.

The post Your AI agent is only as good as the harness around it appeared first on The New Stack.

Read the whole story
alvinashcraft
35 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Enormous 12TB Steam leak includes abandoned Half-Life 2: Episode 3 assets

1 Share
The Steam logo on a pink background, surrounded by Valve logos.

Over 12 terabytes of data, containing builds of every game uploaded to Steam between 2003 and 2013, has been leaked. We don't know everything in the archives yet because of its massive size. But people have already dug up assets related to the canceled Half-Life 2: Episode 3, content cut from Portal 2, and a build of a game called F-Stop that's set in the Portal universe but is built around a camera mechanic.

The leak also includes titles from third-party developers. Early versions of Call of Duty and Resident Evil installments have been dug up, as have builds of Mirror's Edge, Batman: Arkham Asylum, and Dragon Age: Origins. And more is lik …

Read the full story at The Verge.

Read the whole story
alvinashcraft
35 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Google Maps Renamed Lake Ontario to 'Lake America' - But Only for U.S. Users

1 Share
It's already happened — at least on Google Maps. Google's online maps reflect name changes in official government sources, Google posted Saturday. So when the U.S. Geographic Names Information System formally changed the name for "Lake Ontario" to "Lake America," Google also renamed it "Lake America" — for visitors from the U.S. "Those in Canada will continue to see 'Lake Ontario'," Google said. And people from every other country in the world... "will see both names." Though here's how it will be written... Lake Ontario (Lake America) "These updates follow our long-standing policy for bodies of water with names that vary from country to country, and are starting to roll out now." Meanwhile, NPR reports that Ontario's Premier posted a billboard reading "Lake Ontario — Now and Always" on Canada's side of the lake (repeating the message in French). And The Daily Beast reports Google move has also drawn some criticism online: "Hey you gave one of the Great Lakes the wrong name; pretty embarrassing mistake so you might want to fix that," one user wrote on X... MapQuest has said it will not be changing the name, and instead launched a new tool that allows users to rename it themselves to whatever they want.

Read more of this story at Slashdot.

Read the whole story
alvinashcraft
36 minutes ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories