
Security News
Axios Maintainer Confirms Social Engineering Attack Behind npm Compromise
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.
Disclaimer: This is a 100% total, absolute, just-for-fun prototype.
Agents thrive on simple actions, persistent memory, and reactive updates. julets provides an tool and memory agent toolkit on top of the Jules REST API.
create session → poll for status → fetch result)import { jules } from 'julets';
const session = jules.run({
prompt: `Fix visibility issues in the examples/nextjs app.
**Visibility issues**
- White text on white backgrounds
- Low contrast on button hover
**Instructions**
- Update the global styles and page components to a dark theme with the shadcn zinc palette.
`,
source: { github: 'davideast/julets', branch: 'main' },
});
for await (const activity of session.stream()) {
switch (activity.type) {
case 'progressUpdated':
console.log(`[BUSY] ${activity.title}`);
break;
case 'planGenerated':
console.log(`[PLAN] ${activity.plan.steps.length} steps.`);
break;
case 'sessionCompleted':
console.log('[DONE] Session finished successfully.');
break;
case 'sessionFailed':
console.error(`[FAIL] ${activity.reason}`);
break;
}
}
// Get the pull-request URL once complete
const { pullRequest } = await session;
if (pullRequest) {
console.log(`PR: ${pullRequest.url}`);
}
The SDK features a powerful local-first synchronization engine, making your agent-driven applications fast, reliable, and offline-capable. It's accessible via session.activities().
The session.activities().stream() method is the recommended way to interact with session activities. It automatically caches activities to disk, providing a restart-safe stream that combines a complete history with live updates.
const session = jules.session(id);
// Automatically caches to disk and deduplicates events across restarts.
for await (const act of session.activities().stream()) {
console.log(act.type, act.id);
}
This method is ideal for building resilient UIs and services that can seamlessly resume from a previous state. For a simpler, idiomatic stream, session.stream() is also available and uses session.activities().stream() under the hood.
For more granular control, you can use .history() to fetch only the locally cached activities (cold) or .updates() to stream only new, live activities (hot).
The local cache can be queried instantly without network latency using the .select() method.
// Query your local cache instantly without network latency.
const errors = await session.activities().select({
type: 'sessionFailed',
limit: 10,
});
npm i julets
# OR
bun add julets
The julets SDK is designed to work seamlessly in both Node.js and browser environments. It uses conditional exports in its package.json to automatically provide the correct implementation for your platform.
In a Node.js environment, the SDK defaults to using the local filesystem for caching session activities in a .jules/cache directory. This provides a persistent, restart-safe experience.
// Imports the Node.js version by default
import { jules } from 'julets';
const session = await jules.session({
prompt: 'Refactor the user authentication module.',
source: { github: 'your-org/your-repo', branch: 'develop' },
});
When used in a browser environment (e.g., in a web application bundled with Vite, Webpack, or Rollup), the SDK automatically uses a browser-specific implementation that leverages IndexedDB for storage. This allows your web application to maintain session state locally.
To use the browser version, you can explicitly import it:
// Explicitly import the browser-optimized version
import { jules } from 'julets/browser';
// The rest of your code remains the same
const session = await jules.session({
prompt: 'Refactor the user authentication module.',
source: { github: 'your-org/your-repo', branch: 'develop' },
});
There are two primary strategies for handling platform-specific code, and julets is designed to support both.
Automatic Resolution (Recommended for most cases): Modern bundlers that support the exports field in package.json can automatically select the correct file based on the environment. For example, Vite, when building for the browser, will see the browser condition in the exports map and use the dist/browser.es.js file. This is the ideal scenario, as it requires no changes to your import statements.
// In a browser environment, the bundler will automatically
// resolve this to the browser-specific build.
import { jules } from 'julets';
Explicit Imports: In some cases, you may want to be explicit about which version you are using, or your tooling may not fully support conditional exports. In these situations, you can use a direct import path.
// Always imports the browser version, regardless of bundler configuration
import { jules } from 'julets/browser';
When to choose which?
julets) whenever possible. It's cleaner and relies on the standard module resolution features of the JavaScript ecosystem.julets/browser) if you need to override the bundler's resolution, if you are working in an environment that doesn't support conditional exports, or if you want to be very clear in your code that you are using the browser-specific version.export JULES_API_KEY=<api-key>
Use jules.session() for interactive workflows to observe, provide feedback, and guide the process. The SessionClient object maintains state across multiple interactions.
import { jules } from 'julets';
const session = await jules.session({
prompt: 'Refactor the user authentication module.',
source: { github: 'your-org/your-repo', branch: 'develop' },
});
console.log(`Session created: ${session.id}`);
console.log('Waiting for the agent to generate a plan...');
// Wait for the specific state where the plan is ready for review
await session.waitFor('awaitingPlanApproval');
console.log('Plan is ready. Approving it now.');
await session.approve();
// Ask a follow-up question
const reply = await session.ask(
'Start with the first step and let me know when it is done.',
);
console.log(`[AGENT] ${reply.message}`);
// Wait for the final result of the session
const outcome = await session.result();
console.log(`✅ Session finished with state: ${outcome.state}`);
Sessions progress is observed through the .stream() method that is available on both the AutomatedSession and SessionClient objects. An AutomatedSession is created via jules.run() and a SessionClient is create via jules.session(). The .stream() method returns an AsyncIterator to observe the agent's progress.
for await (const activity of session.stream()) {
switch (activity.type) {
case 'planGenerated':
console.log(
'Plan:',
activity.plan.steps.map((s) => s.title),
);
break;
case 'agentMessaged':
console.log('Agent says:', activity.message);
break;
case 'sessionCompleted':
console.log('Session complete!');
break;
}
}
Session progress is represented through an Activity object. Activities can contain artifacts, such as code changes (changeSet), shell output (bashOutput), or images (media). The SDK provides rich objects with helper methods for interacting with them.
// (Inside a stream loop)
for (const artifact of activity.artifacts) {
if (artifact.type === 'bashOutput') {
// The .toString() helper formats the command, output, and exit code
console.log(artifact.toString());
}
if (artifact.type === 'media' && artifact.format === 'image/png') {
// The .save() helper works in both Node.js and browser environments
await artifact.save(`./screenshots/${activity.id}.png`);
// Get a URL for display or download (works cross-platform)
const url = artifact.toUrl();
console.log('Screenshot URL:', url);
}
}
You can configure timeouts and polling intervals by creating a configured client.
import { jules } from 'julets';
// The default jules client initialized with process.env.JULES_API_KEY
const session = jules.session('<session-id-here>');
// Initializes a jules client with another API key
const customJules = jules.with({ apiKey: 'other-api-key' });
const session = customJules.session('<session-id-here>');
import { jules } from 'julets';
const customJules = jules.with({
pollingIntervalMs: 2000, // Poll every 2 seconds
requestTimeoutMs: 60000, // 1 minute request timeout
});
// Use the jules client the same as the default
const session = jules.session('<session-id-here>');
The SDK throws custom errors that extend a base JulesError. This makes it easy to catch all SDK-related exceptions.
import { jules, JulesError } from 'julets';
try {
const session = await jules.session({ ... });
} catch (error) {
if (error instanceof JulesError) {
console.error(`An SDK error occurred: ${error.message}`);
} else {
console.error(`An unexpected error occurred: ${error}`);
}
}
This is a high-level overview of the main SDK components.
jules: The pre-initialized client.jules.with(options): Creates a new client with custom configuration.jules.session(): Creates or rehydrates a session. Returns a SessionClient.session.ask(): Sends a message and awaits the agent's reply.session.send(): Sends a fire-and-forget message.session.approve(): Approves a pending plan.session.waitFor(): Pauses until the session reaches a specific state.session.waitForCompletion(): Awaits the final outcome of the session.session.stream(): Returns an async iterator of all activities.session.info(): Fetches the latest session state.session.activities(): Returns an ActivityClient for advanced stream and cache control.session.activities().stream(): A robust, restart-safe stream of all activities (history + live).session.activities().history(): Returns a stream of locally cached activities.session.activities().updates(): Returns a stream of live activities from the network.session.activities().select(query): Queries the local activity cache.artifact.save(): Decodes the base64 data and saves it. In Node.js, it writes to the filesystem. In the browser, it saves to IndexedDB.artifact.toUrl(): Returns a data: URI for the artifact data, usable in both Node.js and browser.artifact.toString(): Returns a formatted string that combines the command, exit code, stdout, and stderr, to simplify log display.FAQs
The agent-ready SDK for Jules.
The npm package julets receives a total of 0 weekly downloads. As such, julets popularity was classified as not popular.
We found that julets demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.