agent-memory-cli 0.1.2__tar.gz → 0.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/CHANGELOG.md +17 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/PKG-INFO +7 -6
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/README.md +6 -5
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/__init__.py +1 -1
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/cli.py +3 -3
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/doctor.py +9 -7
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/startup.py +47 -4
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/sync.py +258 -63
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/test_doctor.py +19 -15
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/test_startup.py +100 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/test_sync.py +359 -13
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/.gitignore +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/LICENSE +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/Makefile +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/pyproject.toml +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/scripts/check_installed_wheel.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/scripts/release_notes.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/__main__.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/archive.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/debrief.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/events.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/git_runner.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/home.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/layout.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/org.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/prerequisites.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/publication.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/setup/__init__.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/setup/claude.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/setup/codex.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/setup/common.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/setup/legacy.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/status.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/templates/org-memory/decisions.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/templates/org-memory/recent.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/templates/org-memory/rules.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/templates/project-memory/decision_log.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/templates/project-memory/known_debt.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/templates/project-memory/open_threads.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/templates/project-memory/project_facts.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/templates/workflow/SKILL.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/src/agent_memory/workflow.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/bad_layout/expected.json +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/bad_layout/org-memory/debriefs/.hidden/2026/08/20260825-alice-abc12345.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/bad_layout/org-memory/debriefs/demo-project/2026/08/20260825-alice-1f3a9c2b.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/bad_layout/org-memory/debriefs/demo-project/2026/08/20260825-alice-ABCD.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/bad_layout/org-memory/debriefs/demo-project/2026/08/20260825_alice_abc12345.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/bad_layout/org-memory/debriefs/demo-project/2026/09/20260825-alice-def12345.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/bad_layout/org-memory/debriefs/demo-project/20260825-alice-abc12345.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/empty_store/expected.json +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/empty_store/org-memory/debriefs/.gitkeep +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/missing_debriefs_dir/expected.json +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/missing_debriefs_dir/org-memory/recent.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/staging_artifact/expected.json +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/staging_artifact/org-memory/debriefs/demo-project/2026/08/.stage.20260825-alice-77xx88yy.md.a1b2c3 +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/staging_artifact/org-memory/debriefs/demo-project/2026/08/20260825-alice-1f3a9c2b.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/valid_store/expected.json +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/valid_store/org-memory/debriefs/.gitkeep +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/valid_store/org-memory/debriefs/Demo_Project/2026/08/20260825-bob-ops-9f00aa11.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/valid_store/org-memory/debriefs/demo-project/2026/08/20260825-alice-1f3a9c2b.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/valid_store/org-memory/debriefs/demo.project/2026/07/20260701-Alice_2.dev-00aa11bb.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conformance/org_memory/cases/valid_store/org-memory/debriefs/other-project/2026/12/20261203-bob-9e0d44aa.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/conftest.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/fixtures/setup/claude_settings_fleet.json +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/fixtures/setup/codex_hooks_kernel.json +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/golden/canonical_memory_gitignore.txt +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/golden/debrief_record.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/golden/doctor_memory_0.4.5.json +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/golden/doctor_memory_0.4.5.txt +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/golden/event_record.md +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/test_archive.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/test_cli.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/test_debrief.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/test_events.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/test_home.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/test_layout.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/test_org.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/test_prerequisites.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/test_setup.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/test_status.py +0 -0
- {agent_memory_cli-0.1.2 → agent_memory_cli-0.1.4}/tests/test_workflow.py +0 -0
|
@@ -4,6 +4,23 @@ All notable changes to agent-memory are documented in this file. The format
|
|
|
4
4
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the
|
|
5
5
|
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## 0.1.4 - 2026-10-26
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- `pull` and `push` sync one at a time per home and never abort a rebase they did not start ([sync](docs/commands.md#sync)).
|
|
12
|
+
- `pull` with only unpushed commits no longer also reports "already synced" ([sync](docs/commands.md#sync)).
|
|
13
|
+
- `startup --pull` no longer publishes a debrief or event another agent wrote ([startup](docs/commands.md#startup)).
|
|
14
|
+
- `pull` and `push` update past untracked files the incoming commits do not touch, so SessionStart keeps pulling ([sync](docs/commands.md#sync)).
|
|
15
|
+
- `doctor` on Python 3.14 reports a debrief record it cannot inspect as unreadable, not as an irregular file ([doctor](docs/commands.md#doctor)).
|
|
16
|
+
- `enable` starts a new memory repository on `main` even where git's `init.defaultBranch` is `master` ([sync](docs/commands.md#sync)).
|
|
17
|
+
|
|
18
|
+
## 0.1.3 - 2026-09-29
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- `pull`, `push`, and SessionStart sync can resolve homes with local memory changes behind upstream ([sync](docs/commands.md#sync)).
|
|
23
|
+
|
|
7
24
|
## 0.1.2 - 2026-09-17
|
|
8
25
|
|
|
9
26
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agent-memory-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Cross-session memory for coding agents: plain files, git-native, no server
|
|
5
5
|
Project-URL: Homepage, https://github.com/kiloloop/agent-memory
|
|
6
6
|
Project-URL: Source, https://github.com/kiloloop/agent-memory
|
|
@@ -105,7 +105,7 @@ Python 3.10+ is required; sync needs git 2.25+, hooks use Bash, and
|
|
|
105
105
|
agent-memory enable --remote git@github.com:YOUR_ORG/agent-memory-store.git
|
|
106
106
|
agent-memory push --agent codex
|
|
107
107
|
# After another machine pushes, with a clean local tree:
|
|
108
|
-
agent-memory pull
|
|
108
|
+
agent-memory pull --agent claude
|
|
109
109
|
```
|
|
110
110
|
|
|
111
111
|
Elsewhere, `agent-memory clone <remote-url> --home <local-home>`, then repeat
|
|
@@ -141,13 +141,14 @@ points into a home's `projects/` tree. Project selection uses `--project` or a
|
|
|
141
141
|
matching binding/marker. OACP is not required.
|
|
142
142
|
|
|
143
143
|
Sync makes the home a git repository with an allowlist and `.oacp-memory-repo`
|
|
144
|
-
marker. `push`
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
marker. `push` and `pull --agent NAME` commit selected paths, rebase when behind,
|
|
145
|
+
and deliver. A plain `pull` updates a clean tree and reports unpushed commits.
|
|
146
|
+
Conflicts abort the rebase and retain local commits. Keys and setup receipts
|
|
147
|
+
stay local. Network and rebase operations time out after 30 seconds.
|
|
147
148
|
|
|
148
149
|
`setup` installs a SessionStart hook and memory workflow. `startup` lists
|
|
149
150
|
metadata for the four project files, then the three curated org files; it
|
|
150
|
-
injects no content. `--pull`
|
|
151
|
+
injects no content. `--pull` commits and syncs first under the runtime identity, warning on failure. The workflow
|
|
151
152
|
tells the agent to run `recall` for an 8,000-character bounded read. Raise
|
|
152
153
|
`--max-chars` or read remaining files directly when cut. `capture` records
|
|
153
154
|
decisions during work. At the end, update threads and debt, optionally publish
|
|
@@ -75,7 +75,7 @@ Python 3.10+ is required; sync needs git 2.25+, hooks use Bash, and
|
|
|
75
75
|
agent-memory enable --remote git@github.com:YOUR_ORG/agent-memory-store.git
|
|
76
76
|
agent-memory push --agent codex
|
|
77
77
|
# After another machine pushes, with a clean local tree:
|
|
78
|
-
agent-memory pull
|
|
78
|
+
agent-memory pull --agent claude
|
|
79
79
|
```
|
|
80
80
|
|
|
81
81
|
Elsewhere, `agent-memory clone <remote-url> --home <local-home>`, then repeat
|
|
@@ -111,13 +111,14 @@ points into a home's `projects/` tree. Project selection uses `--project` or a
|
|
|
111
111
|
matching binding/marker. OACP is not required.
|
|
112
112
|
|
|
113
113
|
Sync makes the home a git repository with an allowlist and `.oacp-memory-repo`
|
|
114
|
-
marker. `push`
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
marker. `push` and `pull --agent NAME` commit selected paths, rebase when behind,
|
|
115
|
+
and deliver. A plain `pull` updates a clean tree and reports unpushed commits.
|
|
116
|
+
Conflicts abort the rebase and retain local commits. Keys and setup receipts
|
|
117
|
+
stay local. Network and rebase operations time out after 30 seconds.
|
|
117
118
|
|
|
118
119
|
`setup` installs a SessionStart hook and memory workflow. `startup` lists
|
|
119
120
|
metadata for the four project files, then the three curated org files; it
|
|
120
|
-
injects no content. `--pull`
|
|
121
|
+
injects no content. `--pull` commits and syncs first under the runtime identity, warning on failure. The workflow
|
|
121
122
|
tells the agent to run `recall` for an 8,000-character bounded read. Raise
|
|
122
123
|
`--max-chars` or read remaining files directly when cut. `capture` records
|
|
123
124
|
decisions during work. At the end, update threads and debt, optionally publish
|
|
@@ -92,7 +92,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
92
92
|
clone_ = add("clone", _clone, "Clone a memory repository into the home.")
|
|
93
93
|
clone_.add_argument("url", help="git remote URL to clone")
|
|
94
94
|
clone_.add_argument("--force", action="store_true", help="move a non-empty home aside before cloning")
|
|
95
|
-
add("pull", _pull, "
|
|
95
|
+
add("pull", _pull, "Update from upstream; with --agent, commit allowlisted changes and deliver them too.", agent)
|
|
96
96
|
add("push", _push, "Commit the allowlisted memory changes and push them when a remote exists.", agent)
|
|
97
97
|
add("disable", _disable, "Remove the sync marker; the repository stays in place.")
|
|
98
98
|
for name, handler, help_text, positional, positional_help in (
|
|
@@ -121,7 +121,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
121
121
|
"startup",
|
|
122
122
|
_startup,
|
|
123
123
|
"Print the session-start manifest: the memory files to read, in order, with their readability, size and "
|
|
124
|
-
"age, and where the sync stands. Content is never included. With --pull,
|
|
124
|
+
"age, and where the sync stands. Content is never included. With --pull, commit and sync memory under the runtime identity first.",
|
|
125
125
|
)
|
|
126
126
|
startup_.add_argument("--runtime", choices=startup.RUNTIMES, required=True, help="shape the output for this runtime's hook")
|
|
127
127
|
startup_.add_argument("--project", metavar="ID", help="the project tier to list (default: the one the binding or marker names)")
|
|
@@ -297,7 +297,7 @@ def _clone(args: argparse.Namespace) -> int:
|
|
|
297
297
|
|
|
298
298
|
|
|
299
299
|
def _pull(args: argparse.Namespace) -> int:
|
|
300
|
-
return _report(sync.pull(resolve_home(args.home).path))
|
|
300
|
+
return _report(sync.pull(resolve_home(args.home).path, agent=args.agent))
|
|
301
301
|
|
|
302
302
|
|
|
303
303
|
def _push(args: argparse.Namespace) -> int:
|
|
@@ -237,7 +237,7 @@ def check_org_memory(home: Path) -> Category:
|
|
|
237
237
|
for name in sorted(dirnames):
|
|
238
238
|
entry = current / name
|
|
239
239
|
try:
|
|
240
|
-
is_link =
|
|
240
|
+
is_link = stat.S_ISLNK(os.lstat(entry).st_mode)
|
|
241
241
|
except OSError as exc:
|
|
242
242
|
walk_errors.append(f"{_relative(entry)}: {exc.__class__.__name__}")
|
|
243
243
|
continue
|
|
@@ -258,16 +258,18 @@ def check_org_memory(home: Path) -> Category:
|
|
|
258
258
|
staging.append(rel)
|
|
259
259
|
continue
|
|
260
260
|
# The namespace holds regular files reached without following links;
|
|
261
|
-
# a classification failure surfaces, never raises.
|
|
261
|
+
# a classification failure surfaces, never raises. lstat, not the
|
|
262
|
+
# pathlib predicates: since Python 3.14 those answer False on any
|
|
263
|
+
# OSError, so a record that could not be inspected would read as irregular.
|
|
262
264
|
try:
|
|
263
|
-
|
|
264
|
-
irregular.append(f"{rel} (symlink)")
|
|
265
|
-
continue
|
|
266
|
-
regular = file_path.is_file()
|
|
265
|
+
mode = os.lstat(file_path).st_mode
|
|
267
266
|
except OSError as exc:
|
|
268
267
|
walk_errors.append(f"{rel}: {exc.__class__.__name__}")
|
|
269
268
|
continue
|
|
270
|
-
if
|
|
269
|
+
if stat.S_ISLNK(mode):
|
|
270
|
+
irregular.append(f"{rel} (symlink)")
|
|
271
|
+
continue
|
|
272
|
+
if not stat.S_ISREG(mode):
|
|
271
273
|
irregular.append(f"{rel} (not a regular file)")
|
|
272
274
|
continue
|
|
273
275
|
total += 1
|
|
@@ -14,6 +14,12 @@ files, both from the layout table; ``events/``, ``debriefs/`` and
|
|
|
14
14
|
``archive/`` are excluded), and the rendered text is cut at a character
|
|
15
15
|
budget with a notice, so a hook can never flood a session.
|
|
16
16
|
|
|
17
|
+
With ``--pull`` the session's runtime is the publishing identity, and a
|
|
18
|
+
path another agent wrote is not its to publish: a debrief names its agent in
|
|
19
|
+
its filename and an event in its frontmatter, so either one, when it names
|
|
20
|
+
someone else, is held for that agent's own session. Files that carry no
|
|
21
|
+
author (the curated org files, the project tier) publish as before.
|
|
22
|
+
|
|
17
23
|
Output shapes: ``--json`` is the manifest itself, ``schema_version`` first;
|
|
18
24
|
the default is what the runtime's hook expects on stdout: plain text for
|
|
19
25
|
claude, whose session start takes stdout as context, and the hook JSON
|
|
@@ -28,8 +34,9 @@ import stat
|
|
|
28
34
|
from pathlib import Path
|
|
29
35
|
from typing import Any, Dict, List, Optional, Sequence, Tuple
|
|
30
36
|
|
|
31
|
-
from . import layout, sync
|
|
37
|
+
from . import debrief, events, layout, sync
|
|
32
38
|
from .git_runner import GitRunner, run_git
|
|
39
|
+
from .publication import WriterError
|
|
33
40
|
|
|
34
41
|
SCHEMA_VERSION = 1
|
|
35
42
|
RUNTIME_CLAUDE = "claude"
|
|
@@ -46,6 +53,8 @@ RESULT_OK = "ok"
|
|
|
46
53
|
RESULT_DEGRADED = "degraded"
|
|
47
54
|
PULL_NOT_REQUESTED = "not_requested"
|
|
48
55
|
PULL_ERROR = "error"
|
|
56
|
+
#: The most of an event the author check reads; the frontmatter sits at its head.
|
|
57
|
+
_EVENT_HEAD_BYTES = 64 * 1024
|
|
49
58
|
|
|
50
59
|
|
|
51
60
|
def build_manifest(
|
|
@@ -72,7 +81,7 @@ def build_manifest(
|
|
|
72
81
|
warnings: List[str] = list(notes)
|
|
73
82
|
files: List[Dict[str, Any]] = []
|
|
74
83
|
|
|
75
|
-
sync_info, pull_info = _sync(home, pull, runner, warnings)
|
|
84
|
+
sync_info, pull_info = _sync(home, pull, runner, warnings, runtime)
|
|
76
85
|
if project is not None:
|
|
77
86
|
try:
|
|
78
87
|
layout.validate_project_name(project)
|
|
@@ -154,14 +163,14 @@ def _entry(home: Path, path: Path, tier: str, name: str) -> Dict[str, Any]:
|
|
|
154
163
|
|
|
155
164
|
|
|
156
165
|
def _sync(
|
|
157
|
-
home: Path, pull: bool, runner: Optional[GitRunner], warnings: List[str]
|
|
166
|
+
home: Path, pull: bool, runner: Optional[GitRunner], warnings: List[str], runtime: str
|
|
158
167
|
) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
|
159
168
|
marker = sync.is_configured(home)
|
|
160
169
|
info: Dict[str, Any] = {"marker": marker, "last_commit_at_utc": None}
|
|
161
170
|
pull_info: Dict[str, Any] = {"requested": pull, "status": PULL_NOT_REQUESTED, "ok": True, "lines": []}
|
|
162
171
|
if pull:
|
|
163
172
|
try:
|
|
164
|
-
outcome = sync.pull(home, runner=runner)
|
|
173
|
+
outcome = sync.pull(home, agent=runtime, runner=runner, hold=lambda path: _held(home, path, runtime))
|
|
165
174
|
pull_info = {"requested": True, "status": outcome.status, "ok": outcome.ok, "lines": list(outcome.lines)}
|
|
166
175
|
except (sync.SyncError, OSError) as exc:
|
|
167
176
|
pull_info = {"requested": True, "status": PULL_ERROR, "ok": False, "lines": [f"memory pull: {exc}"]}
|
|
@@ -176,6 +185,40 @@ def _sync(
|
|
|
176
185
|
return info, pull_info
|
|
177
186
|
|
|
178
187
|
|
|
188
|
+
def _held(home: Path, relative: str, runtime: str) -> bool:
|
|
189
|
+
"""Whether ``relative`` names an author other than ``runtime``; no author named means not held."""
|
|
190
|
+
author = author_of(home, relative)
|
|
191
|
+
return author is not None and author != runtime
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def author_of(home: Path, relative: str) -> Optional[str]:
|
|
195
|
+
"""The agent a memory path names as its author, or ``None`` when it names none.
|
|
196
|
+
|
|
197
|
+
A debrief, ``org-memory/debriefs/<project>/<YYYY>/<MM>/<YYYYMMDD>-<agent>-<session>.md``,
|
|
198
|
+
names it in the filename: the session id has no dash, so the last dash
|
|
199
|
+
splits the agent from it whatever dashes the agent's own name carries. An
|
|
200
|
+
event, ``org-memory/events/<name>.md``, names it in the frontmatter
|
|
201
|
+
``agent:`` field. Anything else, and anything that does not parse, names
|
|
202
|
+
no one.
|
|
203
|
+
"""
|
|
204
|
+
parts = relative.split("/")
|
|
205
|
+
if parts[:2] == [layout.ORG.pattern, "debriefs"] and len(parts) == 6 and parts[5].endswith(".md"):
|
|
206
|
+
date, _, rest = parts[5][: -len(".md")].partition("-")
|
|
207
|
+
agent, _, session = rest.rpartition("-")
|
|
208
|
+
if len(date) == 8 and date.isdigit() and debrief.SESSION_RE.match(session) and debrief.AGENT_RE.match(agent):
|
|
209
|
+
return agent
|
|
210
|
+
return None
|
|
211
|
+
if parts[:2] == [layout.ORG.pattern, events.EVENTS_DIR] and len(parts) == 3 and parts[2].endswith(".md"):
|
|
212
|
+
try:
|
|
213
|
+
with open(home / relative, "rb") as handle:
|
|
214
|
+
frontmatter, _ = debrief.split_record(handle.read(_EVENT_HEAD_BYTES))
|
|
215
|
+
except (OSError, ValueError, WriterError):
|
|
216
|
+
return None
|
|
217
|
+
agent = frontmatter.get("agent", "")
|
|
218
|
+
return agent if debrief.AGENT_RE.match(agent) else None
|
|
219
|
+
return None
|
|
220
|
+
|
|
221
|
+
|
|
179
222
|
# --- rendering --------------------------------------------------------------
|
|
180
223
|
|
|
181
224
|
|