agentic-runner 2.6.1__tar.gz → 2.7.0__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.
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/PKG-INFO +1 -1
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/__init__.py +1 -1
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/activities.py +106 -3
- agentic_runner-2.7.0/src/agentic_runner/workers/skills.py +164 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/.gitignore +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/LICENSE +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/README.md +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/pyproject.toml +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/attempts.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/callback.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/child_watcher.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/cli.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/config.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/credentials.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/device_login_activities.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/egress.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/heartbeat_link.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/hooks.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/host_store.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/integrations/__init__.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/integrations/git/__init__.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/integrations/git/contracts.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/integrations/git/evidence.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/integrations/git/fake_workspace.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/integrations/git/workspace.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/integrations/github/__init__.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/integrations/github/auth.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/integrations/github/fake_client.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/integrations/github/gh_client.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/lifecycle.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/llm_proxy.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/mcp.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/message_store.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/py.typed +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/recipient_key_secret.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/registration.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/runtime/__init__.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/runtime/verifier_command.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/sealed_box.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/service.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/tiny_http.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/triage_activities.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/user_sources.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/workers/__init__.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/workers/_runtime_support.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/workers/agent_runtime.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/workers/claude_runtime.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/workers/codex_runtime.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/workers/command_policy.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/workers/contract_device_login.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/workers/contract_isolation.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/workers/fastapi_client.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/workers/harness_usage.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/workers/mcp_config.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/workers/settings.py +0 -0
- {agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/workstation.py +0 -0
|
@@ -129,6 +129,14 @@ from agentic_runner.workers.fastapi_client import (
|
|
|
129
129
|
)
|
|
130
130
|
from agentic_runner.workers.harness_usage import extract_claude_result, extract_codex_turn_usage
|
|
131
131
|
from agentic_runner.workers.mcp_config import McpServerEntry
|
|
132
|
+
from agentic_runner.workers.skills import (
|
|
133
|
+
SkillDelivery,
|
|
134
|
+
delivery_for,
|
|
135
|
+
first_digest_mismatch,
|
|
136
|
+
remove_skills,
|
|
137
|
+
skills_preamble,
|
|
138
|
+
write_skills,
|
|
139
|
+
)
|
|
132
140
|
from agentic_runner_contracts.activity_io import (
|
|
133
141
|
LEARNER_CHILDREN_BUDGET_BYTES,
|
|
134
142
|
LEARNING_FAILED,
|
|
@@ -238,6 +246,7 @@ from agentic_runner_contracts.routing import (
|
|
|
238
246
|
from agentic_runner_contracts.runtime_context import (
|
|
239
247
|
EGRESS_ALLOW_LIST_KEY,
|
|
240
248
|
McpServerSpec,
|
|
249
|
+
SkillVersionSpec,
|
|
241
250
|
WorkerRuntimeContext,
|
|
242
251
|
WorkerRuntimeContextResolver,
|
|
243
252
|
work_branch_name,
|
|
@@ -491,6 +500,8 @@ class _RuntimeContextState:
|
|
|
491
500
|
experience: str = ""
|
|
492
501
|
experience_lesson_ids: tuple[str, ...] = ()
|
|
493
502
|
experience_truncated: bool = False
|
|
503
|
+
# Console-v2 issue 28: the Skill versions attached to the Agent.
|
|
504
|
+
skills: tuple[SkillVersionSpec, ...] = ()
|
|
494
505
|
workspace_path: Path | None = None
|
|
495
506
|
branch_head_sha: str | None = None
|
|
496
507
|
# The Autonomy Policy's tier source (PRD issue 14) -- see WorkerRuntimeContext for
|
|
@@ -533,6 +544,9 @@ class _DirectiveAttempt:
|
|
|
533
544
|
# PRD issue 58: what config assembly wrote for the CLI, and where it may reach.
|
|
534
545
|
mcp_servers: tuple[McpServerEntry, ...] | None = None
|
|
535
546
|
egress_allow_list: tuple[str, ...] = ()
|
|
547
|
+
# Console-v2 issue 28: the attached Skills, for a runtime with no skills directory to
|
|
548
|
+
# write them into. Prefixed to the Directive prompt; empty when there is none.
|
|
549
|
+
skills_preamble: str = ""
|
|
536
550
|
|
|
537
551
|
async def run_environment_hook(self) -> None:
|
|
538
552
|
self.extra_env.update(await self.hooks.environment())
|
|
@@ -1325,6 +1339,7 @@ _ROUTING_EVIDENCE_SOURCE = "runner.routing"
|
|
|
1325
1339
|
PRIOR_ATTEMPT_ALIVE_EVIDENCE_SOURCE = "runner.prior_attempt_alive"
|
|
1326
1340
|
DIRECTIVE_TOKEN_EVIDENCE_SOURCE = "runner.directive_token"
|
|
1327
1341
|
AGENT_RUNTIME_EVIDENCE_SOURCE = "runner.agent_runtime"
|
|
1342
|
+
SKILLS_EVIDENCE_SOURCE = "runner.skills"
|
|
1328
1343
|
|
|
1329
1344
|
|
|
1330
1345
|
class _EvidenceWriter(Protocol):
|
|
@@ -1793,7 +1808,8 @@ class RunnerRalphActivities:
|
|
|
1793
1808
|
extra_env=attempt.env,
|
|
1794
1809
|
mcp_servers=attempt.mcp_servers,
|
|
1795
1810
|
egress_allow_list=attempt.egress_allow_list,
|
|
1796
|
-
prompt=
|
|
1811
|
+
prompt=attempt.skills_preamble
|
|
1812
|
+
+ _directive_prompt(
|
|
1797
1813
|
completion_criteria=runtime_state.completion_criteria,
|
|
1798
1814
|
pr_body=request.pr_body,
|
|
1799
1815
|
persona_preamble=_persona_prompt_preamble(
|
|
@@ -2258,6 +2274,7 @@ class RunnerRalphActivities:
|
|
|
2258
2274
|
work_record_id, source=source, payload=dict(payload)
|
|
2259
2275
|
)
|
|
2260
2276
|
|
|
2277
|
+
await self._refuse_skill_digest_mismatch(work_record_id, state)
|
|
2261
2278
|
credentials = await self._resolve_credentials(work_record_id, state)
|
|
2262
2279
|
if credentials is not None:
|
|
2263
2280
|
# Names only, at the moment they resolved (22 A1's Evidence): a support read
|
|
@@ -2321,6 +2338,13 @@ class RunnerRalphActivities:
|
|
|
2321
2338
|
credentials=credentials,
|
|
2322
2339
|
env=callback_env,
|
|
2323
2340
|
)
|
|
2341
|
+
delivered_preamble = await self._deliver_skills(
|
|
2342
|
+
stack,
|
|
2343
|
+
work_record_id=work_record_id,
|
|
2344
|
+
state=state,
|
|
2345
|
+
sandbox=sandbox,
|
|
2346
|
+
directive_number=directive_number,
|
|
2347
|
+
)
|
|
2324
2348
|
session = await stack.enter_async_context(
|
|
2325
2349
|
DirectiveHookSession(
|
|
2326
2350
|
hooks=self._hooks,
|
|
@@ -2342,8 +2366,85 @@ class RunnerRalphActivities:
|
|
|
2342
2366
|
credentials=credentials,
|
|
2343
2367
|
mcp_servers=mcp_servers,
|
|
2344
2368
|
egress_allow_list=egress_allow_list,
|
|
2369
|
+
skills_preamble=delivered_preamble,
|
|
2345
2370
|
)
|
|
2346
2371
|
|
|
2372
|
+
async def _refuse_skill_digest_mismatch(
|
|
2373
|
+
self, work_record_id: str, state: _RuntimeContextState
|
|
2374
|
+
) -> None:
|
|
2375
|
+
"""A Skill whose body is not the version that was published fails the Directive
|
|
2376
|
+
non-retryably (console-v2 issue 28): a retry would fetch the same body, and
|
|
2377
|
+
running on it would hand the Agent prompt material no person reviewed."""
|
|
2378
|
+
|
|
2379
|
+
mismatch = first_digest_mismatch(state.skills)
|
|
2380
|
+
if mismatch is None:
|
|
2381
|
+
return
|
|
2382
|
+
await _raise_recorded(
|
|
2383
|
+
ApplicationError(
|
|
2384
|
+
f"Skill {mismatch.slug} version {mismatch.version} does not match its sha256",
|
|
2385
|
+
non_retryable=True,
|
|
2386
|
+
),
|
|
2387
|
+
self._fastapi_client,
|
|
2388
|
+
work_record_id,
|
|
2389
|
+
source=SKILLS_EVIDENCE_SOURCE,
|
|
2390
|
+
actor=self._actor(),
|
|
2391
|
+
payload={
|
|
2392
|
+
"event": "skills.digest_mismatch",
|
|
2393
|
+
"agent_id": state.agent_id,
|
|
2394
|
+
"slug": mismatch.slug,
|
|
2395
|
+
"version": mismatch.version,
|
|
2396
|
+
"sha256": mismatch.sha256,
|
|
2397
|
+
},
|
|
2398
|
+
)
|
|
2399
|
+
|
|
2400
|
+
async def _deliver_skills(
|
|
2401
|
+
self,
|
|
2402
|
+
stack: contextlib.AsyncExitStack,
|
|
2403
|
+
*,
|
|
2404
|
+
work_record_id: str,
|
|
2405
|
+
state: _RuntimeContextState,
|
|
2406
|
+
sandbox: DirectiveSandbox | None,
|
|
2407
|
+
directive_number: int,
|
|
2408
|
+
) -> str:
|
|
2409
|
+
"""Put the attached Skills where this runtime reads them; return any preamble.
|
|
2410
|
+
|
|
2411
|
+
Written files are removed on the attempt's exit, success or failure. A Runner
|
|
2412
|
+
with no Contract sandbox has no harness root it owns, so it falls back to the
|
|
2413
|
+
preamble rather than writing into a shared ``CODEX_HOME``.
|
|
2414
|
+
"""
|
|
2415
|
+
|
|
2416
|
+
if not state.skills:
|
|
2417
|
+
return ""
|
|
2418
|
+
delivery = delivery_for(state.cli_kind)
|
|
2419
|
+
if delivery is SkillDelivery.DIRECTORY and sandbox is None:
|
|
2420
|
+
delivery = SkillDelivery.PROMPT_PREAMBLE
|
|
2421
|
+
preamble = ""
|
|
2422
|
+
if delivery is SkillDelivery.DIRECTORY:
|
|
2423
|
+
assert sandbox is not None
|
|
2424
|
+
# Registered before the write, so a write that fails half-way is cleaned too.
|
|
2425
|
+
stack.push_async_callback(
|
|
2426
|
+
remove_skills, sandbox.harness_config_dir, state.skills, uid=sandbox.uid
|
|
2427
|
+
)
|
|
2428
|
+
await write_skills(sandbox.harness_config_dir, state.skills, uid=sandbox.uid)
|
|
2429
|
+
else:
|
|
2430
|
+
preamble = skills_preamble(state.skills)
|
|
2431
|
+
await self._fastapi_client.append_evidence(
|
|
2432
|
+
work_record_id,
|
|
2433
|
+
source=SKILLS_EVIDENCE_SOURCE,
|
|
2434
|
+
payload={
|
|
2435
|
+
"event": "skills.delivered",
|
|
2436
|
+
"directive_number": directive_number,
|
|
2437
|
+
"agent_id": state.agent_id,
|
|
2438
|
+
"cli_kind": state.cli_kind,
|
|
2439
|
+
"delivery": delivery.value,
|
|
2440
|
+
"skills": [
|
|
2441
|
+
{"slug": skill.slug, "version": skill.version, "sha256": skill.sha256}
|
|
2442
|
+
for skill in state.skills
|
|
2443
|
+
],
|
|
2444
|
+
},
|
|
2445
|
+
)
|
|
2446
|
+
return preamble
|
|
2447
|
+
|
|
2347
2448
|
async def _plan_mcp(
|
|
2348
2449
|
self,
|
|
2349
2450
|
work_record_id: str,
|
|
@@ -2995,6 +3096,7 @@ class RunnerRalphActivities:
|
|
|
2995
3096
|
experience=runtime_context.experience,
|
|
2996
3097
|
experience_lesson_ids=tuple(runtime_context.experience_lesson_ids),
|
|
2997
3098
|
experience_truncated=runtime_context.experience_truncated,
|
|
3099
|
+
skills=tuple(runtime_context.skills),
|
|
2998
3100
|
workspace_path=resolved_workspace,
|
|
2999
3101
|
branch_head_sha=branch_head_sha,
|
|
3000
3102
|
product_id=runtime_context.product_id,
|
|
@@ -4253,7 +4355,8 @@ class RunnerRalphActivities:
|
|
|
4253
4355
|
extra_env=attempt.env,
|
|
4254
4356
|
mcp_servers=attempt.mcp_servers,
|
|
4255
4357
|
egress_allow_list=attempt.egress_allow_list,
|
|
4256
|
-
prompt=
|
|
4358
|
+
prompt=attempt.skills_preamble
|
|
4359
|
+
+ _learning_prompt(
|
|
4257
4360
|
request,
|
|
4258
4361
|
base_branch=state.base_branch or request.base_ref,
|
|
4259
4362
|
transcript=self._learner_transcript(state, request.work_record_id),
|
|
@@ -4488,7 +4591,7 @@ class RunnerRalphActivities:
|
|
|
4488
4591
|
extra_env=attempt.env,
|
|
4489
4592
|
mcp_servers=attempt.mcp_servers,
|
|
4490
4593
|
egress_allow_list=attempt.egress_allow_list,
|
|
4491
|
-
prompt=await kind.prompt(state, prompt_notes),
|
|
4594
|
+
prompt=attempt.skills_preamble + await kind.prompt(state, prompt_notes),
|
|
4492
4595
|
base_branch=request.base_ref,
|
|
4493
4596
|
work_branch=state.work_branch,
|
|
4494
4597
|
)
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""The Agent's attached Skills, delivered to its Agent Runtime for one Directive.
|
|
2
|
+
|
|
3
|
+
Console-v2 issue 28, ADR-0017 §1. Where each runtime reads a Skill was measured against the
|
|
4
|
+
Runner's own argv, not assumed (local-agents 02 asked the same question over ACP):
|
|
5
|
+
|
|
6
|
+
- **Codex** (codex-cli 0.159.2): ``$CODEX_HOME/skills/<slug>/SKILL.md`` is advertised to the
|
|
7
|
+
model under ``codex exec``, with and without ``--ignore-user-config``. The Runner writes
|
|
8
|
+
there, in the Contract's harness root, and removes it after the Directive.
|
|
9
|
+
- **Claude Code** (2.1.292): the Runner runs ``claude --print --bare``, and ``--bare`` drops
|
|
10
|
+
the Skill tool. ``$CLAUDE_CONFIG_DIR/skills/<slug>/SKILL.md`` is then neither advertised
|
|
11
|
+
nor resolved by ``/<slug>``, and ``--tools …,Skill`` does not bring the tool back. Claude
|
|
12
|
+
Code therefore gets the bodies as a prompt preamble. Dropping ``--bare`` would also
|
|
13
|
+
re-enable the Workspace's hooks and ``CLAUDE.md``, which is not this module's call.
|
|
14
|
+
|
|
15
|
+
The harness root is per Contract, not per Directive, so two concurrent Directives of one
|
|
16
|
+
Contract share its ``skills`` directory.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import asyncio
|
|
22
|
+
import hashlib
|
|
23
|
+
import json
|
|
24
|
+
import sys
|
|
25
|
+
from collections.abc import Sequence
|
|
26
|
+
from enum import StrEnum
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import Final
|
|
29
|
+
|
|
30
|
+
from agentic_runner_contracts.runtime_context import SkillVersionSpec
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"SKILLS_DIR",
|
|
34
|
+
"SkillDelivery",
|
|
35
|
+
"SkillDeliveryError",
|
|
36
|
+
"delivery_for",
|
|
37
|
+
"first_digest_mismatch",
|
|
38
|
+
"remove_skills",
|
|
39
|
+
"skills_preamble",
|
|
40
|
+
"write_skills",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
SKILLS_DIR: Final[str] = "skills"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class SkillDelivery(StrEnum):
|
|
47
|
+
DIRECTORY = "directory"
|
|
48
|
+
PROMPT_PREAMBLE = "prompt_preamble"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
_DELIVERY_BY_CLI_KIND: Final[dict[str, SkillDelivery]] = {
|
|
52
|
+
"codex_cli": SkillDelivery.DIRECTORY,
|
|
53
|
+
"claude_code": SkillDelivery.PROMPT_PREAMBLE,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class SkillDeliveryError(RuntimeError):
|
|
58
|
+
"""Raised when the Skills could not be written into, or removed from, the harness root."""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def delivery_for(cli_kind: str) -> SkillDelivery:
|
|
62
|
+
return _DELIVERY_BY_CLI_KIND.get(cli_kind, SkillDelivery.PROMPT_PREAMBLE)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def first_digest_mismatch(skills: Sequence[SkillVersionSpec]) -> SkillVersionSpec | None:
|
|
66
|
+
for skill in skills:
|
|
67
|
+
if hashlib.sha256(skill.body.encode("utf-8")).hexdigest() != skill.sha256:
|
|
68
|
+
return skill
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def skills_preamble(skills: Sequence[SkillVersionSpec]) -> str:
|
|
73
|
+
"""The bodies, for a runtime with no skills directory. Empty Skills, empty preamble,
|
|
74
|
+
so a Directive without Skills keeps a byte-identical prompt."""
|
|
75
|
+
|
|
76
|
+
if not skills:
|
|
77
|
+
return ""
|
|
78
|
+
sections = "".join(
|
|
79
|
+
f"Skill {skill.slug} (version {skill.version}):\n{skill.body.strip()}\n\n"
|
|
80
|
+
for skill in skills
|
|
81
|
+
)
|
|
82
|
+
return (
|
|
83
|
+
"The Skills below are attached to you. Follow each one where it applies.\n\n"
|
|
84
|
+
f"{sections}"
|
|
85
|
+
"The pipeline rules take precedence over the Skills above: edit files only, do not "
|
|
86
|
+
"run git commit or git push, and do not open a pull request — the pipeline commits, "
|
|
87
|
+
"pushes, and manages the PR.\n\n"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# Both run as the Contract's uid, never the Runner's. The Contract owns its harness root, so
|
|
92
|
+
# an Agent could have left a symlink there; a write or an rmtree by the Runner -- with
|
|
93
|
+
# CAP_CHOWN and CAP_DAC_OVERRIDE -- would follow it out of the tree. As the Contract's uid
|
|
94
|
+
# it can only reach what the Contract already could.
|
|
95
|
+
_WRITE_SCRIPT: Final[str] = """
|
|
96
|
+
import json, os, sys
|
|
97
|
+
root, skills = json.load(sys.stdin)
|
|
98
|
+
os.makedirs(root, mode=0o700, exist_ok=True)
|
|
99
|
+
for slug, body in skills:
|
|
100
|
+
directory = os.path.join(root, slug)
|
|
101
|
+
os.makedirs(directory, mode=0o700, exist_ok=True)
|
|
102
|
+
path = os.path.join(directory, "SKILL.md")
|
|
103
|
+
flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC | os.O_NOFOLLOW
|
|
104
|
+
with os.fdopen(os.open(path, flags, 0o600), "w", encoding="utf-8") as handle:
|
|
105
|
+
handle.write(body)
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
_REMOVE_SCRIPT: Final[str] = """
|
|
109
|
+
import json, os, shutil, sys
|
|
110
|
+
root, slugs = json.load(sys.stdin)
|
|
111
|
+
for slug in slugs:
|
|
112
|
+
shutil.rmtree(os.path.join(root, slug), ignore_errors=True)
|
|
113
|
+
left = [slug for slug in slugs if os.path.lexists(os.path.join(root, slug))]
|
|
114
|
+
if left:
|
|
115
|
+
sys.exit("not removed: " + ", ".join(left))
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _skills_root(harness_config_dir: Path) -> str:
|
|
120
|
+
return str(harness_config_dir / SKILLS_DIR)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
async def write_skills(
|
|
124
|
+
harness_config_dir: Path, skills: Sequence[SkillVersionSpec], *, uid: int | None
|
|
125
|
+
) -> None:
|
|
126
|
+
"""Write each Skill as ``<harness>/skills/<slug>/SKILL.md``, as the Contract's uid."""
|
|
127
|
+
|
|
128
|
+
await _run_as(
|
|
129
|
+
uid,
|
|
130
|
+
_WRITE_SCRIPT,
|
|
131
|
+
[_skills_root(harness_config_dir), [[skill.slug, skill.body] for skill in skills]],
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
async def remove_skills(
|
|
136
|
+
harness_config_dir: Path, skills: Sequence[SkillVersionSpec], *, uid: int | None
|
|
137
|
+
) -> None:
|
|
138
|
+
"""Remove what :func:`write_skills` wrote. The ``skills`` directory itself stays:
|
|
139
|
+
Codex keeps its own ``.system`` Skills there."""
|
|
140
|
+
|
|
141
|
+
await _run_as(
|
|
142
|
+
uid,
|
|
143
|
+
_REMOVE_SCRIPT,
|
|
144
|
+
[_skills_root(harness_config_dir), [skill.slug for skill in skills]],
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
async def _run_as(uid: int | None, script: str, payload: object) -> None:
|
|
149
|
+
identity: dict[str, object] = (
|
|
150
|
+
{} if uid is None else {"user": uid, "group": uid, "extra_groups": []}
|
|
151
|
+
)
|
|
152
|
+
process = await asyncio.create_subprocess_exec(
|
|
153
|
+
sys.executable,
|
|
154
|
+
"-I",
|
|
155
|
+
"-c",
|
|
156
|
+
script,
|
|
157
|
+
stdin=asyncio.subprocess.PIPE,
|
|
158
|
+
stdout=asyncio.subprocess.DEVNULL,
|
|
159
|
+
stderr=asyncio.subprocess.PIPE,
|
|
160
|
+
**identity, # type: ignore[arg-type]
|
|
161
|
+
)
|
|
162
|
+
_, stderr = await process.communicate(json.dumps(payload).encode("utf-8"))
|
|
163
|
+
if process.returncode != 0:
|
|
164
|
+
raise SkillDeliveryError(stderr.decode("utf-8", errors="replace").strip()[-500:])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/integrations/git/__init__.py
RENAMED
|
File without changes
|
{agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/integrations/git/contracts.py
RENAMED
|
File without changes
|
{agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/integrations/git/evidence.py
RENAMED
|
File without changes
|
{agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/integrations/git/fake_workspace.py
RENAMED
|
File without changes
|
{agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/integrations/git/workspace.py
RENAMED
|
File without changes
|
{agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/integrations/github/__init__.py
RENAMED
|
File without changes
|
{agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/integrations/github/auth.py
RENAMED
|
File without changes
|
{agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/integrations/github/fake_client.py
RENAMED
|
File without changes
|
{agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/integrations/github/gh_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/runtime/verifier_command.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/workers/_runtime_support.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/workers/contract_device_login.py
RENAMED
|
File without changes
|
{agentic_runner-2.6.1 → agentic_runner-2.7.0}/src/agentic_runner/workers/contract_isolation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|