Repository navigation
Add workingDir folder selection to Ripple API topic creation - #121
Conversation
Add /workingDirs/list to enumerate agentic-browser subfolders (excluding reserved topic-template/screenshots) with timestamps, and extend /topics/create with optional workingDir + isNewFolder body keys so a caller can reuse an existing folder or require a brand-new one, with path-traversal and reserved-name validation. Enables the web-ui to let users pick or create a Topic's working directory (kanban issue 1789021772221). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
||
| | target | | ||
| self validateWorkingDirName: aDirName. | ||
| target := AbSettings defaultAgenticBrowserRootDirectory / aDirName. |
There was a problem hiding this comment.
Required - create path ignores the injected root, so the create tests pollute the real filesystem.
handleListWorkingDirs: lists from self workingDirsRootDirectory (the overridable ivar), but resolveWorkingDirPath: builds the target against AbSettings defaultAgenticBrowserRootDirectory directly. The accessor comment says the ivar is overridable for test isolation - yet the create path bypasses it entirely, so:
- The list and create endpoints can resolve to different roots.
withTempWorkingDirsRoot:has no effect on the create-path tests.testCreateTopicWithNewWorkingDirCreatesAndSeedsFromTemplate, the reuse test, and the collision test therefore write real folders (seeded from the template, or holding a marker file) directly under the livedefaultAgenticBrowserRootDirectoryand never delete them on teardown - genuine test pollution of the repo/image filesystem.
Suggested: resolve against self workingDirsRootDirectory here (consistent with the list handler) and wrap the three create-path tests in withTempWorkingDirsRoot: so the helper actually isolates and cleans up.
There was a problem hiding this comment.
Fixed in 1ff5dac: resolveWorkingDirPath:isNewFolder: now resolves against self workingDirsRootDirectory (same accessor handleListWorkingDirs: uses) instead of AbSettings defaultAgenticBrowserRootDirectory directly, so both endpoints agree on the root and tests can override it. The three affected create-path tests are now wrapped in withTempWorkingDirsRoot: (which nests the temp root under the real agentic-browser root so AbTopicTemplateDirectory>>copyTo:'s own guard still allows seeding) and clean up via deleteAll in the block's ensure:.
| { #category : 'private' } | ||
| AbTopicManagerRipple >> validateWorkingDirName: aDirName [ | ||
|
|
||
| ((aDirName includesSubstring: '/') |
There was a problem hiding this comment.
Required - empty/whitespace-only and "." workingDir names are not rejected and collapse to the agentic-browser root.
validateWorkingDirName: rejects /, backslash, .., absolute paths, and reserved names - but not an empty string or a single dot. With workingDir: \"\" (or "."), root / \"\" / root / \".\" resolves to the root directory itself, which trivially passes the beginsWith: re-check since it equals the root. A topic then receives the entire agentic-browser root as its working directory (containing topic-template, screenshots, and every other topic folder) - the opposite of the per-topic isolation this feature exists to provide.
Add a reject for empty/whitespace-only and "."/".." names (e.g. aDirName trimmed isEmpty), plus a regression test for the empty-string case.
There was a problem hiding this comment.
Fixed in 1ff5dac: validateWorkingDirName: now rejects an empty/whitespace-only name and a bare "." (via aDirName trimmed isEmpty and aDirName trimmed = '.'), in addition to the existing checks. Added testCreateTopicSignalsErrorForEmptyWorkingDirName and testCreateTopicSignalsErrorForDotWorkingDirName regression tests.
| ] | ||
|
|
||
| { #category : 'private' } | ||
| AbTopicManagerRipple >> isReservedWorkingDirName: aString [ |
There was a problem hiding this comment.
Optional - reserved-name set duplicates knowledge owned elsewhere.
#( topic-template screenshots ) is hardcoded here. These names are derived by AbTopicTemplateDirectory and AbScreenshotAttachment (their root folders under the agentic-browser dir). If a third reserved folder is later added there, this list silently goes stale and future topics could target it. Consider a single source of truth (e.g. those classes exposing their reserved folder basename) and consume it here and in the list handler.
There was a problem hiding this comment.
Addressed in 1ff5dac: added AbTopicTemplateDirectory class >> reservedFolderName and AbScreenshotAttachment class >> reservedFolderName, each returning their own literal, and initialize/screenshotDirectory now derive their path from that method instead of an inline string literal. AbTopicManagerRipple>>isReservedWorkingDirName: now checks against { AbTopicTemplateDirectory reservedFolderName. AbScreenshotAttachment reservedFolderName } instead of a hardcoded array, so a rename in either owning class can't silently drift out of sync.
| ] | ||
|
|
||
| { #category : 'private' } | ||
| AbTopicManagerRipple >> newTopicFrom: body workingDirPath: workingDirPath [ |
There was a problem hiding this comment.
Nit - newTopicFrom: reads like a pure constructor but has side effects.
It registers the topic (addTopic:) and, for non-nil workingDirPath, triggers working-directory creation/seeding. createTopicFrom:workingDirPath: would reveal the side effects better; alternatively split construction from the addTopic:/ensureExists step.
There was a problem hiding this comment.
Renamed to createTopicFrom:workingDirPath: in 1ff5dac to name the side effects (addTopic:, directory creation/seeding) honestly.
| isNewFolder: (body at: 'isNewFolder' ifAbsent: [ false ]) ]. | ||
| [ topic := self newTopicFrom: body workingDirPath: workingDirPath ] | ||
| on: Error | ||
| do: [ :e | self createTopicFailedError: e messageText ]. |
There was a problem hiding this comment.
FYI - validation errors take a different path than creation errors.
resolveWorkingDirPath: (raising the 10009/10010 RpError) runs outside the on: Error handler that wraps newTopicFrom: (which rewraps failures as 10006). That is deliberate so the specific codes propagate - good - but request-validation and creation-failure now flow through two error paths, and correctness depends on resolveWorkingDirPath: staying outside the handler. Consider a comment noting that, so a future move inside the block does not silently collapse 10009/10010 into 10006.
There was a problem hiding this comment.
Good catch - added a comment at the call site in 1ff5dac explaining that resolveWorkingDirPath:isNewFolder: must stay outside the on: Error handler so its 10009/10010 codes aren't rewrapped as 10006, so a future edit doesn't move it in without noticing.
mumez
left a comment
There was a problem hiding this comment.
Reviewing this change from first principles:
- 2 Required — (1) the create path ignores the injected
workingDirsRootDirectory, so the three create-path tests write real folders into the livedefaultAgenticBrowserRootDirectoryand never clean up (list and create also disagree about the root); (2) empty/"."workingDirnames aren't rejected and collapse to the whole agentic-browser root. - 1 Optional — reserved-name list hardcodes names owned by
AbTopicTemplateDirectory/AbScreenshotAttachment. - 2 nits/FYI —
newTopicFrom:name vs. its side effects; validation vs. creation check errors flow through two paths.
Verification story: the PR reports all SUnit tests pass, but I could not independently run the suite in this environment, and the two Required issues concern tests that pass while polluting the real filesystem (they never assert on teardown state, only on exists). So a green suite here is not strong evidence of isolation. I'd like to see the create-path tests routed through withTempWorkingDirsRoot: with a teardown assertion, and an empty-string/"." regression test.
Verdict: request changes on the two Required items (bright-side: the correctness design — workingDirPath: set before ensureExists seeding, default-path behavior preserved when workingDir is omitted, and the collision flag semantics — is sound and well-tested otherwise).
…erved names - Route /topics/create's workingDir resolution through the same overridable workingDirsRootDirectory used by /workingDirs/list (was hardcoded to AbSettings defaultAgenticBrowserRootDirectory), so tests can isolate filesystem effects and both endpoints agree on the root. - Reject empty/whitespace-only and "." workingDir names, which previously collapsed to the agentic-browser root itself and would have handed a topic the entire root (containing every other topic's folder) as its working directory. - Derive the reserved folder name list from AbTopicTemplateDirectory and AbScreenshotAttachment (each now exposes reservedFolderName) instead of a hardcoded literal array, so a future rename can't silently drift. - Rename newTopicFrom:workingDirPath: to createTopicFrom:workingDirPath: to name its side effects (addTopic:, directory creation) honestly. - Wrap the three create-path tests that exercise workingDir in withTempWorkingDirsRoot:, and add regression tests for empty/"." names. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Pushed 1ff5dac addressing both Required items from the review plus the optional and nit suggestions:
All 76 |
|
String >> trimmed comment says that its obsolete. trimBoth is better. |
trimmed is deprecated in Pharo 14 in favor of trimBoth (pharo-project/pharo#18307). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Good catch, fixed in b591d04: replaced both |
|
In AbTopicManagerRipple >> validateWorkingDirName: aDirName,
would be better for avoiding deep nesting |
…sfy: Reduces nesting by replacing the chained includesSubstring: or: checks for '..', '/', '\' with a single anySatisfy: over the literal array. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Fixed in 1008d33: flattened the three |
Adds the new /workingDirs/list request endpoint, documents the optional workingDir/isNewFolder body keys on /topics/create, and adds error codes 10009/10010 to the reference. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Pushed e901508: updated docs/web-ui-api.md with the new |
Refactor `workingDir` to `workingDirectory`, `isNewFolder` to `checkExistingDirectory`, and `/workingDirs/list` to `/workingDirectories/list` for consistency and clarity.
* Replace trimmed with trimBoth for string normalization * Persist AbTopicManager on image shutdown Register AbTopicManager for system shutdown to ensure topics are saved automatically when the image closes, even if the browser window is not explicitly closed. Added safety checks to prevent creating an empty manager instance during shutdown. * Fix layout constraints for hideThinkCheckBox in AbMessagesPresenter * Add AbTopicManager defaultLoaded: helper Introduce a helper method to set the default topic manager and announce the change, ensuring UI components correctly refresh. Update existing persistence and test logic to use this method. * Fix nil check in AbTopicManager defaultLoaded: * Lower default log level to 2 in AbLocalLogger * Prevent serialization errors from blocking image shutdown * Automate stall recovery for agent topics (#117) * Add openCurrent method to AgenticBrowser class * Reduce default topic stall threshold to 120 seconds * Automate stall recovery for agent topics - Add `stallMonitoringIntervalSeconds` to `AbSettings` - Implement background stall monitoring in `AbTopicManager` - Add `resumePrompt` to `AbTopicGoal` to nudge stalled topics - Decouple topic monitoring and teardown from UI lifecycle * Update goal stall threshold to 120 seconds * Fix AbTopicManager startup and default loading Ensure stalled topic monitoring and package watching are correctly initialized or cleaned up during system startup and when swapping the default manager. * Confirm save on window close in AbBrowserPresenter * Add step timeout handling with retry support (#118) * Add timeout handling to orchestration steps Introduce `onTimeout:` to `AbBaseOrchestrationStep` to allow custom handling of timeouts instead of raising an exception. Update step implementations to use `handleTimeout:` for consistent error reporting. * Refactor orchestration timeout handling Replace manual exception passing with `signalTimeout` to encapsulate timeout creation and error handling logic. * Add timeout handling to AbBaseOrchestration forkRunThen: * Refactor orchestration steps to support retryable timeouts Introduce a template method pattern in `AbBaseOrchestrationStep` where `run` handles `AbOrchestrationStepTimeout` exceptions by invoking an optional `onTimeout:` block. Add `retry` capability and convenience methods for configuring wait timeouts across orchestration steps. * Add AbMockOrchestrationForGroupCompletesAfterRetry test mock * Improve orchestration step retry and timeout handling - Reset `lastError` and `stepResult` during retry to ensure clean state. - Ensure `AbOrchestrationGroupParallelStep` unsubscribes from item announcers on completion or timeout using `ensure:`. - Add `resetForRetry` hooks to orchestration steps to properly clear transient state. * Add runOnTimeout: to AbBaseOrchestration * Document timeout handling in scripting DSL Add documentation for `runOnTimeout:`, `forkRunThen:onTimeout:`, and per-step `onTimeout:` handlers to prevent debugger invocation on orchestration timeouts. * Update SKILL.md to enforce forkRunThen:onTimeout: Require the use of `forkRunThen:onTimeout:` for all orchestration scripts to ensure background processes handle timeouts gracefully. Add guidance on monitoring and recovery for stalled steps. * Add resetForRerun to orchestration mock classes Ensure all items in an orchestration group implement resetForRerun by adding the method to mock classes and removing the conditional check in AbOrchestrationGroupStep. * Isolate step settings in orchestrations Ensure `waitTimeoutSeconds:` only affects the individual step by using a private copy of `AbSettings` instead of mutating the shared orchestration settings. * Add goalChanged event to Web UI API Introduce AbTopicGoalChanged announcement to propagate goal updates to connected clients. Update AbTopicGoal and AbTopic to trigger this announcement when descriptions are modified, and ensure the Web UI broadcasts the event. * Update ab-scripting-feature-dev workflow Add support for positional arguments to specify output directory and generate-only mode, and update documentation to reflect these changes. * Update documentation for ab-scripting-feature-dev skill * Update UI terminology for human-in-the-loop approvals * Add workingDir folder selection to Ripple API topic creation Add /workingDirs/list to enumerate agentic-browser subfolders (excluding reserved topic-template/screenshots) with timestamps, and extend /topics/create with optional workingDir + isNewFolder body keys so a caller can reuse an existing folder or require a brand-new one, with path-traversal and reserved-name validation. Enables the web-ui to let users pick or create a Topic's working directory (kanban issue 1789021772221). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Revert "Add workingDir folder selection to Ripple API topic creation" This reverts commit 2480335. * Add workingDir folder selection to Ripple API topic creation (#121) * Add workingDir folder selection to Ripple API topic creation Add /workingDirs/list to enumerate agentic-browser subfolders (excluding reserved topic-template/screenshots) with timestamps, and extend /topics/create with optional workingDir + isNewFolder body keys so a caller can reuse an existing folder or require a brand-new one, with path-traversal and reserved-name validation. Enables the web-ui to let users pick or create a Topic's working directory (kanban issue 1789021772221). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Address review: fix root-mismatch, reject empty/dot names, dedupe reserved names - Route /topics/create's workingDir resolution through the same overridable workingDirsRootDirectory used by /workingDirs/list (was hardcoded to AbSettings defaultAgenticBrowserRootDirectory), so tests can isolate filesystem effects and both endpoints agree on the root. - Reject empty/whitespace-only and "." workingDir names, which previously collapsed to the agentic-browser root itself and would have handed a topic the entire root (containing every other topic's folder) as its working directory. - Derive the reserved folder name list from AbTopicTemplateDirectory and AbScreenshotAttachment (each now exposes reservedFolderName) instead of a hardcoded literal array, so a future rename can't silently drift. - Rename newTopicFrom:workingDirPath: to createTopicFrom:workingDirPath: to name its side effects (addTopic:, directory creation) honestly. - Wrap the three create-path tests that exercise workingDir in withTempWorkingDirsRoot:, and add regression tests for empty/"." names. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Use trimBoth instead of deprecated String>>trimmed trimmed is deprecated in Pharo 14 in favor of trimBoth (pharo-project/pharo#18307). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Flatten path-separator checks in validateWorkingDirName: with anySatisfy: Reduces nesting by replacing the chained includesSubstring: or: checks for '..', '/', '\' with a single anySatisfy: over the literal array. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Document /workingDirs/list and /topics/create workingDir/isNewFolder Adds the new /workingDirs/list request endpoint, documents the optional workingDir/isNewFolder body keys on /topics/create, and adds error codes 10009/10010 to the reference. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Rename working directory API fields Refactor `workingDir` to `workingDirectory`, `isNewFolder` to `checkExistingDirectory`, and `/workingDirs/list` to `/workingDirectories/list` for consistency and clarity. * Recategorize error signaling methods in AbTopicManagerRipple --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Summary
/workingDirs/listendpoint toAbTopicManagerRippleand extend/topics/createwith optionalworkingDir/isNewFolderparameters.Changes
AbTopicManagerRipple: new/workingDirs/listrequest handler listing immediate subfolders of the agentic-browser root directory (excluding reservedtopic-template/screenshots), each withname,createdAt,modifiedAt.AbTopicManagerRipple>>handleCreateTopic:: accepts optionalworkingDir(relative folder name) andisNewFolder(defaultfalse) body keys, validated against path traversal, reserved names, and root-boundary escape; reuses an existing folder or creates+seeds a new one from the topic template; raisesRpErrorcode10009(folder-name collision whenisNewFolder: true) or10010(invalid/reserved name).resolveWorkingDirPath:isNewFolder:,validateWorkingDirName:,isReservedWorkingDirName:,workingDirJson:,newTopicFrom:workingDirPath:,workingDirAlreadyExistsError:,invalidWorkingDirNameError:, andworkingDirsRootDirectory/workingDirsRootDirectory:accessors.AbTopicManagerRippleTest: new tests covering working-dir listing (empty, populated, reserved-folder exclusion) and topic creation with new/existing/colliding working directories and invalid names, plus awithTempWorkingDirsRoot:test helper.docs/scripting-features/feature-ripple-api-topic-working-dir.scripting.md, the orchestration script used to generate this change.Commits Included
5ea23f4 Add workingDir folder selection to Ripple API topic creation
Files Changed
docs/scripting-features/feature-ripple-api-topic-working-dir.scripting.md
src/AgenticBrowser-WebUI-Tests/AbTopicManagerRippleTest.class.st
src/AgenticBrowser-WebUI/AbTopicManagerRipple.class.st