ur-agent 1.12.2 → 1.12.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.12.3
4
+
5
+ ### Added
6
+ - **Agent feature expansion commands.** Added `ur agent-features`,
7
+ `ur agent-templates`, `ur automation`, `ur agent-task`, `ur model-doctor`,
8
+ `ur semantic-memory`, `ur claim-ledger`, and `ur browser-qa` so the agent
9
+ platform roadmap is visible and executable from both CLI and slash command
10
+ surfaces.
11
+ - **Opt-in A2A task server.** `ur a2a serve` now exposes loopback Agent Card,
12
+ health, and dry-run task endpoints from the launcher, with off-loopback binds
13
+ requiring a bearer token.
14
+ - **Project scaffolds and examples.** `ur agent-features init` creates reusable
15
+ project assets for agents, automations, GitHub workflow entrypoints, A2A,
16
+ memory, provenance, and browser QA, with `examples/agent_features.md`
17
+ documenting the workflow.
18
+
19
+ ### Fixed
20
+ - **Agent template typo safety.** `ur agent-templates install <name>` now
21
+ rejects unknown template names instead of interpreting a misspelling as
22
+ "install all templates."
23
+ - **Ollama model inspection request.** `ur model-doctor` now uses the preferred
24
+ `/api/show` request body key (`model`) when inspecting local Ollama models.
25
+
26
+ ### Verified
27
+ - Added focused Bun tests for feature scaffolds, template installation,
28
+ automations, PR dry-run generation, local memory/provenance/browser QA
29
+ commands, and the model-doctor Ollama request body.
30
+
3
31
  ## 1.12.2
4
32
 
5
33
  ### Changed
package/README.md CHANGED
@@ -104,9 +104,10 @@ ur plugin --help
104
104
  - [Usage Guide](docs/USAGE.md)
105
105
  - [Configuration](docs/CONFIGURATION.md)
106
106
  - [Agent Trend Coverage](docs/AGENT_TRENDS.md)
107
+ - [Agent Feature Expansion](docs/AGENT_FEATURES.md)
107
108
  - [Development Guide](docs/DEVELOPMENT.md)
108
109
 
109
- The `examples/` directory also contains prompt examples for coding, research, browser, image, video, MCP, memory, and agent-trend workflows.
110
+ The `examples/` directory also contains prompt examples for coding, research, browser, image, video, MCP, memory, agent-trend, and agent-feature workflows.
110
111
 
111
112
  ## License And Responsibility
112
113
 
package/bin/ur.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn } from 'node:child_process'
3
3
  import { existsSync, readFileSync } from 'node:fs'
4
+ import { createServer } from 'node:http'
4
5
  import { dirname, resolve } from 'node:path'
5
6
  import { fileURLToPath } from 'node:url'
6
7
 
@@ -38,6 +39,196 @@ const bun = process.env.BUN_BIN || process.env.BUN_EXECUTABLE || 'bun'
38
39
  const ollamaModel =
39
40
  process.env.OLLAMA_MODEL || process.env.UR_MODEL
40
41
  const userArgs = process.argv.slice(2)
