Skip to content

Conversation

@rupertsworld
Copy link
Contributor

  • Async processes working properly
  • Adjusted message renderer to add tool calls/results where there are async delays
  • improved testing

Copilot AI review requested due to automatic review settings August 14, 2025 02:48
@rupertsworld rupertsworld merged commit 7dab2f4 into main Aug 14, 2025
1 check passed
@rupertsworld rupertsworld deleted the feat/processes branch August 14, 2025 02:49
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR enhances async process handling in the kernel system by refactoring the tool execution architecture and improving message rendering. The changes enable proper handling of async processes through a cleaner separation between execute and process-based tools, while ensuring tool calls and results are properly rendered even with async delays.

Key changes:

  • Refactored tool architecture to support both execute and process properties
  • Enhanced message renderer to handle async tool calls with pending states
  • Improved testing infrastructure with timeout utilities and better fixtures

Reviewed Changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
test/utils.ts Adds timeout utility for async test handling
test/tools.test.ts New test suite for tool execution and process instantiation
test/fixtures.ts Consolidated test fixtures with process classes and mock model
src/tools.ts Simplified tool interface to support both execute and process functions
src/stream.ts Enhanced message renderer with async tool call handling
src/kernel.ts Refactored tool call handling to support async processes properly
src/processes/*.ts Updated process architecture with snapshot-based serialization
src/runtime.ts Improved process lifecycle management and event handling
cli/src/tools/*.ts Updated tool implementations to use new process-based architecture

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

resolve();
}
});
}, 1);
Copy link

Copilot AI Aug 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The timeout value of 1ms is too short and will likely cause the test to fail before the kernel can process the message and emit the expected event. Consider using a more reasonable timeout like 1000ms.

Suggested change
}, 1);
}, 1000);

Copilot uses AI. Check for mistakes.
resolve();
}
});
}, 1);
Copy link

Copilot AI Aug 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The timeout value of 1ms is too short and will likely cause the test to fail before the kernel can process the message and emit the expected event. Consider using a more reasonable timeout like 1000ms.

Suggested change
}, 1);
}, 1000);

Copilot uses AI. Check for mistakes.
})
);

withTimeout((resolve) => {
Copy link

Copilot AI Aug 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The withTimeout function returns a Promise but this test doesn't await it or return it, which means the test will complete before the async operation finishes. Add 'await' or 'return' before withTimeout.

Suggested change
withTimeout((resolve) => {
it('calls a simple execute tool', async () => {
kernel.send(
createMessage('tool-call', {
name: 'simple_execute',
})
);
await withTimeout((resolve) => {

Copilot uses AI. Check for mistakes.
})
);

withTimeout((resolve) => {
Copy link

Copilot AI Aug 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The withTimeout function returns a Promise but this test doesn't await it or return it, which means the test will complete before the async operation finishes. Add 'await' or 'return' before withTimeout.

Suggested change
withTimeout((resolve) => {
it('instantiates a process & returns the call output', async () => {
kernel.send(
createMessage('tool-call', {
name: 'simple_process',
})
);
await withTimeout((resolve) => {

Copilot uses AI. Check for mistakes.
* Called with a snapshot to load the process. Replace this with logic to restore your snapshot data.
*/
static fromSnapshot(snapshot: unknown): Process {
const ctor = this.constructor as ProcessConstructor;
Copy link

Copilot AI Aug 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method tries to access this.constructor but this refers to the class constructor, not an instance. It should be this directly since this is a static method. Change to const ctor = this;

Suggested change
const ctor = this.constructor as ProcessConstructor;
const ctor = this as ProcessConstructor;

Copilot uses AI. Check for mistakes.
if (!this.process)
throw new Error('Tried to unmount, but process not running.');
if (!this.process.unmount)
throw new Error('Cannot unmound process, no unmount function supplied.');
Copy link

Copilot AI Aug 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in error message: 'unmound' should be 'unmount'.

Suggested change
throw new Error('Cannot unmound process, no unmount function supplied.');
throw new Error('Cannot unmount process, no unmount function supplied.');

Copilot uses AI. Check for mistakes.
// If the next message is not a valid tool response, add pending tool
// results message.
const nextMsg = msgs.at(index + 1);
if (nextMsg && !isValidToolResponse(msg, nextMsg)) {
Copy link

Copilot AI Aug 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using msgs.at(index + 1) when the next message doesn't exist will return undefined, but the code doesn't handle this case properly. The check nextMsg && !isValidToolResponse(msg, nextMsg) could pass when nextMsg is undefined, which might not be the intended behavior.

Suggested change
if (nextMsg && !isValidToolResponse(msg, nextMsg)) {
if (!nextMsg || !isValidToolResponse(msg, nextMsg)) {

Copilot uses AI. Check for mistakes.
toolName: result.name,
args: {},
})),
});
Copy link

Copilot AI Aug 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using msgs.at(index - 1) when the previous message doesn't exist will return undefined, but the check !prevMsg || !isValidToolResponse(prevMsg, msg) assumes prevMsg can be falsy. However, this could lead to unexpected behavior when index is 0.

Suggested change
});
if (index > 0) {
const prevMsg = msgs.at(index - 1);
if (!prevMsg || !isValidToolResponse(prevMsg, msg)) {
renderedMsgs.push({
role: 'assistant',
content: msg.results.map((result) => ({
type: 'tool-call',
toolCallId: result.callId,
toolName: result.name,
args: {},
})),
});
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants