-
Notifications
You must be signed in to change notification settings - Fork 6
feat: live user skill catalog refresh, streaming and session-list fixes #298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
5a48004
9c1f7b8
bf38798
1403d96
350e7d9
25be5dc
c16fd4f
6559142
55d111d
59837ff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@pymodel/pythinker-code": patch | ||
| --- | ||
|
|
||
| Fix a crash when a model config entry lacks its model name. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@pymodel/pythinker-code": patch | ||
| --- | ||
|
|
||
| Add environment variable overrides for the background Bash task timeout and the print-mode background policy. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@pymodel/pythinker-code": patch | ||
| --- | ||
|
|
||
| Fix slow resume replay for sessions with many cron turns. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@pymodel/pythinker-code": patch | ||
| --- | ||
|
|
||
| Fix recent sessions missing from the session list when the sessions folder contains stray files. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@pymodel/pythinker-code": patch | ||
| --- | ||
|
|
||
| Warn in print mode when an untrusted folder skips project-level MCP servers. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@pymodel/pythinker-code": minor | ||
| --- | ||
|
|
||
| Refresh the skill catalog automatically when user-level skills are created, changed, or deleted while Pythinker Code is running. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,7 +30,9 @@ import { | |
| IBootstrapService, | ||
| IConfigService, | ||
| IEventBus, | ||
| IHostFileSystem, | ||
| ISessionIndex, | ||
| IWorkspaceInstanceManager, | ||
| ISessionManager, | ||
| ITelemetryService, | ||
| PRINT_MAX_TURNS_DEFAULT, | ||
|
|
@@ -54,8 +56,13 @@ import { | |
| type ISessionScopeHandle, | ||
| type LoopRunResult, | ||
| type PrintBackgroundMode, | ||
| type McpServerConfig, | ||
| type Scope, | ||
| } from '@pymodel/agent-core-v2'; | ||
| import { | ||
| loadMcpServersDetailed, | ||
| resolveMcpJsonPaths, | ||
| } from '@pymodel/agent-core-v2/app/mcpConfig/configLoader'; | ||
| import { createPythinkerDefaultHeaders, createPythinkerDeviceId } from '@pymodel/pythinker-code-oauth'; | ||
| import type { GoalUpdated } from '@pymodel/agent-core-v2/features/goal/goalOps'; | ||
| import type { TurnEnded } from '@pymodel/agent-core-v2/agent/loop/turnOps'; | ||
|
|
@@ -217,6 +224,13 @@ export async function runV2Print( | |
| ); | ||
| } | ||
|
|
||
| try { | ||
| const gated = await listTrustGatedMcpServers(app, workDir, homeDir); | ||
| if (gated.length > 0) stderr.write(formatTrustGatedMcpWarning(gated)); | ||
| } catch { | ||
| // Best-effort: a broken mcp.json or trust store must not fail the run. | ||
| } | ||
|
|
||
| const resolved = await resolveNativeSession(app, opts, workDir, defaultModel, stderr); | ||
| restorePermission = resolved.restorePermission; | ||
|
|
||
|
|
@@ -266,6 +280,49 @@ interface ResolvedNativeSession { | |
| readonly goalModel: string | undefined; | ||
| } | ||
|
|
||
| export interface TrustGatedMcpServer { | ||
| readonly name: string; | ||
| readonly target: string; | ||
| } | ||
|
|
||
| export async function listTrustGatedMcpServers( | ||
| app: Scope, | ||
| workDir: string, | ||
| homeDir: string, | ||
| ): Promise<readonly TrustGatedMcpServer[]> { | ||
| const workspace = await app.accessor | ||
| .get(IWorkspaceInstanceManager) | ||
| .getOrCreate({ root: workDir }); | ||
| if (await workspace.program.trust.get()) return []; | ||
| const fs = app.accessor.get(IHostFileSystem); | ||
| const [paths, loaded] = await Promise.all([ | ||
| resolveMcpJsonPaths({ fs, cwd: workDir, homeDir }), | ||
| loadMcpServersDetailed({ fs, cwd: workDir, homeDir, includeProject: true }), | ||
| ]); | ||
| const projectPaths = new Set([paths.projectRoot, paths.project]); | ||
| return Object.entries(loaded.servers) | ||
| .filter(([name]) => projectPaths.has(loaded.origins[name] ?? '')) | ||
| .map(([name, config]) => ({ name, target: describeMcpTarget(config) })) | ||
| .toSorted((a, b) => a.name.localeCompare(b.name)); | ||
| } | ||
|
|
||
| export function formatTrustGatedMcpWarning(servers: readonly TrustGatedMcpServer[]): string { | ||
| const noun = servers.length === 1 ? 'server' : 'servers'; | ||
| const list = servers.map((server) => `${server.name} (${server.target})`).join(', '); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win Escape project-controlled values before writing the warning.
Encode control characters before interpolation. Add a regression test with an ESC control character in a server name and target. 🤖 Prompt for AI Agents |
||
| return ( | ||
| `Warning: this folder is not trusted; skipped ${servers.length} project-level MCP ${noun}: ${list}.\n` + | ||
| ' Run `pythinker` here and choose "Trust this folder" to enable them.\n\n' | ||
| ); | ||
| } | ||
|
|
||
| function describeMcpTarget(config: McpServerConfig): string { | ||
| if (config.transport === 'stdio') { | ||
| const args = config.args === undefined ? '' : ` ${config.args.join(' ')}`; | ||
| return `stdio: ${config.command}${args}`; | ||
| } | ||
| return `${config.transport}: ${config.url}`; | ||
| } | ||
|
|
||
| async function resolveNativeSession( | ||
| app: Scope, | ||
| opts: CLIOptions, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Release the Node SDK behavior change.
Add
@pymodel/node-sdk: patchto this changeset.getWorkspaceTrustInfonow returns differentgatedMcpServersresults for merged project and user MCP configurations.As per coding guidelines, “Every PR that affects release artifacts (code, behavior, public API) must include a changeset.” Based on learnings, use
patchunless a breaking change is confirmed.🤖 Prompt for AI Agents
Sources: Coding guidelines, Learnings