42
+
43
+ function argValue(flag, fallback) {
44
+ const index = userArgs.indexOf(flag)
45
+ return index === -1 ? fallback : (userArgs[index + 1] ?? fallback)
46
+ }
47
+
48
+ function isLoopback(host) {
49
+ return host === '127.0.0.1' || host === 'localhost' || host === '::1'
50
+ }
51
+
52
+ function sendJson(res, status, body) {
53
+ res.writeHead(status, { 'content-type': 'application/json' })
54
+ res.end(JSON.stringify(body, null, 2))
55
+ }
56
+
57
+ function readJson(req) {
58
+ return new Promise(resolve => {
59
+ const chunks = []
60
+ req.on('data', chunk => chunks.push(Buffer.from(chunk)))
61
+ req.on('end', () => {
62
+ try {
63
+ resolve(JSON.parse(Buffer.concat(chunks).toString('utf8') || '{}'))
64
+ } catch {
65
+ resolve(null)
66
+ }
67
+ })
68
+ req.on('error', () => resolve(null))
69
+ })
70
+ }
71
+
72
+ function buildAgentCard(baseUrl) {
73
+ return {
74
+ protocolVersion: '0.3.0',
75
+ name: 'UR Agent',
76
+ description:
77
+ 'Local-first terminal coding agent powered through the local Ollama app, with MCP tools, custom agents, browser workflows, memory, verifier gates, and permission controls.',
78
+ url: `${baseUrl}/a2a`,
79
+ version,
80
+ documentationUrl:
81
+ 'https://github.com/Maitham16/UR-mapek/blob/master/docs/AGENT_TRENDS.md',
82
+ capabilities: {
83
+ streaming: true,
84
+ pushNotifications: false,
85
+ stateTransitionHistory: true,
86
+ },
87
+ defaultInputModes: ['text/plain', 'text/markdown', 'application/json'],
88
+ defaultOutputModes: ['text/plain', 'text/markdown', 'application/json'],
89
+ provider: {
90
+ organization: 'Maitham Al-rubaye',
91
+ url: 'https://github.com/Maitham16/UR-mapek',
92
+ },
93
+ skills: [
94
+ {
95
+ id: 'coding-agent',
96
+ name: 'Coding Agent',
97
+ description:
98
+ 'Read, edit, test, verify, and explain code inside a local workspace with permission controls.',
99
+ tags: ['coding', 'terminal', 'verification'],
100
+ examples: [
101
+ 'Fix this failing test and run the relevant checks.',
102
+ 'Review the current diff for behavioral regressions.',
103
+ ],
104
+ inputModes: ['text/plain', 'text/markdown'],
105
+ outputModes: ['text/plain', 'text/markdown'],
106
+ },
107
+ ],
108
+ }
109
+ }
110
+
111
+ function runAgentPrompt(prompt) {
112
+ const childArgs = existsSync(bundledEntrypoint)
113
+ ? [bundledEntrypoint, '-p', '--output-format', 'json', prompt]
114
+ : [
115
+ 'run',
116
+ '--preload',
117
+ preload,
118
+ '--define',
119
+ defineMacro('MACRO.VERSION', version),
120
+ '--define',
121
+ defineMacro('MACRO.BUILD_TIME', ''),
122
+ '--define',
123
+ defineMacro('MACRO.PACKAGE_URL', packageName),
124
+ '--define',
125
+ defineMacro('MACRO.NATIVE_PACKAGE_URL', undefined),
126
+ '--define',
127
+ defineMacro('MACRO.FEEDBACK_CHANNEL', issuesUrl),
128
+ '--define',
129
+ defineMacro('MACRO.ISSUES_EXPLAINER', `file an issue at ${issuesUrl}`),
130
+ '--define',
131
+ defineMacro('MACRO.VERSION_CHANGELOG', ''),
132
+ entrypoint,
133
+ '-p',
134
+ '--output-format',
135
+ 'json',
136
+ prompt,
137
+ ]
138
+
139
+ return new Promise(resolve => {
140
+ const child = spawn(bun, childArgs, {
141
+ cwd: process.cwd(),
142
+ env: {
143
+ ...process.env,
144
+ ...(ollamaModel ? { OLLAMA_MODEL: ollamaModel } : {}),
145
+ },
146
+ stdio: ['ignore', 'pipe', 'pipe'],
147
+ })
148
+ const stdout = []
149
+ const stderr = []
150
+ child.stdout.on('data', chunk => stdout.push(Buffer.from(chunk)))
151
+ child.stderr.on('data', chunk => stderr.push(Buffer.from(chunk)))
152
+ child.on('error', error => {
153
+ resolve({ code: 1, stdout: '', stderr: error.message })
154
+ })
155
+ child.on('exit', code => {
156
+ resolve({
157
+ code: code ?? 1,
158
+ stdout: Buffer.concat(stdout).toString('utf8'),
159
+ stderr: Buffer.concat(stderr).toString('utf8'),
160
+ })
161
+ })
162
+ })
163
+ }
164
+
165
+ function runA2AServer() {
166
+ const host = argValue('--host', '127.0.0.1')
167
+ const port = Number(argValue('--port', '8765'))
168
+ const token = argValue('--token')
169
+ const dryRun = userArgs.includes('--dry-run')
170
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
171
+ console.error(`Invalid --port value: ${argValue('--port')}`)
172
+ process.exit(1)
173
+ }
174
+ if (!isLoopback(host) && !token) {
175
+ console.error('Refusing to bind a2a server off-loopback without --token')
176
+ process.exit(1)
177
+ }
178
+
179
+ const baseUrl = `http://${host}:${port}`
180
+ const server = createServer(async (req, res) => {
181
+ const url = new URL(req.url ?? '/', baseUrl)
182
+ if (req.method === 'GET' && url.pathname === '/healthz') {
183
+ sendJson(res, 200, { ok: true })
184
+ return
185
+ }
186
+ if (
187
+ req.method === 'GET' &&
188
+ (url.pathname === '/.well-known/agent-card.json' ||
189
+ url.pathname === '/agent-card.json')
190
+ ) {
191
+ sendJson(res, 200, buildAgentCard(baseUrl))
192
+ return
193
+ }
194
+ if (req.method === 'POST' && url.pathname === '/a2a/tasks') {
195
+ if (token && req.headers.authorization !== `Bearer ${token}`) {
196
+ sendJson(res, 401, { error: 'unauthorized' })
197
+ return
198
+ }
199
+ const body = await readJson(req)
200
+ const prompt =
201
+ body && typeof body.prompt === 'string' ? body.prompt.trim() : ''
202
+ if (!prompt) {
203
+ sendJson(res, 400, { error: 'missing prompt' })
204
+ return
205
+ }
206
+ const command = [bun, bundledEntrypoint, '-p', '--output-format', 'json', prompt]
207
+ if (dryRun) {
208
+ sendJson(res, 200, { dryRun: true, command })
209
+ return
210
+ }
211
+ const result = await runAgentPrompt(prompt)
212
+ sendJson(res, result.code === 0 ? 200 : 500, result)
213
+ return
214
+ }
215
+ sendJson(res, 404, { error: 'not found' })
216
+ })
217
+ server.listen(port, host, () => {
218
+ const actual = server.address()
219
+ const actualPort = actual && typeof actual === 'object' ? actual.port : port
220
+ console.log(`A2A server listening on http://${host}:${actualPort}`)
221
+ })
222
+ }
223
+
224
+ if (
225
+ userArgs[0] === 'a2a' &&
226
+ userArgs[1] === 'serve' &&
227
+ !userArgs.includes('--help') &&
228
+ !userArgs.includes('-h')
229
+ ) {
230
+ runA2AServer()
231
+ } else {
41
232
  const args =
42
233
  existsSync(bundledEntrypoint)
43
234
  ? [bundledEntrypoint, ...userArgs]
@@ -92,3 +283,4 @@ child.on('exit', (code, signal) => {
92
283
 
93
284
  process.exit(code ?? 1)
94
285
  })
286
+ }