openshell-agent-runner 0.0.2__tar.gz → 0.0.3__tar.gz

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.
Files changed (43) hide show
  1. openshell_agent_runner-0.0.3/PKG-INFO +138 -0
  2. openshell_agent_runner-0.0.3/README.md +115 -0
  3. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/src/openshell_agent_runner/cli.py +46 -8
  4. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/src/openshell_agent_runner/config.py +37 -2
  5. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/src/openshell_agent_runner/harnesses/pi/resources.py +10 -2
  6. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/src/openshell_agent_runner/harnesses/pi/runtime/extensions/validate-tools.ts +3 -3
  7. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/src/openshell_agent_runner/harnesses/pi/runtime/image/exec.sh +1 -0
  8. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/src/openshell_agent_runner/openshell.py +2 -0
  9. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/src/openshell_agent_runner/profile_init.py +1 -1
  10. openshell_agent_runner-0.0.3/src/openshell_agent_runner/profiles/code-reviewer/profile.yaml +21 -0
  11. openshell_agent_runner-0.0.3/src/openshell_agent_runner/profiles/code-reviewer/prompt-repository.md +15 -0
  12. openshell_agent_runner-0.0.3/src/openshell_agent_runner/profiles/code-reviewer/schemas/review.json +159 -0
  13. openshell_agent_runner-0.0.3/src/openshell_agent_runner/profiles/code-reviewer/skills/review-code/SKILL.md +111 -0
  14. openshell_agent_runner-0.0.3/src/openshell_agent_runner/profiles/code-reviewer/skills/review-code/agents/openai.yaml +4 -0
  15. openshell_agent_runner-0.0.3/src/openshell_agent_runner/profiles/technical-writing-reviewer/models.json +19 -0
  16. openshell_agent_runner-0.0.3/src/openshell_agent_runner/profiles/technical-writing-reviewer/policy.yaml +15 -0
  17. openshell_agent_runner-0.0.3/src/openshell_agent_runner/profiles/technical-writing-reviewer/profile.yaml +21 -0
  18. openshell_agent_runner-0.0.3/src/openshell_agent_runner/profiles/technical-writing-reviewer/prompt-document.md +15 -0
  19. openshell_agent_runner-0.0.3/src/openshell_agent_runner/profiles/technical-writing-reviewer/schemas/review.json +160 -0
  20. openshell_agent_runner-0.0.3/src/openshell_agent_runner/profiles/technical-writing-reviewer/settings.json +5 -0
  21. openshell_agent_runner-0.0.3/src/openshell_agent_runner/profiles/technical-writing-reviewer/skills/review-technical-writing/SKILL.md +120 -0
  22. openshell_agent_runner-0.0.3/src/openshell_agent_runner/profiles/technical-writing-reviewer/skills/review-technical-writing/agents/openai.yaml +4 -0
  23. openshell_agent_runner-0.0.3/src/openshell_agent_runner/prompt_templates.py +48 -0
  24. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/src/openshell_agent_runner/runner.py +123 -18
  25. openshell_agent_runner-0.0.2/PKG-INFO +0 -164
  26. openshell_agent_runner-0.0.2/README.md +0 -141
  27. openshell_agent_runner-0.0.2/src/openshell_agent_runner/profiles/reviewer/profile.yaml +0 -12
  28. openshell_agent_runner-0.0.2/src/openshell_agent_runner/profiles/reviewer/prompt.md +0 -5
  29. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/.gitignore +0 -0
  30. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/LICENSE +0 -0
  31. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/pyproject.toml +0 -0
  32. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/src/openshell_agent_runner/__init__.py +0 -0
  33. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/src/openshell_agent_runner/artifacts.py +0 -0
  34. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/src/openshell_agent_runner/errors.py +0 -0
  35. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/src/openshell_agent_runner/harnesses/__init__.py +0 -0
  36. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/src/openshell_agent_runner/harnesses/pi/__init__.py +0 -0
  37. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/src/openshell_agent_runner/harnesses/pi/runtime/extensions/submit-result.ts +0 -0
  38. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/src/openshell_agent_runner/harnesses/pi/runtime/image/Dockerfile +0 -0
  39. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/src/openshell_agent_runner/harnesses/resources.py +0 -0
  40. {openshell_agent_runner-0.0.2 → openshell_agent_runner-0.0.3}/src/openshell_agent_runner/profiles/__init__.py +0 -0
  41. {openshell_agent_runner-0.0.2/src/openshell_agent_runner/profiles/reviewer → openshell_agent_runner-0.0.3/src/openshell_agent_runner/profiles/code-reviewer}/models.json +0 -0
  42. {openshell_agent_runner-0.0.2/src/openshell_agent_runner/profiles/reviewer → openshell_agent_runner-0.0.3/src/openshell_agent_runner/profiles/code-reviewer}/policy.yaml +0 -0
  43. {openshell_agent_runner-0.0.2/src/openshell_agent_runner/profiles/reviewer → openshell_agent_runner-0.0.3/src/openshell_agent_runner/profiles/code-reviewer}/settings.json +0 -0
