@event4u/agent-config 1.18.0 → 1.19.0
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/.agent-src/commands/council/default.md +74 -76
- package/.agent-src/commands/feature/roadmap.md +22 -0
- package/.agent-src/commands/roadmap/create.md +38 -6
- package/.agent-src/commands/roadmap/execute.md +36 -9
- package/.agent-src/rules/agent-authority.md +1 -0
- package/.agent-src/rules/agent-docs.md +1 -0
- package/.agent-src/rules/analysis-skill-routing.md +1 -0
- package/.agent-src/rules/architecture.md +1 -0
- package/.agent-src/rules/artifact-drafting-protocol.md +1 -0
- package/.agent-src/rules/artifact-engagement-recording.md +1 -0
- package/.agent-src/rules/ask-when-uncertain.md +1 -0
- package/.agent-src/rules/augment-portability.md +1 -0
- package/.agent-src/rules/augment-source-of-truth.md +1 -0
- package/.agent-src/rules/autonomous-execution.md +1 -0
- package/.agent-src/rules/capture-learnings.md +1 -0
- package/.agent-src/rules/chat-history-cadence.md +34 -0
- package/.agent-src/rules/chat-history-ownership.md +1 -0
- package/.agent-src/rules/chat-history-visibility.md +1 -0
- package/.agent-src/rules/cli-output-handling.md +2 -2
- package/.agent-src/rules/command-suggestion-policy.md +1 -0
- package/.agent-src/rules/commit-conventions.md +1 -0
- package/.agent-src/rules/commit-policy.md +1 -0
- package/.agent-src/rules/context-hygiene.md +22 -0
- package/.agent-src/rules/direct-answers.md +1 -0
- package/.agent-src/rules/docker-commands.md +1 -0
- package/.agent-src/rules/docs-sync.md +1 -0
- package/.agent-src/rules/downstream-changes.md +1 -0
- package/.agent-src/rules/e2e-testing.md +1 -0
- package/.agent-src/rules/guidelines.md +1 -0
- package/.agent-src/rules/improve-before-implement.md +1 -0
- package/.agent-src/rules/language-and-tone.md +1 -0
- package/.agent-src/rules/laravel-translations.md +1 -0
- package/.agent-src/rules/markdown-safe-codeblocks.md +1 -0
- package/.agent-src/rules/minimal-safe-diff.md +1 -0
- package/.agent-src/rules/missing-tool-handling.md +1 -0
- package/.agent-src/rules/model-recommendation.md +1 -0
- package/.agent-src/rules/no-cheap-questions.md +1 -0
- package/.agent-src/rules/no-roadmap-references.md +1 -0
- package/.agent-src/rules/non-destructive-by-default.md +1 -0
- package/.agent-src/rules/onboarding-gate.md +26 -0
- package/.agent-src/rules/package-ci-checks.md +1 -0
- package/.agent-src/rules/php-coding.md +1 -0
- package/.agent-src/rules/preservation-guard.md +1 -0
- package/.agent-src/rules/review-routing-awareness.md +1 -0
- package/.agent-src/rules/reviewer-awareness.md +1 -0
- package/.agent-src/rules/roadmap-progress-sync.md +22 -0
- package/.agent-src/rules/role-mode-adherence.md +2 -2
- package/.agent-src/rules/rule-type-governance.md +1 -0
- package/.agent-src/rules/runtime-safety.md +1 -0
- package/.agent-src/rules/scope-control.md +1 -0
- package/.agent-src/rules/security-sensitive-stop.md +1 -0
- package/.agent-src/rules/size-enforcement.md +1 -0
- package/.agent-src/rules/skill-improvement-trigger.md +1 -0
- package/.agent-src/rules/skill-quality.md +1 -0
- package/.agent-src/rules/slash-command-routing-policy.md +39 -0
- package/.agent-src/rules/think-before-action.md +1 -0
- package/.agent-src/rules/token-efficiency.md +1 -0
- package/.agent-src/rules/tool-safety.md +1 -0
- package/.agent-src/rules/ui-audit-gate.md +1 -0
- package/.agent-src/rules/upstream-proposal.md +1 -0
- package/.agent-src/rules/user-interaction.md +1 -0
- package/.agent-src/rules/verify-before-complete.md +1 -0
- package/.agent-src/skills/roadmap-management/SKILL.md +29 -4
- package/.agent-src/skills/verify-completion-evidence/SKILL.md +8 -1
- package/.agent-src/templates/agent-settings.md +16 -0
- package/.agent-src/templates/roadmaps.md +8 -3
- package/.agent-src/templates/scripts/work_engine/hook_bootstrap.py +9 -0
- package/.agent-src/templates/scripts/work_engine/hooks/__init__.py +4 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +4 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.py +163 -0
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.py +111 -0
- package/.agent-src/templates/scripts/work_engine/hooks/settings.py +36 -0
- package/.agent-src/templates/scripts/work_engine/scoring/decision_trace.py +141 -0
- package/.agent-src/templates/scripts/work_engine/scoring/memory_visibility.py +125 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +62 -0
- package/README.md +19 -19
- package/config/agent-settings.template.yml +23 -0
- package/docs/catalog.md +5 -2
- package/docs/contracts/adr-settings-sync-engine.md +127 -0
- package/docs/contracts/decision-trace-v1.md +146 -0
- package/docs/contracts/file-ownership-matrix.json +7 -0
- package/docs/contracts/hook-architecture-v1.md +213 -0
- package/docs/contracts/memory-visibility-v1.md +138 -0
- package/docs/contracts/one-off-script-lifecycle.md +109 -0
- package/docs/contracts/rule-interactions.yml +22 -0
- package/docs/customization.md +1 -0
- package/docs/development.md +4 -1
- package/docs/guidelines/agent-infra/layered-settings.md +32 -13
- package/package.json +1 -1
- package/scripts/agent-config +44 -0
- package/scripts/ai_council/bundler.py +3 -3
- package/scripts/ai_council/clients.py +24 -8
- package/scripts/ai_council/one_off_archive/2026-05/README.md +22 -0
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_roundtrip.py +13 -8
- package/scripts/ai_council/one_off_archive/2026-05/_one_off_tier_retrofit.py +180 -0
- package/scripts/ai_council/session.py +92 -0
- package/scripts/capture_showcase_session.py +361 -0
- package/scripts/chat_history.py +11 -1
- package/scripts/check_always_budget.py +7 -2
- package/scripts/context_hygiene_hook.py +14 -6
- package/scripts/council_cli.py +357 -0
- package/scripts/hook_manifest.yaml +184 -0
- package/scripts/hooks/__init__.py +1 -0
- package/scripts/hooks/augment-dispatcher.sh +72 -0
- package/scripts/hooks/cline-dispatcher.sh +86 -0
- package/scripts/hooks/cursor-dispatcher.sh +76 -0
- package/scripts/hooks/dispatch_hook.py +348 -0
- package/scripts/hooks/envelope.py +98 -0
- package/scripts/hooks/gemini-dispatcher.sh +117 -0
- package/scripts/hooks/state_io.py +122 -0
- package/scripts/hooks/windsurf-dispatcher.sh +123 -0
- package/scripts/hooks_status.py +146 -0
- package/scripts/install.py +725 -87
- package/scripts/install.sh +1 -1
- package/scripts/lint_hook_manifest.py +216 -0
- package/scripts/lint_one_off_age.py +184 -0
- package/scripts/lint_rule_tiers.py +78 -0
- package/scripts/lint_showcase_sessions.py +148 -0
- package/scripts/minimal_safe_diff_hook.py +245 -0
- package/scripts/onboarding_gate_hook.py +13 -8
- package/scripts/readme_linter.py +12 -3
- package/scripts/roadmap_progress_hook.py +5 -0
- package/scripts/sync_agent_settings.py +32 -129
- package/scripts/sync_yaml_rt.py +734 -0
- package/scripts/verify_before_complete_hook.py +216 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
---
|
|
2
|
+
stability: beta
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Memory-visibility v1
|
|
6
|
+
|
|
7
|
+
**Purpose.** Pin the format of the user-facing visibility line that
|
|
8
|
+
every memory-using `/work` and `/implement-ticket` run prints, so the
|
|
9
|
+
user can tell what the agent retrieved and what it ignored.
|
|
10
|
+
Complements [`agent-memory-contract.md`](agent-memory-contract.md):
|
|
11
|
+
that doc describes the **CLI surface and backend states**; this doc
|
|
12
|
+
describes the **operator-facing surface** the engine emits per turn.
|
|
13
|
+
|
|
14
|
+
**Scope.** Defines the line shape, the privacy floor, the opt-out
|
|
15
|
+
toggle, and the interaction with the chat-history heartbeat. Does
|
|
16
|
+
**not** define how memory entries are scored or routed — that is the
|
|
17
|
+
sibling agent-memory package.
|
|
18
|
+
|
|
19
|
+
Last refreshed: 2026-05-04.
|
|
20
|
+
|
|
21
|
+
## Line shape
|
|
22
|
+
|
|
23
|
+
A single one-line ASCII record, prefixed with the memory icon `🧠`
|
|
24
|
+
and a single space:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
🧠 Memory: <hits>/<asks> · ids=[<comma-separated-ids>]
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Examples:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
🧠 Memory: 3/4 · ids=[mem_42, mem_57, mem_91]
|
|
34
|
+
🧠 Memory: 0/2 · ids=[]
|
|
35
|
+
🧠 Memory: 5/5 · ids=[mem_a01, mem_a02, mem_a03, …+2]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Cap at 5 ids inline; remainder rendered as `…+N`. The full id list
|
|
39
|
+
lives in the decision-trace JSON
|
|
40
|
+
([`decision-trace-v1.md`](decision-trace-v1.md)).
|
|
41
|
+
|
|
42
|
+
## Field semantics
|
|
43
|
+
|
|
44
|
+
| Field | Meaning |
|
|
45
|
+
|---|---|
|
|
46
|
+
| `hits` | Count of `memory_retrieve_*` calls during this turn that returned ≥ 1 entry. |
|
|
47
|
+
| `asks` | Count of `memory_retrieve_*` calls during this turn — both successful and empty. |
|
|
48
|
+
| `ids` | Stable memory entry ids returned across all calls, deduped, ordered by retrieval timestamp. |
|
|
49
|
+
|
|
50
|
+
`hits ≤ asks` is invariant. If `asks == 0`, the engine MUST suppress
|
|
51
|
+
the line entirely — no `0/0` noise.
|
|
52
|
+
|
|
53
|
+
## Privacy floor
|
|
54
|
+
|
|
55
|
+
The visibility line and the JSON it derives from MUST NOT contain:
|
|
56
|
+
|
|
57
|
+
- Entry **bodies**, summaries, or quoted snippets.
|
|
58
|
+
- Secrets, tokens, environment values, or paths outside the
|
|
59
|
+
package's `agents/state/` and `tests/` allowlist.
|
|
60
|
+
- User identifiers beyond what is already public in the working
|
|
61
|
+
directory's `.agent-settings.yml` (e.g. developer name).
|
|
62
|
+
|
|
63
|
+
The privacy floor is enforced by
|
|
64
|
+
`tests/contracts/test_memory_visibility_redaction.py` — any new
|
|
65
|
+
content path that ships memory output adds a fixture there.
|
|
66
|
+
|
|
67
|
+
## Opt-out
|
|
68
|
+
|
|
69
|
+
On by default whenever memory is asked at all in a turn. Users can
|
|
70
|
+
suppress the visibility line via:
|
|
71
|
+
|
|
72
|
+
```yaml
|
|
73
|
+
memory:
|
|
74
|
+
visibility: off
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Off-mode does not silence the underlying memory calls; it only stops
|
|
78
|
+
the line from rendering. The decision-trace JSON still records the
|
|
79
|
+
counts and ids for downstream metrics.
|
|
80
|
+
|
|
81
|
+
## Interaction with chat-history heartbeat
|
|
82
|
+
|
|
83
|
+
The chat-history heartbeat (`📒` marker) and the memory-visibility
|
|
84
|
+
line are **independent**:
|
|
85
|
+
|
|
86
|
+
- Heartbeat fires on cadence boundaries
|
|
87
|
+
(`per_turn` / `per_phase` / `per_tool`).
|
|
88
|
+
- Visibility line fires whenever `asks ≥ 1` for the current turn,
|
|
89
|
+
regardless of cadence.
|
|
90
|
+
- Both render on the same reply when both fire — heartbeat first,
|
|
91
|
+
visibility line second, separated by a single newline.
|
|
92
|
+
|
|
93
|
+
The visibility line is **not** part of the heartbeat payload — that
|
|
94
|
+
keeps the heartbeat contract bytes-stable.
|
|
95
|
+
|
|
96
|
+
## Cadence interaction
|
|
97
|
+
|
|
98
|
+
| Cost profile | Visibility line | Heartbeat |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| `lean` | suppress unless `asks ≥ 3` | per-phase |
|
|
101
|
+
| `standard` | always when `asks ≥ 1` | per-turn |
|
|
102
|
+
| `verbose` | always when `asks ≥ 1` | per-tool |
|
|
103
|
+
|
|
104
|
+
Cost-profile lookup respects `.agent-settings.yml`'s `cost_profile`
|
|
105
|
+
key. Default is `standard`.
|
|
106
|
+
|
|
107
|
+
## Audit-as-memory feed
|
|
108
|
+
|
|
109
|
+
The visibility output produced by the engine is the input to the
|
|
110
|
+
audit-as-memory pipeline (consumed by the sibling distribution +
|
|
111
|
+
adoption work). Concretely:
|
|
112
|
+
|
|
113
|
+
- The engine emits the line + the underlying counts to the
|
|
114
|
+
decision-trace JSON.
|
|
115
|
+
- A consumer hook reads `agents/state/work/<work-id>/decision-trace-*.json`,
|
|
116
|
+
rolls counts up to the session level, and feeds the result back
|
|
117
|
+
into the agent-memory store as an audit entry.
|
|
118
|
+
|
|
119
|
+
This contract pins the **producer** side. The audit-feed consumer
|
|
120
|
+
lives outside the package's stable surface and must read this
|
|
121
|
+
contract before parsing.
|
|
122
|
+
|
|
123
|
+
## Stability
|
|
124
|
+
|
|
125
|
+
Beta. Breaking changes between v1 and v2 are allowed in a minor
|
|
126
|
+
release if the change appears in `CHANGELOG.md` under a `### Breaking`
|
|
127
|
+
heading. Engines MUST gate on the visibility line shape — clients
|
|
128
|
+
parsing the stream MUST treat unknown trailing fields as forward-
|
|
129
|
+
compat extensions.
|
|
130
|
+
|
|
131
|
+
## Cross-references
|
|
132
|
+
|
|
133
|
+
- CLI surface and backend states:
|
|
134
|
+
[`agent-memory-contract.md`](agent-memory-contract.md).
|
|
135
|
+
- Decision-trace JSON consumes the same counts:
|
|
136
|
+
[`decision-trace-v1.md`](decision-trace-v1.md).
|
|
137
|
+
- Privacy regression test path:
|
|
138
|
+
`tests/contracts/test_memory_visibility_redaction.py`.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
stability: beta
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# One-off-script lifecycle
|
|
6
|
+
|
|
7
|
+
**Purpose.** Pin the naming, location, age, and purge policy for
|
|
8
|
+
**one-off scripts** so the package does not accumulate a graveyard
|
|
9
|
+
under `scripts/`. One-off here means: a script written for a
|
|
10
|
+
specific migration, retrofit, audit, or council run, with no ongoing
|
|
11
|
+
caller and no place in the durable Taskfile.
|
|
12
|
+
|
|
13
|
+
**Scope.** Defines the file pattern, the directory, the maximum age,
|
|
14
|
+
the TTL extension mechanism, and the CI purge gate. Does **not**
|
|
15
|
+
specify the content of any specific one-off — that belongs to the
|
|
16
|
+
script itself or the cleanup-mechanics context.
|
|
17
|
+
|
|
18
|
+
Last refreshed: 2026-05-04.
|
|
19
|
+
|
|
20
|
+
## Naming
|
|
21
|
+
|
|
22
|
+
One-off scripts MUST match this regex:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
^_one_off_[a-z0-9-]+\.py$
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The `_one_off_` prefix is the load-bearing signal. Files outside
|
|
29
|
+
this prefix are treated as durable scripts and MUST be referenced by
|
|
30
|
+
the Taskfile or by another script.
|
|
31
|
+
|
|
32
|
+
## Location
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
scripts/_one_off/<YYYY-MM>/_one_off_<slug>.py
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`<YYYY-MM>` is the UTC month the script was first committed. The
|
|
39
|
+
month directory groups one-offs for archival sweeps. Scripts MUST
|
|
40
|
+
NOT live at `scripts/_one_off/_one_off_*.py` (no month) or under
|
|
41
|
+
`scripts/` directly (no `_one_off/`).
|
|
42
|
+
|
|
43
|
+
## TTL
|
|
44
|
+
|
|
45
|
+
| State | Action |
|
|
46
|
+
|---|---|
|
|
47
|
+
| Age ≤ 60 days from month-directory date | active, no warning |
|
|
48
|
+
| 60 < Age ≤ 90 days | warning emitted by `lint_one_off_age.py`, no failure |
|
|
49
|
+
| Age > 90 days | `lint_one_off_age.py` fails CI; the script is purged in the next housekeeping pass |
|
|
50
|
+
|
|
51
|
+
Age = `today − first-of-month(<YYYY-MM>)` in UTC days. The 60-day
|
|
52
|
+
soft floor and 30-day grace window are intentional — they cover one
|
|
53
|
+
release cycle plus a sprint of grace.
|
|
54
|
+
|
|
55
|
+
## TTL extension
|
|
56
|
+
|
|
57
|
+
A one-off MAY extend its TTL exactly once, by adding a frontmatter
|
|
58
|
+
block at the top of the script:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
"""
|
|
62
|
+
---
|
|
63
|
+
ttl_extended_until: 2026-08-31
|
|
64
|
+
ttl_reason: blocked on PROJ-123 — re-runs after cutover
|
|
65
|
+
---
|
|
66
|
+
"""
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The linter respects `ttl_extended_until` if it is ≤ 180 days from
|
|
70
|
+
the file's `<YYYY-MM>` directory date. Beyond 180 days, the linter
|
|
71
|
+
hard-fails — no second extension. The intent is: if a "one-off" is
|
|
72
|
+
still live at six months, it is a durable concern and belongs in
|
|
73
|
+
`scripts/` or a Taskfile group.
|
|
74
|
+
|
|
75
|
+
## Purge mechanism
|
|
76
|
+
|
|
77
|
+
`lint_one_off_age.py` runs in `task ci`. On a clean working tree, it
|
|
78
|
+
prints purge candidates as a list. Purge itself is a separate human-
|
|
79
|
+
or-CI action — `task purge-one-offs` removes flagged files. The
|
|
80
|
+
linter does not auto-delete.
|
|
81
|
+
|
|
82
|
+
## Allowed exceptions
|
|
83
|
+
|
|
84
|
+
Two patterns are exempt from the prefix requirement:
|
|
85
|
+
|
|
86
|
+
- **Bundler / orchestrator helpers** under `scripts/ai_council/`
|
|
87
|
+
that exist to support the council CLI — they are not one-offs even
|
|
88
|
+
though council *runs* are one-offs.
|
|
89
|
+
- **`scripts/_one_off/<YYYY-MM>/README.md`** — a free-form readme is
|
|
90
|
+
allowed in each month directory documenting why the scripts exist.
|
|
91
|
+
|
|
92
|
+
Council run scripts that wrap a question and write the response file
|
|
93
|
+
DO live under `scripts/_one_off/<YYYY-MM>/` and DO follow the prefix
|
|
94
|
+
rule.
|
|
95
|
+
|
|
96
|
+
## Cross-references
|
|
97
|
+
|
|
98
|
+
- The contract that defines council CLI surface (and so what gets
|
|
99
|
+
archived as a one-off): the council CLI section of the package's
|
|
100
|
+
command catalog.
|
|
101
|
+
- The cleanup-mechanics context for housekeeping passes:
|
|
102
|
+
`agents/contexts/cleanup-mechanics.md`.
|
|
103
|
+
- Linter implementation: `scripts/lint_one_off_age.py`.
|
|
104
|
+
|
|
105
|
+
## Stability
|
|
106
|
+
|
|
107
|
+
Beta. Breaking changes (e.g. raising the age cap, changing the
|
|
108
|
+
prefix, or removing TTL extensions) require a minor-version bump and
|
|
109
|
+
a `### Breaking` entry in `CHANGELOG.md`.
|
|
@@ -221,6 +221,28 @@ pairs:
|
|
|
221
221
|
- .agent-src.uncompressed/rules/ask-when-uncertain.md#iron-law--one-question-per-turn-always
|
|
222
222
|
- .agent-src.uncompressed/rules/direct-answers.md#iron-law-3--brevity-by-default
|
|
223
223
|
|
|
224
|
+
- id: scope-x-verify-before-complete
|
|
225
|
+
rules: [verify-before-complete, scope-control]
|
|
226
|
+
relation: complements
|
|
227
|
+
conflict: >-
|
|
228
|
+
Agent has just finished a change that touches user-permission-gated
|
|
229
|
+
operations (push, branch, PR, tag) and is preparing to claim "done"
|
|
230
|
+
in the same turn. Both rules can fire: `verify-before-complete`
|
|
231
|
+
gates the completion claim on fresh evidence; `scope-control`
|
|
232
|
+
gates the git operation on explicit permission this turn.
|
|
233
|
+
resolution: >-
|
|
234
|
+
Both rules apply independently and compose. The
|
|
235
|
+
`verify-before-complete` Iron Law still requires fresh
|
|
236
|
+
verification output in this message before any "done" claim,
|
|
237
|
+
regardless of whether the user has authorised the next git op.
|
|
238
|
+
Conversely, verification passing does not authorise pushing or
|
|
239
|
+
merging — those stay behind the `scope-control` permission gate.
|
|
240
|
+
Skipping either is a rule violation; satisfying one does not
|
|
241
|
+
satisfy the other.
|
|
242
|
+
evidence:
|
|
243
|
+
- .agent-src.uncompressed/rules/verify-before-complete.md#the-iron-law
|
|
244
|
+
- .agent-src.uncompressed/rules/scope-control.md#git-operations--permission-gated
|
|
245
|
+
|
|
224
246
|
- id: language-x-direct-answers
|
|
225
247
|
rules: [language-and-tone, direct-answers]
|
|
226
248
|
relation: complements
|
package/docs/customization.md
CHANGED
|
@@ -66,6 +66,7 @@ those sections.
|
|
|
66
66
|
| `ai_council.cost_budget.max_calls` | `10` | Maximum council members per invocation. |
|
|
67
67
|
| `ai_council.cost_budget.max_total_usd` | `0.0` | Per-invocation USD ceiling. `0` disables (token caps still apply). |
|
|
68
68
|
| `ai_council.cost_budget.daily_limit_usd` | `0.0` | Rolling 24h USD ceiling across all `/council` calls. `0` disables. Ledger lives at `~/.config/agent-config/council-spend.jsonl` (mode 0600). |
|
|
69
|
+
| `ai_council.session_retention_days` | `14` | Auto-prune for `agents/council-sessions/` audit folders. Older session directories are removed on the next `save()`. `0` disables (keep forever). |
|
|
69
70
|
|
|
70
71
|
> **Experimental.** AI Council is not yet validated by external users. API costs apply per consultation.
|
|
71
72
|
|
package/docs/development.md
CHANGED
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
|
|
18
18
|
## Task Commands
|
|
19
19
|
|
|
20
|
-
All commands use [Task](https://taskfile.dev/).
|
|
20
|
+
All commands use [Task](https://taskfile.dev/). The root `Taskfile.yml` orchestrates
|
|
21
|
+
`ci`/`_ci-*` and includes the four task groups under `taskfiles/`
|
|
22
|
+
(`ci-fast.yml`, `content.yml`, `engine.yml`, `release.yml`) with `flatten: true`,
|
|
23
|
+
so every task stays in the root namespace. Run `task --list` for the full list.
|
|
21
24
|
|
|
22
25
|
### CI & Verification
|
|
23
26
|
|
|
@@ -152,27 +152,46 @@ MUST follow these rules. Initial file creation and legacy migration
|
|
|
152
152
|
are owned by `scripts/install.py`; these rules govern every edit
|
|
153
153
|
after that.
|
|
154
154
|
|
|
155
|
+
The contract is **additive merge with user-line preservation** —
|
|
156
|
+
the user's file is the ground truth, the template only contributes
|
|
157
|
+
keys the user is missing. Round-trip parser and merger live in
|
|
158
|
+
[`scripts/sync_yaml_rt.py`](../../scripts/sync_yaml_rt.py); the
|
|
159
|
+
supported YAML subset (block-mappings, scalars, lists, comments,
|
|
160
|
+
CRLF/LF) is documented in its module docstring. The stdlib-only
|
|
161
|
+
choice (vs. `ruamel.yaml`) and its revisit triggers are recorded in
|
|
162
|
+
[`docs/contracts/adr-settings-sync-engine.md`](../../contracts/adr-settings-sync-engine.md).
|
|
163
|
+
|
|
155
164
|
For each section in the template
|
|
156
|
-
([`agent-settings.md`](../../templates/agent-settings.md))
|
|
157
|
-
template order:
|
|
165
|
+
([`agent-settings.md`](../../templates/agent-settings.md)):
|
|
158
166
|
|
|
159
|
-
- Keep the section header and its comments verbatim from the template.
|
|
160
167
|
- For each key under the section:
|
|
161
|
-
- **Key exists in user's file** →
|
|
162
|
-
|
|
163
|
-
- **
|
|
164
|
-
|
|
165
|
-
|
|
168
|
+
- **Key exists in user's file** → keep the user's line **verbatim**
|
|
169
|
+
(value, quoting, inline comment, indent — all preserved).
|
|
170
|
+
- **Key missing** → insert the template's line at the position
|
|
171
|
+
after the user's last preceding sibling that is also in the
|
|
172
|
+
template (max-index insertion).
|
|
173
|
+
- **Unknown sections/keys** the user has added → preserved verbatim
|
|
174
|
+
at their existing position. They are not moved to a trailing
|
|
175
|
+
`_user:` block, not re-prefixed, not flattened.
|
|
166
176
|
|
|
167
177
|
Invariants:
|
|
168
178
|
|
|
169
|
-
-
|
|
170
|
-
|
|
179
|
+
- **User order wins.** Template order is only consulted to decide
|
|
180
|
+
where to insert missing keys; existing user keys are never
|
|
181
|
+
reordered.
|
|
171
182
|
- Existing scalar values are **never overwritten** unless the user
|
|
172
183
|
asked for that specific change.
|
|
173
|
-
- New keys added to the template land with their default value
|
|
174
|
-
|
|
175
|
-
|
|
184
|
+
- New keys added to the template land with their default value and
|
|
185
|
+
the template's leading comments.
|
|
186
|
+
- **User comments are preserved verbatim** on every existing key.
|
|
187
|
+
Template comments only land with keys the merger inserts; once a
|
|
188
|
+
key is in the user's file, its surrounding comments are owned by
|
|
189
|
+
the user.
|
|
190
|
+
- Legacy `_user._user.foo` corruption (accumulated by older buggy
|
|
191
|
+
syncs) heals on the next sync — the leading `_user.` chain is
|
|
192
|
+
stripped and the leaf is re-homed at its template path, or kept
|
|
193
|
+
as a single-level orphan under `_user:` if no template home
|
|
194
|
+
exists.
|
|
176
195
|
- Write with 2-space indent, no tabs, no trailing whitespace.
|
|
177
196
|
- Never commit — `.agent-settings.yml` is git-ignored.
|
|
178
197
|
- If a legacy flat `.agent-settings` (key=value) is still present,
|
package/package.json
CHANGED
package/scripts/agent-config
CHANGED
|
@@ -76,9 +76,20 @@ Commands:
|
|
|
76
76
|
Writes .augment/state/onboarding-gate.json from .agent-settings.yml
|
|
77
77
|
context-hygiene:hook PostToolUse hook entry point (read JSON from stdin)
|
|
78
78
|
Maintains .augment/state/context-hygiene.json (turn count, loop, freshness)
|
|
79
|
+
dispatch:hook Universal hook dispatcher (Phase 7, hook-architecture-v1.md)
|
|
80
|
+
Usage: dispatch:hook --platform <name> --event <event> [--native-event <native>]
|
|
81
|
+
Reads scripts/hook_manifest.yaml and runs the resolved concern chain.
|
|
82
|
+
hooks:status Print the runtime hook matrix (per-platform install + bindings)
|
|
83
|
+
Flags: --format json|table, --strict (CI), --project-root <path>
|
|
79
84
|
telemetry:record Append one artefact-engagement event (default-off)
|
|
80
85
|
telemetry:status Print artefact-engagement telemetry status (read-only)
|
|
81
86
|
telemetry:report Aggregate the engagement log into a quartile report
|
|
87
|
+
council:estimate Pre-call council cost preview (no API call, no spend)
|
|
88
|
+
Usage: council:estimate <question> [--input-mode prompt|roadmap]
|
|
89
|
+
council:run Run the council. Requires --confirm to spend.
|
|
90
|
+
Usage: council:run <question> --output <path> --confirm
|
|
91
|
+
council:render Re-render a saved council responses JSON to markdown
|
|
92
|
+
Usage: council:render <responses.json>
|
|
82
93
|
help Show this help
|
|
83
94
|
--version, -V Print package version
|
|
84
95
|
|
|
@@ -102,6 +113,9 @@ Examples:
|
|
|
102
113
|
./agent-config telemetry:status --format json
|
|
103
114
|
./agent-config telemetry:report --since 30d --top 20
|
|
104
115
|
./agent-config telemetry:report --since 7d --format json --top 0
|
|
116
|
+
./agent-config council:estimate prompt.txt
|
|
117
|
+
./agent-config council:run prompt.txt --output agents/council-sessions/out.json --confirm
|
|
118
|
+
./agent-config council:render agents/council-sessions/out.json
|
|
105
119
|
|
|
106
120
|
All commands operate on the CURRENT DIRECTORY (your project root).
|
|
107
121
|
The CLI is strictly consumer-facing. Maintainer tasks live in Taskfile.yml.
|
|
@@ -343,6 +357,20 @@ cmd_context_hygiene_hook() {
|
|
|
343
357
|
exec python3 "$script" "$@"
|
|
344
358
|
}
|
|
345
359
|
|
|
360
|
+
cmd_dispatch_hook() {
|
|
361
|
+
require_python3
|
|
362
|
+
local script
|
|
363
|
+
script="$(resolve_script "scripts/hooks/dispatch_hook.py")" || return 1
|
|
364
|
+
exec python3 "$script" "$@"
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
cmd_hooks_status() {
|
|
368
|
+
require_python3
|
|
369
|
+
local script
|
|
370
|
+
script="$(resolve_script "scripts/hooks_status.py")" || return 1
|
|
371
|
+
exec python3 "$script" "$@"
|
|
372
|
+
}
|
|
373
|
+
|
|
346
374
|
cmd_chat_history_checkpoint() {
|
|
347
375
|
require_python3
|
|
348
376
|
local script
|
|
@@ -438,6 +466,17 @@ cmd_keys_install_openai() {
|
|
|
438
466
|
exec bash "$script" "$@"
|
|
439
467
|
}
|
|
440
468
|
|
|
469
|
+
# Council CLI — non-interactive wrapper around scripts.ai_council.orchestrator.
|
|
470
|
+
# Three subcommands share one Python entry point; we forward the subcommand
|
|
471
|
+
# verb so `./agent-config council:run --confirm` lands on `council_cli.py run`.
|
|
472
|
+
cmd_council() {
|
|
473
|
+
require_python3
|
|
474
|
+
local sub="$1"; shift || true
|
|
475
|
+
local script
|
|
476
|
+
script="$(resolve_script "scripts/council_cli.py")" || return 1
|
|
477
|
+
exec env PYTHONPATH="$PACKAGE_ROOT" python3 "$script" "$sub" "$@"
|
|
478
|
+
}
|
|
479
|
+
|
|
441
480
|
main() {
|
|
442
481
|
local cmd="${1-}"
|
|
443
482
|
[[ $# -gt 0 ]] && shift || true
|
|
@@ -466,9 +505,14 @@ main() {
|
|
|
466
505
|
roadmap-progress:hook) cmd_roadmap_progress_hook "$@" ;;
|
|
467
506
|
onboarding-gate:hook) cmd_onboarding_gate_hook "$@" ;;
|
|
468
507
|
context-hygiene:hook) cmd_context_hygiene_hook "$@" ;;
|
|
508
|
+
dispatch:hook) cmd_dispatch_hook "$@" ;;
|
|
509
|
+
hooks:status) cmd_hooks_status "$@" ;;
|
|
469
510
|
telemetry:record) cmd_telemetry_record "$@" ;;
|
|
470
511
|
telemetry:status) cmd_telemetry_status "$@" ;;
|
|
471
512
|
telemetry:report) cmd_telemetry_report "$@" ;;
|
|
513
|
+
council:estimate) cmd_council estimate "$@" ;;
|
|
514
|
+
council:run) cmd_council run "$@" ;;
|
|
515
|
+
council:render) cmd_council render "$@" ;;
|
|
472
516
|
help|--help|-h|"") usage ;;
|
|
473
517
|
--version|-V) print_version ;;
|
|
474
518
|
*)
|
|
@@ -38,11 +38,11 @@ class CouncilContext:
|
|
|
38
38
|
# placeholder. Order matters — the most specific pattern goes first.
|
|
39
39
|
|
|
40
40
|
_REDACTION_LINE_PATTERNS: list[tuple[re.Pattern[str], str]] = [
|
|
41
|
-
(re.compile(r"
|
|
41
|
+
(re.compile(r"~?/?\.config/agent-config/[^/\s]+\.key"),
|
|
42
42
|
"[redacted: agent-config key path]"),
|
|
43
|
-
(re.compile(r"^\s*Authorization:\s
|
|
43
|
+
(re.compile(r"^\s*Authorization:\s", re.IGNORECASE),
|
|
44
44
|
"[redacted: Authorization header]"),
|
|
45
|
-
(re.compile(r"(?i)
|
|
45
|
+
(re.compile(r"(?i)(api[_-]?key|secret|token|password)\s*[:=]"),
|
|
46
46
|
"[redacted: secret-like assignment]"),
|
|
47
47
|
(re.compile(r"sk-ant-[A-Za-z0-9_\-]{8,}"), "[redacted: anthropic-key-like token]"),
|
|
48
48
|
(re.compile(r"sk-[A-Za-z0-9_\-]{20,}"), "[redacted: openai-key-like token]"),
|
|
@@ -34,6 +34,16 @@ OPENAI_KEY_PATH = Path.home() / ".config" / "agent-config" / "openai.key"
|
|
|
34
34
|
DEFAULT_ANTHROPIC_MODEL = "claude-sonnet-4-5"
|
|
35
35
|
DEFAULT_OPENAI_MODEL = "gpt-4o"
|
|
36
36
|
|
|
37
|
+
# OpenAI reasoning models (o1, o3, o4 families) reject `max_tokens` and the
|
|
38
|
+
# `system` role; they require `max_completion_tokens` and accept only `user`
|
|
39
|
+
# (and `developer`) messages.
|
|
40
|
+
_REASONING_PREFIXES = ("o1", "o3", "o4")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _is_reasoning_model(model: str) -> bool:
|
|
44
|
+
name = model.lower()
|
|
45
|
+
return any(name == p or name.startswith(p + "-") for p in _REASONING_PREFIXES)
|
|
46
|
+
|
|
37
47
|
|
|
38
48
|
class KeyGateError(RuntimeError):
|
|
39
49
|
"""Raised when a provider key file violates the 0600 contract."""
|
|
@@ -189,15 +199,21 @@ class OpenAIClient(ExternalAIClient):
|
|
|
189
199
|
|
|
190
200
|
def ask(self, system_prompt: str, user_prompt: str, max_tokens: int = 1024) -> CouncilResponse:
|
|
191
201
|
t0 = time.monotonic()
|
|
202
|
+
kwargs: dict[str, object] = {"model": self.model}
|
|
203
|
+
if _is_reasoning_model(self.model):
|
|
204
|
+
# o1/o3/o4 reasoning models reject `max_tokens` and `system` role.
|
|
205
|
+
kwargs["max_completion_tokens"] = max_tokens
|
|
206
|
+
kwargs["messages"] = [
|
|
207
|
+
{"role": "user", "content": f"{system_prompt}\n\n---\n\n{user_prompt}"},
|
|
208
|
+
]
|
|
209
|
+
else:
|
|
210
|
+
kwargs["max_tokens"] = max_tokens
|
|
211
|
+
kwargs["messages"] = [
|
|
212
|
+
{"role": "system", "content": system_prompt},
|
|
213
|
+
{"role": "user", "content": user_prompt},
|
|
214
|
+
]
|
|
192
215
|
try:
|
|
193
|
-
response = self._client.chat.completions.create(
|
|
194
|
-
model=self.model,
|
|
195
|
-
max_tokens=max_tokens,
|
|
196
|
-
messages=[
|
|
197
|
-
{"role": "system", "content": system_prompt},
|
|
198
|
-
{"role": "user", "content": user_prompt},
|
|
199
|
-
],
|
|
200
|
-
)
|
|
216
|
+
response = self._client.chat.completions.create(**kwargs)
|
|
201
217
|
except Exception as exc: # noqa: BLE001 - normalise all SDK errors
|
|
202
218
|
return CouncilResponse(
|
|
203
219
|
provider=self.name, model=self.model, text="",
|
|
@@ -8,6 +8,28 @@
|
|
|
8
8
|
> `scripts/check_one_off_location.py` enforces that no new
|
|
9
9
|
> `_one_off_*.py` lands outside this folder.
|
|
10
10
|
|
|
11
|
+
## Going forward — use the CLI, not new one-offs
|
|
12
|
+
|
|
13
|
+
> **Canonical pattern (Phase 6.7+):** new council runs go through
|
|
14
|
+
> `./agent-config council:{estimate,run,render}`. The CLI handles
|
|
15
|
+
> bundling, redaction, the cost gate, the `0600` key contract, the
|
|
16
|
+
> `enabled` check, and session persistence — every concern these
|
|
17
|
+
> archived one-offs reimplemented inline.
|
|
18
|
+
>
|
|
19
|
+
> ```bash
|
|
20
|
+
> ./agent-config council:estimate <question.md>
|
|
21
|
+
> ./agent-config council:run <question.md> \
|
|
22
|
+
> --output agents/council-sessions/<UTC-ts>.json --confirm
|
|
23
|
+
> ./agent-config council:render agents/council-sessions/<UTC-ts>.json
|
|
24
|
+
> ```
|
|
25
|
+
>
|
|
26
|
+
> Wire-level access (`scripts.ai_council.orchestrator`,
|
|
27
|
+
> `scripts.ai_council.bundler`) is still public for tests and library
|
|
28
|
+
> use, but writing a new `_one_off_*.py` purely to fan out to the
|
|
29
|
+
> council members is **not** the path. The scripts below are kept as
|
|
30
|
+
> historical evidence of the runs that produced specific roadmap
|
|
31
|
+
> decisions; they are not a template for new work.
|
|
32
|
+
|
|
11
33
|
## Lifecycle rule (uniform — Phase 0.2 of context-layer-maturity)
|
|
12
34
|
|
|
13
35
|
> A one-off is **archived**, never deleted. The session manifest under
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
"""One-off Phase-1 round-trip runner.
|
|
1
|
+
"""One-off Phase-1 round-trip runner — HISTORICAL ARCHIVE.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
the capture-only fence on `road-to-ai-council.md` Phase 2+ and the
|
|
5
|
-
end-to-end verification on `road-to-council-modes.md` Phase 2a.
|
|
3
|
+
Going forward, council runs go through the CLI:
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
./agent-config council:estimate <question.md>
|
|
6
|
+
./agent-config council:run <question.md> \
|
|
7
|
+
--output agents/council-sessions/<UTC-ts>.json --confirm
|
|
8
|
+
./agent-config council:render agents/council-sessions/<UTC-ts>.json
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
This script predates `scripts/council_cli.py` (Phase 6.7) and is kept
|
|
11
|
+
only as the evidence artefact that lifted the capture-only fence on
|
|
12
|
+
`road-to-ai-council.md` Phase 2+ and the end-to-end verification on
|
|
13
|
+
`road-to-council-modes.md` Phase 2a. Do **not** copy it as a template
|
|
14
|
+
for new one-offs — write a question file and use the CLI instead.
|
|
15
|
+
|
|
16
|
+
Invocation (historical):
|
|
12
17
|
.venv/bin/python -m scripts.ai_council._one_off_roundtrip
|
|
13
18
|
"""
|
|
14
19
|
from __future__ import annotations
|