@event4u/agent-config 1.19.0 → 1.20.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/agent-handoff.md +14 -10
- package/.agent-src/commands/chat-history/import.md +170 -0
- package/.agent-src/commands/chat-history/learn.md +178 -0
- package/.agent-src/commands/chat-history/show.md +17 -18
- package/.agent-src/commands/chat-history.md +26 -25
- package/.agent-src/commands/council/default.md +4 -7
- package/.agent-src/commands/create-pr.md +28 -8
- package/.agent-src/commands/sync-gitignore.md +1 -1
- package/.agent-src/contexts/communication/rules-auto/skill-quality-mechanics.md +76 -0
- package/.agent-src/contexts/communication/rules-auto/slash-command-routing-policy-mechanics.md +3 -3
- package/.agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +5 -12
- package/.agent-src/rules/direct-answers.md +10 -2
- package/.agent-src/rules/language-and-tone.md +37 -6
- package/.agent-src/rules/no-attribution-footers.md +48 -0
- package/.agent-src/rules/no-roadmap-references.md +1 -1
- package/.agent-src/rules/skill-quality.md +49 -0
- package/.agent-src/rules/user-interaction.md +21 -5
- package/.agent-src/skills/ai-council/SKILL.md +4 -5
- package/.agent-src/skills/dcf-modeling/SKILL.md +89 -0
- package/.agent-src/skills/funnel-analysis/SKILL.md +100 -0
- package/.agent-src/skills/md-language-check/SKILL.md +1 -1
- package/.agent-src/skills/okr-tree-modeling/SKILL.md +93 -0
- package/.agent-src/skills/rice-prioritization/SKILL.md +100 -0
- package/.agent-src/skills/subagent-orchestration/SKILL.md +34 -2
- package/.agent-src/skills/unit-economics-modeling/SKILL.md +104 -0
- package/.agent-src/skills/using-git-worktrees/SKILL.md +1 -0
- package/.agent-src/templates/agent-settings.md +5 -26
- package/.agent-src/templates/scripts/work_engine/hook_bootstrap.py +7 -5
- package/.agent-src/templates/scripts/work_engine/hooks/__init__.py +0 -4
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +0 -4
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/_chat_history_base.py +7 -51
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_append.py +1 -2
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_halt_append.py +1 -2
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.py +2 -3
- package/.agent-src/templates/skill.md +30 -1
- package/.claude-plugin/marketplace.json +8 -4
- package/AGENTS.md +44 -3
- package/CHANGELOG.md +111 -0
- package/README.md +6 -6
- package/config/agent-settings.template.yml +19 -13
- package/config/gitignore-block.txt +4 -4
- package/docs/architecture.md +3 -3
- package/docs/catalog.md +14 -12
- package/docs/contracts/adr-chat-history-split.md +10 -1
- package/docs/contracts/command-clusters.md +1 -1
- package/docs/contracts/cross-wing-handoff.md +133 -0
- package/docs/contracts/file-ownership-matrix.json +341 -126
- package/docs/contracts/hook-architecture-v1.md +8 -1
- package/docs/contracts/memory-visibility-v1.md +8 -24
- package/docs/customization.md +1 -1
- package/docs/getting-started.md +21 -29
- package/docs/guidelines/agent-infra/ask-when-uncertain-demos.md +1 -1
- package/docs/hook-payload-capture.md +221 -0
- package/docs/migrations/commands-1.15.0.md +17 -12
- package/docs/skills-catalog.md +5 -4
- package/llms.txt +4 -3
- package/package.json +1 -1
- package/scripts/agent-config +1 -1
- package/scripts/ai_council/_default_prices.py +4 -4
- package/scripts/ai_council/clients.py +1 -1
- package/scripts/ai_council/modes.py +3 -4
- package/scripts/ai_council/pricing.py +10 -9
- package/scripts/build_rule_trigger_matrix.py +1 -9
- package/scripts/chat_history.py +952 -596
- package/scripts/check_references.py +12 -2
- package/scripts/council_cli.py +54 -4
- package/scripts/hook_manifest.yaml +33 -0
- package/scripts/hooks/augment-chat-history.sh +10 -0
- package/scripts/hooks/cowork-dispatcher.sh +98 -0
- package/scripts/hooks/dispatch_hook.py +35 -0
- package/scripts/hooks_status.py +12 -1
- package/scripts/install-hooks.sh +2 -2
- package/scripts/install.sh +37 -0
- package/scripts/lint_handoffs.py +214 -0
- package/scripts/lint_hook_manifest.py +2 -1
- package/scripts/redact_hook_capture.py +148 -0
- package/scripts/schemas/skill.schema.json +5 -0
- package/scripts/skill_linter.py +163 -1
- package/scripts/update_prices.py +3 -3
- package/.agent-src/commands/chat-history/checkpoint.md +0 -126
- package/.agent-src/commands/chat-history/clear.md +0 -103
- package/.agent-src/commands/chat-history/resume.md +0 -183
- package/.agent-src/rules/chat-history-cadence.md +0 -143
- package/.agent-src/rules/chat-history-ownership.md +0 -124
- package/.agent-src/rules/chat-history-visibility.md +0 -97
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_heartbeat.py +0 -50
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_turn_check.py +0 -49
- package/scripts/check_phase_coupling.py +0 -148
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: chat-history:resume
|
|
3
|
-
cluster: chat-history
|
|
4
|
-
sub: resume
|
|
5
|
-
description: Load the persistent chat-history log into the current conversation — picks match/returning/foreign flow and supports resume, merge, replace, or continue
|
|
6
|
-
disable-model-invocation: true
|
|
7
|
-
suggestion:
|
|
8
|
-
eligible: false
|
|
9
|
-
rationale: "Explicit resume mechanic with foreign/returning state machine."
|
|
10
|
-
---
|
|
11
|
-
<!-- cloud_safe: noop -->
|
|
12
|
-
|
|
13
|
-
# /chat-history resume
|
|
14
|
-
Reconnects the current conversation with an existing `.agent-chat-history`
|
|
15
|
-
file. Depending on the 4-state ownership check, it routes to the right
|
|
16
|
-
flow: silent summarize, adopt, merge, or replace.
|
|
17
|
-
|
|
18
|
-
Use after a crashed chat, after switching tools (Augment → Claude Code),
|
|
19
|
-
or when the agent showed the foreign- or returning-session prompt from
|
|
20
|
-
the [`chat-history`](../rules/chat-history-ownership.md) rule and the user picked
|
|
21
|
-
"resume".
|
|
22
|
-
|
|
23
|
-
## When NOT to use
|
|
24
|
-
|
|
25
|
-
- Just inspect metadata → [`/chat-history`](chat-history.md).
|
|
26
|
-
- Start fresh instead of resuming → [`/chat-history-clear`](chat-history-clear.md)
|
|
27
|
-
or pick "New start" in the foreign prompt.
|
|
28
|
-
- Logging is disabled (`chat_history.enabled: false`) → enable it in
|
|
29
|
-
`.agent-settings.yml` first; this command refuses to run otherwise.
|
|
30
|
-
|
|
31
|
-
## Preconditions
|
|
32
|
-
|
|
33
|
-
- `.agent-settings.yml` exists and `chat_history.enabled: true`.
|
|
34
|
-
- `.agent-chat-history` exists at the project root.
|
|
35
|
-
|
|
36
|
-
If either is missing, tell the user and stop — do not create files here.
|
|
37
|
-
|
|
38
|
-
## Steps
|
|
39
|
-
|
|
40
|
-
### 1. Load status
|
|
41
|
-
|
|
42
|
-
Run `scripts/chat_history.py status`. If `exists: false` or
|
|
43
|
-
`entries: 0`, tell the user there is nothing to resume and stop.
|
|
44
|
-
|
|
45
|
-
### 2. Determine ownership state
|
|
46
|
-
|
|
47
|
-
Run `scripts/chat_history.py state --first-user-msg "<current first user
|
|
48
|
-
message>"`. Branch on the result:
|
|
49
|
-
|
|
50
|
-
- `match` → step 3a (already owner)
|
|
51
|
-
- `returning` → step 3b (pick Merge / Replace / Continue)
|
|
52
|
-
- `foreign` → step 3c (pick Resume / New start / Ignore)
|
|
53
|
-
- `missing` → header corrupt; tell the user and suggest
|
|
54
|
-
`/chat-history-clear` to start fresh. Stop.
|
|
55
|
-
|
|
56
|
-
### 3a. `match` — already owner
|
|
57
|
-
|
|
58
|
-
Nothing to adopt. Skip to step 4 (summarize) for the user's benefit.
|
|
59
|
-
|
|
60
|
-
### 3b. `returning` — this chat once owned the file
|
|
61
|
-
|
|
62
|
-
Another session took over. Present the Returning-Prompt from the rule
|
|
63
|
-
and wait for a number:
|
|
64
|
-
|
|
65
|
-
```
|
|
66
|
-
> 📒 Welcome back. This chat once owned the history file; another
|
|
67
|
-
> session has written to it since.
|
|
68
|
-
>
|
|
69
|
-
> On-file entries: {N} Size: {X} KB
|
|
70
|
-
>
|
|
71
|
-
> 1. Merge — my in-memory history first, the foreign entries after
|
|
72
|
-
> 2. Replace — wipe the foreign entries, keep only my history
|
|
73
|
-
> 3. Continue — leave the file as-is; only new entries from now on
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
- **1 (Merge)** — build the in-memory entries list (see below), then:
|
|
77
|
-
```bash
|
|
78
|
-
scripts/chat_history.py prepend --entries-json '<list>'
|
|
79
|
-
scripts/chat_history.py adopt --first-user-msg "<msg>"
|
|
80
|
-
```
|
|
81
|
-
- **2 (Replace)** — build the in-memory entries list, then:
|
|
82
|
-
```bash
|
|
83
|
-
scripts/chat_history.py reset --first-user-msg "<msg>" \
|
|
84
|
-
--freq <frequency> --entries-json '<list>'
|
|
85
|
-
```
|
|
86
|
-
- **3 (Continue)** —
|
|
87
|
-
```bash
|
|
88
|
-
scripts/chat_history.py adopt --first-user-msg "<msg>"
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### 3c. `foreign` — new chat finds an unknown session's file
|
|
92
|
-
|
|
93
|
-
Present the Foreign-Prompt from the rule and wait for a number:
|
|
94
|
-
|
|
95
|
-
```
|
|
96
|
-
> 📒 Found chat history from an unknown session.
|
|
97
|
-
>
|
|
98
|
-
> Entries on file: {N} Size: {X} KB
|
|
99
|
-
>
|
|
100
|
-
> 1. Resume — adopt this file, load entries as context, keep appending
|
|
101
|
-
> 2. New start — archive to .agent-chat-history.bak, init fresh
|
|
102
|
-
> 3. Ignore — leave the file untouched, disable logging for this session
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
- **1 (Resume)** — `adopt --first-user-msg "<msg>"`.
|
|
106
|
-
- **2 (New start)** — rename file to `.agent-chat-history.bak`, run
|
|
107
|
-
`init --first-user-msg "<msg>" --freq <frequency>`. Skip summary.
|
|
108
|
-
- **3 (Ignore)** — do nothing. Report that logging is off for this
|
|
109
|
-
conversation. Stop.
|
|
110
|
-
|
|
111
|
-
### 3d. Building the in-memory entries list (Merge / Replace)
|
|
112
|
-
|
|
113
|
-
Reconstruct the agent's prior turns as a JSON array:
|
|
114
|
-
|
|
115
|
-
- one `{"t":"user","text":"<preview>","ts":"<iso>"}` per user message
|
|
116
|
-
- one `{"t":"agent","text":"<preview>","ts":"<iso>"}` per agent reply
|
|
117
|
-
- `text` previews capped at ~200 chars (whitespace flattened)
|
|
118
|
-
- timestamps in ISO-8601 UTC (current time is acceptable if exact times
|
|
119
|
-
are unknown; order is what matters)
|
|
120
|
-
- no tool-call payloads, no file contents, no secrets
|
|
121
|
-
|
|
122
|
-
If the list is large (>30 KB), stream it via stdin:
|
|
123
|
-
`prepend --entries-stdin` or `reset --entries-stdin`.
|
|
124
|
-
|
|
125
|
-
### 4. Overflow check
|
|
126
|
-
|
|
127
|
-
After any Merge or Replace, run
|
|
128
|
-
`scripts/chat_history.py rotate --max-kb <max_size_kb>
|
|
129
|
-
--mode <on_overflow>` so the combined body stays within the user's
|
|
130
|
-
budget.
|
|
131
|
-
|
|
132
|
-
### 5. Summarize into conversation context
|
|
133
|
-
|
|
134
|
-
Read the entries via the helper (`read --all` or `read --last N` for
|
|
135
|
-
bounded). Produce a short, structured summary — **not** a verbatim dump:
|
|
136
|
-
|
|
137
|
-
```
|
|
138
|
-
> 📒 Resumed chat-history ({entries} entries, {age})
|
|
139
|
-
>
|
|
140
|
-
> ## What was done
|
|
141
|
-
> - {1–3 bullets from agent/decision entries}
|
|
142
|
-
>
|
|
143
|
-
> ## Open threads
|
|
144
|
-
> - {1–3 bullets from the most recent entries and any pending questions}
|
|
145
|
-
>
|
|
146
|
-
> ## Key decisions
|
|
147
|
-
> - {decisions captured during the prior session}
|
|
148
|
-
>
|
|
149
|
-
> Ready to continue. What would you like to do?
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
Keep the summary under ~25 lines. Rationale: this is input into the new
|
|
153
|
-
turn, not a display artifact (see
|
|
154
|
-
[`token-efficiency`](../rules/token-efficiency.md)).
|
|
155
|
-
|
|
156
|
-
### 6. Hand control back to the user
|
|
157
|
-
|
|
158
|
-
After presenting the summary, stop and wait. Do not auto-resume work —
|
|
159
|
-
the user decides what to do next.
|
|
160
|
-
|
|
161
|
-
## Gotchas
|
|
162
|
-
|
|
163
|
-
- **Iron law — one question at a time.** Even if the log contains
|
|
164
|
-
several open threads, ask about one at a time after the summary
|
|
165
|
-
(see [`ask-when-uncertain`](../rules/ask-when-uncertain.md)).
|
|
166
|
-
- `adopt` rewrites the header in place and pushes the previous fp into
|
|
167
|
-
`former_fps` (capped at 10). No backup is created — use
|
|
168
|
-
`/chat-history-clear` first if you want a clean slate.
|
|
169
|
-
- `reset` discards whatever was on disk. Only use it when the user
|
|
170
|
-
explicitly picks "Replace".
|
|
171
|
-
- The summary is derived from the agent's reading of the JSONL. Old
|
|
172
|
-
entries may be incomplete (especially under `per_turn` with
|
|
173
|
-
previews only). Flag gaps explicitly.
|
|
174
|
-
|
|
175
|
-
## See also
|
|
176
|
-
|
|
177
|
-
- [`chat-history`](../rules/chat-history-ownership.md) — the rule that triggers
|
|
178
|
-
this command via the foreign- and returning-session prompts
|
|
179
|
-
- [`/chat-history`](chat-history.md) — status inspection without adopting
|
|
180
|
-
- [`/chat-history-clear`](chat-history-clear.md) — wipe instead of adopt
|
|
181
|
-
- [`/agent-handoff`](agent-handoff.md) — complementary: generates a
|
|
182
|
-
paste-into-new-chat prompt (no disk file)
|
|
183
|
-
- [`scripts/chat_history.py`](../../../scripts/chat_history.py) — helper API
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: "auto"
|
|
3
|
-
tier: "mechanical-already"
|
|
4
|
-
description: "Appending to .agent-chat-history — cadence boundaries (per_turn/per_phase/per_tool), turn-check ownership refusal handling, never writing the file directly; cadence is the trigger, not reply length"
|
|
5
|
-
alwaysApply: false
|
|
6
|
-
source: package
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
<!-- cloud_safe: noop -->
|
|
10
|
-
|
|
11
|
-
# Chat History — Cadence
|
|
12
|
-
|
|
13
|
-
Owns gate 2 of the chat-history Iron Law: WHEN to call `append`.
|
|
14
|
-
Ownership (gate 1) lives in [`chat-history-ownership`](chat-history-ownership.md);
|
|
15
|
-
visibility (gate 3) lives in [`chat-history-visibility`](chat-history-visibility.md).
|
|
16
|
-
File I/O is owned by [`scripts/chat_history.py`](../../../scripts/chat_history.py)
|
|
17
|
-
— this rule decides the trigger boundaries, not the file format.
|
|
18
|
-
|
|
19
|
-
## Iron Law — gate 2 (append at every cadence boundary)
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
EVERY CADENCE BOUNDARY GETS ONE append CALL — WITH --first-user-msg.
|
|
23
|
-
SKIPPING IS A RULE VIOLATION. NEVER WRITE .agent-chat-history DIRECTLY.
|
|
24
|
-
ON HOOK / ENGINE PATHS, THE PLATFORM / ENGINE PERFORMS append STRUCTURALLY
|
|
25
|
-
— THE AGENT MUST NOT DUPLICATE.
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Cadence is **gated by ownership**: gate 1 (turn-check from
|
|
29
|
-
[`chat-history-ownership`](chat-history-ownership.md)) must have cleared
|
|
30
|
-
this turn before any append fires. Skip turn-check → append refuses with
|
|
31
|
-
exit `3` (`OWNERSHIP_REFUSED`).
|
|
32
|
-
|
|
33
|
-
## Append cadence — boundaries
|
|
34
|
-
|
|
35
|
-
Cadence comes from `chat_history.frequency` in `.agent-settings.yml`:
|
|
36
|
-
|
|
37
|
-
- `per_turn` → one entry at the end of every agent turn.
|
|
38
|
-
- `per_phase` → at phase boundaries (user question answered, decision
|
|
39
|
-
taken, task-list item completed, significant tool sequence finished).
|
|
40
|
-
Pure clarification turns may skip.
|
|
41
|
-
- `per_tool` → after each tool-call sequence.
|
|
42
|
-
|
|
43
|
-
Every append goes through
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
scripts/chat_history.py append --first-user-msg "<msg>" \
|
|
47
|
-
--type <user|agent|tool|decision|phase> --json '<obj>'
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
Never write the file directly. Prefer `phase` over `agent` for boundaries.
|
|
51
|
-
**Cadence is the trigger, not reply length** — a one-line reply that
|
|
52
|
-
crosses a boundary still appends; a 200-line reply that doesn't cross
|
|
53
|
-
one still doesn't. **Do not batch missed turns** — crashes happen
|
|
54
|
-
between turns, and a batched append loses the timeline that crash
|
|
55
|
-
recovery depends on.
|
|
56
|
-
|
|
57
|
-
## Ownership refusal — exit `3`
|
|
58
|
-
|
|
59
|
-
`append` exits `3` when the file's ownership header doesn't match the
|
|
60
|
-
current session (`turn-check` was skipped or the file was hijacked
|
|
61
|
-
between turns). Surface the failure to the user, **do not swallow it**:
|
|
62
|
-
|
|
63
|
-
1. Stop the current append.
|
|
64
|
-
2. Render the Foreign-Prompt from [`chat-history-ownership`](chat-history-ownership.md)
|
|
65
|
-
so the user re-handshakes.
|
|
66
|
-
3. Resume cadence only after gate 1 clears again.
|
|
67
|
-
|
|
68
|
-
This is the structural enforcement layer that catches the case where
|
|
69
|
-
an agent technically called `turn-check` but on the wrong platform path
|
|
70
|
-
(e.g. HOOK platform also tried CHECKPOINT cooperative gates and the
|
|
71
|
-
file got into a mixed state).
|
|
72
|
-
|
|
73
|
-
## Path-specific behavior
|
|
74
|
-
|
|
75
|
-
**CHECKPOINT path** — gates 1 + 2 are cooperative. The agent runs
|
|
76
|
-
`turn-check` first, then runs `append` at every cadence boundary.
|
|
77
|
-
`/chat-history-checkpoint` is the recommended boundary-trigger; it
|
|
78
|
-
captures phase entries with the right metadata.
|
|
79
|
-
|
|
80
|
-
**HOOK path** — `work_engine` and platform `SessionStart` /
|
|
81
|
-
`PreToolUse` hooks fire `turn-check` and `append` structurally. The
|
|
82
|
-
agent must **not** call either — double-write produces interleaved
|
|
83
|
-
entries and breaks the JSONL schema. Hooks emit the same exit codes,
|
|
84
|
-
so refusal handling stays identical.
|
|
85
|
-
|
|
86
|
-
**ENGINE path** — `work_engine` fires `append --type phase` per
|
|
87
|
-
successful step and `append --type decision` on halt. Free-form prose
|
|
88
|
-
around the engine output falls back to whatever path the platform
|
|
89
|
-
supplies (HOOK or CHECKPOINT). Engine-driven turns inherit the
|
|
90
|
-
structural guarantee for the duration of the dispatch cycle only.
|
|
91
|
-
|
|
92
|
-
## What this rule does NOT do
|
|
93
|
-
|
|
94
|
-
Manage ownership decisions (handshake, foreign/returning) — those
|
|
95
|
-
live in [`chat-history-ownership`](chat-history-ownership.md).
|
|
96
|
-
Manage the heartbeat marker — that lives in [`chat-history-visibility`](chat-history-visibility.md).
|
|
97
|
-
Decide cadence dynamically based on reply content — cadence is
|
|
98
|
-
the configured frequency, period.
|
|
99
|
-
|
|
100
|
-
## Cloud Behavior
|
|
101
|
-
|
|
102
|
-
On cloud surfaces (Claude.ai Web, Skills API) cadence is **inert** —
|
|
103
|
-
no append calls, no `scripts/`, no JSONL file. Treat
|
|
104
|
-
`chat_history.enabled` as `false`.
|
|
105
|
-
|
|
106
|
-
## Copilot fallback
|
|
107
|
-
|
|
108
|
-
GitHub Copilot has no `SessionStart` / `PostToolUse` / `Stop` hook
|
|
109
|
-
surface, so `scripts/chat_history.py hook-dispatch` cannot fire
|
|
110
|
-
structurally. `.agent-chat-history` is not maintained for the agent
|
|
111
|
-
unless the agent runs the cooperative path itself.
|
|
112
|
-
|
|
113
|
-
Treat Copilot as the **CHECKPOINT path** described above — gates 1 +
|
|
114
|
-
2 are cooperative:
|
|
115
|
-
|
|
116
|
-
1. Run `turn-check` on the first turn (and after any ownership
|
|
117
|
-
reset) to establish ownership against the first user message.
|
|
118
|
-
2. Run `append --first-user-msg "<msg>" --type <…> --json '<…>'` at
|
|
119
|
-
every cadence boundary, exactly as the cooperative path
|
|
120
|
-
specifies.
|
|
121
|
-
3. Surface ownership-refusal exit `3` to the user; do not swallow it.
|
|
122
|
-
|
|
123
|
-
The state "file" the hook would have written is `.agent-chat-history`
|
|
124
|
-
itself (JSONL, project root). The manual command that reproduces a
|
|
125
|
-
single hook-dispatch step is the same one the dispatcher would
|
|
126
|
-
have invoked:
|
|
127
|
-
|
|
128
|
-
```bash
|
|
129
|
-
scripts/chat_history.py hook-dispatch \
|
|
130
|
-
--platform copilot \
|
|
131
|
-
--event <session_start|post_tool_use|stop> \
|
|
132
|
-
< envelope.json
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
— but in practice the cooperative `turn-check` + `append` calls
|
|
136
|
-
above are the supported Copilot path; `hook-dispatch` is mainly the
|
|
137
|
-
dispatcher's entry point.
|
|
138
|
-
|
|
139
|
-
## Interactions & references
|
|
140
|
-
|
|
141
|
-
- Sibling rules: [`chat-history-ownership`](chat-history-ownership.md) (gate 1 — turn-check) · [`chat-history-visibility`](chat-history-visibility.md) (gate 3 — heartbeat marker).
|
|
142
|
-
- `token-efficiency` — never load the full log; cadence keeps appends small and bounded.
|
|
143
|
-
- API: [`scripts/chat_history.py`](../../../scripts/chat_history.py) `append` subcommand. Settings: [`agent-settings`](../templates/agent-settings.md) `chat_history.frequency`. Engine hooks: [`agents/contexts/work-engine-hooks.md`](../../../agents/contexts/work-engine-hooks.md).
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: "auto"
|
|
3
|
-
tier: "1"
|
|
4
|
-
description: "First turn or reference to .agent-chat-history — detects ownership (match/returning/foreign/missing) and HOOK/ENGINE/CHECKPOINT/MANUAL path classification with numbered-options prompt"
|
|
5
|
-
alwaysApply: false
|
|
6
|
-
source: package
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
<!-- cloud_safe: noop -->
|
|
10
|
-
|
|
11
|
-
# Chat History — Ownership
|
|
12
|
-
|
|
13
|
-
Owns gate 1 of the chat-history Iron Law: the first-turn handshake
|
|
14
|
-
that decides whose `.agent-chat-history` file the current session is
|
|
15
|
-
talking to. Cadence (gate 2) lives in [`chat-history-cadence`](chat-history-cadence.md);
|
|
16
|
-
visibility (gate 3) lives in [`chat-history-visibility`](chat-history-visibility.md).
|
|
17
|
-
File I/O is owned by [`scripts/chat_history.py`](../../../scripts/chat_history.py)
|
|
18
|
-
— this rule says **when** to handshake, not how the file is structured.
|
|
19
|
-
|
|
20
|
-
## Two paths — platform decides which Iron Law applies
|
|
21
|
-
|
|
22
|
-
Population of `.agent-chat-history` is **structural** (platform-driven)
|
|
23
|
-
on platforms with native lifecycle hooks, and **cooperative**
|
|
24
|
-
(agent-driven) on platforms without. Both paths converge on the same
|
|
25
|
-
JSONL schema; only the trigger differs. Per-platform classification
|
|
26
|
-
lives in
|
|
27
|
-
[`agents/contexts/chat-history-platform-hooks.md`](../../../agents/contexts/chat-history-platform-hooks.md).
|
|
28
|
-
|
|
29
|
-
| Path | Platforms / Surfaces | Trigger | Agent's role |
|
|
30
|
-
|---|---|---|---|
|
|
31
|
-
| **HOOK** | Claude Code, Augment CLI, Cursor 1.7+, Cline non-Windows, Windsurf, Gemini CLI | Platform fires native lifecycle hooks → `./agent-config chat-history:hook --platform <name>` | Read-only — observe, do not duplicate appends |
|
|
32
|
-
| **ENGINE** | `/implement-ticket`, `/work`, any flow driven by `scripts/work_engine/cli.py` | `work_engine` fires `turn-check` (before-dispatch), `append --type phase` (per successful step), `--type decision` (on-halt), `heartbeat` (after-dispatch) via the hook layer | Read-only during engine-driven turns — do not duplicate appends. See [`agents/contexts/work-engine-hooks.md`](../../../agents/contexts/work-engine-hooks.md) |
|
|
33
|
-
| **CHECKPOINT** | Augment IDE plugin, Cursor < 1.7, Cline on Windows | Agent invokes `/chat-history-checkpoint` at phase boundaries | Cooperative — gates 1 + 2 + 3 are mandatory |
|
|
34
|
-
| **MANUAL** | Cloud surfaces (Claude.ai Web, Skills API) | Rule is inert — see Cloud Behavior | None |
|
|
35
|
-
|
|
36
|
-
Detect the path on first turn: read `chat_history.platform` from
|
|
37
|
-
`.agent-settings.yml` if set, else fall back to `chat_history.path`
|
|
38
|
-
(`hook` / `checkpoint` / `manual`). Missing both → assume CHECKPOINT
|
|
39
|
-
(safest cooperative default; HOOK platforms install the platform
|
|
40
|
-
config explicitly via `scripts/install.py`).
|
|
41
|
-
|
|
42
|
-
## Iron Law — gate 1 (turn-check is the first tool call)
|
|
43
|
-
|
|
44
|
-
```
|
|
45
|
-
ON CHECKPOINT, turn-check IS THE FIRST TOOL CALL OF EVERY SESSION.
|
|
46
|
-
ON HOOK, THE PLATFORM FIRES turn-check STRUCTURALLY — DO NOT DUPLICATE.
|
|
47
|
-
ON ENGINE, work_engine FIRES turn-check BEFORE DISPATCH — DO NOT DUPLICATE.
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
CHECKPOINT path — run silently before any other tool call:
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
scripts/chat_history.py turn-check --first-user-msg "<first-user-msg>"
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Exit codes: `0` = `ok`/`disabled` (proceed), `10` = `missing`
|
|
57
|
-
(run `init --first-user-msg "..." --freq <freq>`), `11` = `foreign`
|
|
58
|
-
(render Foreign-Prompt + stop), `12` = `returning` (render
|
|
59
|
-
Returning-Prompt + stop). The script writes a one-line
|
|
60
|
-
`ACTION REQUIRED:` hint to stderr on non-zero exits.
|
|
61
|
-
|
|
62
|
-
## Activation & handshake
|
|
63
|
-
|
|
64
|
-
Read `chat_history.*` from `.agent-settings.yml` **once per conversation**
|
|
65
|
-
(first turn) and cache. `enabled: false` or section missing → all three
|
|
66
|
-
chat-history rules are a **no-op** (do not read, write, or mention the
|
|
67
|
-
file). Otherwise cache `frequency`, `max_size_kb`, `on_overflow`, and
|
|
68
|
-
the **path** (HOOK / CHECKPOINT / MANUAL — see the table above).
|
|
69
|
-
|
|
70
|
-
**HOOK path** — skip `turn-check` entirely. The platform's
|
|
71
|
-
`SessionStart` hook already initialized the file; the agent's job is to
|
|
72
|
-
read `status` once for context awareness (header preview, entry count)
|
|
73
|
-
and otherwise leave I/O to the hook dispatcher. Foreign / Returning
|
|
74
|
-
prompts still apply because hooks call into the same ownership state
|
|
75
|
-
machine — when the dispatcher reports `foreign` or `returning` via
|
|
76
|
-
exit code or stderr, render the corresponding prompt.
|
|
77
|
-
|
|
78
|
-
**CHECKPOINT path** — run `turn-check` as the first tool call. State
|
|
79
|
-
token branches to one of: `missing` → `init`, `ok` → continue,
|
|
80
|
-
`foreign` → Foreign-Prompt, `returning` → Returning-Prompt.
|
|
81
|
-
`/chat-history-checkpoint` is the recommended way to satisfy gate 2
|
|
82
|
-
at phase boundaries (see [`chat-history-cadence`](chat-history-cadence.md)).
|
|
83
|
-
|
|
84
|
-
In `foreign` and `returning`, **always read the file's current contents
|
|
85
|
-
into the agent's working context before any write** — the user chose to
|
|
86
|
-
log history for a reason; losing it silently is never acceptable. The
|
|
87
|
-
legacy `state` subcommand still works for shell scripts; agents prefer
|
|
88
|
-
`turn-check` (folds in `enabled` + distinct exit codes).
|
|
89
|
-
|
|
90
|
-
## Foreign / Returning prompts — full mechanics
|
|
91
|
-
|
|
92
|
-
When `turn-check` exits `11` (foreign) or `12` (returning), render the
|
|
93
|
-
matching numbered-options block from
|
|
94
|
-
[`agents/contexts/chat-history-handshake.md`](../../../agents/contexts/chat-history-handshake.md).
|
|
95
|
-
That doc holds the prompt bodies, the option → script-call mapping
|
|
96
|
-
(`adopt` / `init` / `prepend` / `reset`), the in-memory entries-list
|
|
97
|
-
shape, free-text fallbacks, and the overflow handling per
|
|
98
|
-
`on_overflow` (`rotate` / `compress`). Read it once on first foreign
|
|
99
|
-
or returning event; cache the chosen option for the rest of the
|
|
100
|
-
conversation.
|
|
101
|
-
|
|
102
|
-
## What this rule does NOT do
|
|
103
|
-
|
|
104
|
-
Display/reload/clear (`/chat-history*` commands). Auto-flip `enabled` or
|
|
105
|
-
`on_overflow`. Run when `enabled: false`. Decide ownership
|
|
106
|
-
heuristically — only `turn-check` / `state` does that. Double-write on
|
|
107
|
-
HOOK platforms — when hooks fire structurally, the agent does **not**
|
|
108
|
-
also call `turn-check`. Cadence (gate 2) and heartbeat (gate 3) are
|
|
109
|
-
covered in their sibling rules.
|
|
110
|
-
|
|
111
|
-
## Cloud Behavior
|
|
112
|
-
|
|
113
|
-
On cloud surfaces (Claude.ai Web, Skills API) the rule is **fully inert** —
|
|
114
|
-
no `.agent-chat-history`, no `scripts/`, no Iron Law gates, no foreign/returning
|
|
115
|
-
prompts. Treat `chat_history.enabled` as `false`; persistence is a
|
|
116
|
-
local-agent concern.
|
|
117
|
-
|
|
118
|
-
## Interactions & references
|
|
119
|
-
|
|
120
|
-
- Sibling rules: [`chat-history-cadence`](chat-history-cadence.md) (gate 2 — append timing) · [`chat-history-visibility`](chat-history-visibility.md) (gate 3 — heartbeat marker).
|
|
121
|
-
- `ask-when-uncertain` + `user-interaction` — foreign/returning prompts use numbered options, one question per turn.
|
|
122
|
-
- `language-and-tone` — prompt translated at runtime; `.md` stays English.
|
|
123
|
-
- `onboarding-gate` — runs first; this rule activates only after it clears.
|
|
124
|
-
- API: [`scripts/chat_history.py`](../../../scripts/chat_history.py). Commands: [`/chat-history`](../commands/chat-history.md), [`/chat-history-resume`](../commands/chat-history-resume.md), [`/chat-history-clear`](../commands/chat-history-clear.md), [`/chat-history-checkpoint`](../commands/chat-history-checkpoint.md). Settings: [`agent-settings`](../templates/agent-settings.md). Platform classification: [`agents/contexts/chat-history-platform-hooks.md`](../../../agents/contexts/chat-history-platform-hooks.md). Types: [`rule-type-governance`](rule-type-governance.md).
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
type: "auto"
|
|
3
|
-
tier: "mechanical-already"
|
|
4
|
-
description: "Emitting the chat-history heartbeat marker — paste subprocess stdout verbatim or nothing, never type from memory, hybrid mode prints on drift only, slip handling per language-and-tone"
|
|
5
|
-
alwaysApply: false
|
|
6
|
-
source: package
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
<!-- cloud_safe: noop -->
|
|
10
|
-
|
|
11
|
-
# Chat History — Visibility
|
|
12
|
-
|
|
13
|
-
Owns gate 3 of the chat-history Iron Law: the heartbeat marker that
|
|
14
|
-
makes append-cadence drift visible. Ownership (gate 1) lives in
|
|
15
|
-
[`chat-history-ownership`](chat-history-ownership.md); cadence (gate 2)
|
|
16
|
-
lives in [`chat-history-cadence`](chat-history-cadence.md). File I/O is
|
|
17
|
-
owned by [`scripts/chat_history.py`](../../../scripts/chat_history.py).
|
|
18
|
-
|
|
19
|
-
## Iron Law — gate 3 (heartbeat is subprocess stdout, not memory)
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
HEARTBEAT IS THE SCRIPT OUTPUT OF THE CURRENT TURN, VERBATIM, OR NOTHING.
|
|
23
|
-
NEVER TYPE THE LINE FROM MEMORY. NEVER REUSE THE PRIOR TURN'S MARKER.
|
|
24
|
-
EMPTY STDOUT → NO MARKER LINE.
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
Run silently before emitting the final reply:
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
scripts/chat_history.py heartbeat --first-user-msg "<first-user-msg>"
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Stdout is **at most** one line, e.g.
|
|
34
|
-
`📒 chat-history: ok · 9 entries · per_phase · last 30s ago`. Non-empty →
|
|
35
|
-
paste **verbatim** as the last line of the reply. Empty → emit nothing.
|
|
36
|
-
Always exits 0 — observability, not a gate.
|
|
37
|
-
|
|
38
|
-
## Visibility modes — `chat_history.heartbeat`
|
|
39
|
-
|
|
40
|
-
| Mode | When marker prints | Token cost |
|
|
41
|
-
|---|---|---|
|
|
42
|
-
| `on` | every reply (legacy) | ~20 tokens / reply |
|
|
43
|
-
| `off` | never — full silence | 0 |
|
|
44
|
-
| `hybrid` *(default)* | drift states only (`missing`/`foreign`/`returning`) | 0 in normal flow, ~20 on drift |
|
|
45
|
-
|
|
46
|
-
`hybrid` ships zero tokens when healthy, loud on ownership drift. YAML 1.1
|
|
47
|
-
booleanizes bare `on`/`off`; the reader coerces both back, so
|
|
48
|
-
`heartbeat: on` works unquoted.
|
|
49
|
-
|
|
50
|
-
## Memory-typing the marker — rule violation, not a slip
|
|
51
|
-
|
|
52
|
-
Format is memorizable; counts and timestamps are not. A typed-from-
|
|
53
|
-
memory line shows stale entries and a healthy-looking `ok` while the
|
|
54
|
-
file is silently behind — observability collapses, invisible until
|
|
55
|
-
`status` is checked. Heartbeat is the script output of the **current
|
|
56
|
-
turn**, verbatim, or nothing.
|
|
57
|
-
|
|
58
|
-
**Self-check before send — MANDATORY.** (1) Did `heartbeat` run on
|
|
59
|
-
this turn? (2) Is the line byte-identical to that subprocess stdout?
|
|
60
|
-
(3) Empty stdout → no marker line. Any "no" → drop it.
|
|
61
|
-
|
|
62
|
-
**Slip handling.** Stale marker called out → acknowledge once in the
|
|
63
|
-
user's language; run `status`; on CHECKPOINT call `append` for
|
|
64
|
-
missed phase-boundaries (see [`chat-history-cadence`](chat-history-cadence.md));
|
|
65
|
-
run a real `heartbeat`; paste stdout verbatim or nothing. Don't promise
|
|
66
|
-
"from now on" — only behaviour proves compliance (mirrors
|
|
67
|
-
`language-and-tone` § slip handling).
|
|
68
|
-
|
|
69
|
-
## Path-specific behavior
|
|
70
|
-
|
|
71
|
-
Heartbeat (gate 3) stays useful for visibility on **every** path —
|
|
72
|
-
HOOK, ENGINE, CHECKPOINT — because the marker reports the file's
|
|
73
|
-
state, not the agent's intent. On HOOK and ENGINE paths the agent
|
|
74
|
-
still emits the marker even though it didn't fire `turn-check` or
|
|
75
|
-
`append` itself: the marker is read-only observability over whatever
|
|
76
|
-
the platform / engine wrote.
|
|
77
|
-
|
|
78
|
-
## What this rule does NOT do
|
|
79
|
-
|
|
80
|
-
Manage ownership decisions — those live in [`chat-history-ownership`](chat-history-ownership.md).
|
|
81
|
-
Manage append timing — that lives in [`chat-history-cadence`](chat-history-cadence.md).
|
|
82
|
-
Auto-decide visibility mode — `chat_history.heartbeat` is the only
|
|
83
|
-
trigger. Insert decoration: the marker is functional output per
|
|
84
|
-
[`direct-answers`](direct-answers.md) emoji whitelist, not a status
|
|
85
|
-
badge.
|
|
86
|
-
|
|
87
|
-
## Cloud Behavior
|
|
88
|
-
|
|
89
|
-
On cloud surfaces (Claude.ai Web, Skills API) heartbeat is **inert** —
|
|
90
|
-
no marker line, no `scripts/`. Treat `chat_history.enabled` as `false`.
|
|
91
|
-
|
|
92
|
-
## Interactions & references
|
|
93
|
-
|
|
94
|
-
- Sibling rules: [`chat-history-ownership`](chat-history-ownership.md) (gate 1 — turn-check) · [`chat-history-cadence`](chat-history-cadence.md) (gate 2 — append).
|
|
95
|
-
- `direct-answers` — emoji whitelist permits the `📒` marker as functional output, not decoration.
|
|
96
|
-
- `language-and-tone` § slip handling — stale-marker acknowledgement.
|
|
97
|
-
- API: [`scripts/chat_history.py`](../../../scripts/chat_history.py) `heartbeat` and `status` subcommands. Settings: [`agent-settings`](../templates/agent-settings.md) `chat_history.heartbeat`.
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
"""``ChatHistoryHeartbeatHook`` — visibility marker before save.
|
|
2
|
-
|
|
3
|
-
Fires on ``before_save``. Runs ``chat_history.py heartbeat`` and,
|
|
4
|
-
if the script emits a marker line, threads it onto ``state.report``
|
|
5
|
-
so the agent's reply naturally carries the heartbeat without manual
|
|
6
|
-
copy/paste.
|
|
7
|
-
|
|
8
|
-
Why ``before_save`` and not ``after_dispatch``: the marker must land
|
|
9
|
-
in the report that gets persisted. ``cli._sync_back`` runs between
|
|
10
|
-
``after_dispatch`` and ``before_save`` and reassigns
|
|
11
|
-
``work.report = delivery.report`` — a marker written on
|
|
12
|
-
``after_dispatch`` would be overwritten before ``_save``. Firing on
|
|
13
|
-
``before_save`` runs after the sync, so the marker survives.
|
|
14
|
-
"""
|
|
15
|
-
from __future__ import annotations
|
|
16
|
-
|
|
17
|
-
from ..context import HookContext
|
|
18
|
-
from ..events import HookEvent
|
|
19
|
-
from ..exceptions import HookError
|
|
20
|
-
from ..registry import HookRegistry
|
|
21
|
-
from ._chat_history_base import EXIT_OK, _ChatHistoryHookBase
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class ChatHistoryHeartbeatHook(_ChatHistoryHookBase):
|
|
25
|
-
"""Run heartbeat before save; thread marker into ``state.report``."""
|
|
26
|
-
|
|
27
|
-
def register(self, registry: HookRegistry) -> None:
|
|
28
|
-
registry.register(HookEvent.BEFORE_SAVE, self._on_before_save)
|
|
29
|
-
|
|
30
|
-
def _on_before_save(self, ctx: HookContext) -> None:
|
|
31
|
-
msg = self._resolve_msg(ctx)
|
|
32
|
-
proc = self._invoke("heartbeat", "--first-user-msg", msg)
|
|
33
|
-
if proc.returncode != EXIT_OK:
|
|
34
|
-
raise HookError(
|
|
35
|
-
f"chat-history heartbeat failed (exit {proc.returncode})"
|
|
36
|
-
)
|
|
37
|
-
marker = (proc.stdout or "").strip()
|
|
38
|
-
if not marker or ctx.work is None:
|
|
39
|
-
return
|
|
40
|
-
existing = getattr(ctx.work, "report", "") or ""
|
|
41
|
-
if marker in existing:
|
|
42
|
-
return
|
|
43
|
-
sep = "\n\n" if existing else ""
|
|
44
|
-
try:
|
|
45
|
-
ctx.work.report = f"{existing}{sep}{marker}"
|
|
46
|
-
except AttributeError:
|
|
47
|
-
raise HookError("chat-history heartbeat: state.report not writable")
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
__all__ = ["ChatHistoryHeartbeatHook"]
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
"""``ChatHistoryTurnCheckHook`` — guards engine-driven turns.
|
|
2
|
-
|
|
3
|
-
Fires on ``before_dispatch``; classifies the chat-history file via
|
|
4
|
-
``scripts/chat_history.py turn-check``:
|
|
5
|
-
|
|
6
|
-
- exit 0 (``ok``) → continue
|
|
7
|
-
- exit 10 (``missing``) → continue (auto-init handled by chat_history.py)
|
|
8
|
-
- exit 11 (``foreign``) → raise :class:`HookHalt` so CLI exits 2
|
|
9
|
-
- exit 12 (``returning``)→ raise :class:`HookHalt` so CLI exits 2
|
|
10
|
-
- any other exit → raise :class:`HookError` (warn, continue)
|
|
11
|
-
"""
|
|
12
|
-
from __future__ import annotations
|
|
13
|
-
|
|
14
|
-
from ..context import HookContext
|
|
15
|
-
from ..events import HookEvent
|
|
16
|
-
from ..exceptions import HookError, HookHalt
|
|
17
|
-
from ..registry import HookRegistry
|
|
18
|
-
from ._chat_history_base import (
|
|
19
|
-
EXIT_FOREIGN,
|
|
20
|
-
EXIT_MISSING,
|
|
21
|
-
EXIT_OK,
|
|
22
|
-
EXIT_RETURNING,
|
|
23
|
-
_ChatHistoryHookBase,
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class ChatHistoryTurnCheckHook(_ChatHistoryHookBase):
|
|
28
|
-
"""Run ``turn-check`` at the start of dispatch; halt on drift."""
|
|
29
|
-
|
|
30
|
-
def register(self, registry: HookRegistry) -> None:
|
|
31
|
-
registry.register(HookEvent.BEFORE_DISPATCH, self._on_before_dispatch)
|
|
32
|
-
|
|
33
|
-
def _on_before_dispatch(self, ctx: HookContext) -> None:
|
|
34
|
-
msg = self._resolve_msg(ctx)
|
|
35
|
-
result = self._invoke("turn-check", "--first-user-msg", msg)
|
|
36
|
-
code = result.returncode
|
|
37
|
-
if code in (EXIT_OK, EXIT_MISSING):
|
|
38
|
-
return
|
|
39
|
-
if code in (EXIT_FOREIGN, EXIT_RETURNING):
|
|
40
|
-
text = (result.stderr or result.stdout or "").strip()
|
|
41
|
-
reason = "foreign" if code == EXIT_FOREIGN else "returning"
|
|
42
|
-
surface = [line for line in text.splitlines() if line] or [
|
|
43
|
-
f"chat-history turn-check: {reason}",
|
|
44
|
-
]
|
|
45
|
-
raise HookHalt(f"chat_history_turn_check_{reason}", surface=surface)
|
|
46
|
-
raise HookError(f"chat-history turn-check failed (exit {code})")
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
__all__ = ["ChatHistoryTurnCheckHook"]
|