create-egregore 0.19.23 → 0.19.24
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/assets/egregore-launcher.js +50 -7
- package/assets/egregore-launcher.sh +10 -2
- package/bin/cli.js +21 -3
- package/lib/codex-runtime.js +25 -3
- package/lib/connect-runtime.js +329 -0
- package/lib/pi-runtime.js +23 -3
- package/lib/setup.js +13 -0
- package/package.json +3 -3
- package/runtime/codex/.codex/skills/handoff/SKILL.md +35 -11
- package/runtime/codex/.codex/skills/save/SKILL.md +39 -4
- package/runtime/codex/.codex/spec-manifest.json +2 -2
- package/runtime/codex/AGENTS.md +18 -1
- package/runtime/codex/bin/agent.sh +28 -14
- package/runtime/codex/bin/attendant.sh +169 -6
- package/runtime/codex/bin/codex-render-spec.mjs +18 -1
- package/runtime/codex/bin/codex-session-start.sh +13 -0
- package/runtime/codex/bin/codex-sync-skills.sh +1 -0
- package/runtime/codex/bin/graph.sh +21 -1
- package/runtime/codex/bin/handoff-run.sh +25 -5
- package/runtime/codex/bin/lib/context.sh +220 -172
- package/runtime/codex/bin/lib/dashboard-artifact.sh +113 -13
- package/runtime/codex/bin/lib/git-sync.sh +44 -8
- package/runtime/codex/bin/lib/greeting.sh +34 -11
- package/runtime/codex/bin/lib/identity.sh +32 -14
- package/runtime/codex/bin/person.py +20 -0
- package/runtime/codex/manifest.json +3 -72
- package/runtime/pi/.codex/skills/handoff/SKILL.md +35 -11
- package/runtime/pi/.codex/skills/save/SKILL.md +39 -4
- package/runtime/pi/.codex/spec-manifest.json +2 -2
- package/runtime/pi/.pi/APPEND_SYSTEM.md +18 -1
- package/runtime/pi/.pi/spec-manifest.json +2 -2
- package/runtime/pi/AGENTS.md +18 -1
- package/runtime/pi/bin/agent.sh +28 -14
- package/runtime/pi/bin/attendant.sh +169 -6
- package/runtime/pi/bin/codex-render-spec.mjs +18 -1
- package/runtime/pi/bin/codex-session-start.sh +13 -0
- package/runtime/pi/bin/codex-sync-skills.sh +1 -0
- package/runtime/pi/bin/graph.sh +21 -1
- package/runtime/pi/bin/handoff-run.sh +25 -5
- package/runtime/pi/bin/lib/context.sh +220 -172
- package/runtime/pi/bin/lib/dashboard-artifact.sh +113 -13
- package/runtime/pi/bin/lib/git-sync.sh +44 -8
- package/runtime/pi/bin/lib/greeting.sh +34 -11
- package/runtime/pi/bin/lib/identity.sh +32 -14
- package/runtime/pi/bin/person.py +20 -0
- package/runtime/pi/bin/pi-product-workflows.mjs +5 -1
- package/runtime/pi/manifest.json +3 -72
- package/runtime/codex/.codex/skills/add/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/answer/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/ask/SKILL.md +0 -67
- package/runtime/codex/.codex/skills/audit/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/autosave/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/branch/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/commit/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/demo-mode/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/deploy-bridge/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/design/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/egregore-voice/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/emissary/SKILL.md +0 -45
- package/runtime/codex/.codex/skills/env/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/ingest-google/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/minion/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/mock/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/pr/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/product-voice/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/project/SKILL.md +0 -45
- package/runtime/codex/.codex/skills/pull/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/push/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/quest-suggest/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/review-pr/SKILL.md +0 -45
- package/runtime/codex/.codex/skills/scroll/SKILL.md +0 -288
- package/runtime/codex/.codex/skills/scroll/agents/openai.yaml +0 -4
- package/runtime/codex/.codex/skills/scroll/assets/shell.html +0 -871
- package/runtime/codex/.codex/skills/search/SKILL.md +0 -79
- package/runtime/codex/.codex/skills/session-view/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/setup/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/summon/SKILL.md +0 -45
- package/runtime/codex/.codex/skills/task/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/teams-connect/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/telegram-connect/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/the-spiral/SKILL.md +0 -84
- package/runtime/codex/.codex/skills/thread/SKILL.md +0 -29
- package/runtime/codex/.codex/skills/todo/SKILL.md +0 -45
- package/runtime/pi/.codex/skills/add/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/answer/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/ask/SKILL.md +0 -67
- package/runtime/pi/.codex/skills/audit/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/autosave/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/branch/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/commit/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/demo-mode/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/deploy-bridge/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/design/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/egregore-voice/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/emissary/SKILL.md +0 -45
- package/runtime/pi/.codex/skills/env/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/ingest-google/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/minion/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/mock/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/pr/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/product-voice/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/project/SKILL.md +0 -45
- package/runtime/pi/.codex/skills/pull/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/push/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/quest-suggest/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/review-pr/SKILL.md +0 -45
- package/runtime/pi/.codex/skills/scroll/SKILL.md +0 -288
- package/runtime/pi/.codex/skills/scroll/agents/openai.yaml +0 -4
- package/runtime/pi/.codex/skills/scroll/assets/shell.html +0 -871
- package/runtime/pi/.codex/skills/search/SKILL.md +0 -79
- package/runtime/pi/.codex/skills/session-view/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/setup/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/summon/SKILL.md +0 -45
- package/runtime/pi/.codex/skills/task/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/teams-connect/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/telegram-connect/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/the-spiral/SKILL.md +0 -84
- package/runtime/pi/.codex/skills/thread/SKILL.md +0 -29
- package/runtime/pi/.codex/skills/todo/SKILL.md +0 -45
|
@@ -22,13 +22,20 @@ an addressed recipient belongs to `$wrap`.
|
|
|
22
22
|
`name`, then git config.
|
|
23
23
|
3. Parse a short topic and optional recipient from the user request. Match the
|
|
24
24
|
recipient against `memory/people/*.md`; omit the recipient when uncertain.
|
|
25
|
-
4.
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
25
|
+
4. Classify the content before drafting:
|
|
26
|
+
- **Supplied content**: the user pasted substantial prose, supplied a
|
|
27
|
+
document, or approved exact wording. Preserve wording, order, headings,
|
|
28
|
+
lists, code, and links. Add only capture frontmatter with
|
|
29
|
+
`content_mode: supplied`. Do not create a composed JSON or attach session
|
|
30
|
+
artifacts unless the user asks.
|
|
31
|
+
- **Generated content**: the user supplied only a topic or fragmentary
|
|
32
|
+
notes. Draft the canonical markdown record with these sections:
|
|
33
|
+
- `## Briefing`
|
|
34
|
+
- `## Key Decisions`
|
|
35
|
+
- `## Current State`
|
|
36
|
+
- `## Open Threads`
|
|
37
|
+
- `## Next Steps`
|
|
38
|
+
- `## Entry Points`
|
|
32
39
|
Use this metadata contract even when the user supplied only prose:
|
|
33
40
|
|
|
34
41
|
```markdown
|
|
@@ -41,12 +48,22 @@ addressed_to: <recipient, if any>
|
|
|
41
48
|
date: YYYY-MM-DD
|
|
42
49
|
topic: <topic>
|
|
43
50
|
intent: <action | feedback | fyi>
|
|
51
|
+
content_mode: <supplied | generated>
|
|
44
52
|
claim: <one-line claim>
|
|
45
53
|
ask: <what the receiver should do>
|
|
46
54
|
---
|
|
47
55
|
```
|
|
48
56
|
|
|
49
|
-
5.
|
|
57
|
+
5. Build the preview. For supplied content, render the Markdown directly
|
|
58
|
+
through the native handoff renderer and verify fidelity:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
node packages/egregore-artifacts/bin/cli.js handoff "$BODY_FILE" \
|
|
62
|
+
--verify-fidelity
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
For generated content, compose the HTML view from that record. Markdown is
|
|
66
|
+
canonical; the JSON is
|
|
50
67
|
only a presentation map. Give every JSON section a stable `id` and add a
|
|
51
68
|
top-level `sourceMap` from every authored `##` heading to the section that
|
|
52
69
|
contains its complete text:
|
|
@@ -76,14 +93,16 @@ ask: <what the receiver should do>
|
|
|
76
93
|
JSON. For command-heavy sections, keep command headings, explanatory text,
|
|
77
94
|
fenced commands, and expected output verbatim.
|
|
78
95
|
|
|
79
|
-
|
|
80
|
-
publishing. This is the approval surface—not a list of component names:
|
|
96
|
+
Render generated content with semantic coverage enabled:
|
|
81
97
|
|
|
82
98
|
```bash
|
|
83
99
|
node packages/egregore-artifacts/bin/cli.js composed "$COMPOSED_FILE" \
|
|
84
100
|
--source "$BODY_FILE" --verify-fidelity
|
|
85
101
|
```
|
|
86
102
|
|
|
103
|
+
6. Show the actual browser HTML before publishing. This is the approval
|
|
104
|
+
surface—not a list of component names.
|
|
105
|
+
|
|
87
106
|
Stop for confirmation after the browser preview. If validation fails, fix
|
|
88
107
|
the mapping/content and render again. Skip confirmation only when the user
|
|
89
108
|
explicitly said to send without preview.
|
|
@@ -93,9 +112,12 @@ node packages/egregore-artifacts/bin/cli.js composed "$COMPOSED_FILE" \
|
|
|
93
112
|
```bash
|
|
94
113
|
TMPDIR="$(mktemp -d -t codex-handoff-XXXXXX)"
|
|
95
114
|
TMPDIR="$TMPDIR" bash bin/capture-run.sh --mode addressed --author "$AUTHOR" \
|
|
96
|
-
--topic "$TOPIC" ${RECIPIENT_ARG} --
|
|
115
|
+
--topic "$TOPIC" ${RECIPIENT_ARG} --content-mode "$CONTENT_MODE" \
|
|
116
|
+
${COMPOSED_ARG} < "$BODY_FILE"
|
|
97
117
|
```
|
|
98
118
|
|
|
119
|
+
Use `COMPOSED_ARG=(--composed "$COMPOSED_FILE")` only for generated content.
|
|
120
|
+
|
|
99
121
|
Use `--recipient "$RECIPIENT"` only when a clear teammate or future-self
|
|
100
122
|
recipient is known. Do not pass `--no-publish` unless the user explicitly
|
|
101
123
|
asked for a dry run. The runner may plan a notification, but never dispatches.
|
|
@@ -132,6 +154,8 @@ asks for a summary.
|
|
|
132
154
|
- Do not use Claude Code commands.
|
|
133
155
|
- Keep the briefing concise and operational; include exact files, branches,
|
|
134
156
|
commands, open questions, and verification state when relevant.
|
|
157
|
+
- Supplied content is authoritative. Do not replace it with a session summary,
|
|
158
|
+
presentation JSON, related artifacts, or repo state.
|
|
135
159
|
- The artifact is a view of the handoff, never a summary of it. Publishing
|
|
136
160
|
must fail when any non-empty authored section is unmapped or loses content.
|
|
137
161
|
- Structured UX parity is required: the rendered card is the acknowledgment.
|
|
@@ -42,7 +42,42 @@ esac
|
|
|
42
42
|
- Stop with "everything is already saved" only when there are no repo or
|
|
43
43
|
memory changes and either the branch has no commits to integrate
|
|
44
44
|
(`AHEAD=0`) or an open integration PR already exists.
|
|
45
|
-
4.
|
|
45
|
+
4. Run the skill distribution checkpoint before any commit or push when
|
|
46
|
+
`capability-distribution.json` exists:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
node bin/capability-distribution.mjs validate --root .
|
|
50
|
+
node bin/capability-distribution.mjs changes --root . --base "origin/$BASE" --json
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Inspect the diff as well as the receipt. Ask only when this work introduces
|
|
54
|
+
a new or unclassified Egregore skill, or changes a skill's placement without
|
|
55
|
+
an explicit user decision in the current conversation. Ordinary edits to an
|
|
56
|
+
already classified skill do not prompt. Packages, APIs, sites, workers,
|
|
57
|
+
infrastructure, and schemas remain outside this checkpoint.
|
|
58
|
+
|
|
59
|
+
Show the tracker before asking:
|
|
60
|
+
|
|
61
|
+
`artifacts/capability-pipeline.html`
|
|
62
|
+
|
|
63
|
+
Ask once using structured Codex question tooling when available. Otherwise
|
|
64
|
+
render the compact numbered question and wait:
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
Where should {name} live?
|
|
68
|
+
1. OSS + Connect — part of the open runtime; Connect inherits it
|
|
69
|
+
2. Connect only — delivered only to authenticated Connect users
|
|
70
|
+
3. Curve Labs / Egregore — used only in this development instance
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Record every new skill as `queued` under the selected placement. Availability
|
|
74
|
+
is changed separately through the tracker after review. Group skills into one
|
|
75
|
+
checkpoint only when they share the same placement. Apply the answer to
|
|
76
|
+
`capability-distribution.json`, regenerate `skill-distribution.json` and both
|
|
77
|
+
artifacts, then validate again. Rebuild runtime packs only when an available
|
|
78
|
+
skill changes placement. If the user already made this exact decision during
|
|
79
|
+
the current work, do not ask twice.
|
|
80
|
+
5. Synthesize:
|
|
46
81
|
- a short topic from the work,
|
|
47
82
|
- a clear commit message,
|
|
48
83
|
- a one-line user-facing scope summary,
|
|
@@ -50,7 +85,7 @@ esac
|
|
|
50
85
|
`## What` (1–4 bullets), `## Why` (1–3 sentences), and
|
|
51
86
|
`## Verification` (how the change was checked — required when the
|
|
52
87
|
diff touches non-markdown files; be honest if unverified).
|
|
53
|
-
|
|
88
|
+
6. If the scope is ambiguous or includes unrelated changes, ask for one compact
|
|
54
89
|
confirmation. Use structured Codex question tooling when available;
|
|
55
90
|
otherwise render:
|
|
56
91
|
|
|
@@ -61,7 +96,7 @@ Save these changes?
|
|
|
61
96
|
Other:
|
|
62
97
|
```
|
|
63
98
|
|
|
64
|
-
|
|
99
|
+
7. Run the bridge command:
|
|
65
100
|
|
|
66
101
|
```bash
|
|
67
102
|
bin/agent.sh save --message "$MESSAGE" --topic "$TOPIC" --pr-body "$PR_BODY"
|
|
@@ -72,7 +107,7 @@ commit repo changes, push, and create or reuse a pull request when available.
|
|
|
72
107
|
Always pass the synthesized `--pr-body` — the bridge only auto-generates a
|
|
73
108
|
skeleton body as a last resort, and the CI `pr-format` check gates every PR.
|
|
74
109
|
|
|
75
|
-
|
|
110
|
+
8. Parse the output and report only useful user information:
|
|
76
111
|
- branch name,
|
|
77
112
|
- whether a commit was created,
|
|
78
113
|
- whether push succeeded,
|
|
@@ -29,13 +29,13 @@
|
|
|
29
29
|
{
|
|
30
30
|
"section": "On Launch — MANDATORY FIRST ACTION",
|
|
31
31
|
"action": "replace",
|
|
32
|
-
"sourceHash": "
|
|
32
|
+
"sourceHash": "3def4421213a",
|
|
33
33
|
"note": "Claude SessionStart hook does not run on Codex; the egregore launcher renders the card via bin/codex-session-start.sh before the session. Greeting-replay instruction dropped (card is already on screen)."
|
|
34
34
|
},
|
|
35
35
|
{
|
|
36
36
|
"section": "After Greeting — BRANCH ON FIRST RESPONSE",
|
|
37
37
|
"action": "replace",
|
|
38
|
-
"sourceHash": "
|
|
38
|
+
"sourceHash": "88410d7c6c00",
|
|
39
39
|
"note": "EnterWorktree -> bin/agent.sh branch with git checkout fallback; AskUserQuestion -> numbered options in plain text; branch-guard mapped to .codex/hooks/branch-guard.js with instruction fallback; plan-mode note dropped (no plan mode on Codex); /branch + /onboarding -> skill tokens. Handoff claiming and repoState auto-checkout kept verbatim (pure git)."
|
|
40
40
|
},
|
|
41
41
|
{
|
package/runtime/codex/AGENTS.md
CHANGED
|
@@ -132,7 +132,13 @@ integration branch.
|
|
|
132
132
|
|
|
133
133
|
1. Derive a topic slug from what the user said (kebab-case, 2–4 words)
|
|
134
134
|
2. Run `bin/agent.sh branch --topic "<topic>"` — it resolves the configured base and creates a work branch from `origin/{base}` in a task worktree (`dev/{author}/{slug}`, or `feature/{slug}` / `bugfix/{slug}` when the topic reads as a feature or fix). Continue all file work from the printed path.
|
|
135
|
-
3.
|
|
135
|
+
3. Give a value-first workspace receipt. Do not lead with Git terminology:
|
|
136
|
+
|
|
137
|
+
`Your stable project is protected. I’m working in a separate workspace for **{topic}**, where changes stay isolated, reviewable, and reversible.`
|
|
138
|
+
|
|
139
|
+
Then expose the implementation as secondary detail:
|
|
140
|
+
|
|
141
|
+
`Workspace: {branch} (worktree).`
|
|
136
142
|
|
|
137
143
|
**Fallback:** If `bin/agent.sh branch` fails, resolve `{base}` with
|
|
138
144
|
`_get_base_branch`, then use
|
|
@@ -140,6 +146,17 @@ integration branch.
|
|
|
140
146
|
|
|
141
147
|
4. Update graph (fire-and-forget): `bash bin/graph-op.sh set-topic "$(cat .egregore-session-id 2>/dev/null)" "topic from slug" "dev/author/slug" 2>/dev/null &`
|
|
142
148
|
|
|
149
|
+
### Starting-work UX contract
|
|
150
|
+
|
|
151
|
+
Before execution begins, make Egregore's useful structure legible without turning every task start into a tutorial:
|
|
152
|
+
|
|
153
|
+
- **Workspace** — use the value-first receipt above for a newly created workspace or topic pivot. If already on the appropriate working branch, do not repeat it.
|
|
154
|
+
- **Context** — when organizational retrieval materially informs the work, keep the required Egregore Retrieval Beat and then add one compact result receipt: `↳ Context restored: {decision, handoff, or prior work} · {source/date}`. Never claim context was restored when retrieval found nothing useful.
|
|
155
|
+
- **Assumptions** — surface only consequential assumptions that could change the implementation. Use `Assumption: {assumption} — based on {evidence}.` Add a correction path when cheap; do not narrate obvious operational choices.
|
|
156
|
+
- **Transition** — once workspace, context, and assumptions are settled, state the outcome you are starting toward in one short sentence and begin.
|
|
157
|
+
|
|
158
|
+
The intended sequence is: **intent → safe workspace → relevant context → consequential assumptions → execution**. Keep technical identifiers available but subordinate them to user value.
|
|
159
|
+
|
|
143
160
|
### Handoff claiming
|
|
144
161
|
|
|
145
162
|
If `addressed_to_user` handoffs exist and the user is picking one up, create the IMPLEMENTS link after branch creation:
|
|
@@ -506,7 +506,7 @@ cmd_wrap() {
|
|
|
506
506
|
cmd_handoff() {
|
|
507
507
|
ensure_memory
|
|
508
508
|
local from="" to="" topic="" body="" body_file="" project="" intent="action"
|
|
509
|
-
local no_publish=0 no_notify=0 json=0
|
|
509
|
+
local no_publish=0 no_notify=0 json=0 body_supplied=0 content_mode="generated"
|
|
510
510
|
NO_PUSH=0
|
|
511
511
|
|
|
512
512
|
while [ $# -gt 0 ]; do
|
|
@@ -536,12 +536,16 @@ cmd_handoff() {
|
|
|
536
536
|
if [ -n "$body_file" ]; then
|
|
537
537
|
[ -f "$body_file" ] || die "body file not found: $body_file"
|
|
538
538
|
body="$(cat "$body_file")"
|
|
539
|
+
body_supplied=1
|
|
539
540
|
elif [ -z "$body" ] && [ ! -t 0 ]; then
|
|
540
541
|
body="$(cat)"
|
|
542
|
+
[ -n "$body" ] && body_supplied=1
|
|
543
|
+
elif [ -n "$body" ]; then
|
|
544
|
+
body_supplied=1
|
|
541
545
|
fi
|
|
542
|
-
[
|
|
546
|
+
[ "$body_supplied" = "1" ] && content_mode="supplied"
|
|
543
547
|
|
|
544
|
-
local today tmpd result result_out rel
|
|
548
|
+
local today tmpd result result_out rel stdout_file
|
|
545
549
|
local handoff_args=()
|
|
546
550
|
today="$(date +%Y-%m-%d)"
|
|
547
551
|
tmpd="$(mktemp -d -t egregore-agent-handoff-XXXXXX)"
|
|
@@ -549,24 +553,34 @@ cmd_handoff() {
|
|
|
549
553
|
result_out="$(mktemp -t egregore-agent-handoff-result-XXXXXX.json)"
|
|
550
554
|
stdout_file="$tmpd/stdout"
|
|
551
555
|
|
|
552
|
-
project_line=""
|
|
553
|
-
[ -n "$project" ] && project_line="**Project**: $project"
|
|
554
556
|
[ -n "$to" ] && handoff_args+=(--recipient "$to")
|
|
555
557
|
[ -n "$project" ] && handoff_args+=(--project "$project")
|
|
556
|
-
handoff_args+=(--intent "$intent")
|
|
558
|
+
handoff_args+=(--intent "$intent" --content-mode "$content_mode")
|
|
559
|
+
[ "$content_mode" = "generated" ] && handoff_args+=(--include-session-artifacts)
|
|
557
560
|
[ "$NO_PUSH" = "1" ] && handoff_args+=(--no-push)
|
|
558
561
|
[ "$no_publish" = "1" ] && handoff_args+=(--no-publish)
|
|
559
562
|
[ "$no_notify" = "1" ] && handoff_args+=(--no-notify)
|
|
560
563
|
|
|
561
564
|
{
|
|
562
|
-
printf '
|
|
563
|
-
printf '
|
|
564
|
-
printf '
|
|
565
|
-
|
|
566
|
-
printf '
|
|
567
|
-
|
|
568
|
-
printf '
|
|
569
|
-
printf '
|
|
565
|
+
printf '%s\n' '---'
|
|
566
|
+
printf 'capture_schema: egregore-capture/v1\n'
|
|
567
|
+
printf 'capture_mode: addressed\n'
|
|
568
|
+
printf 'kind: addressed\n'
|
|
569
|
+
printf 'content_mode: %s\n' "$content_mode"
|
|
570
|
+
printf 'from: %s\n' "$(yaml_str "$from")"
|
|
571
|
+
[ -n "$to" ] && printf 'addressed_to: %s\n' "$(yaml_str "$to")"
|
|
572
|
+
printf 'date: %s\n' "$today"
|
|
573
|
+
printf 'topic: %s\n' "$(yaml_str "$topic")"
|
|
574
|
+
printf 'intent: %s\n' "$intent"
|
|
575
|
+
[ -n "$project" ] && printf 'project: %s\n' "$(yaml_str "$project")"
|
|
576
|
+
printf '%s\n\n' '---'
|
|
577
|
+
if [ "$content_mode" = "supplied" ]; then
|
|
578
|
+
printf '%s\n' "$body"
|
|
579
|
+
else
|
|
580
|
+
printf '# Handoff: %s\n\n' "$topic"
|
|
581
|
+
printf '## Briefing\n\nNo briefing was supplied.\n\n'
|
|
582
|
+
printf '## Next Steps\n\n1. Continue from this handoff using the shared Egregore memory protocol.\n'
|
|
583
|
+
fi
|
|
570
584
|
} | TMPDIR="$tmpd" bash "$SCRIPT_DIR/bin/capture-run.sh" \
|
|
571
585
|
--mode addressed \
|
|
572
586
|
--author "$from" \
|
|
@@ -387,26 +387,57 @@ Co-Authored-By: Egregore Attendant <noreply@egregore.xyz>" 2>/dev/null
|
|
|
387
387
|
WARM_MIN="${ATTENDANT_WARM_MIN:-5}"
|
|
388
388
|
WARM_MARKER="$STATE_DIR/$KEY.warm-ts"
|
|
389
389
|
|
|
390
|
+
# Non-secret fingerprint of the effective graph endpoint + credential, matching
|
|
391
|
+
# bin/graph.sh's resolution order exactly (process env → .env → committed
|
|
392
|
+
# api_url). Scopes the graph cache and the snapshot provenance to the tenant
|
|
393
|
+
# answers actually come from, not just the tenant the committed config names.
|
|
394
|
+
_effective_scope() {
|
|
395
|
+
local url key
|
|
396
|
+
url="${EGREGORE_API_URL:-$(grep '^EGREGORE_API_URL=' "$MAIN_DIR/.env" 2>/dev/null | cut -d'=' -f2- || true)}"
|
|
397
|
+
key="${EGREGORE_API_KEY:-$(grep '^EGREGORE_API_KEY=' "$MAIN_DIR/.env" 2>/dev/null | cut -d'=' -f2- || true)}"
|
|
398
|
+
[ -n "$url" ] || url=$(jq -r '.api_url // empty' "$CONFIG" 2>/dev/null)
|
|
399
|
+
echo -n "${url}|${key}" | cksum | cut -d' ' -f1
|
|
400
|
+
}
|
|
401
|
+
|
|
390
402
|
_warm() {
|
|
391
403
|
local now last
|
|
392
404
|
now=$(date +%s)
|
|
393
405
|
last=$(cat "$WARM_MARKER" 2>/dev/null || echo 0)
|
|
406
|
+
# Numeric + not-future guard: a malformed legacy marker must not abort
|
|
407
|
+
# warming, and a future timestamp must not suppress it indefinitely.
|
|
408
|
+
case "$last" in ''|*[!0-9]*) last=0 ;; esac
|
|
409
|
+
[ "$last" -gt "$now" ] && last=0
|
|
394
410
|
[ $(( now - last )) -lt $(( WARM_MIN * 60 )) ] && return 0
|
|
395
411
|
[ "$DRY_RUN" = "1" ] && { log "DRY warm"; return 0; }
|
|
396
412
|
|
|
397
|
-
|
|
413
|
+
# --prune keeps remote-tracking refs honest so the launch path can answer
|
|
414
|
+
# "does my branch still exist on origin?" from local refs (worktree stale
|
|
415
|
+
# check in git-sync.sh) instead of a ~2s ls-remote round-trip.
|
|
416
|
+
local _origin_pid _origin_ok="false"
|
|
417
|
+
git -C "$MAIN_DIR" fetch origin --prune --quiet 2>/dev/null &
|
|
418
|
+
_origin_pid=$!
|
|
398
419
|
[ -d "$MEMORY_DIR/.git" ] || [ -f "$MEMORY_DIR/.git" ] && git -C "$MEMORY_DIR" fetch origin --quiet 2>/dev/null &
|
|
399
420
|
local repo
|
|
400
421
|
jq -r '(.repos[]? // empty) | if type == "object" then .name else . end' "$CONFIG" 2>/dev/null | while read -r repo; do
|
|
401
422
|
[ -d "$MAIN_DIR/../$repo/.git" ] && git -C "$MAIN_DIR/../$repo" fetch origin --quiet 2>/dev/null
|
|
402
423
|
done &
|
|
424
|
+
wait "$_origin_pid" 2>/dev/null && _origin_ok="true"
|
|
403
425
|
wait
|
|
404
|
-
|
|
426
|
+
# Stamp the marker ONLY when the origin fetch actually succeeded, and stamp
|
|
427
|
+
# it atomically (tmp+mv): the launch path treats a fresh marker as "local
|
|
428
|
+
# refs are authoritative", so a failed fetch must not silence live fetches
|
|
429
|
+
# for ten minutes, and a reader must never catch a half-written timestamp.
|
|
430
|
+
if [ "$_origin_ok" = "true" ]; then
|
|
431
|
+
date +%s > "$WARM_MARKER.tmp" && mv "$WARM_MARKER.tmp" "$WARM_MARKER"
|
|
432
|
+
fi
|
|
405
433
|
|
|
406
434
|
# Replay graph cache entries touched in the last 24h (connected mode only —
|
|
407
435
|
# in local mode graph.sh exits instantly and no cache exists).
|
|
408
436
|
local cache_dir f q p
|
|
409
|
-
|
|
437
|
+
# Key must match graph.sh's _CACHE_DIR derivation exactly: repo root plus
|
|
438
|
+
# the effective endpoint/credential fingerprint, so entries never cross
|
|
439
|
+
# tenants when the same checkout is repointed at a different org/API.
|
|
440
|
+
cache_dir="$HOME/.egregore/graph-cache/$(echo -n "${MAIN_DIR}|$(_effective_scope)" | cksum | cut -d' ' -f1)"
|
|
410
441
|
if [ -d "$cache_dir" ]; then
|
|
411
442
|
for f in $(find "$cache_dir" -name '*.json' -mmin -1440 2>/dev/null); do
|
|
412
443
|
q=$(jq -r '.query // empty' "$f" 2>/dev/null)
|
|
@@ -414,7 +445,90 @@ _warm() {
|
|
|
414
445
|
[ -n "$q" ] && EGREGORE_GRAPH_CACHE_TTL=1 bash "$MAIN_DIR/bin/graph.sh" query "$q" "$p" >/dev/null 2>&1 || true
|
|
415
446
|
done
|
|
416
447
|
fi
|
|
417
|
-
|
|
448
|
+
_prebake_context
|
|
449
|
+
log "warm: refs fetched, graph cache replayed, context baked"
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
# --- Pre-bake the session-start context snapshot -----------------------------
|
|
453
|
+
# Runs the same context gather session-start would (bin/lib/context.sh) and
|
|
454
|
+
# stores the result as a single tar at $STATE_DIR/$KEY.ctx.tar. On boot,
|
|
455
|
+
# context.sh reuses the baked graph-bound results (todos, lifecycle, pulse,
|
|
456
|
+
# metrics, service health) when the bake is <15min old and its provenance
|
|
457
|
+
# matches (author, mode, config), re-running the local collectors live —
|
|
458
|
+
# taking the greeting's graph work off the launch path entirely.
|
|
459
|
+
#
|
|
460
|
+
# One tar + atomic rename, not a directory swap: readers open either the whole
|
|
461
|
+
# old bake or the whole new one, never a half-replaced directory. The tmp name
|
|
462
|
+
# carries $$ so two attendants racing (possible before the run-loop self-check
|
|
463
|
+
# converges them) never write through each other's file.
|
|
464
|
+
_prebake_context() {
|
|
465
|
+
[ -f "$MAIN_DIR/bin/lib/context.sh" ] || return 0
|
|
466
|
+
local snap="$STATE_DIR/$KEY.ctx.tar"
|
|
467
|
+
(
|
|
468
|
+
set +u +e
|
|
469
|
+
unset CTX_SEED_TAR CTX_SEED_DIR # never let a bake seed itself from an old bake
|
|
470
|
+
SCRIPT_DIR="$MAIN_DIR"
|
|
471
|
+
CONFIG="$MAIN_DIR/egregore.json"
|
|
472
|
+
STATE_FILE="$MAIN_DIR/.egregore-state.json"
|
|
473
|
+
ENV_FILE="$MAIN_DIR/.env"
|
|
474
|
+
AUTHOR=$(jq -r '.github_username // empty' "$STATE_FILE" 2>/dev/null)
|
|
475
|
+
[ -n "$AUTHOR" ] || exit 0
|
|
476
|
+
# Canonical mode semantics (config.sh _detect_mode): local when the config
|
|
477
|
+
# says so OR api_url is absent — legacy OSS configs carry no mode key, and
|
|
478
|
+
# a bare '.mode == local' test would bake failed connected health for them.
|
|
479
|
+
LOCAL_MODE="false"
|
|
480
|
+
source "$MAIN_DIR/bin/lib/config.sh" 2>/dev/null || true
|
|
481
|
+
if type _detect_mode >/dev/null 2>&1; then
|
|
482
|
+
[ "$(_detect_mode)" = "local" ] && LOCAL_MODE="true"
|
|
483
|
+
elif ! jq -e '(.mode // "") != "local" and ((.api_url // "") | length > 0)' "$CONFIG" >/dev/null 2>&1; then
|
|
484
|
+
LOCAL_MODE="true"
|
|
485
|
+
fi
|
|
486
|
+
# Capture ALL provenance BEFORE gathering, then publish only if it is
|
|
487
|
+
# unchanged AFTER — a user, config, or credential switch mid-gather would
|
|
488
|
+
# otherwise produce mixed-tenant/mixed-user data labeled with whatever the
|
|
489
|
+
# final values happened to be (TOCTOU).
|
|
490
|
+
_PRE_CONFIG=$(cksum "$CONFIG" 2>/dev/null | cut -d' ' -f1)
|
|
491
|
+
_PRE_SCOPE=$(_effective_scope)
|
|
492
|
+
_PRE_SCHEMA=$(cksum "$MAIN_DIR/bin/lib/context.sh" 2>/dev/null | cut -d' ' -f1)
|
|
493
|
+
# PIN the effective credentials for the entire gather: graph.sh re-reads
|
|
494
|
+
# env/.env in every subprocess, so without pinning, an A→B→A .env flip
|
|
495
|
+
# during the gather could fetch B-tenant data that the pre/post scope
|
|
496
|
+
# samples cannot see. With the pin, every graph call in this bake uses
|
|
497
|
+
# the credentials the provenance stamp names. Empty values stay empty
|
|
498
|
+
# (local mode) — graph.sh treats them as unset.
|
|
499
|
+
export EGREGORE_API_URL="${EGREGORE_API_URL:-$(grep '^EGREGORE_API_URL=' "$MAIN_DIR/.env" 2>/dev/null | cut -d'=' -f2- || true)}"
|
|
500
|
+
export EGREGORE_API_KEY="${EGREGORE_API_KEY:-$(grep '^EGREGORE_API_KEY=' "$MAIN_DIR/.env" 2>/dev/null | cut -d'=' -f2- || true)}"
|
|
501
|
+
export EGREGORE_GRAPH_CACHE_TTL=600
|
|
502
|
+
source "$MAIN_DIR/bin/lib/context.sh"
|
|
503
|
+
[ -n "$CTX_DIR" ] && [ -d "$CTX_DIR" ] || exit 0
|
|
504
|
+
_POST_AUTHOR=$(jq -r '.github_username // empty' "$STATE_FILE" 2>/dev/null)
|
|
505
|
+
_POST_CONFIG=$(cksum "$CONFIG" 2>/dev/null | cut -d' ' -f1)
|
|
506
|
+
_POST_SCOPE=$(_effective_scope)
|
|
507
|
+
_POST_SCHEMA=$(cksum "$MAIN_DIR/bin/lib/context.sh" 2>/dev/null | cut -d' ' -f1)
|
|
508
|
+
if [ "$_POST_AUTHOR" != "$AUTHOR" ] || [ "$_POST_CONFIG" != "$_PRE_CONFIG" ] \
|
|
509
|
+
|| [ "$_POST_SCOPE" != "$_PRE_SCOPE" ] || [ "$_POST_SCHEMA" != "$_PRE_SCHEMA" ]; then
|
|
510
|
+
rm -rf "$CTX_DIR"
|
|
511
|
+
exit 0
|
|
512
|
+
fi
|
|
513
|
+
# Provenance the reader validates before trusting the bake.
|
|
514
|
+
date +%s > "$CTX_DIR/.baked-ts"
|
|
515
|
+
printf '%s\n' "$AUTHOR" > "$CTX_DIR/.baked-author"
|
|
516
|
+
printf '%s\n' "$LOCAL_MODE" > "$CTX_DIR/.baked-local-mode"
|
|
517
|
+
printf '%s\n' "$_PRE_CONFIG" > "$CTX_DIR/.baked-config"
|
|
518
|
+
# Effective endpoint/credential + the context.sh revision that produced
|
|
519
|
+
# this bake: a seed from a different tenant or a different collector
|
|
520
|
+
# schema must never be trusted.
|
|
521
|
+
printf '%s\n' "$_PRE_SCOPE" > "$CTX_DIR/.baked-scope"
|
|
522
|
+
printf '%s\n' "$_PRE_SCHEMA" > "$CTX_DIR/.baked-schema"
|
|
523
|
+
if (cd "$CTX_DIR" && tar -cf "$snap.tmp.$$" .) 2>/dev/null; then
|
|
524
|
+
mv -f "$snap.tmp.$$" "$snap" 2>/dev/null || rm -f "$snap.tmp.$$" 2>/dev/null
|
|
525
|
+
else
|
|
526
|
+
rm -f "$snap.tmp.$$" 2>/dev/null
|
|
527
|
+
fi
|
|
528
|
+
rm -rf "$CTX_DIR"
|
|
529
|
+
) >/dev/null 2>&1 || true
|
|
530
|
+
# Clean up the directory-format snapshot from the previous iteration
|
|
531
|
+
rm -rf "$STATE_DIR/$KEY.ctx" 2>/dev/null || true
|
|
418
532
|
}
|
|
419
533
|
|
|
420
534
|
# --- Handoff lifecycle: recurring close / nudge / expiry --------------------
|
|
@@ -482,6 +596,11 @@ cmd_run() {
|
|
|
482
596
|
log "attendant up (pid $$, stale=${STALE_MIN}m, $MAIN_DIR)"
|
|
483
597
|
while true; do
|
|
484
598
|
[ -d "$MAIN_DIR" ] || { log "project gone — exiting"; break; }
|
|
599
|
+
# Duplicate-daemon convergence: cmd_ensure's check-then-spawn is lockless,
|
|
600
|
+
# so two concurrent cold starts can both launch a run loop. Whoever owns
|
|
601
|
+
# the pidfile keeps running; the other notices within one cycle and exits
|
|
602
|
+
# without touching shared state.
|
|
603
|
+
[ "$(cat "$PIDFILE" 2>/dev/null)" = "$$" ] || { log "superseded (pid $$) — exiting"; exit 0; }
|
|
485
604
|
cmd_sweep
|
|
486
605
|
sleep 60
|
|
487
606
|
done
|
|
@@ -498,8 +617,52 @@ cmd_ensure() {
|
|
|
498
617
|
kill "$(cat "$PIDFILE")" 2>/dev/null || true
|
|
499
618
|
rm -f "$PIDFILE"
|
|
500
619
|
fi
|
|
501
|
-
|
|
502
|
-
|
|
620
|
+
# Atomic spawn gate: the pidfile check above is check-then-spawn, so two
|
|
621
|
+
# concurrent cold starts could both reach here and launch two daemons that
|
|
622
|
+
# overlap for a full cycle (lifecycle jobs, ripcord, auto-push). mkdir is
|
|
623
|
+
# the portable atomic primitive; a crashed spawner's stale lock breaks
|
|
624
|
+
# after 5 minutes. Losing the race means someone else is spawning — done.
|
|
625
|
+
local _spawn_lock="$STATE_DIR/$KEY.spawn-lock"
|
|
626
|
+
if ! mkdir "$_spawn_lock" 2>/dev/null; then
|
|
627
|
+
local _lock_age
|
|
628
|
+
_lock_age=$(( $(date +%s) - $(stat -f %m "$_spawn_lock" 2>/dev/null || stat -c %Y "$_spawn_lock" 2>/dev/null || echo 0) ))
|
|
629
|
+
# Stale reap by atomic rename, never rmdir+mkdir: two reapers doing
|
|
630
|
+
# remove-then-recreate can alternately delete each other's FRESH lock and
|
|
631
|
+
# both spawn. Exactly one mv succeeds; losing the mv or the follow-up
|
|
632
|
+
# mkdir means someone else owns a fresh lock — back off.
|
|
633
|
+
if [ "$_lock_age" -gt 300 ] && mv "$_spawn_lock" "$_spawn_lock.reap.$$" 2>/dev/null; then
|
|
634
|
+
rm -rf "$_spawn_lock.reap.$$" 2>/dev/null
|
|
635
|
+
mkdir "$_spawn_lock" 2>/dev/null || exit 0
|
|
636
|
+
else
|
|
637
|
+
exit 0
|
|
638
|
+
fi
|
|
639
|
+
fi
|
|
640
|
+
# Re-check under the lock — the racing ensure may already have spawned.
|
|
641
|
+
if ! { [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE" 2>/dev/null)" 2>/dev/null; }; then
|
|
642
|
+
nohup bash "$MAIN_DIR/bin/attendant.sh" run >> "$LOGFILE" 2>&1 &
|
|
643
|
+
disown 2>/dev/null || true
|
|
644
|
+
# Hold the lock until the child's ownership is durably visible (it writes
|
|
645
|
+
# its pidfile first thing in cmd_run). Releasing earlier reopens the race:
|
|
646
|
+
# another ensure passes the pidfile re-check before the child registers
|
|
647
|
+
# and spawns a second daemon whose first sweep overlaps this one.
|
|
648
|
+
local _i _spawned="false"
|
|
649
|
+
for _i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25; do
|
|
650
|
+
if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE" 2>/dev/null)" 2>/dev/null; then
|
|
651
|
+
_spawned="true"
|
|
652
|
+
break
|
|
653
|
+
fi
|
|
654
|
+
sleep 0.2
|
|
655
|
+
done
|
|
656
|
+
if [ "$_spawned" != "true" ]; then
|
|
657
|
+
# Child still hasn't registered: KEEP the lock rather than reopen the
|
|
658
|
+
# spawn window — a merely-delayed child may yet come up, and the stale
|
|
659
|
+
# reaper above frees the lock in 5 minutes if it never does. Fail
|
|
660
|
+
# closed: no attendant for a few minutes beats two attendants sweeping.
|
|
661
|
+
log "ensure: spawned child slow to register — holding spawn lock (pid $$)"
|
|
662
|
+
exit 0
|
|
663
|
+
fi
|
|
664
|
+
fi
|
|
665
|
+
rmdir "$_spawn_lock" 2>/dev/null || true
|
|
503
666
|
}
|
|
504
667
|
|
|
505
668
|
cmd_status() {
|
|
@@ -186,7 +186,13 @@ integration branch.
|
|
|
186
186
|
|
|
187
187
|
1. Derive a topic slug from what the user said (kebab-case, 2–4 words)
|
|
188
188
|
2. Run \`bin/agent.sh branch --topic "<topic>"\` — it resolves the configured base and creates a work branch from \`origin/{base}\` in a task worktree (\`dev/{author}/{slug}\`, or \`feature/{slug}\` / \`bugfix/{slug}\` when the topic reads as a feature or fix). Continue all file work from the printed path.
|
|
189
|
-
3.
|
|
189
|
+
3. Give a value-first workspace receipt. Do not lead with Git terminology:
|
|
190
|
+
|
|
191
|
+
\`Your stable project is protected. I’m working in a separate workspace for **{topic}**, where changes stay isolated, reviewable, and reversible.\`
|
|
192
|
+
|
|
193
|
+
Then expose the implementation as secondary detail:
|
|
194
|
+
|
|
195
|
+
\`Workspace: {branch} (worktree).\`
|
|
190
196
|
|
|
191
197
|
**Fallback:** If \`bin/agent.sh branch\` fails, resolve \`{base}\` with
|
|
192
198
|
\`_get_base_branch\`, then use
|
|
@@ -194,6 +200,17 @@ integration branch.
|
|
|
194
200
|
|
|
195
201
|
4. Update graph (fire-and-forget): \`bash bin/graph-op.sh set-topic "$(cat .egregore-session-id 2>/dev/null)" "topic from slug" "dev/author/slug" 2>/dev/null &\`
|
|
196
202
|
|
|
203
|
+
### Starting-work UX contract
|
|
204
|
+
|
|
205
|
+
Before execution begins, make Egregore's useful structure legible without turning every task start into a tutorial:
|
|
206
|
+
|
|
207
|
+
- **Workspace** — use the value-first receipt above for a newly created workspace or topic pivot. If already on the appropriate working branch, do not repeat it.
|
|
208
|
+
- **Context** — when organizational retrieval materially informs the work, keep the required Egregore Retrieval Beat and then add one compact result receipt: \`↳ Context restored: {decision, handoff, or prior work} · {source/date}\`. Never claim context was restored when retrieval found nothing useful.
|
|
209
|
+
- **Assumptions** — surface only consequential assumptions that could change the implementation. Use \`Assumption: {assumption} — based on {evidence}.\` Add a correction path when cheap; do not narrate obvious operational choices.
|
|
210
|
+
- **Transition** — once workspace, context, and assumptions are settled, state the outcome you are starting toward in one short sentence and begin.
|
|
211
|
+
|
|
212
|
+
The intended sequence is: **intent → safe workspace → relevant context → consequential assumptions → execution**. Keep technical identifiers available but subordinate them to user value.
|
|
213
|
+
|
|
197
214
|
### Handoff claiming
|
|
198
215
|
|
|
199
216
|
If \`addressed_to_user\` handoffs exist and the user is picking one up, create the IMPLEMENTS link after branch creation:
|
|
@@ -68,7 +68,20 @@ if [ "$LOCAL_MODE" != "true" ]; then
|
|
|
68
68
|
fi
|
|
69
69
|
|
|
70
70
|
source "$SCRIPT_DIR/bin/lib/git-sync.sh" >/dev/null 2>/dev/null
|
|
71
|
+
# Warm-state parity with the Claude SessionStart path: hand context.sh the
|
|
72
|
+
# pre-baked snapshot and route greeting reads through the warm graph cache.
|
|
73
|
+
# The attendant daemon is deliberately NOT started from the Codex/Pi path —
|
|
74
|
+
# its dead-session detection reads Claude transcripts only, so a live Codex
|
|
75
|
+
# or Pi session with 12 quiet minutes would look dead and could have its
|
|
76
|
+
# unfinished work ripcorded/auto-pushed. Codex sessions still benefit from
|
|
77
|
+
# any snapshot a Claude session's attendant maintains; runtime-neutral
|
|
78
|
+
# liveness tracking is the prerequisite for starting the daemon here.
|
|
79
|
+
if [ -n "${_ATT_KEY:-}" ]; then
|
|
80
|
+
export CTX_SEED_TAR="${ATTENDANT_HOME:-$HOME/.egregore/attendant}/${_ATT_KEY}.ctx.tar"
|
|
81
|
+
fi
|
|
82
|
+
export EGREGORE_GRAPH_CACHE_TTL=600
|
|
71
83
|
source "$SCRIPT_DIR/bin/lib/context.sh" >/dev/null 2>/dev/null
|
|
84
|
+
unset EGREGORE_GRAPH_CACHE_TTL CTX_SEED_TAR
|
|
72
85
|
source "$SCRIPT_DIR/bin/lib/metrics.sh" >/dev/null 2>/dev/null
|
|
73
86
|
trap 'rm -rf "${CTX_DIR:-}"' EXIT
|
|
74
87
|
|
|
@@ -48,7 +48,27 @@ _millis() {
|
|
|
48
48
|
# greeting) pay zero network round-trips when warm. TTL in seconds; unset/0
|
|
49
49
|
# disables. Mutating queries are never cached.
|
|
50
50
|
_CACHE_TTL="${EGREGORE_GRAPH_CACHE_TTL:-0}"
|
|
51
|
-
|
|
51
|
+
# Key the cache by the MAIN checkout so worktree sessions hit the same warm
|
|
52
|
+
# cache the attendant maintains (its replay uses the same derivation).
|
|
53
|
+
# Worktrees have .git as a FILE pointing into the main repo's .git/worktrees/.
|
|
54
|
+
# api_url + slug are part of the key so entries never cross tenants when the
|
|
55
|
+
# same checkout is repointed at a different org/API (cache entries are keyed
|
|
56
|
+
# only by query+params below, so the directory must carry the tenant).
|
|
57
|
+
_CACHE_ROOT="$SCRIPT_DIR"
|
|
58
|
+
if [ -f "$SCRIPT_DIR/.git" ]; then
|
|
59
|
+
_WT_GITDIR=$(sed 's/^gitdir: //' "$SCRIPT_DIR/.git" 2>/dev/null || true)
|
|
60
|
+
if [ -n "$_WT_GITDIR" ]; then
|
|
61
|
+
_CACHE_ROOT=$(cd "$_WT_GITDIR/../../.." 2>/dev/null && pwd || echo "$SCRIPT_DIR")
|
|
62
|
+
fi
|
|
63
|
+
fi
|
|
64
|
+
# The scope is a non-secret fingerprint of the EFFECTIVE endpoint + credential
|
|
65
|
+
# (already resolved above: process env → .env → committed api_url). Committed
|
|
66
|
+
# config alone is not enough — an EGREGORE_API_URL/KEY override or a swapped
|
|
67
|
+
# .env can point this checkout at a different tenant than egregore.json names,
|
|
68
|
+
# and the entry key below carries only query+params. cksum output is a CRC,
|
|
69
|
+
# not a reversible or usable credential.
|
|
70
|
+
_CACHE_SCOPE=$(echo -n "${API_URL:-}|${API_KEY:-}" | cksum | cut -d' ' -f1)
|
|
71
|
+
_CACHE_DIR="$HOME/.egregore/graph-cache/$(echo -n "${_CACHE_ROOT}|${_CACHE_SCOPE}" | cksum | cut -d' ' -f1)"
|
|
52
72
|
|
|
53
73
|
_cache_key() {
|
|
54
74
|
echo -n "$1|${2:-}" | cksum | cut -d' ' -f1
|