@@ -0,0 +1,138 @@
1
+ Metadata-Version: 2.5
2
+ Name: openshell-agent-runner
3
+ Version: 0.0.3
4
+ Summary: Launch ephemeral agents for single tasks in OpenShell sandboxes.
5
+ Project-URL: Repository, https://github.com/NVIDIA/OpenShell-Research
6
+ Project-URL: Documentation, https://nvidia.github.io/OpenShell-Research/documentation/openshell-agent-runner/
7
+ Author: NVIDIA CORPORATION & AFFILIATES
8
+ License-Expression: Apache-2.0
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: Apache Software License
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Topic :: Software Development
17
+ Requires-Python: >=3.12
18
+ Requires-Dist: jsonschema<5,>=4.25
19
+ Requires-Dist: pydantic<3,>=2.11
20
+ Requires-Dist: pyyaml<7,>=6
21
+ Requires-Dist: typer<1,>=0.16
22
+ Description-Content-Type: text/markdown
23
+
24
+ # OpenShell Agent Runner
25
+
26
+ OpenShell Agent Runner (OAR) runs an agent task in an isolated OpenShell sandbox
27
+ and saves the result to a file. Use it to review code, review technical writing,
28
+ or run your own tasks from a terminal or CI job.
29
+
30
+ ```text
31
+ Profile + input → OAR → Agent in a temporary sandbox → Result file
32
+ sandbox removed when the run ends
33
+ ```
34
+
35
+ A **profile** packages prompts, reusable skills, and settings. A **task** is one
36
+ job in that profile; it selects the prompt and skills to use. OAR includes two
37
+ ready-to-use reviewers:
38
+
39
+ | Profile | Task | Input |
40
+ | --- | --- | --- |
41
+ | `code-reviewer` | `review-repository` | A local project directory |
42
+ | `technical-writing-reviewer` | `review-document` | A text file, such as Markdown or `.txt` |
43
+
44
+ ## Get started
45
+
46
+ You need [uv](https://docs.astral.sh/uv/getting-started/installation/) and
47
+ OpenShell 0.0.111 or newer, with a running gateway and configured inference.
48
+ If OpenShell is not ready, follow its
49
+ [quickstart](https://docs.nvidia.com/openshell/latest/get-started/quickstart).
50
+ OAR uses that setup to create sandboxes and reach your model.
51
+
52
+ **1. Install OAR and check your connection.**
53
+
54
+ ```bash
55
+ uv tool install openshell-agent-runner
56
+ oar doctor
57
+ ```
58
+
59
+ Alternatively, use `uvx --from openshell-agent-runner oar` in place of `oar`.
60
+
61
+ These commands use your selected OpenShell gateway and its `default` workspace.
62
+ For another target, add `--gateway NAME --workspace NAME` to `doctor` and `run`.
63
+
64
+ **2. Create your profiles.** Replace `YOUR_MODEL_ID` with the model ID shown in
65
+ the inference output from `oar doctor`.
66
+
67
+ ```bash
68
+ oar init ./profiles --model YOUR_MODEL_ID
69
+ ```
70
+
71
+ This creates editable copies of both reviewers. For a model without reasoning
72
+ support, add `--thinking off`.
73
+
74
+ **3. Review a document.** Replace `./README.md` with an existing text file.
75
+
76
+ ```bash
77
+ oar run ./profiles/technical-writing-reviewer \
78
+ --task review-document \
79
+ --input ./README.md \
80
+ --output ./review.json
81
+ ```
82
+
83
+ Open `review.json` for the verdict, a score from 0 to 100, and specific findings.
84
+ OAR saves the validated result and removes the sandbox. Add `--dry-run` to
85
+ preview the operation without launching an agent.
86
+
87
+ ## Review code
88
+
89
+ Pass a local project directory. Optional `focus` and `context` values help the
90
+ reviewer understand what matters for this run:
91
+
92
+ ```bash
93
+ oar run ./profiles/code-reviewer \
94
+ --task review-repository \
95
+ --input ./my-project \
96
+ --prompt-var focus="src/auth and tests/auth" \
97
+ --prompt-var context="A small internal tool; keep recommendations proportionate." \
98
+ --output ./code-review.json
99
+ ```
100
+
101
+ The agent works on an uploaded copy. Changes in the sandbox stay there; only the
102
+ result is downloaded. A `focus` value guides attention but does not limit which
103
+ files are uploaded.
104
+
105
+ ## Learn more
106
+
107
+ - [Get started](https://nvidia.github.io/OpenShell-Research/documentation/openshell-agent-runner/): setup and your first review.
108
+ - [Run reviews](https://nvidia.github.io/OpenShell-Research/documentation/openshell-agent-runner/reviews/): inputs, focus, context, and scores.
109
+ - [Customize profiles](https://nvidia.github.io/OpenShell-Research/documentation/openshell-agent-runner/profiles/): prompts, variables, skills, and output formats.
110
+ - [Command reference](https://nvidia.github.io/OpenShell-Research/documentation/openshell-agent-runner/reference/): options, CI behavior, and troubleshooting.
111
+
112
+ For help with a specific task:
113
+
114
+ ```bash
115
+ oar run ./profiles/code-reviewer --task review-repository --help
116
+ ```
117
+
118
+ ## Develop OAR
119
+
120
+ From `projects/openshell-agent-runner`, run:
121
+
122
+ ```bash
123
+ make check
124
+ make build
125
+ ```
126
+
127
+ `make check` includes CLI workflows against a simulated OpenShell. With Docker,
128
+ run `make test-runtime` to exercise the real Pi harness without external inference.
129
+ For runtime changes, one live CI task checks input transfer, prompt variables,
130
+ structured output, and sandbox cleanup. It does not grade reviewer opinions.
131
+
132
+ Use `uv run --frozen oar` in this directory to run the checked-out code instead
133
+ of the installed release. Run a focused test with
134
+ `make test PYTEST_ARGS="tests/test_config.py"`. See
135
+ [RELEASING.md](https://github.com/NVIDIA/OpenShell-Research/blob/main/projects/openshell-agent-runner/RELEASING.md)
136
+ for publishing, and the
137
+ [repository CI guide](https://github.com/NVIDIA/OpenShell-Research/blob/main/docs/development/ci.md)
138
+ for automated project reviews and the live OAR smoke test.
@@ -0,0 +1,115 @@
1
+ # OpenShell Agent Runner
2
+
3
+ OpenShell Agent Runner (OAR) runs an agent task in an isolated OpenShell sandbox
4
+ and saves the result to a file. Use it to review code, review technical writing,
5
+ or run your own tasks from a terminal or CI job.
6
+
7
+ ```text
8
+ Profile + input → OAR → Agent in a temporary sandbox → Result file
9
+ sandbox removed when the run ends
10
+ ```
11
+
12
+ A **profile** packages prompts, reusable skills, and settings. A **task** is one
13
+ job in that profile; it selects the prompt and skills to use. OAR includes two
14
+ ready-to-use reviewers:
15
+
16
+ | Profile | Task | Input |
17
+ | --- | --- | --- |
18
+ | `code-reviewer` | `review-repository` | A local project directory |
19
+ | `technical-writing-reviewer` | `review-document` | A text file, such as Markdown or `.txt` |
20
+
21
+ ## Get started
22
+
23
+ You need [uv](https://docs.astral.sh/uv/getting-started/installation/) and
24
+ OpenShell 0.0.111 or newer, with a running gateway and configured inference.
25
+ If OpenShell is not ready, follow its
26
+ [quickstart](https://docs.nvidia.com/openshell/latest/get-started/quickstart).
27
+ OAR uses that setup to create sandboxes and reach your model.
28
+
29
+ **1. Install OAR and check your connection.**
30
+
31
+ ```bash
32
+ uv tool install openshell-agent-runner
33
+ oar doctor
34
+ ```
35
+
36
+ Alternatively, use `uvx --from openshell-agent-runner oar` in place of `oar`.
37
+
38
+ These commands use your selected OpenShell gateway and its `default` workspace.
39
+ For another target, add `--gateway NAME --workspace NAME` to `doctor` and `run`.
40
+
41
+ **2. Create your profiles.** Replace `YOUR_MODEL_ID` with the model ID shown in
42
+ the inference output from `oar doctor`.
43
+
44
+ ```bash
45
+ oar init ./profiles --model YOUR_MODEL_ID
46
+ ```
47
+
48
+ This creates editable copies of both reviewers. For a model without reasoning
49
+ support, add `--thinking off`.
50
+
51
+ **3. Review a document.** Replace `./README.md` with an existing text file.
52
+
53
+ ```bash
54
+ oar run ./profiles/technical-writing-reviewer \
55
+ --task review-document \
56
+ --input ./README.md \
57
+ --output ./review.json
58
+ ```
59
+
60
+ Open `review.json` for the verdict, a score from 0 to 100, and specific findings.
61
+ OAR saves the validated result and removes the sandbox. Add `--dry-run` to
62
+ preview the operation without launching an agent.
63
+
64
+ ## Review code
65
+
66
+ Pass a local project directory. Optional `focus` and `context` values help the
67
+ reviewer understand what matters for this run:
68
+
69
+ ```bash
70
+ oar run ./profiles/code-reviewer \
71
+ --task review-repository \
72
+ --input ./my-project \
73
+ --prompt-var focus="src/auth and tests/auth" \
74
+ --prompt-var context="A small internal tool; keep recommendations proportionate." \
75
+ --output ./code-review.json
76
+ ```
77
+
78
+ The agent works on an uploaded copy. Changes in the sandbox stay there; only the
79
+ result is downloaded. A `focus` value guides attention but does not limit which
80
+ files are uploaded.
81
+
82
+ ## Learn more
83
+
84
+ - [Get started](https://nvidia.github.io/OpenShell-Research/documentation/openshell-agent-runner/): setup and your first review.
85
+ - [Run reviews](https://nvidia.github.io/OpenShell-Research/documentation/openshell-agent-runner/reviews/): inputs, focus, context, and scores.
86
+ - [Customize profiles](https://nvidia.github.io/OpenShell-Research/documentation/openshell-agent-runner/profiles/): prompts, variables, skills, and output formats.
87
+ - [Command reference](https://nvidia.github.io/OpenShell-Research/documentation/openshell-agent-runner/reference/): options, CI behavior, and troubleshooting.
88
+
89
+ For help with a specific task:
90
+
91
+ ```bash
92
+ oar run ./profiles/code-reviewer --task review-repository --help
93
+ ```
94
+
95
+ ## Develop OAR
96
+
97
+ From `projects/openshell-agent-runner`, run:
98
+
99
+ ```bash
100
+ make check
101
+ make build
102
+ ```
103
+
104
+ `make check` includes CLI workflows against a simulated OpenShell. With Docker,
105
+ run `make test-runtime` to exercise the real Pi harness without external inference.
106
+ For runtime changes, one live CI task checks input transfer, prompt variables,
107
+ structured output, and sandbox cleanup. It does not grade reviewer opinions.
108
+
109
+ Use `uv run --frozen oar` in this directory to run the checked-out code instead
110
+ of the installed release. Run a focused test with
111
+ `make test PYTEST_ARGS="tests/test_config.py"`. See
112
+ [RELEASING.md](https://github.com/NVIDIA/OpenShell-Research/blob/main/projects/openshell-agent-runner/RELEASING.md)
113
+ for publishing, and the
114
+ [repository CI guide](https://github.com/NVIDIA/OpenShell-Research/blob/main/docs/development/ci.md)
115
+ for automated project reviews and the live OAR smoke test.
@@ -120,9 +120,16 @@ def run(
120
120
  output: Annotated[
121
121
  Path, typer.Option("--output", help="Host path for the agent result.")
122
122
  ],
123
- input_document: Annotated[
123
+ input_path: Annotated[
124
124
  Path | None,
125
- typer.Option("--input", help="Host document required by document tasks."),
125
+ typer.Option("--input", help="Host input required by the selected task."),
126
+ ] = None,
127
+ prompt_variable: Annotated[
128
+ list[str] | None,
129
+ typer.Option(
130
+ "--prompt-var",
131
+ help="Non-secret NAME=VALUE prompt variable. Repeat for several.",
132
+ ),
126
133
  ] = None,
127
134
  upload: Annotated[
128
135
  list[str] | None,
@@ -159,7 +166,8 @@ def run(
159
166
  profile_directory=profile,
160
167
  task_id=task,
161
168
  output=output,
162
- input_document=input_document,
169
+ input_path=input_path,
170
+ prompt_variables=prompt_variable or (),
163
171
  uploads=upload or (),
164
172
  environments=environment or (),
165
173
  gateway=gateway,
@@ -185,12 +193,17 @@ def doctor(
185
193
  str, typer.Option("--workspace", help="OpenShell workspace name.")
186
194
  ] = "default",
187
195
  ) -> None:
188
- """Check OpenShell readiness without changing its state."""
196
+ """Inspect OpenShell connection and configuration without changing its state."""
189
197
  try:
190
198
  checks = run_doctor(NativeTarget(gateway=gateway, workspace=workspace))
191
199
  except OarError as error:
192
200
  _fail(error)
193
201
  typer.echo("\n\n".join(result for _, result in checks))
202
+ typer.echo(
203
+ "\nThese are configuration checks, not an inference request. "
204
+ "Before running a task, confirm that an inference provider and model "
205
+ "are listed above."
206
+ )
194
207
 
195
208
 
196
209
  def _fail(error: OarError) -> NoReturn:
@@ -227,8 +240,10 @@ def _render_task_help(
227
240
  _help_command(f" oar run {shlex.quote(str(profile_directory))} \\"),
228
241
  _help_command(f" --task {shlex.quote(task_id)} \\"),
229
242
  ]
230
- if task.required_input == "document":
231
- usage_lines.append(_help_command(" --input DOCUMENT \\"))
243
+ if task.required_input is not None:
244
+ usage_lines.append(
245
+ _help_command(f" --input {task.required_input.upper()} \\")
246
+ )
232
247
  usage_lines.append(_help_command(" --output OUTPUT"))
233
248
 
234
249
  upload_lines = [_help_heading("Additional configured uploads:")]
@@ -244,6 +259,22 @@ def _render_task_help(
244
259
  environment_lines.append(" None. Add values with --env KEY=VALUE.")
245
260
 
246
261
  input_lines = _required_input_help(task.required_input)
262
+ prompt_variable_lines = [_help_heading("Prompt variables:")]
263
+ if task.prompt_variables:
264
+ for name, variable in task.prompt_variables.items():
265
+ requirement = (
266
+ f"Default: {variable.default}"
267
+ if variable.default is not None
268
+ else "Required."
269
+ )
270
+ prompt_variable_lines.extend(
271
+ [
272
+ _help_command(f" --prompt-var {name}=VALUE"),
273
+ f" {variable.description} {requirement}",
274
+ ]
275
+ )
276
+ else:
277
+ prompt_variable_lines.append(" None.")
247
278
 
248
279
  output_description = (
249
280
  f"JSON validated against {task.output_schema}."
@@ -260,6 +291,8 @@ def _render_task_help(
260
291
  "",
261
292
  *input_lines,
262
293
  "",
294
+ *prompt_variable_lines,
295
+ "",
263
296
  *upload_lines,
264
297
  "",
265
298
  *environment_lines,
@@ -274,10 +307,15 @@ def _render_task_help(
274
307
  def _required_input_help(required_input: str | None) -> list[str]:
275
308
  if required_input is None:
276
309
  return [_help_heading("Required input:"), " None."]
310
+ description = (
311
+ "Host document to review."
312
+ if required_input == "document"
313
+ else "Host code repository to review."
314
+ )
277
315
  return [
278
316
  _help_heading("Required argument:"),
279
- _help_command(" --input DOCUMENT"),
280
- " Host document to review.",
317
+ _help_command(f" --input {required_input.upper()}"),
318
+ f" {description}",
281
319
  ]
282
320
 
283
321
 
@@ -23,6 +23,11 @@ from pydantic import (
23
23
  )
24
24
 
25
25
  from openshell_agent_runner.errors import ConfigurationError
26
+ from openshell_agent_runner.prompt_templates import (
27
+ BUILTIN_PROMPT_VARIABLES,
28
+ PROMPT_VARIABLE_NAME_PATTERN,
29
+ validate_prompt_template,
30
+ )
26
31
 
27
32
  IDENTIFIER_PATTERN = r"^[a-z][a-z0-9-]{0,62}$"
28
33
  RESOURCE_IDENTIFIER_PATTERN = r"^[a-z][a-z0-9_-]{0,62}$"
@@ -62,6 +67,7 @@ class SandboxConfig(StrictModel):
62
67
 
63
68
 
64
69
  ToolName = Annotated[str, Field(pattern=RESOURCE_IDENTIFIER_PATTERN)]
70
+ PromptVariableName = Annotated[str, Field(pattern=PROMPT_VARIABLE_NAME_PATTERN)]
65
71
 
66
72
 
67
73
  class ExtensionConfig(StrictModel):
@@ -76,10 +82,18 @@ class ExtensionConfig(StrictModel):
76
82
  return values
77
83
 
78
84
 
85
+ class PromptVariableConfig(StrictModel):
86
+ description: str = Field(min_length=1, max_length=1000)
87
+ default: str | None = Field(default=None, min_length=1)
88
+
89
+
79
90
  class TaskConfig(StrictModel):
80
91
  description: str | None = Field(default=None, min_length=1, max_length=1000)
81
- required_input: Literal["document"] | None = None
92
+ required_input: Literal["document", "repository"] | None = None
82
93
  prompt: Path
94
+ prompt_variables: dict[PromptVariableName, PromptVariableConfig] = Field(
95
+ default_factory=dict
96
+ )
83
97
  output_schema: Path | None = None
84
98
  tools: list[ToolName] = Field(default_factory=list)
85
99
  skills: list[Path] = Field(default_factory=list)
@@ -125,6 +139,11 @@ class TaskConfig(StrictModel):
125
139
  f"{sorted(BUILTIN_PI_TOOLS)}; declare each custom tool under a "
126
140
  "referenced extension"
127
141
  )
142
+ if self.skills and "read" not in self.tools:
143
+ raise ValueError(
144
+ "tasks with skills must include 'read' in tools so Pi can "
145
+ "discover and load the skills"
146
+ )
128
147
  return self
129
148
 
130
149
 
@@ -345,7 +364,23 @@ def _validate_profile_resources(resolved: ResolvedProfile) -> None:
345
364
  directory = resolved.profile_dir
346
365
  _inside(directory, directory / resolved.profile.sandbox.policy, "sandbox policy")
347
366
  for task_id, task in resolved.profile.tasks.items():
348
- _inside(directory, directory / task.prompt, f"prompt for task {task_id}")
367
+ prompt = _inside(
368
+ directory, directory / task.prompt, f"prompt for task {task_id}"
369
+ )
370
+ available_builtins = (
371
+ BUILTIN_PROMPT_VARIABLES if task.required_input is not None else frozenset()
372
+ )
373
+ try:
374
+ template = prompt.read_text(encoding="utf-8")
375
+ validate_prompt_template(
376
+ template,
377
+ task.prompt_variables.keys(),
378
+ available_builtins,
379
+ )
380
+ except (OSError, UnicodeError, ValueError) as error:
381
+ raise ConfigurationError(
382
+ f"invalid prompt template for task {task_id}: {error}"
383
+ ) from error
349
384
  if task.output_schema is not None:
350
385
  schema = _inside(
351
386
  directory,
@@ -6,6 +6,7 @@
6
6
  import json
7
7
  import shutil
8
8
  import tempfile
9
+ from collections.abc import Mapping
9
10
  from importlib.resources import files
10
11
  from pathlib import Path
11
12
 
@@ -15,6 +16,7 @@ from openshell_agent_runner.config import (
15
16
  ResolvedProfile,
16
17
  )
17
18
  from openshell_agent_runner.harnesses.resources import PreparedResources
19
+ from openshell_agent_runner.prompt_templates import render_prompt_template
18
20
 
19
21
  SANDBOX_RUNTIME_ROOT = "/sandbox/oar-runtime"
20
22
 
@@ -23,13 +25,19 @@ def image_directory() -> Path:
23
25
  return Path(str(files("openshell_agent_runner.harnesses.pi") / "runtime" / "image"))
24
26
 
25
27
 
26
- def prepare_resources(resolved: ResolvedProfile, task_id: str) -> PreparedResources:
28
+ def prepare_resources(
29
+ resolved: ResolvedProfile,
30
+ task_id: str,
31
+ prompt_variables: Mapping[str, str] | None = None,
32
+ ) -> PreparedResources:
27
33
  temporary = tempfile.TemporaryDirectory(prefix="oar-pi-")
28
34
  runtime = Path(temporary.name) / "runtime"
29
35
  (runtime / "skills").mkdir(parents=True, exist_ok=True)
30
36
  (runtime / "extensions").mkdir(parents=True, exist_ok=True)
31
37
  task = resolved.profile.tasks[task_id]
32
- shutil.copy2(resolved.profile_dir / task.prompt, runtime / "prompt.md")
38
+ template = (resolved.profile_dir / task.prompt).read_text(encoding="utf-8")
39
+ rendered_prompt = render_prompt_template(template, prompt_variables or {})
40
+ (runtime / "prompt.md").write_text(rendered_prompt, encoding="utf-8")
33
41
  shutil.copy2(resolved.profile_dir / MODELS_FILENAME, runtime / MODELS_FILENAME)
34
42
  shutil.copy2(resolved.profile_dir / SETTINGS_FILENAME, runtime / SETTINGS_FILENAME)
35
43
  arguments = [
@@ -19,10 +19,10 @@ export function findMissingTools(
19
19
  }
20
20
 
21
21
  export default function (pi: ExtensionAPI) {
22
- pi.on("before_agent_start", (_event, context) => {
23
- const availableTools = context.getAllTools();
22
+ pi.on("before_agent_start", () => {
23
+ const availableTools = pi.getAllTools();
24
24
  const missingTools = findMissingTools(requestedTools, availableTools);
25
- const activeTools = context.getActiveTools();
25
+ const activeTools = pi.getActiveTools();
26
26
  const activeNames = new Set(activeTools);
27
27
  const inactiveTools = requestedTools.filter((name) => !activeNames.has(name));
28
28
  const unavailableTools = [...new Set([...missingTools, ...inactiveTools])];
@@ -25,6 +25,7 @@ for required in "$payload/prompt.md" "$payload/models.json" "$payload/settings.j
25
25
  exit 2
26
26
  fi
27
27
  done
28
+ ln -s /usr/local/lib/node_modules "$payload/node_modules"
28
29
 
29
30
  pi_home=/sandbox/pi-home
30
31
  mkdir -p "$pi_home/.pi/agent" /sandbox/artifacts /sandbox/tmp
@@ -131,6 +131,8 @@ def run(
131
131
  command,
132
132
  check=True,
133
133
  text=True,
134
+ # Native commands are noninteractive; do not drain a caller's task list.
135
+ stdin=subprocess.DEVNULL,
134
136
  capture_output=capture,
135
137
  timeout=timeout,
136
138
  preexec_fn=(
@@ -17,7 +17,7 @@ from pathlib import Path
17
17
  from openshell_agent_runner.config import MODEL_IDENTIFIER_PATTERN, load_profile
18
18
  from openshell_agent_runner.errors import ConfigurationError
19
19
 
20
- PACKAGED_PROFILES = ("reviewer",)
20
+ PACKAGED_PROFILES = ("code-reviewer", "technical-writing-reviewer")
21
21
 
22
22
 
23
23
  class ThinkingLevel(StrEnum):
@@ -0,0 +1,21 @@
1
+ id: code-reviewer
2
+ description: Review an input code repository for concrete engineering issues.
3
+
4
+ sandbox:
5
+ policy: policy.yaml
6
+
7
+ tasks:
8
+ review-repository:
9
+ description: Review an input code repository and return a structured result.
10
+ required_input: repository
11
+ prompt: prompt-repository.md
12
+ prompt_variables:
13
+ focus:
14
+ description: Files, directories, behavior, or risks that deserve special attention.
15
+ default: Review the complete repository.
16
+ context:
17
+ description: Intent, constraints, non-goals, or maturity that should calibrate the review.
18
+ default: No additional context was provided.
19
+ output_schema: schemas/review.json
20
+ tools: [read, grep, find, ls, bash]
21
+ skills: [skills/review-code]
@@ -0,0 +1,15 @@
1
+ # Review the input code repository
2
+
3
+ Work as a code review agent. Load and follow the `review-code` skill.
4
+
5
+ Review the repository at `{{ oar.input_path }}`, originally provided as
6
+ `{{ oar.input_name }}`.
7
+
8
+ Review focus: {{ focus }}
9
+
10
+ Additional context: {{ context }}
11
+
12
+ Treat the focus as a priority, not permission to ignore directly related code.
13
+ Review the repository as it exists; do not assume it represents a pull request
14
+ or has useful Git history. Do not edit source files. Finish only by submitting a
15
+ result that satisfies the configured output schema.